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 for( LIB_PIN* pin = symbol->GetNextPin(); pin;
168 pin = symbol->GetNextPin( pin ) )
169 {
170 if( !got_unit[pin->GetUnit()]
171 && pin->GetPosition() == cur_pin->GetPosition()
172 && pin->GetOrientation() == cur_pin->GetOrientation()
173 && pin->GetConvert() == cur_pin->GetConvert()
174 && pin->GetType() == cur_pin->GetType()
175 && pin->GetName() == cur_pin->GetName() )
176 {
177 if( sync_pins.insert( pin ).second )
178 got_unit[pin->GetUnit()] = true;
179 }
180 }
181 }
182 }
183
184 for( LIB_PIN* pin : sync_pins )
185 m_selectionTool->AddItemToSel( pin, true /*quiet mode*/ );
186 }
187
188 // Apply any initial offset in case we're coming from a previous command.
189 //
190 for( EDA_ITEM* item : selection )
191 moveItem( item, m_moveOffset );
192
193 // Set up the starting position and move/drag offset
194 //
195 m_cursor = controls->GetCursorPosition( !evt->DisableGridSnapping() );
196
197 if( lib_item->IsNew() )
198 {
199 m_anchorPos = selection.GetReferencePoint();
201
202 // Drag items to the current cursor position
203 for( EDA_ITEM* item : selection )
204 {
205 moveItem( item, delta );
206 updateItem( item, false );
207 }
208
210 }
211 else if( m_frame->GetMoveWarpsCursor() )
212 {
213 VECTOR2I itemPos = selection.GetTopLeftItem()->GetPosition();
214 m_anchorPos = VECTOR2I( itemPos.x, -itemPos.y );
215
218 }
219 else
220 {
221 m_cursor = controls->GetCursorPosition( !evt->DisableGridSnapping() );
223 }
224
225 controls->SetCursorPosition( m_cursor, false );
227
228 prevPos = m_cursor;
229 controls->SetAutoPan( true );
230 m_moveInProgress = true;
231 }
232
233 //------------------------------------------------------------------------
234 // Follow the mouse
235 //
236 m_cursor = controls->GetCursorPosition( !evt->DisableGridSnapping() );
237 VECTOR2I delta( m_cursor - prevPos );
239
241 prevPos = m_cursor;
242
243 for( EDA_ITEM* item : selection )
244 {
245 moveItem( item, delta );
246 updateItem( item, false );
247 }
248
250 }
251 //------------------------------------------------------------------------
252 // Handle cancel
253 //
254 else if( evt->IsCancelInteractive() || evt->IsActivate() )
255 {
256 if( m_moveInProgress )
257 {
258 evt->SetPassEvent( false );
259 restore_state = true;
260 }
261
262 break;
263 }
264 //------------------------------------------------------------------------
265 // Handle TOOL_ACTION special cases
266 //
267 else if( evt->Action() == TA_UNDO_REDO_PRE )
268 {
269 unselect = true;
270 break;
271 }
272 else if( evt->Category() == TC_COMMAND )
273 {
274 if( evt->IsAction( &ACTIONS::doDelete ) )
275 {
276 // Exit on a remove operation; there is no further processing for removed items.
277 break;
278 }
279 else if( evt->IsAction( &ACTIONS::duplicate ) )
280 {
281 if( selection.Front()->IsNew() )
282 {
283 // This doesn't really make sense; we'll just end up dragging a stack of
284 // objects so Duplicate() is going to ignore this and we'll just carry on.
285 continue;
286 }
287
288 // Move original back and exit. The duplicate will run in its own loop.
289 restore_state = true;
290 unselect = false;
291 chain_commands = true;
292 break;
293 }
294 else
295 {
296 evt->SetPassEvent();
297 }
298 }
299 //------------------------------------------------------------------------
300 // Handle context menu
301 //
302 else if( evt->IsClick( BUT_RIGHT ) )
303 {
305 }
306 //------------------------------------------------------------------------
307 // Handle drop
308 //
309 else if( evt->IsMouseUp( BUT_LEFT )
310 || evt->IsClick( BUT_LEFT )
311 || evt->IsDblClick( BUT_LEFT ) )
312 {
313 if( selection.GetSize() == 1 && selection.Front()->Type() == LIB_PIN_T )
314 {
316
317 try
318 {
319 if( !pinTool->PlacePin( (LIB_PIN*) selection.Front() ) )
320 restore_state = true;
321 }
322 catch( const boost::bad_pointer& e )
323 {
324 restore_state = true;
325 wxLogError( "Boost pointer exception occurred: \"%s\"", e.what() );
326 }
327 }
328
329 break; // Finish
330 }
331 else
332 {
333 evt->SetPassEvent();
334 }
335
336 } while( ( evt = Wait() ) ); // Assignment intentional; not equality test
337
338 controls->ForceCursorPosition( false );
339 controls->ShowCursor( false );
340 controls->SetAutoPan( false );
341
342 if( !chain_commands )
343 m_moveOffset = { 0, 0 };
344
345 m_anchorPos = { 0, 0 };
346
347 for( EDA_ITEM* item : selection )
348 item->ClearEditFlags();
349
350 if( restore_state )
351 {
353
354 if( unselect )
356 else
358 }
359 else
360 {
361 if( unselect )
363
364 m_frame->OnModify();
365 }
366
367 m_moveInProgress = false;
368 m_frame->PopTool( aEvent );
369 return 0;
370}
371
372
374{
375 static_cast<LIB_ITEM*>( aItem )->Offset( mapCoords( aDelta ) );
376 aItem->SetFlags( IS_MOVING );
377}
378
379
381{
384}
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:147
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:142
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:123
static TOOL_ACTION clearSelection
Clears the current selection.
Definition: ee_actions.h:56
static TOOL_ACTION symbolMoveActivate
Definition: ee_actions.h:121
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:205
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:210
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:213
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:224
const wxString & GetName() const
Definition: lib_pin.h:112
Define a library symbol object.
Definition: lib_symbol.h:98
LIB_PIN * GetNextPin(LIB_PIN *aItem=nullptr)
Return the next pin object from the draw list.
Definition: lib_symbol.h:412
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:81
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:97
EDA_ITEM * Front() const
Definition: selection.h:200
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:107
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:214
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