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>
31 #include "symbol_editor_pin_tool.h"
32 
33 
35  EE_TOOL_BASE( "eeschema.SymbolMoveTool" ),
36  m_moveInProgress( false ),
37  m_moveOffset( 0, 0 )
38 {
39 }
40 
41 
43 {
45 
46  //
47  // Add move actions to the selection tool menu
48  //
50 
51  auto canMove =
52  [&]( const SELECTION& sel )
53  {
54  SYMBOL_EDIT_FRAME* editor = static_cast<SYMBOL_EDIT_FRAME*>( m_frame );
55  wxCHECK( editor, false );
56 
57  if( !editor->IsSymbolEditable() )
58  return false;
59 
60  if( editor->IsSymbolAlias() )
61  {
62  for( EDA_ITEM* item : sel )
63  {
64  if( item->Type() != LIB_FIELD_T )
65  return false;
66  }
67  }
68 
69  return true;
70  };
71 
72  selToolMenu.AddItem( EE_ACTIONS::move, canMove && EE_CONDITIONS::IdleSelection, 150 );
73 
74  return true;
75 }
76 
77 
79 {
80  EE_TOOL_BASE::Reset( aReason );
81 
82  if( aReason == MODEL_RELOAD )
83  {
84  m_moveInProgress = false;
85  m_moveOffset = { 0, 0 };
86  }
87 }
88 
89 
91 {
92  static KICAD_T fieldsOnly[] = { LIB_FIELD_T, EOT };
93 
95 
96  m_anchorPos = { 0, 0 };
97 
98  // Be sure that there is at least one item that we can move. If there's no selection try
99  // looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection).
100  EE_SELECTION& selection = m_frame->IsSymbolAlias()
101  ? m_selectionTool->RequestSelection( fieldsOnly )
103  bool unselect = selection.IsHover();
104 
105  if( !m_frame->IsSymbolEditable() || selection.Empty() || m_moveInProgress )
106  return 0;
107 
108  std::string tool = aEvent.GetCommandStr().get();
109  m_frame->PushTool( tool );
110  Activate();
111 
112  controls->ShowCursor( true );
113  controls->SetAutoPan( true );
114 
115  bool restore_state = false;
116  bool chain_commands = false;
117  TOOL_EVENT* evt = const_cast<TOOL_EVENT*>( &aEvent );
118  VECTOR2I prevPos;
119 
120  if( !selection.Front()->IsNew() )
122 
123  m_cursor = controls->GetCursorPosition();
124 
125  // Main loop: keep receiving events
126  do
127  {
129 
130  if( evt->IsAction( &EE_ACTIONS::move )
131  || evt->IsMotion()
132  || evt->IsDrag( BUT_LEFT )
135  {
136  if( !m_moveInProgress ) // Prepare to start moving/dragging
137  {
138  LIB_ITEM* lib_item = static_cast<LIB_ITEM*>( selection.Front() );
139 
140  // Pick up any synchronized pins
141  //
142  // Careful when pasting. The pasted pin will be at the same location as it
143  // was copied from, leading us to believe it's a synchronized pin. It's not.
144  if( m_frame->SynchronizePins()
145  && ( lib_item->GetEditFlags() & IS_PASTED ) == 0 )
146  {
147  std::set<LIB_PIN*> sync_pins;
148 
149  for( EDA_ITEM* sel_item : selection )
150  {
151  lib_item = static_cast<LIB_ITEM*>( sel_item );
152 
153  if( lib_item->Type() == LIB_PIN_T )
154  {
155  LIB_PIN* cur_pin = static_cast<LIB_PIN*>( lib_item );
156  LIB_PART* part = m_frame->GetCurPart();
157  std::vector<bool> got_unit( part->GetUnitCount() );
158 
159  got_unit[cur_pin->GetUnit()] = true;
160 
161  for( LIB_PIN* pin = part->GetNextPin(); pin; pin = part->GetNextPin( pin ) )
162  {
163  if( !got_unit[pin->GetUnit()]
164  && pin->GetPosition() == cur_pin->GetPosition()
165  && pin->GetOrientation() == cur_pin->GetOrientation()
166  && pin->GetConvert() == cur_pin->GetConvert()
167  && pin->GetType() == cur_pin->GetType()
168  && pin->GetName() == cur_pin->GetName() )
169  {
170  if( sync_pins.insert( pin ).second )
171  got_unit[pin->GetUnit()] = true;
172  }
173  }
174  }
175  }
176 
177  for( LIB_PIN* pin : sync_pins )
178  m_selectionTool->AddItemToSel( pin, true /*quiet mode*/ );
179  }
180 
181  // Apply any initial offset in case we're coming from a previous command.
182  //
183  for( EDA_ITEM* item : selection )
184  moveItem( item, m_moveOffset );
185 
186  // Set up the starting position and move/drag offset
187  //
188  m_cursor = controls->GetCursorPosition();
189 
190  if( lib_item->IsNew() )
191  {
192  m_anchorPos = selection.GetReferencePoint();
193  VECTOR2I delta = m_cursor - mapCoords( m_anchorPos );
194 
195  // Drag items to the current cursor position
196  for( EDA_ITEM* item : selection )
197  {
198  moveItem( item, delta );
199  updateItem( item, false );
200  }
201 
203  }
204  else if( selection.Size() == 1 && m_frame->GetMoveWarpsCursor() )
205  {
206  wxPoint itemPos = lib_item->GetPosition();
207  m_anchorPos = wxPoint( itemPos.x, -itemPos.y );
208 
209  getViewControls()->WarpCursor( m_anchorPos, true, true );
211  }
212  else
213  {
216  }
217 
218  controls->SetCursorPosition( m_cursor, false );
220 
221  prevPos = m_cursor;
222  controls->SetAutoPan( true );
223  m_moveInProgress = true;
224  }
225 
226  //------------------------------------------------------------------------
227  // Follow the mouse
228  //
229  m_cursor = controls->GetCursorPosition();
230  VECTOR2I delta( m_cursor - prevPos );
232 
233  m_moveOffset += delta;
234  prevPos = m_cursor;
235 
236  for( EDA_ITEM* item : selection )
237  {
238  moveItem( item, delta );
239  updateItem( item, false );
240  }
241 
243  }
244  //------------------------------------------------------------------------
245  // Handle cancel
246  //
247  else if( evt->IsCancelInteractive() || evt->IsActivate() )
248  {
249  if( m_moveInProgress )
250  {
251  evt->SetPassEvent( false );
252  restore_state = true;
253  }
254 
255  break;
256  }
257  //------------------------------------------------------------------------
258  // Handle TOOL_ACTION special cases
259  //
260  else if( evt->Action() == TA_UNDO_REDO_PRE )
261  {
262  unselect = true;
263  break;
264  }
265  else if( evt->Category() == TC_COMMAND )
266  {
267  if( evt->IsAction( &ACTIONS::doDelete ) )
268  {
269  // Exit on a remove operation; there is no further processing for removed items.
270  break;
271  }
272  else if( evt->IsAction( &ACTIONS::duplicate ) )
273  {
274  if( selection.Front()->IsNew() )
275  {
276  // This doesn't really make sense; we'll just end up dragging a stack of
277  // objects so Duplicate() is going to ignore this and we'll just carry on.
278  continue;
279  }
280 
281  // Move original back and exit. The duplicate will run in its own loop.
282  restore_state = true;
283  unselect = false;
284  chain_commands = true;
285  break;
286  }
287  else
288  {
289  evt->SetPassEvent();
290  }
291  }
292  //------------------------------------------------------------------------
293  // Handle context menu
294  //
295  else if( evt->IsClick( BUT_RIGHT ) )
296  {
298  }
299  //------------------------------------------------------------------------
300  // Handle drop
301  //
302  else if( evt->IsMouseUp( BUT_LEFT )
303  || evt->IsClick( BUT_LEFT )
304  || evt->IsDblClick( BUT_LEFT ) )
305  {
306  if( selection.GetSize() == 1 && selection.Front()->Type() == LIB_PIN_T )
307  {
309 
310  try
311  {
312  if( !pinTool->PlacePin( (LIB_PIN*) selection.Front() ) )
313  restore_state = true;
314  }
315  catch( const boost::bad_pointer& e )
316  {
317  restore_state = true;
318  wxLogError( "Boost pointer exception occurred: \"%s\"", e.what() );
319  }
320  }
321 
322  break; // Finish
323  }
324  else
325  {
326  evt->SetPassEvent();
327  }
328 
329  } while( ( evt = Wait() ) ); // Assignment intentional; not equality test
330 
331  controls->ForceCursorPosition( false );
332  controls->ShowCursor( false );
333  controls->SetAutoPan( false );
334 
335  if( !chain_commands )
336  m_moveOffset = { 0, 0 };
337 
338  m_anchorPos = { 0, 0 };
339 
340  for( auto item : selection )
341  item->ClearEditFlags();
342 
343  if( restore_state )
344  {
346 
347  if( unselect )
349  else
351  }
352  else
353  {
354  if( unselect )
356 
357  m_frame->OnModify();
358  }
359 
360  m_moveInProgress = false;
361  m_frame->PopTool( tool );
362  return 0;
363 }
364 
365 
367 {
368  static_cast<LIB_ITEM*>( aItem )->Offset( mapCoords( aDelta ) );
369  aItem->SetFlags( IS_MOVED );
370 }
371 
372 
374 {
377 }
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:85
static const TOOL_EVENT SelectedEvent
Definition: actions.h:209
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:72
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:257
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:81
static TOOL_ACTION doDelete
Definition: actions.h:75
int GetOrientation() const
Definition: lib_pin.h:125
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:251
bool IsClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:181
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:46
static TOOL_ACTION symbolMoveActivate
Definition: ee_actions.h:109
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:215
virtual void PushTool(const std::string &actionName)
NB: the definition of "tool" is different at the user level.
TOOL_MENU & GetToolMenu()
bool IsMotion() const
Definition: tool_event.h:316
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:141
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:248
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:301
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).
void SetCurrentCursor(KICURSOR cursor)
Set the current cursor shape for this panel.
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:214
bool IsCancelInteractive() const
Indicate the event should restart/end an ongoing interactive tool's event loop (eg esc key,...
Definition: tool_event.cpp:193
The base class for drawable items used by schematic library components.
Definition: lib_item.h:62
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:70
bool IsNew() const
Definition: eda_item.h:168
bool Init() override
Init() is called once upon a registration of the tool.
Definition: ee_tool_base.h:65
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:262
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:306
void SetFlags(STATUS_FLAGS aMask)
Definition: eda_item.h:202
int GetUnitCount() const override
For items with units, return the number of units.
bool IsSymbolAlias() const
Restore the empty editor screen, without any part or library selected.
void saveCopyInUndoList(EDA_ITEM *aItem, UNDO_REDO aType, bool aAppend=false)
Definition: ee_tool_base.h:133
STATUS_FLAGS GetEditFlags() const
Definition: eda_item.h:207
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:156
bool IsDblClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:187
virtual void PopTool(const std::string &actionName)
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:173
Define a library symbol object.
Definition: lib_symbol.h:93
An interface for classes handling user events controlling the view behavior such as zooming,...
EE_SELECTION_TOOL * m_selectionTool
Definition: ee_tool_base.h:176
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:217
static TOOL_ACTION clearSelection
Clears the current selection.
Definition: ee_actions.h:54
bool IsMouseUp(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:311
static VECTOR2D mapCoords(const wxPoint &aCoord)
#define IS_MOVED
Item being moved.
Definition: eda_item.h:105
int GetConvert() const
Definition: lib_item.h:265
LIB_PART * GetCurPart()
Return the current part being edited or NULL if none selected.
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:122
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:102
wxPoint GetPosition() const override
Definition: lib_pin.h:258
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:101
LIB_PIN * GetNextPin(LIB_PIN *aItem=NULL)
Return the next pin object from the draw list.
Definition: lib_symbol.h:366
#define IS_PASTED
Modifier on IS_NEW which indicates it came from clipboard.
Definition: eda_item.h:119
OPT< std::string > GetCommandStr() const
Definition: tool_event.h:471
ELECTRICAL_PINTYPE GetType() const
Definition: lib_pin.h:134
bool IsActivate() const
Definition: tool_event.h:331
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:149
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
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:140
void Activate()
Run the tool.
static TOOL_ACTION move
Definition: ee_actions.h:111
A foundation class for a tool operating on a schematic or symbol.
Definition: ee_tool_base.h:48
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.
Definition: tool_manager.h:267
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:74
static TOOL_ACTION refreshPreview
Definition: actions.h:109
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
EDA_ITEM * Front() const
Definition: selection.h:203
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:162
The symbol library editor main window.