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-2022 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 <eda_item.h>
29#include <wx/log.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 {
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{
93
94 m_anchorPos = { 0, 0 };
95
96 // Be sure that there is at least one item that we can move. If there's no selection try
97 // looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection).
98 EE_SELECTION& selection = m_frame->IsSymbolAlias()
101 bool unselect = selection.IsHover();
102
103 if( !m_frame->IsSymbolEditable() || selection.Empty() )
104 return 0;
105
106 if( m_moveInProgress )
107 {
108 // The tool hotkey is interpreted as a click when already moving
110 return 0;
111 }
112
113 m_frame->PushTool( aEvent );
114
115 Activate();
116 // Must be done after Activate() so that it gets set into the correct context
117 controls->ShowCursor( true );
118 controls->SetAutoPan( true );
119
120 bool restore_state = false;
121 bool chain_commands = false;
122 TOOL_EVENT copy = aEvent;
123 TOOL_EVENT* evt = &copy;
124 VECTOR2I prevPos;
125
126 if( !selection.Front()->IsNew() )
128
129 m_cursor = controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
130
131 // Main loop: keep receiving events
132 do
133 {
135
136 if( evt->IsAction( &EE_ACTIONS::move )
137 || evt->IsMotion()
138 || evt->IsDrag( BUT_LEFT )
141 {
142 if( !m_moveInProgress ) // Prepare to start moving/dragging
143 {
144 LIB_ITEM* lib_item = static_cast<LIB_ITEM*>( selection.Front() );
145
146 // Pick up any synchronized pins
147 //
148 // Careful when pasting. The pasted pin will be at the same location as it
149 // was copied from, leading us to believe it's a synchronized pin. It's not.
151 && ( lib_item->GetEditFlags() & IS_PASTED ) == 0 )
152 {
153 std::set<LIB_PIN*> sync_pins;
154
155 for( EDA_ITEM* sel_item : selection )
156 {
157 lib_item = static_cast<LIB_ITEM*>( sel_item );
158
159 if( lib_item->Type() == LIB_PIN_T )
160 {
161 LIB_PIN* cur_pin = static_cast<LIB_PIN*>( lib_item );
162 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
163 std::vector<bool> got_unit( symbol->GetUnitCount() + 1 );
164
165 got_unit[cur_pin->GetUnit()] = true;
166
167 std::vector<LIB_PIN*> pins = symbol->GetAllLibPins();
168
169 for( LIB_PIN* pin : pins )
170 {
171 if( !got_unit[pin->GetUnit()]
172 && pin->GetPosition() == cur_pin->GetPosition()
173 && pin->GetOrientation() == cur_pin->GetOrientation()
174 && pin->GetConvert() == cur_pin->GetConvert()
175 && pin->GetType() == cur_pin->GetType()
176 && pin->GetName() == cur_pin->GetName() )
177 {
178 if( sync_pins.insert( pin ).second )
179 got_unit[pin->GetUnit()] = true;
180 }
181 }
182 }
183 }
184
185 for( LIB_PIN* pin : sync_pins )
186 m_selectionTool->AddItemToSel( pin, true /*quiet mode*/ );
187 }
188
189 // Apply any initial offset in case we're coming from a previous command.
190 //
191 for( EDA_ITEM* item : selection )
192 moveItem( item, m_moveOffset );
193
194 // Set up the starting position and move/drag offset
195 //
196 m_cursor = controls->GetCursorPosition( !evt->DisableGridSnapping() );
197
198 if( lib_item->IsNew() )
199 {
200 m_anchorPos = selection.GetReferencePoint();
202
203 // Drag items to the current cursor position
204 for( EDA_ITEM* item : selection )
205 {
206 moveItem( item, delta );
207 updateItem( item, false );
208 }
209
211 }
212 else if( m_frame->GetMoveWarpsCursor() )
213 {
214 VECTOR2I itemPos = selection.GetTopLeftItem()->GetPosition();
215 m_anchorPos = VECTOR2I( itemPos.x, -itemPos.y );
216
219 }
220 else
221 {
222 m_cursor = controls->GetCursorPosition( !evt->DisableGridSnapping() );
224 }
225
226 controls->SetCursorPosition( m_cursor, false );
228
229 prevPos = m_cursor;
230 controls->SetAutoPan( true );
231 m_moveInProgress = true;
232 }
233
234 //------------------------------------------------------------------------
235 // Follow the mouse
236 //
237 m_cursor = controls->GetCursorPosition( !evt->DisableGridSnapping() );
238 VECTOR2I delta( m_cursor - prevPos );
240
242 prevPos = m_cursor;
243
244 for( EDA_ITEM* item : selection )
245 {
246 moveItem( item, delta );
247 updateItem( item, false );
248 }
249
251 }
252 //------------------------------------------------------------------------
253 // Handle cancel
254 //
255 else if( evt->IsCancelInteractive() || evt->IsActivate() )
256 {
257 if( m_moveInProgress )
258 {
259 evt->SetPassEvent( false );
260 restore_state = true;
261 }
262
263 break;
264 }
265 //------------------------------------------------------------------------
266 // Handle TOOL_ACTION special cases
267 //
268 else if( evt->Action() == TA_UNDO_REDO_PRE )
269 {
270 unselect = true;
271 break;
272 }
273 else if( evt->Category() == TC_COMMAND )
274 {
275 if( evt->IsAction( &ACTIONS::doDelete ) )
276 {
277 // Exit on a remove operation; there is no further processing for removed items.
278 break;
279 }
280 else if( evt->IsAction( &ACTIONS::duplicate ) )
281 {
282 if( selection.Front()->IsNew() )
283 {
284 // This doesn't really make sense; we'll just end up dragging a stack of
285 // objects so Duplicate() is going to ignore this and we'll just carry on.
286 continue;
287 }
288
289 // Move original back and exit. The duplicate will run in its own loop.
290 restore_state = true;
291 unselect = false;
292 chain_commands = true;
293 break;
294 }
295 else
296 {
297 evt->SetPassEvent();
298 }
299 }
300 //------------------------------------------------------------------------
301 // Handle context menu
302 //
303 else if( evt->IsClick( BUT_RIGHT ) )
304 {
306 }
307 //------------------------------------------------------------------------
308 // Handle drop
309 //
310 else if( evt->IsMouseUp( BUT_LEFT )
311 || evt->IsClick( BUT_LEFT )
312 || evt->IsDblClick( BUT_LEFT ) )
313 {
314 if( selection.GetSize() == 1 && selection.Front()->Type() == LIB_PIN_T )
315 {
317
318 try
319 {
320 if( !pinTool->PlacePin( (LIB_PIN*) selection.Front() ) )
321 restore_state = true;
322 }
323 catch( const boost::bad_pointer& e )
324 {
325 restore_state = true;
326 wxLogError( "Boost pointer exception occurred: \"%s\"", e.what() );
327 }
328 }
329
330 break; // Finish
331 }
332 else
333 {
334 evt->SetPassEvent();
335 }
336
337 } while( ( evt = Wait() ) ); // Assignment intentional; not equality test
338
339 controls->ForceCursorPosition( false );
340 controls->ShowCursor( false );
341 controls->SetAutoPan( false );
342
343 if( !chain_commands )
344 m_moveOffset = { 0, 0 };
345
346 m_anchorPos = { 0, 0 };
347
348 for( EDA_ITEM* item : selection )
349 item->ClearEditFlags();
350
351 if( restore_state )
352 {
354
355 if( unselect )
357 else
359 }
360 else
361 {
362 if( unselect )
364
365 m_frame->OnModify();
366 }
367
368 m_moveInProgress = false;
369 m_frame->PopTool( aEvent );
370 return 0;
371}
372
373
375{
376 static_cast<LIB_ITEM*>( aItem )->Offset( mapCoords( aDelta ) );
377 aItem->SetFlags( IS_MOVING );
378}
379
380
382{
385}
VECTOR2D mapCoords(const VECTOR2D &aSource)
Definition: PS_plotter.cpp:568
static TOOL_ACTION duplicate
Definition: actions.h:72
static TOOL_ACTION doDelete
Definition: actions.h:73
static TOOL_ACTION cursorClick
Definition: actions.h:126
static TOOL_ACTION refreshPreview
Definition: actions.h:109
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 SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:85
EDA_ITEM_FLAGS GetEditFlags() const
Definition: eda_item.h:145
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:139
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
bool IsNew() const
Definition: eda_item.h:103
static TOOL_ACTION move
Definition: ee_actions.h:120
static TOOL_ACTION clearSelection
Clears the current selection.
Definition: ee_actions.h:56
static TOOL_ACTION symbolMoveActivate
Definition: ee_actions.h:118
EE_SELECTION & RequestSelection(const std::vector< KICAD_T > &aScanTypes={ SCH_LOCATE_ANY_T })
Return either an existing selection (filtered), or the selection at the current cursor position if th...
EE_SELECTION & GetSelection()
A foundation class for a tool operating on a schematic or symbol.
Definition: ee_tool_base.h:50
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
Definition: ee_tool_base.h:86
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
void saveCopyInUndoList(EDA_ITEM *aItem, UNDO_REDO aType, bool aAppend=false, bool aDirtyConnectivity=true)
Definition: ee_tool_base.h:134
EE_SELECTION_TOOL * m_selectionTool
Definition: ee_tool_base.h:184
bool Init() override
Init() is called once upon a registration of the tool.
Definition: ee_tool_base.h:66
static const TOOL_EVENT SelectedEvent
Definition: actions.h:206
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:213
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:216
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
virtual void WarpMouseCursor(const VECTOR2D &aPosition, bool aWorldCoordinates=false, bool aWarpView=false)=0
If enabled (.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
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 void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
The base class for drawable items used by schematic library symbols.
Definition: lib_item.h:61
int GetUnit() const
Definition: lib_item.h:273
int GetConvert() const
Definition: lib_item.h:276
ELECTRICAL_PINTYPE GetType() const
Definition: lib_pin.h:90
int GetOrientation() const
Definition: lib_pin.h:74
VECTOR2I GetPosition() const override
Definition: lib_pin.h:222
const wxString & GetName() const
Definition: lib_pin.h:112
Define a library symbol object.
Definition: lib_symbol.h:99
std::vector< LIB_PIN * > GetAllLibPins() const
Return a list of pin pointers for all units / converts.
Definition: lib_symbol.cpp:861
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.
static bool IdleSelection(const SELECTION &aSelection)
Test if all selected items are not being edited.
int AddItemToSel(const TOOL_EVENT &aEvent)
bool IsHover() const
Definition: selection.h:83
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:99
EDA_ITEM * Front() const
Definition: selection.h:208
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:109
void moveItem(EDA_ITEM *aItem, VECTOR2I aDelta)
Set up handlers for various events.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
void setTransitions() override
Flag determining if anything is being dragged right now.
VECTOR2I m_moveOffset
Last cursor position (needed for getModificationPoint() to avoid changes of edit reference point).
bool Init() override
Init() is called once upon a registration of the tool.
int Main(const TOOL_EVENT &aEvent)
Run an interactive move of the selected items, or the item under the cursor.
bool m_moveInProgress
Used for chaining commands.
The symbol library editor main window.
bool IsSymbolAlias() const
Return true if aLibId is an alias for the editor screen symbol.
bool IsSymbolEditable() const
Test if a symbol is loaded and can be edited.
LIB_SYMBOL * GetCurSymbol() const
Return the current symbol being edited or NULL if none selected.
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag of the current symbol.
virtual void PopTool(const TOOL_EVENT &aEvent)
Pops a tool from the stack.
bool GetMoveWarpsCursor() const
Indicate that a move operation should warp the mouse pointer to the origin of the move object.
Definition: tools_holder.h:153
virtual void PushTool(const TOOL_EVENT &aEvent)
NB: the definition of "tool" is different at the user level.
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:215
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
@ MODEL_RELOAD
Model changes (required full reload)
Definition: tool_base.h:80
Generic, UI-independent tool event.
Definition: tool_event.h:156
bool DisableGridSnapping() const
Definition: tool_event.h:344
bool IsCancelInteractive() const
Indicate the event should restart/end an ongoing interactive tool's event loop (eg esc key,...
Definition: tool_event.cpp:212
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:233
bool IsActivate() const
Definition: tool_event.h:318
bool IsClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:200
TOOL_EVENT_CATEGORY Category() const
Returns more specific information about the type of an event.
Definition: tool_event.h:230
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:288
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 IsDblClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:206
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:239
bool IsMouseUp(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:298
bool IsMotion() const
Definition: tool_event.h:303
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).
TOOL_MENU & GetToolMenu()
TOOL_MENU m_menu
The functions below are not yet implemented - their interface may change.
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.
void Activate()
Run the tool.
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
void PostEvent(const TOOL_EVENT &aEvent)
Put an event to the event queue to be processed at the end of event processing cycle.
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:142
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:44
void ShowContextMenu(SELECTION &aSelection)
Helper function to set and immediately show a CONDITIONAL_MENU in concert with the given SELECTION.
Definition: tool_menu.cpp:57
#define IS_PASTED
Modifier on IS_NEW which indicates it came from clipboard.
#define IS_MOVING
Item being moved.
constexpr int delta
@ TA_UNDO_REDO_PRE
Definition: tool_event.h:101
@ TC_COMMAND
Definition: tool_event.h:52
@ BUT_LEFT
Definition: tool_event.h:127
@ BUT_RIGHT
Definition: tool_event.h:128
@ LIB_PIN_T
Definition: typeinfo.h:202
@ LIB_FIELD_T
Definition: typeinfo.h:208
VECTOR2< int > VECTOR2I
Definition: vector2d.h:618