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 //
54 CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
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()
129 ? m_selectionTool->RequestSelection( { SCH_FIELD_T } )
130 : m_selectionTool->RequestSelection();
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
139 m_toolMgr->RunAction( ACTIONS::cursorClick );
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 aCommit->Modify( m_frame->GetCurSymbol(), m_frame->GetScreen() );
157
158 m_cursor = controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
159
160 // Main loop: keep receiving events
161 do
162 {
163 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
164 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
165 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
166
167 if( evt->IsAction( &SCH_ACTIONS::move )
168 || evt->IsMotion()
169 || evt->IsDrag( BUT_LEFT )
171 {
172 GRID_HELPER_GRIDS snapLayer = grid.GetSelectionGrid( selection );
173
174 if( !m_moveInProgress ) // Prepare to start moving/dragging
175 {
176 SCH_ITEM* lib_item = static_cast<SCH_ITEM*>( selection.Front() );
177
178 // Pick up any synchronized pins
179 //
180 // Careful when pasting. The pasted pin will be at the same location as it
181 // was copied from, leading us to believe it's a synchronized pin. It's not.
182 if( m_frame->SynchronizePins() && !( lib_item->GetEditFlags() & IS_PASTED ) )
183 {
184 std::set<SCH_PIN*> sync_pins;
185
186 for( EDA_ITEM* sel_item : selection )
187 {
188 lib_item = static_cast<SCH_ITEM*>( sel_item );
189
190 if( lib_item->Type() == SCH_PIN_T )
191 {
192 SCH_PIN* cur_pin = static_cast<SCH_PIN*>( lib_item );
193 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
194 std::vector<bool> got_unit( symbol->GetUnitCount() + 1 );
195
196 got_unit[cur_pin->GetUnit()] = true;
197
198 for( SCH_PIN* pin : symbol->GetPins() )
199 {
200 if( !got_unit[pin->GetUnit()]
201 && pin->GetPosition() == cur_pin->GetPosition()
202 && pin->GetOrientation() == cur_pin->GetOrientation()
203 && pin->GetBodyStyle() == cur_pin->GetBodyStyle()
204 && pin->GetType() == cur_pin->GetType()
205 && pin->GetName() == cur_pin->GetName() )
206 {
207 if( sync_pins.insert( pin ).second )
208 got_unit[pin->GetUnit()] = true;
209 }
210 }
211 }
212 }
213
214 for( SCH_PIN* pin : sync_pins )
215 m_selectionTool->AddItemToSel( pin, true /*quiet mode*/ );
216 }
217
218 // Apply any initial offset in case we're coming from a previous command.
219 //
220 for( EDA_ITEM* item : selection )
221 moveItem( item, moveOffset );
222
223 // Set up the starting position and move/drag offset
224 //
225 m_cursor = controls->GetCursorPosition( !evt->DisableGridSnapping() );
226
227 if( lib_item->IsNew() )
228 {
229 m_anchorPos = selection.GetReferencePoint();
231
232 // Drag items to the current cursor position
233 for( EDA_ITEM* item : selection )
234 {
235 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
236
237 moveItem( schItem, delta );
238 updateItem( schItem, false );
239
240 // While SCH_COMMIT::Push() will add any new items to the entered group,
241 // we need to do it earlier so that the previews while moving are correct.
242 if( SCH_GROUP* enteredGroup = m_selectionTool->GetEnteredGroup() )
243 {
244 if( schItem->IsGroupableType() && !item->GetParentGroup() )
245 {
246 aCommit->Modify( enteredGroup, m_frame->GetScreen(), RECURSE_MODE::NO_RECURSE );
247 enteredGroup->AddItem( schItem );
248 }
249 }
250 }
251
253 }
254 else if( m_frame->GetMoveWarpsCursor() )
255 {
256 // User wants to warp the mouse
257 m_cursor = grid.BestDragOrigin( m_cursor, snapLayer, selection );
258 selection.SetReferencePoint( m_cursor );
260 }
261 else
262 {
263 m_cursor = controls->GetCursorPosition( !evt->DisableGridSnapping() );
265 }
266
267 controls->SetCursorPosition( m_cursor, false );
268
269 prevPos = m_cursor;
270 controls->SetAutoPan( true );
271 m_moveInProgress = true;
272 }
273
274 //------------------------------------------------------------------------
275 // Follow the mouse
276 //
277 m_cursor = grid.BestSnapAnchor( controls->GetCursorPosition( false ), snapLayer,
278 selection );
279 VECTOR2I delta( m_cursor - prevPos );
281
282 moveOffset += delta;
283 prevPos = m_cursor;
284
285 for( EDA_ITEM* item : selection )
286 {
287 moveItem( item, delta );
288 updateItem( item, false );
289 }
290
292 }
293 //------------------------------------------------------------------------
294 // Handle cancel
295 //
296 else if( evt->IsCancelInteractive() || evt->IsActivate() )
297 {
298 if( m_moveInProgress )
299 {
300 evt->SetPassEvent( false );
301 restore_state = true;
302 }
303
304 break;
305 }
306 //------------------------------------------------------------------------
307 // Handle TOOL_ACTION special cases
308 //
309 else if( evt->Action() == TA_UNDO_REDO_PRE )
310 {
311 unselect = true;
312 break;
313 }
314 else if( evt->IsAction( &ACTIONS::doDelete ) )
315 {
316 // Exit on a remove operation; there is no further processing for removed items.
317 break;
318 }
319 else if( evt->IsAction( &ACTIONS::duplicate ) )
320 {
321 wxBell();
322 }
323 //------------------------------------------------------------------------
324 // Handle context menu
325 //
326 else if( evt->IsClick( BUT_RIGHT ) )
327 {
328 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
329 }
330 //------------------------------------------------------------------------
331 // Handle drop
332 //
333 else if( evt->IsMouseUp( BUT_LEFT )
334 || evt->IsClick( BUT_LEFT )
335 || evt->IsDblClick( BUT_LEFT ) )
336 {
337 if( selection.GetSize() == 1 && selection.Front()->Type() == SCH_PIN_T )
338 {
340
341 try
342 {
343 SCH_PIN* curr_pin = static_cast<SCH_PIN*>( selection.Front() );
344
345 if( pinTool->PlacePin( aCommit, curr_pin ) )
346 {
347 // PlacePin() clears the current selection, which we don't want. Not only
348 // is it a poor user experience, but it also prevents us from doing the
349 // proper cleanup at the end of this routine (ie: clearing the edit flags).
350 m_selectionTool->AddItemToSel( curr_pin, true /*quiet mode*/ );
351 }
352 else
353 {
354 restore_state = true;
355 }
356 }
357 catch( const boost::bad_pointer& e )
358 {
359 restore_state = true;
360 wxFAIL_MSG( wxString::Format( wxT( "Boost pointer exception occurred: %s" ),
361 e.what() ) );
362 }
363 }
364
365 break; // Finish
366 }
367 else
368 {
369 evt->SetPassEvent();
370 }
371
372 } while( ( evt = Wait() ) ); // Assignment intentional; not equality test
373
374 controls->ForceCursorPosition( false );
375 controls->ShowCursor( false );
376 controls->SetAutoPan( false );
377
378 m_anchorPos = { 0, 0 };
379
380 for( EDA_ITEM* item : selection )
381 item->ClearEditFlags();
382
383 if( unselect )
385
386 m_moveInProgress = false;
387 m_frame->PopTool( aEvent );
388
389 return !restore_state;
390}
391
392
394{
396 SCH_SELECTION& selection = m_selectionTool->RequestSelection();
397 SCH_COMMIT commit( m_toolMgr );
398
399 for( EDA_ITEM* item : selection )
400 {
401 VECTOR2I newPos = grid.AlignGrid( item->GetPosition(), grid.GetItemGrid( item ) );
402 VECTOR2I delta = newPos - item->GetPosition();
403
404 if( delta != VECTOR2I( 0, 0 ) )
405 {
406 commit.Modify( item, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
407 static_cast<SCH_ITEM*>( item )->Move( delta );
408 updateItem( item, true );
409 };
410
411 if( SCH_PIN* pin = dynamic_cast<SCH_PIN*>( item ) )
412 {
413 int length = pin->GetLength();
414 int pinGrid;
415
416 if( pin->GetOrientation() == PIN_ORIENTATION::PIN_LEFT
417 || pin->GetOrientation() == PIN_ORIENTATION::PIN_RIGHT )
418 {
419 pinGrid = KiROUND( grid.GetGridSize( grid.GetItemGrid( item ) ).x );
420 }
421 else
422 {
423 pinGrid = KiROUND( grid.GetGridSize( grid.GetItemGrid( item ) ).y );
424 }
425
426 int newLength = KiROUND( (double) length / pinGrid ) * pinGrid;
427
428 if( newLength > 0 )
429 pin->SetLength( newLength );
430 }
431 }
432
434
435 commit.Push( _( "Align Items to Grid" ) );
436 return 0;
437}
438
439
441{
442 static_cast<SCH_ITEM*>( aItem )->Move( aDelta );
443 aItem->SetFlags( IS_MOVING );
444}
445
446
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:179
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:223
static TOOL_ACTION refreshPreview
Definition actions.h:158
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:106
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.
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:356
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() const override
int GetUnitCount() const override
static TOOL_ACTION alignToGrid
static TOOL_ACTION move
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
virtual void Revert() override
Revert the commit by restoring the modified items state.
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:167
int GetBodyStyle() const
Definition sch_item.h:244
int GetUnit() const
Definition sch_item.h:238
bool IsGroupableType() const
Definition sch_item.cpp:105
const wxString & GetName() const
Definition sch_pin.cpp:400
PIN_ORIENTATION GetOrientation() const
Definition sch_pin.cpp:263
VECTOR2I GetPosition() const override
Definition sch_pin.cpp:255
ELECTRICAL_PINTYPE GetType() const
Definition sch_pin.cpp:312
void updateItem(EDA_ITEM *aItem, bool aUpdateRTree) const
bool Init() override
Init() is called once upon a registration of the tool.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
SCH_TOOL_BASE(const std::string &aName)
SCH_SELECTION_TOOL * m_selectionTool
static bool IdleSelection(const SELECTION &aSelection)
Test if all selected items are not being edited.
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.
KIGFX::VIEW_CONTROLS * getViewControls() const
Definition tool_base.cpp:44
KIGFX::VIEW * getView() const
Definition tool_base.cpp:38
Generic, UI-independent tool event.
Definition tool_event.h:171
bool DisableGridSnapping() const
Definition tool_event.h:371
bool IsCancelInteractive() const
Indicate the event should restart/end an ongoing interactive tool's event loop (eg esc key,...
TOOL_ACTIONS Action() const
Returns more specific information about the type of an event.
Definition tool_event.h:250
bool IsActivate() const
Definition tool_event.h:345
COMMIT * Commit() const
Definition tool_event.h:283
bool IsClick(int aButtonMask=BUT_ANY) const
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition tool_event.h:315
int Modifier(int aMask=MD_MODIFIER_MASK) const
Return information about key modifiers state (Ctrl, Alt, etc.).
Definition tool_event.h:366
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
bool IsDblClick(int aButtonMask=BUT_ANY) const
std::atomic< SYNCRONOUS_TOOL_STATE > * SynchronousState() const
Definition tool_event.h:280
void SetPassEvent(bool aPass=true)
Definition tool_event.h:256
bool IsMouseUp(int aButtonMask=BUT_ANY) const
Definition tool_event.h:325
bool IsMotion() const
Definition tool_event.h:330
void Go(int(SYMBOL_EDIT_FRAME::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
std::unique_ptr< TOOL_MENU > m_menu
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
@ MOVING
Definition cursors.h:48
#define _(s)
@ RECURSE
Definition eda_item.h:51
@ NO_RECURSE
Definition eda_item.h:52
#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
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:111
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:118
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:164
@ STS_FINISHED
Definition tool_event.h:163
@ STS_RUNNING
Definition tool_event.h:162
@ BUT_LEFT
Definition tool_event.h:132
@ BUT_RIGHT
Definition tool_event.h:133
@ SCH_FIELD_T
Definition typeinfo.h:152
@ SCH_PIN_T
Definition typeinfo.h:155
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695