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 
112  Activate();
113  // Must be done after Activate() so that it gets set into the correct context
114  controls->ShowCursor( true );
115  controls->SetAutoPan( true );
116 
117  bool restore_state = false;
118  bool chain_commands = false;
119  TOOL_EVENT* evt = const_cast<TOOL_EVENT*>( &aEvent );
120  VECTOR2I prevPos;
121 
122  if( !selection.Front()->IsNew() )
124 
125  m_cursor = controls->GetCursorPosition();
126 
127  // Main loop: keep receiving events
128  do
129  {
131 
132  if( evt->IsAction( &EE_ACTIONS::move )
133  || evt->IsMotion()
134  || evt->IsDrag( BUT_LEFT )
137  {
138  if( !m_moveInProgress ) // Prepare to start moving/dragging
139  {
140  LIB_ITEM* lib_item = static_cast<LIB_ITEM*>( selection.Front() );
141 
142  // Pick up any synchronized pins
143  //
144  // Careful when pasting. The pasted pin will be at the same location as it
145  // was copied from, leading us to believe it's a synchronized pin. It's not.
146  if( m_frame->SynchronizePins()
147  && ( lib_item->GetEditFlags() & IS_PASTED ) == 0 )
148  {
149  std::set<LIB_PIN*> sync_pins;
150 
151  for( EDA_ITEM* sel_item : selection )
152  {
153  lib_item = static_cast<LIB_ITEM*>( sel_item );
154 
155  if( lib_item->Type() == LIB_PIN_T )
156  {
157  LIB_PIN* cur_pin = static_cast<LIB_PIN*>( lib_item );
158  LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
159  std::vector<bool> got_unit( symbol->GetUnitCount() );
160 
161  got_unit[cur_pin->GetUnit()] = true;
162 
163  for( LIB_PIN* pin = symbol->GetNextPin(); pin;
164  pin = symbol->GetNextPin( pin ) )
165  {
166  if( !got_unit[pin->GetUnit()]
167  && pin->GetPosition() == cur_pin->GetPosition()
168  && pin->GetOrientation() == cur_pin->GetOrientation()
169  && pin->GetConvert() == cur_pin->GetConvert()
170  && pin->GetType() == cur_pin->GetType()
171  && pin->GetName() == cur_pin->GetName() )
172  {
173  if( sync_pins.insert( pin ).second )
174  got_unit[pin->GetUnit()] = true;
175  }
176  }
177  }
178  }
179 
180  for( LIB_PIN* pin : sync_pins )
181  m_selectionTool->AddItemToSel( pin, true /*quiet mode*/ );
182  }
183 
184  // Apply any initial offset in case we're coming from a previous command.
185  //
186  for( EDA_ITEM* item : selection )
187  moveItem( item, m_moveOffset );
188 
189  // Set up the starting position and move/drag offset
190  //
191  m_cursor = controls->GetCursorPosition();
192 
193  if( lib_item->IsNew() )
194  {
195  m_anchorPos = selection.GetReferencePoint();
197 
198  // Drag items to the current cursor position
199  for( EDA_ITEM* item : selection )
200  {
201  moveItem( item, delta );
202  updateItem( item, false );
203  }
204 
206  }
207  else if( selection.Size() == 1 && m_frame->GetMoveWarpsCursor() )
208  {
209  wxPoint itemPos = lib_item->GetPosition();
210  m_anchorPos = wxPoint( itemPos.x, -itemPos.y );
211 
212  getViewControls()->WarpCursor( m_anchorPos, true, true );
214  }
215  else
216  {
219  }
220 
221  controls->SetCursorPosition( m_cursor, false );
223 
224  prevPos = m_cursor;
225  controls->SetAutoPan( true );
226  m_moveInProgress = true;
227  }
228 
229  //------------------------------------------------------------------------
230  // Follow the mouse
231  //
232  m_cursor = controls->GetCursorPosition();
233  VECTOR2I delta( m_cursor - prevPos );
235 
236  m_moveOffset += delta;
237  prevPos = m_cursor;
238 
239  for( EDA_ITEM* item : selection )
240  {
241  moveItem( item, delta );
242  updateItem( item, false );
243  }
244 
246  }
247  //------------------------------------------------------------------------
248  // Handle cancel
249  //
250  else if( evt->IsCancelInteractive() || evt->IsActivate() )
251  {
252  if( m_moveInProgress )
253  {
254  evt->SetPassEvent( false );
255  restore_state = true;
256  }
257 
258  break;
259  }
260  //------------------------------------------------------------------------
261  // Handle TOOL_ACTION special cases
262  //
263  else if( evt->Action() == TA_UNDO_REDO_PRE )
264  {
265  unselect = true;
266  break;
267  }
268  else if( evt->Category() == TC_COMMAND )
269  {
270  if( evt->IsAction( &ACTIONS::doDelete ) )
271  {
272  // Exit on a remove operation; there is no further processing for removed items.
273  break;
274  }
275  else if( evt->IsAction( &ACTIONS::duplicate ) )
276  {
277  if( selection.Front()->IsNew() )
278  {
279  // This doesn't really make sense; we'll just end up dragging a stack of
280  // objects so Duplicate() is going to ignore this and we'll just carry on.
281  continue;
282  }
283 
284  // Move original back and exit. The duplicate will run in its own loop.
285  restore_state = true;
286  unselect = false;
287  chain_commands = true;
288  break;
289  }
290  else
291  {
292  evt->SetPassEvent();
293  }
294  }
295  //------------------------------------------------------------------------
296  // Handle context menu
297  //
298  else if( evt->IsClick( BUT_RIGHT ) )
299  {
301  }
302  //------------------------------------------------------------------------
303  // Handle drop
304  //
305  else if( evt->IsMouseUp( BUT_LEFT )
306  || evt->IsClick( BUT_LEFT )
307  || evt->IsDblClick( BUT_LEFT ) )
308  {
309  if( selection.GetSize() == 1 && selection.Front()->Type() == LIB_PIN_T )
310  {
312 
313  try
314  {
315  if( !pinTool->PlacePin( (LIB_PIN*) selection.Front() ) )
316  restore_state = true;
317  }
318  catch( const boost::bad_pointer& e )
319  {
320  restore_state = true;
321  wxLogError( "Boost pointer exception occurred: \"%s\"", e.what() );
322  }
323  }
324 
325  break; // Finish
326  }
327  else
328  {
329  evt->SetPassEvent();
330  }
331 
332  } while( ( evt = Wait() ) ); // Assignment intentional; not equality test
333 
334  controls->ForceCursorPosition( false );
335  controls->ShowCursor( false );
336  controls->SetAutoPan( false );
337 
338  if( !chain_commands )
339  m_moveOffset = { 0, 0 };
340 
341  m_anchorPos = { 0, 0 };
342 
343  for( EDA_ITEM* item : selection )
344  item->ClearEditFlags();
345 
346  if( restore_state )
347  {
349 
350  if( unselect )
352  else
354  }
355  else
356  {
357  if( unselect )
359 
360  m_frame->OnModify();
361  }
362 
363  m_moveInProgress = false;
364  m_frame->PopTool( tool );
365  return 0;
366 }
367 
368 
370 {
371  static_cast<LIB_ITEM*>( aItem )->Offset( mapCoords( aDelta ) );
372  aItem->SetFlags( IS_MOVING );
373 }
374 
375 
377 {
380 }
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:74
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:111
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:300
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:258
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:56
EDA_ITEM_FLAGS GetEditFlags() const
Definition: eda_item.h:158
bool IsMouseUp(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:295
static VECTOR2D mapCoords(const wxPoint &aCoord)
int GetConvert() const
Definition: lib_item.h:261
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:98
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:88
OPT< std::string > GetCommandStr() const
Definition: tool_event.h:460
ELECTRICAL_PINTYPE GetType() const
Definition: lib_pin.h:84
bool IsActivate() const
Definition: tool_event.h:315
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.
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
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:145
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
The symbol library editor main window.