KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 The 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 <sch_actions.h>
28#include <ee_grid_helper.h>
29#include <eda_item.h>
31#include <sch_shape.h>
32#include <sch_group.h>
33#include <sch_commit.h>
34#include <wx/debug.h>
35#include <view/view_controls.h>
38
39
41 SCH_TOOL_BASE( "eeschema.SymbolMoveTool" ),
42 m_moveInProgress( false )
43{
44}
45
46
48{
50
51 //
52 // Add move actions to the selection tool menu
53 //
55
56 auto canMove =
57 [&]( const SELECTION& sel )
58 {
60 wxCHECK( editor, false );
61
62 if( !editor->IsSymbolEditable() )
63 return false;
64
65 if( editor->IsSymbolAlias() )
66 {
67 for( EDA_ITEM* item : sel )
68 {
69 if( item->Type() != SCH_FIELD_T )
70 return false;
71 }
72 }
73
74 return true;
75 };
76
77 selToolMenu.AddItem( SCH_ACTIONS::move, canMove && SCH_CONDITIONS::IdleSelection, 150 );
79
80 return true;
81}
82
83
85{
86 SCH_TOOL_BASE::Reset( aReason );
87
88 if( aReason == MODEL_RELOAD )
89 m_moveInProgress = false;
90}
91
92
94{
95 if( SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() ) )
96 {
97 wxCHECK( aEvent.SynchronousState(), 0 );
98 aEvent.SynchronousState()->store( STS_RUNNING );
99
100 if( doMoveSelection( aEvent, commit ) )
101 aEvent.SynchronousState()->store( STS_FINISHED );
102 else
103 aEvent.SynchronousState()->store( STS_CANCELLED );
104 }
105 else
106 {
107 SCH_COMMIT localCommit( m_toolMgr );
108
109 if( doMoveSelection( aEvent, &localCommit ) )
110 localCommit.Push( _( "Move" ) );
111 else
112 localCommit.Revert();
113 }
114
115 return 0;
116}
117
118
120{
123
124 m_anchorPos = { 0, 0 };
125
126 // Be sure that there is at least one item that we can move. If there's no selection try
127 // looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection).
128 SCH_SELECTION& selection = m_frame->IsSymbolAlias()
131 bool unselect = selection.IsHover();
132
133 if( !m_frame->IsSymbolEditable() || selection.Empty() )
134 return false;
135
136 if( m_moveInProgress )
137 {
138 // The tool hotkey is interpreted as a click when already moving
140 return true;
141 }
142
143 m_frame->PushTool( aEvent );
144
145 Activate();
146 // Must be done after Activate() so that it gets set into the correct context
147 controls->ShowCursor( true );
148 controls->SetAutoPan( true );
149
150 bool restore_state = false;
151 TOOL_EVENT copy = aEvent;
152 TOOL_EVENT* evt = &copy;
153 VECTOR2I prevPos;
154 VECTOR2I moveOffset;
155
156 if( !selection.Front()->IsNew() )
157 aCommit->Modify( m_frame->GetCurSymbol(), m_frame->GetScreen() );
158
159 m_cursor = controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
160
161 // Main loop: keep receiving events
162 do
163 {
164 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
165 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
166 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
167
168 if( evt->IsAction( &SCH_ACTIONS::move )
169 || evt->IsMotion()
170 || evt->IsDrag( BUT_LEFT )
172 {
173 GRID_HELPER_GRIDS snapLayer = grid.GetSelectionGrid( selection );
174
175 if( !m_moveInProgress ) // Prepare to start moving/dragging
176 {
177 SCH_ITEM* lib_item = static_cast<SCH_ITEM*>( selection.Front() );
178
179 // Pick up any synchronized pins
180 //
181 // Careful when pasting. The pasted pin will be at the same location as it
182 // was copied from, leading us to believe it's a synchronized pin. It's not.
183 if( m_frame->SynchronizePins() && !( lib_item->GetEditFlags() & IS_PASTED ) )
184 {
185 std::set<SCH_PIN*> sync_pins;
186
187 for( EDA_ITEM* sel_item : selection )
188 {
189 lib_item = static_cast<SCH_ITEM*>( sel_item );
190
191 if( lib_item->Type() == SCH_PIN_T )
192 {
193 SCH_PIN* cur_pin = static_cast<SCH_PIN*>( lib_item );
194 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
195 std::vector<bool> got_unit( symbol->GetUnitCount() + 1 );
196
197 got_unit[cur_pin->GetUnit()] = true;
198
199 for( SCH_PIN* pin : symbol->GetPins() )
200 {
201 if( !got_unit[pin->GetUnit()]
202 && pin->GetPosition() == cur_pin->GetPosition()
203 && pin->GetOrientation() == cur_pin->GetOrientation()
204 && pin->GetBodyStyle() == cur_pin->GetBodyStyle()
205 && pin->GetType() == cur_pin->GetType()
206 && pin->GetName() == cur_pin->GetName() )
207 {
208 if( sync_pins.insert( pin ).second )
209 got_unit[pin->GetUnit()] = true;
210 }
211 }
212 }
213 }
214
215 for( SCH_PIN* pin : sync_pins )
216 m_selectionTool->AddItemToSel( pin, true /*quiet mode*/ );
217 }
218
219 // Apply any initial offset in case we're coming from a previous command.
220 //
221 for( EDA_ITEM* item : selection )
222 moveItem( item, moveOffset );
223
224 // Set up the starting position and move/drag offset
225 //
226 m_cursor = controls->GetCursorPosition( !evt->DisableGridSnapping() );
227
228 if( lib_item->IsNew() )
229 {
230 m_anchorPos = selection.GetReferencePoint();
232
233 // Drag items to the current cursor position
234 for( EDA_ITEM* item : selection )
235 {
236 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
237
238 moveItem( schItem, delta );
239 updateItem( schItem, false );
240
241 // While SCH_COMMIT::Push() will add any new items to the entered group,
242 // we need to do it earlier so that the previews while moving are correct.
243 if( SCH_GROUP* enteredGroup = m_selectionTool->GetEnteredGroup() )
244 {
245 if( schItem->IsGroupableType() && !item->GetParentGroup() )
246 {
247 aCommit->Modify( enteredGroup, m_frame->GetScreen(), RECURSE_MODE::NO_RECURSE );
248 enteredGroup->AddItem( schItem );
249 }
250 }
251 }
252
254 }
255 else if( m_frame->GetMoveWarpsCursor() )
256 {
257 // User wants to warp the mouse
258 m_cursor = grid.BestDragOrigin( m_cursor, snapLayer, selection );
259 selection.SetReferencePoint( m_cursor );
261 }
262 else
263 {
264 m_cursor = controls->GetCursorPosition( !evt->DisableGridSnapping() );
266 }
267
268 controls->SetCursorPosition( m_cursor, false );
269
270 prevPos = m_cursor;
271 controls->SetAutoPan( true );
272 m_moveInProgress = true;
273 }
274
275 //------------------------------------------------------------------------
276 // Follow the mouse
277 //
278 m_cursor = grid.BestSnapAnchor( controls->GetCursorPosition( false ), snapLayer,
279 selection );
280 VECTOR2I delta( m_cursor - prevPos );
282
283 moveOffset += delta;
284 prevPos = m_cursor;
285
286 for( EDA_ITEM* item : selection )
287 {
288 moveItem( item, delta );
289 updateItem( item, false );
290 }
291
293 }
294 //------------------------------------------------------------------------
295 // Handle cancel
296 //
297 else if( evt->IsCancelInteractive() || evt->IsActivate() )
298 {
299 if( m_moveInProgress )
300 {
301 evt->SetPassEvent( false );
302 restore_state = true;
303 }
304
305 break;
306 }
307 //------------------------------------------------------------------------
308 // Handle TOOL_ACTION special cases
309 //
310 else if( evt->Action() == TA_UNDO_REDO_PRE )
311 {
312 unselect = true;
313 break;
314 }
315 else if( evt->IsAction( &ACTIONS::doDelete ) )
316 {
317 // Exit on a remove operation; there is no further processing for removed items.
318 break;
319 }
320 else if( evt->IsAction( &ACTIONS::duplicate ) )
321 {
322 wxBell();
323 }
324 //------------------------------------------------------------------------
325 // Handle context menu
326 //
327 else if( evt->IsClick( BUT_RIGHT ) )
328 {
329 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
330 }
331 //------------------------------------------------------------------------
332 // Handle drop
333 //
334 else if( evt->IsMouseUp( BUT_LEFT )
335 || evt->IsClick( BUT_LEFT )
336 || evt->IsDblClick( BUT_LEFT ) )
337 {
338 if( selection.GetSize() == 1 && selection.Front()->Type() == SCH_PIN_T )
339 {
341
342 try
343 {
344 SCH_PIN* curr_pin = static_cast<SCH_PIN*>( selection.Front() );
345
346 if( pinTool->PlacePin( aCommit, curr_pin ) )
347 {
348 // PlacePin() clears the current selection, which we don't want. Not only
349 // is it a poor user experience, but it also prevents us from doing the
350 // proper cleanup at the end of this routine (ie: clearing the edit flags).
351 m_selectionTool->AddItemToSel( curr_pin, true /*quiet mode*/ );
352 }
353 else
354 {
355 restore_state = true;
356 }
357 }
358 catch( const boost::bad_pointer& e )
359 {
360 restore_state = true;
361 wxFAIL_MSG( wxString::Format( wxT( "Boost pointer exception occurred: %s" ),
362 e.what() ) );
363 }
364 }
365
366 break; // Finish
367 }
368 else
369 {
370 evt->SetPassEvent();
371 }
372
373 } while( ( evt = Wait() ) ); // Assignment intentional; not equality test
374
375 controls->ForceCursorPosition( false );
376 controls->ShowCursor( false );
377 controls->SetAutoPan( false );
378
379 m_anchorPos = { 0, 0 };
380
381 for( EDA_ITEM* item : selection )
382 item->ClearEditFlags();
383
384 if( unselect )
386
387 m_moveInProgress = false;
388 m_frame->PopTool( aEvent );
389
390 return !restore_state;
391}
392
393
395{
398 SCH_COMMIT commit( m_toolMgr );
399
400 for( EDA_ITEM* item : selection )
401 {
402 VECTOR2I newPos = grid.AlignGrid( item->GetPosition(), grid.GetItemGrid( item ) );
403 VECTOR2I delta = newPos - item->GetPosition();
404
405 if( delta != VECTOR2I( 0, 0 ) )
406 {
407 commit.Modify( item, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
408 static_cast<SCH_ITEM*>( item )->Move( delta );
409 updateItem( item, true );
410 };
411
412 if( SCH_PIN* pin = dynamic_cast<SCH_PIN*>( item ) )
413 {
414 int length = pin->GetLength();
415 int pinGrid;
416
417 if( pin->GetOrientation() == PIN_ORIENTATION::PIN_LEFT
418 || pin->GetOrientation() == PIN_ORIENTATION::PIN_RIGHT )
419 {
420 pinGrid = KiROUND( grid.GetGridSize( grid.GetItemGrid( item ) ).x );
421 }
422 else
423 {
424 pinGrid = KiROUND( grid.GetGridSize( grid.GetItemGrid( item ) ).y );
425 }
426
427 int newLength = KiROUND( (double) length / pinGrid ) * pinGrid;
428
429 if( newLength > 0 )
430 pin->SetLength( newLength );
431 }
432 }
433
435
436 commit.Push( _( "Align Items to Grid" ) );
437 return 0;
438}
439
440
442{
443 static_cast<SCH_ITEM*>( aItem )->Move( aDelta );
444 aItem->SetFlags( IS_MOVING );
445}
446
447
449{
452}
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
static TOOL_ACTION duplicate
Definition: actions.h:84
static TOOL_ACTION doDelete
Definition: actions.h:85
static TOOL_ACTION cursorClick
Definition: actions.h:177
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: actions.h:221
static TOOL_ACTION refreshPreview
Definition: actions.h:156
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition: commit.h:107
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:98
EDA_ITEM_FLAGS GetEditFlags() const
Definition: eda_item.h:148
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:142
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:110
bool IsNew() const
Definition: eda_item.h:124
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:352
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.
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.
Define a library symbol object.
Definition: lib_symbol.h:85
std::vector< SCH_PIN * > GetPins(int aUnit, int aBodyStyle) const
Return a list of pin object pointers from the draw item list.
Definition: lib_symbol.cpp:799
int GetUnitCount() const override
static TOOL_ACTION alignToGrid
Definition: sch_actions.h:116
static TOOL_ACTION move
Definition: sch_actions.h:117
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
Definition: sch_commit.cpp:489
virtual void Revert() override
Revert the commit by restoring the modified items state.
Definition: sch_commit.cpp:567
A set of SCH_ITEMs (i.e., without duplicates).
Definition: sch_group.h:52
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:168
int GetBodyStyle() const
Definition: sch_item.h:248
int GetUnit() const
Definition: sch_item.h:239
bool IsGroupableType() const
Definition: sch_item.cpp:105
const wxString & GetName() const
Definition: sch_pin.cpp:357
PIN_ORIENTATION GetOrientation() const
Definition: sch_pin.cpp:220
VECTOR2I GetPosition() const override
Definition: sch_pin.cpp:212
ELECTRICAL_PINTYPE GetType() const
Definition: sch_pin.cpp:269
SCH_SELECTION & GetSelection()
SCH_SELECTION & RequestSelection(const std::vector< KICAD_T > &aScanTypes={ SCH_LOCATE_ANY_T }, bool aPromoteCellSelections=false, bool aPromoteGroups=false)
Return either an existing selection (filtered), or the selection at the current cursor position if th...
SCH_GROUP * GetEnteredGroup()
A foundation class for a tool operating on a schematic or symbol.
Definition: sch_tool_base.h:49
void updateItem(EDA_ITEM *aItem, bool aUpdateRTree) const
Similar to getView()->Update(), but handles items that are redrawn by their parents and updating the ...
bool Init() override
Init() is called once upon a registration of the tool.
Definition: sch_tool_base.h:65
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
Definition: sch_tool_base.h:85
SCH_SELECTION_TOOL * m_selectionTool
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:89
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:105
EDA_ITEM * Front() const
Definition: selection.h:177
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:115
bool doMoveSelection(const TOOL_EVENT &aEvent, SCH_COMMIT *aCommit)
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
void moveItem(EDA_ITEM *aItem, const VECTOR2I &aDelta)
Set up handlers for various events.
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.
int AlignElements(const TOOL_EVENT &aEvent)
Align selected elements to the grid.
bool m_moveInProgress
Last cursor position (needed for getModificationPoint() to avoid changes of edit reference point).
bool PlacePin(SCH_COMMIT *aCommit, SCH_PIN *aPin)
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.
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:150
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:44
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:38
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
@ MODEL_RELOAD
Model changes (the sheet for a schematic)
Definition: tool_base.h:80
Generic, UI-independent tool event.
Definition: tool_event.h:168
bool DisableGridSnapping() const
Definition: tool_event.h:368
bool IsCancelInteractive() const
Indicate the event should restart/end an ongoing interactive tool's event loop (eg esc key,...
Definition: tool_event.cpp:221
TOOL_ACTIONS Action() const
Returns more specific information about the type of an event.
Definition: tool_event.h:247
bool IsActivate() const
Definition: tool_event.h:342
COMMIT * Commit() const
Definition: tool_event.h:280
bool IsClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:209
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:312
int Modifier(int aMask=MD_MODIFIER_MASK) const
Return information about key modifiers state (Ctrl, Alt, etc.).
Definition: tool_event.h:363
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:82
bool IsDblClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:215
std::atomic< SYNCRONOUS_TOOL_STATE > * SynchronousState() const
Definition: tool_event.h:277
void SetPassEvent(bool aPass=true)
Definition: tool_event.h:253
bool IsMouseUp(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:322
bool IsMotion() const
Definition: tool_event.h:327
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()
std::unique_ptr< 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.
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, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:150
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:44
#define _(s)
#define IS_PASTED
Modifier on IS_NEW which indicates it came from clipboard.
#define IS_MOVING
Item being moved.
GRID_HELPER_GRIDS
Definition: grid_helper.h:43
Class to handle a set of SCH_ITEMs.
int delta
@ TA_UNDO_REDO_PRE
This event is sent before undo/redo command is performed.
Definition: tool_event.h:106
@ MD_SHIFT
Definition: tool_event.h:143
@ STS_CANCELLED
Definition: tool_event.h:161
@ STS_FINISHED
Definition: tool_event.h:160
@ STS_RUNNING
Definition: tool_event.h:159
@ BUT_LEFT
Definition: tool_event.h:132
@ BUT_RIGHT
Definition: tool_event.h:133
@ SCH_FIELD_T
Definition: typeinfo.h:151
@ SCH_PIN_T
Definition: typeinfo.h:154
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695