KiCad PCB EDA Suite
symbol_editor_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>
27 #include <ee_actions.h>
28 #include <bitmaps.h>
29 #include <eda_item.h>
30 #include <wx/log.h>
32 #include "symbol_editor_pin_tool.h"
33 
34 
36  EE_TOOL_BASE( "eeschema.SymbolMoveTool" ),
37  m_moveInProgress( false ),
38  m_moveOffset( 0, 0 )
39 {
40 }
41 
42 
44 {
46 
47  //
48  // Add move actions to the selection tool menu
49  //
51 
52  auto canMove =
53  [&]( const SELECTION& sel )
54  {
55  SYMBOL_EDIT_FRAME* editor = static_cast<SYMBOL_EDIT_FRAME*>( m_frame );
56  wxCHECK( editor, false );
57 
58  if( !editor->IsSymbolEditable() )
59  return false;
60 
61  if( editor->IsSymbolAlias() )
62  {
63  for( EDA_ITEM* item : sel )
64  {
65  if( item->Type() != LIB_FIELD_T )
66  return false;
67  }
68  }
69 
70  return true;
71  };
72 
73  selToolMenu.AddItem( EE_ACTIONS::move, canMove && EE_CONDITIONS::IdleSelection, 150 );
74 
75  return true;
76 }
77 
78 
80 {
81  EE_TOOL_BASE::Reset( aReason );
82 
83  if( aReason == MODEL_RELOAD )
84  {
85  m_moveInProgress = false;
86  m_moveOffset = { 0, 0 };
87  }
88 }
89 
90 
92 {
93  static KICAD_T fieldsOnly[] = { LIB_FIELD_T, EOT };
94 
96 
97  m_anchorPos = { 0, 0 };
98 
99  // Be sure that there is at least one item that we can move. If there's no selection try
100  // looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection).
101  EE_SELECTION& selection = m_frame->IsSymbolAlias()
102  ? m_selectionTool->RequestSelection( fieldsOnly )
104  bool unselect = selection.IsHover();
105 
106  if( !m_frame->IsSymbolEditable() || selection.Empty() || m_moveInProgress )
107  return 0;
108 
109  std::string tool = aEvent.GetCommandStr().get();
110  m_frame->PushTool( tool );
111  Activate();
112 
113  controls->ShowCursor( true );
114  controls->SetAutoPan( true );
115 
116  bool restore_state = false;
117  bool chain_commands = false;
118  TOOL_EVENT* evt = const_cast<TOOL_EVENT*>( &aEvent );
119  VECTOR2I prevPos;
120 
121  if( !selection.Front()->IsNew() )
123 
124  m_cursor = controls->GetCursorPosition();
125 
126  // Main loop: keep receiving events
127  do
128  {
130 
131  if( evt->IsAction( &EE_ACTIONS::move )
132  || evt->IsMotion()
133  || evt->IsDrag( BUT_LEFT )
136  {
137  if( !m_moveInProgress ) // Prepare to start moving/dragging
138  {
139  LIB_ITEM* lib_item = static_cast<LIB_ITEM*>( selection.Front() );
140 
141  // Pick up any synchronized pins
142  //
143  // Careful when pasting. The pasted pin will be at the same location as it
144  // was copied from, leading us to believe it's a synchronized pin. It's not.
145  if( m_frame->SynchronizePins()
146  && ( lib_item->GetEditFlags() & IS_PASTED ) == 0 )
147  {
148  std::set<LIB_PIN*> sync_pins;
149 
150  for( EDA_ITEM* sel_item : selection )
151  {
152  lib_item = static_cast<LIB_ITEM*>( sel_item );
153 
154  if( lib_item->Type() == LIB_PIN_T )
155  {
156  LIB_PIN* cur_pin = static_cast<LIB_PIN*>( lib_item );
157  LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
158  std::vector<bool> got_unit( symbol->GetUnitCount() );
159 
160  got_unit[cur_pin->GetUnit()] = true;
161 
162  for( LIB_PIN* pin = symbol->GetNextPin(); pin;
163  pin = symbol->GetNextPin( pin ) )
164  {
165  if( !got_unit[pin->GetUnit()]
166  && pin->GetPosition() == cur_pin->GetPosition()
167  && pin->GetOrientation() == cur_pin->GetOrientation()
168  && pin->GetConvert() == cur_pin->GetConvert()
169  && pin->GetType() == cur_pin->GetType()
170  && pin->GetName() == cur_pin->GetName() )
171  {
172  if( sync_pins.insert( pin ).second )
173  got_unit[pin->GetUnit()] = true;
174  }
175  }
176  }
177  }
178 
179  for( LIB_PIN* pin : sync_pins )
180  m_selectionTool->AddItemToSel( pin, true /*quiet mode*/ );
181  }
182 
183  // Apply any initial offset in case we're coming from a previous command.
184  //
185  for( EDA_ITEM* item : selection )
186  moveItem( item, m_moveOffset );
187 
188  // Set up the starting position and move/drag offset
189  //
190  m_cursor = controls->GetCursorPosition();
191 
192  if( lib_item->IsNew() )
193  {
194  m_anchorPos = selection.GetReferencePoint();
195  VECTOR2I delta = m_cursor - mapCoords( m_anchorPos );
196 
197  // Drag items to the current cursor position
198  for( EDA_ITEM* item : selection )
199  {
200  moveItem( item, delta );
201  updateItem( item, false );
202  }
203 
205  }
206  else if( selection.Size() == 1 && m_frame->GetMoveWarpsCursor() )
207  {
208  wxPoint itemPos = lib_item->GetPosition();
209  m_anchorPos = wxPoint( itemPos.x, -itemPos.y );
210 
211  getViewControls()->WarpCursor( m_anchorPos, true, true );
213  }
214  else
215  {
218  }
219 
220  controls->SetCursorPosition( m_cursor, false );
222 
223  prevPos = m_cursor;
224  controls->SetAutoPan( true );
225  m_moveInProgress = true;
226  }
227 
228  //------------------------------------------------------------------------
229  // Follow the mouse
230  //
231  m_cursor = controls->GetCursorPosition();
232  VECTOR2I delta( m_cursor - prevPos );
234 
235  m_moveOffset += delta;
236  prevPos = m_cursor;
237 
238  for( EDA_ITEM* item : selection )
239  {
240  moveItem( item, delta );
241  updateItem( item, false );
242  }
243 
245  }
246  //------------------------------------------------------------------------
247  // Handle cancel
248  //
249  else if( evt->IsCancelInteractive() || evt->IsActivate() )
250  {
251  if( m_moveInProgress )
252  {
253  evt->SetPassEvent( false );
254  restore_state = true;
255  }
256 
257  break;
258  }
259  //------------------------------------------------------------------------
260  // Handle TOOL_ACTION special cases
261  //
262  else if( evt->Action() == TA_UNDO_REDO_PRE )
263  {
264  unselect = true;
265  break;
266  }
267  else if( evt->Category() == TC_COMMAND )
268  {
269  if( evt->IsAction( &ACTIONS::doDelete ) )
270  {
271  // Exit on a remove operation; there is no further processing for removed items.
272  break;
273  }
274  else if( evt->IsAction( &ACTIONS::duplicate ) )
275  {
276  if( selection.Front()->IsNew() )
277  {
278  // This doesn't really make sense; we'll just end up dragging a stack of
279  // objects so Duplicate() is going to ignore this and we'll just carry on.
280  continue;
281  }
282 
283  // Move original back and exit. The duplicate will run in its own loop.
284  restore_state = true;
285  unselect = false;
286  chain_commands = true;
287  break;
288  }
289  else
290  {
291  evt->SetPassEvent();
292  }
293  }
294  //------------------------------------------------------------------------
295  // Handle context menu
296  //
297  else if( evt->IsClick( BUT_RIGHT ) )
298  {
300  }
301  //------------------------------------------------------------------------
302  // Handle drop
303  //
304  else if( evt->IsMouseUp( BUT_LEFT )
305  || evt->IsClick( BUT_LEFT )
306  || evt->IsDblClick( BUT_LEFT ) )
307  {
308  if( selection.GetSize() == 1 && selection.Front()->Type() == LIB_PIN_T )
309  {
311 
312  try
313  {
314  if( !pinTool->PlacePin( (LIB_PIN*) selection.Front() ) )
315  restore_state = true;
316  }
317  catch( const boost::bad_pointer& e )
318  {
319  restore_state = true;
320  wxLogError( "Boost pointer exception occurred: \"%s\"", e.what() );
321  }
322  }
323 
324  break; // Finish
325  }
326  else
327  {
328  evt->SetPassEvent();
329  }
330 
331  } while( ( evt = Wait() ) ); // Assignment intentional; not equality test
332 
333  controls->ForceCursorPosition( false );
334  controls->ShowCursor( false );
335  controls->SetAutoPan( false );
336 
337  if( !chain_commands )
338  m_moveOffset = { 0, 0 };
339 
340  m_anchorPos = { 0, 0 };
341 
342  for( EDA_ITEM* item : selection )
343  item->ClearEditFlags();
344 
345  if( restore_state )
346  {
348 
349  if( unselect )
351  else
353  }
354  else
355  {
356  if( unselect )
358 
359  m_frame->OnModify();
360  }
361 
362  m_moveInProgress = false;
363  m_frame->PopTool( tool );
364  return 0;
365 }
366 
367 
369 {
370  static_cast<LIB_ITEM*>( aItem )->Offset( mapCoords( aDelta ) );
371  aItem->SetFlags( IS_MOVING );
372 }
373 
374 
376 {
379 }
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag of the current symbol.
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
Definition: ee_tool_base.h:86
static const TOOL_EVENT SelectedEvent
Definition: actions.h:199
TOOL_MENU m_menu
The functions below are not yet implemented - their interface may change.
void setTransitions() override
Flag determining if anything is being dragged right now.
bool IsHover() const
Definition: selection.h:73
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.
Model changes (required full reload)
Definition: tool_base.h:80
static TOOL_ACTION doDelete
Definition: actions.h:72
int GetOrientation() const
Definition: lib_pin.h:75
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
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:46
static TOOL_ACTION symbolMoveActivate
Definition: ee_actions.h:108
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:214
virtual void PushTool(const std::string &actionName)
NB: the definition of "tool" is different at the user level.
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
bool Init() override
Init() is called once upon a registration of the tool.
TOOL_EVENT_CATEGORY Category() const
Returns more specific information about the type of an event.
Definition: tool_event.h:227
virtual void SetCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true, bool aTriggeredByArrows=false, long aArrowCommand=0)=0
Move cursor to the requested position expressed in world coordinates.
virtual wxPoint GetPosition() const
Definition: eda_item.h:252
Define a library symbol object.
Definition: lib_symbol.h:96
static bool IdleSelection(const SELECTION &aSelection)
Test if all selected items are not being edited.
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).
search types array terminator (End Of Types)
Definition: typeinfo.h:81
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 IsCancelInteractive() const
Indicate the event should restart/end an ongoing interactive tool's event loop (eg esc key,...
Definition: tool_event.cpp:211
The base class for drawable items used by schematic library symbols.
Definition: lib_item.h:61
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
bool IsNew() const
Definition: eda_item.h:119
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...
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
virtual void WarpCursor(const VECTOR2D &aPosition, bool aWorldCoordinates=false, bool aWarpView=false)=0
If enabled (.
int GetUnit() const
Definition: lib_item.h:259
LIB_SYMBOL * GetCurSymbol() const
Return the current symbol being edited or NULL if none selected.
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:285
#define IS_PASTED
Modifier on IS_NEW which indicates it came from clipboard.
#define IS_MOVING
Item being moved.
bool IsSymbolAlias() const
Restore the empty editor screen, without any symbol or library selected.
void saveCopyInUndoList(EDA_ITEM *aItem, UNDO_REDO aType, bool aAppend=false)
Definition: ee_tool_base.h:134
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
const wxString & GetName() const
Definition: lib_pin.h:106
bool IsDblClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:205
virtual void PopTool(const std::string &actionName)
int GetUnitCount() const override
For items with units, return the number of units.
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
An interface for classes handling user events controlling the view behavior such as zooming,...
EE_SELECTION_TOOL * m_selectionTool
Definition: ee_tool_base.h:177
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:207
static TOOL_ACTION clearSelection
Clears the current selection.
Definition: ee_actions.h:53
EDA_ITEM_FLAGS GetEditFlags() const
Definition: eda_item.h:158
bool IsMouseUp(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:290
static VECTOR2D mapCoords(const wxPoint &aCoord)
int GetConvert() const
Definition: lib_item.h:262
int Main(const TOOL_EVENT &aEvent)
Run an interactive move of the selected items, or the item under the cursor.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:97
VECTOR2I m_moveOffset
Last cursor position (needed for getModificationPoint() to avoid changes of edit reference point).
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
wxPoint GetPosition() const override
Definition: lib_pin.h:210
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:87
OPT< std::string > GetCommandStr() const
Definition: tool_event.h:455
ELECTRICAL_PINTYPE GetType() const
Definition: lib_pin.h:84
bool IsActivate() const
Definition: tool_event.h:310
bool IsSymbolEditable() const
Test if a symbol is loaded and can be edited.
int AddItemToSel(const TOOL_EVENT &aEvent)
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:100
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:77
bool m_moveInProgress
Used for chaining commands.
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
LIB_PIN * GetNextPin(LIB_PIN *aItem=nullptr)
Return the next pin object from the draw list.
Definition: lib_symbol.h:372
void Activate()
Run the tool.
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
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
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 ShowContextMenu(SELECTION &aSelection)
Helper function to set and immediately show a CONDITIONAL_MENU in concert with the given SELECTION.
Definition: tool_menu.cpp:59
void moveItem(EDA_ITEM *aItem, VECTOR2I aDelta)
Set up handlers for various events.
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
The symbol library editor main window.