KiCad PCB EDA Suite
Loading...
Searching...
No Matches
position_relative_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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
25
26#include <board_commit.h>
27#include <collectors.h>
30#include <footprint.h>
33#include <kiplatform/ui.h>
34#include <pad.h>
35#include <pcb_group.h>
38#include <pcb_painter.h>
39#include <pgm_base.h>
41#include <render_settings.h>
43#include <status_popup.h>
44#include <tools/pcb_actions.h>
48#include <view/view_controls.h>
49
50
56static void moveSelectionBy( const PCB_SELECTION& aSelection, const VECTOR2I& aMoveVec,
57 BOARD_COMMIT& commit )
58{
59 for( EDA_ITEM* item : aSelection )
60 {
61 if( !item->IsBOARD_ITEM() )
62 continue;
63
64 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
65 commit.Modify( boardItem, nullptr, RECURSE_MODE::RECURSE );
66 boardItem->Move( aMoveVec );
67 }
68}
69
70
72 PCB_TOOL_BASE( "pcbnew.PositionRelative" ),
73 m_dialog( nullptr ),
74 m_selectionTool( nullptr ),
76{
77}
78
79
81{
82 if( aReason != RUN )
83 m_commit = std::make_unique<BOARD_COMMIT>( this );
84}
85
86
88{
89 // Find the selection tool, so they can cooperate
91
92 return m_selectionTool != nullptr;
93}
94
95
97{
99
100 const auto& selection = m_selectionTool->RequestSelection(
101 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
102 {
103 sTool->FilterCollectorForHierarchy( aCollector, true );
104 sTool->FilterCollectorForMarkers( aCollector );
105 sTool->FilterCollectorForFreePads( aCollector, false );
106 sTool->FilterCollectorForLockedItems( aCollector );
107 } );
108
109 if( selection.Empty() )
110 return 0;
111
113
114 // We prefer footprints, then pads, then anything else here.
115 EDA_ITEM* preferredItem = m_selection.GetTopLeftItem( true );
116
117 if( !preferredItem && m_selection.HasType( PCB_PAD_T ) )
118 {
119 PCB_SELECTION padsOnly = m_selection;
120 std::deque<EDA_ITEM*>& items = padsOnly.Items();
121 items.erase( std::remove_if( items.begin(), items.end(),
122 []( const EDA_ITEM* aItem )
123 {
124 return aItem->Type() != PCB_PAD_T;
125 } ), items.end() );
126
127 preferredItem = padsOnly.GetTopLeftItem();
128 }
129
130 if( preferredItem )
131 m_selectionAnchor = preferredItem->GetPosition();
132 else
133 m_selectionAnchor = m_selection.GetTopLeftItem()->GetPosition();
134
135 // The dialog is not modal and not deleted between calls.
136 // It means some options can have changed since the last call.
137 // Therefore we need to rebuild it in case UI units have changed since the last call.
138 if( m_dialog && m_dialog->GetUserUnits() != editFrame->GetUserUnits() )
139 {
140 m_dialog->Destroy();
141 m_dialog = nullptr;
142 }
143
144 if( !m_dialog )
145 m_dialog = new DIALOG_POSITION_RELATIVE( editFrame );
146
147 m_dialog->Show( true );
148
149 return 0;
150}
151
153{
155 return false;
156
158
159 // First, acquire the selection that we will be moving after we have the new offset vector.
160 const PCB_SELECTION& selection = m_selectionTool->RequestSelection(
161 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
162 {
163 sTool->FilterCollectorForHierarchy( aCollector, true );
164 sTool->FilterCollectorForMarkers( aCollector );
165 sTool->FilterCollectorForFreePads( aCollector, false );
166 sTool->FilterCollectorForLockedItems( aCollector );
167 } );
168
169 if( selection.Empty() )
170 return 0;
171
172 if( m_isFootprintEditor && !frame()->GetModel() )
173 return 0;
174
175 if( frame()->IsCurrentTool( ACTIONS::measureTool ) )
176 return 0;
177
178 auto& view = *getView();
179 auto& controls = *getViewControls();
180
181 frame()->PushTool( aEvent );
182
183 bool invertXAxis = displayOptions().m_DisplayInvertXAxis;
184 bool invertYAxis = displayOptions().m_DisplayInvertYAxis;
185
187 {
188 invertXAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertXAxis;
189 invertYAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertYAxis;
190 }
191
193 PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
194 bool originSet = false;
195 EDA_UNITS units = frame()->GetUserUnits();
196 KIGFX::PREVIEW::RULER_ITEM ruler( twoPtMgr, pcbIUScale, units, invertXAxis, invertYAxis );
197 STATUS_TEXT_POPUP statusPopup( frame() );
198
199 // Some colour to make it obviously not just a ruler
200 ruler.SetColor( view.GetPainter()->GetSettings()->GetLayerColor( LAYER_ANCHOR ) );
201 ruler.SetShowTicks( false );
202 ruler.SetShowEndArrowHead( true );
203
204 view.Add( &ruler );
205 view.SetVisible( &ruler, false );
206
207 auto setCursor =
208 [&]()
209 {
210 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
211 };
212
213 const auto setInitialMsg =
214 [&]()
215 {
216 statusPopup.SetText( _( "Select the reference point on the item to move." ) );
217 };
218
219 const auto setDragMsg =
220 [&]()
221 {
222 statusPopup.SetText( _( "Select the point to define the new offset from." ) );
223 };
224
225 const auto setPopupPosition =
226 [&]()
227 {
228 statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
229 };
230
231 auto cleanup =
232 [&] ()
233 {
234 view.SetVisible( &ruler, false );
235 controls.SetAutoPan( false );
236 controls.CaptureCursor( false );
237 controls.ForceCursorPosition( false );
238 originSet = false;
239 setInitialMsg();
240 };
241
242 const auto applyVector =
243 [&]( const VECTOR2I& aMoveVec )
244 {
245 BOARD_COMMIT commit( frame() );
246 moveSelectionBy( selection, aMoveVec, commit );
247 commit.Push( _( "Set Relative Position Interactively" ) );
248 };
249
250 Activate();
251 // Must be done after Activate() so that it gets set into the correct context
252 controls.ShowCursor( true );
253 controls.SetAutoPan( false );
254 controls.CaptureCursor( false );
255 controls.ForceCursorPosition( false );
256
257 // Set initial cursor
258 setCursor();
259
260 setInitialMsg();
261
262 setPopupPosition();
263 statusPopup.Popup();
264 canvas()->SetStatusPopup( statusPopup.GetPanel() );
265
266 while( TOOL_EVENT* evt = Wait() )
267 {
268 setCursor();
269 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
270 grid.SetUseGrid( view.GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
271 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : controls.GetMousePosition();
272 cursorPos = grid.BestSnapAnchor( cursorPos, nullptr );
273 controls.ForceCursorPosition( true, cursorPos );
274 setPopupPosition();
275
276 if( evt->IsCancelInteractive() )
277 {
278 if( originSet )
279 {
280 cleanup();
281 }
282 else
283 {
284 frame()->PopTool( aEvent );
285 break;
286 }
287 }
288 else if( evt->IsActivate() )
289 {
290 if( originSet )
291 cleanup();
292
293 frame()->PopTool( aEvent );
294 break;
295 }
296 // click or drag starts
297 else if( !originSet && ( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
298 {
299 twoPtMgr.SetOrigin( cursorPos );
300 twoPtMgr.SetEnd( cursorPos );
301
302 setDragMsg();
303
304 controls.CaptureCursor( true );
305 controls.SetAutoPan( true );
306
307 originSet = true;
308 }
309 // second click or mouse up after drag ends
310 else if( originSet && ( evt->IsClick( BUT_LEFT ) || evt->IsMouseUp( BUT_LEFT ) ) )
311 {
312 // Hide the popup text so it doesn't get in the way
313 statusPopup.Hide();
314
315 // This is the forward vector from the ruler item
316 const VECTOR2I origVector = twoPtMgr.GetEnd() - twoPtMgr.GetOrigin();
317 VECTOR2I offsetVector = origVector;
318 // Start with the value of that vector in the dialog (will match the rule HUD)
319 DIALOG_SET_OFFSET dlg( *frame(), offsetVector, false );
320
321 int ret = dlg.ShowModal();
322
323 if( ret == wxID_OK )
324 {
325 const VECTOR2I move = origVector - offsetVector;
326
327 applyVector( move );
328
329 // Leave the arrow in place but update it
330 twoPtMgr.SetOrigin( twoPtMgr.GetOrigin() + move );
331 view.Update( &ruler, KIGFX::GEOMETRY );
332 canvas()->Refresh();
333 }
334
335 originSet = false;
336
337 setInitialMsg();
338
339 controls.SetAutoPan( false );
340 controls.CaptureCursor( false );
341
342 statusPopup.Popup();
343 }
344 // move or drag when origin set updates rules
345 else if( originSet && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
346 {
347 auto snap = LEADER_MODE::DIRECT;
348
349 if( frame()->IsType( FRAME_PCB_EDITOR ) )
350 {
351 snap = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_AngleSnapMode;
352 }
353 else
354 {
355 snap = GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode;
356 }
357
358 twoPtMgr.SetAngleSnap( snap );
359 twoPtMgr.SetEnd( cursorPos );
360
361 view.SetVisible( &ruler, true );
362 view.Update( &ruler, KIGFX::GEOMETRY );
363 }
364 else if( evt->IsAction( &ACTIONS::updateUnits ) )
365 {
366 if( frame()->GetUserUnits() != units )
367 {
368 units = frame()->GetUserUnits();
369 ruler.SwitchUnits( units );
370 view.Update( &ruler, KIGFX::GEOMETRY );
371 canvas()->ForceRefresh();
372 }
373
374 evt->SetPassEvent();
375 }
376 else if( evt->IsAction( &ACTIONS::updatePreferences ) )
377 {
378 invertXAxis = displayOptions().m_DisplayInvertXAxis;
379 invertYAxis = displayOptions().m_DisplayInvertYAxis;
380
382 {
383 invertXAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertXAxis;
384 invertYAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertYAxis;
385 }
386
387 ruler.UpdateDir( invertXAxis, invertYAxis );
388
389 view.Update( &ruler, KIGFX::GEOMETRY );
390 canvas()->Refresh();
391 evt->SetPassEvent();
392 }
393 else if( !evt->IsMouseAction() )
394 {
395 // Often this will end up changing the items we just moved, so the ruler will be
396 // in the wrong place. Clear it away and the user can restart
397 twoPtMgr.Reset();
398 view.SetVisible( &ruler, false );
399 view.Update( &ruler, KIGFX::GEOMETRY );
400
401 evt->SetPassEvent();
402 }
403 else
404 {
405 evt->SetPassEvent();
406 }
407 }
408
409 view.SetVisible( &ruler, false );
410 view.Remove( &ruler );
411
412 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
413 controls.SetAutoPan( false );
414 controls.CaptureCursor( false );
415 controls.ForceCursorPosition( false );
416
417 canvas()->SetStatusPopup( nullptr );
418 return 0;
419}
420
421
423 const VECTOR2I& aTranslation )
424{
425 VECTOR2I aggregateTranslation = aPosAnchor + aTranslation - GetSelectionAnchorPosition();
426 moveSelectionBy( m_selection, aggregateTranslation, *m_commit );
427 m_commit->Push( _( "Position Relative" ) );
428
429 if( m_selection.IsHover() )
431
433
434 canvas()->Refresh();
435 return 0;
436}
437
438
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
static TOOL_ACTION updatePreferences
Definition actions.h:277
static TOOL_ACTION updateUnits
Definition actions.h:206
static TOOL_ACTION measureTool
Definition actions.h:251
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:223
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:79
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition board_item.h:339
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
Dialog that invites the user to enter some kind of offset.
int ShowModal() override
void ForceRefresh()
Force a redraw.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
void SetStatusPopup(wxWindow *aPopup)
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
virtual VECTOR2I GetPosition() const
Definition eda_item.h:272
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:353
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:207
A drawn ruler item for showing the distance between two points.
Definition ruler_item.h:45
void SetShowEndArrowHead(bool aShow)
Definition ruler_item.h:63
void SwitchUnits(EDA_UNITS aUnits)
Switch the ruler units.
Definition ruler_item.h:91
void UpdateDir(bool aFlipX, bool aFlipY)
Definition ruler_item.h:93
void SetColor(const COLOR4D &aColor)
Definition ruler_item.h:59
void SetShowTicks(bool aShow)
Definition ruler_item.h:61
Represent a very simple geometry manager for items that have a start and end point.
void SetOrigin(const VECTOR2I &aOrigin)
< Set the origin of the ruler (the fixed end)
void Reset()
Reset the manager to the initial state.
void SetEnd(const VECTOR2I &aEnd)
Set the current end of the rectangle (the end that moves with the cursor.
static TOOL_ACTION positionRelativeInteractively
static TOOL_ACTION positionRelative
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
The selection tool: currently supports:
void FilterCollectorForMarkers(GENERAL_COLLECTOR &aCollector) const
Drop any PCB_MARKERs from the collector.
void FilterCollectorForFreePads(GENERAL_COLLECTOR &aCollector, bool aForcePromotion=false) const
Check the "allow free pads" setting and if disabled, replace any pads in the collector with their par...
void FilterCollectorForHierarchy(GENERAL_COLLECTOR &aCollector, bool aMultiselect) const
In general we don't want to select both a parent and any of it's children.
void FilterCollectorForLockedItems(GENERAL_COLLECTOR &aCollector)
In the PCB editor strip out any locked items unless the OverrideLocks checkbox is set.
EDA_ITEM * GetTopLeftItem(bool aFootprintsOnly=false) const override
T * frame() const
KIGFX::PCB_VIEW * view() const
KIGFX::VIEW_CONTROLS * controls() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
PCB_DRAW_PANEL_GAL * canvas() const
PCBNEW_SETTINGS::DISPLAY_OPTIONS & displayOptions() const
const PCB_SELECTION & selection() const
int RelativeItemSelectionMove(const VECTOR2I &anchor, const VECTOR2I &translation)
Position the m_position_relative_selection selection relative to anchor position using the given tran...
int PositionRelative(const TOOL_EVENT &aEvent)
Invoke a dialog box to allow positioning of the item relative to another by an exact amount.
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
bool Init() override
Init() is called once upon a registration of the tool.
int PositionRelativeInteractively(const TOOL_EVENT &aEvent)
Draw a line connecting two points and allow the user to enter what it should be.
DIALOG_POSITION_RELATIVE * m_dialog
std::unique_ptr< BOARD_COMMIT > m_commit
VECTOR2I GetSelectionAnchorPosition() const
Return the position of the selected item(s)
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
PCB_SELECTION_TOOL * m_selectionTool
std::deque< EDA_ITEM * > & Items()
Definition selection.h:182
wxWindow * GetPanel()
virtual void Popup(wxWindow *aFocus=nullptr)
virtual void Move(const wxPoint &aWhere)
Extension of STATUS_POPUP for displaying a single line text.
void SetText(const wxString &aText)
Display a text.
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:186
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
@ RUN
Tool is invoked after being inactive.
Definition tool_base.h:79
Generic, UI-independent tool event.
Definition tool_event.h:171
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_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.
EDA_UNITS GetUserUnits() const
@ MEASURE
Definition cursors.h:66
@ ARROW
Definition cursors.h:46
#define _(s)
@ RECURSE
Definition eda_item.h:51
EDA_UNITS
Definition eda_units.h:48
@ FRAME_PCB_EDITOR
Definition frame_type.h:42
@ DIRECT
Unconstrained point-to-point.
@ LAYER_ANCHOR
Anchor of items having an anchor point (texts, footprints).
Definition layer_ids.h:247
@ GEOMETRY
Position or shape has changed.
Definition view_item.h:55
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition wxgtk/ui.cpp:689
Class to handle a set of BOARD_ITEMs.
int GetUserUnits()
Return the currently selected user unit value for the interface.
see class PGM_BASE
static void moveSelectionBy(const PCB_SELECTION &aSelection, const VECTOR2I &aMoveVec, BOARD_COMMIT &commit)
Move each item in the selection by the given vector.
T * GetAppSettings(const char *aFilename)
@ MD_SHIFT
Definition tool_event.h:143
@ BUT_LEFT
Definition tool_event.h:132
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695