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 );
66 boardItem->Move( aMoveVec );
67 }
68}
69
70
75 GENERAL_COLLECTOR& aCollector,
76 PCB_SELECTION_TOOL* sTool )
77{
78 sTool->FilterCollectorForHierarchy( aCollector, true );
79 sTool->FilterCollectorForMarkers( aCollector );
80 sTool->FilterCollectorForFreePads( aCollector, false );
81}
82
83
85 PCB_TOOL_BASE( "pcbnew.PositionRelative" ),
86 m_dialog( nullptr ),
87 m_selectionTool( nullptr ),
88 m_inInteractivePosition( false )
89{
90}
91
92
94{
95 if( aReason != RUN )
96 m_commit = std::make_unique<BOARD_COMMIT>( this );
97}
98
99
101{
102 // Find the selection tool, so they can cooperate
104
105 return m_selectionTool != nullptr;
106}
107
108
110{
111 PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
112
115 !m_isFootprintEditor /* prompt user regarding locked items */ );
116
117 if( selection.Empty() )
118 return 0;
119
121
122 // We prefer footprints, then pads, then anything else here.
123 EDA_ITEM* preferredItem = m_selection.GetTopLeftItem( true );
124
125 if( !preferredItem && m_selection.HasType( PCB_PAD_T ) )
126 {
127 PCB_SELECTION padsOnly = m_selection;
128 std::deque<EDA_ITEM*>& items = padsOnly.Items();
129 items.erase( std::remove_if( items.begin(), items.end(),
130 []( const EDA_ITEM* aItem )
131 {
132 return aItem->Type() != PCB_PAD_T;
133 } ), items.end() );
134
135 preferredItem = padsOnly.GetTopLeftItem();
136 }
137
138 if( preferredItem )
139 m_selectionAnchor = preferredItem->GetPosition();
140 else
142
143 // The dialog is not modal and not deleted between calls.
144 // It means some options can have changed since the last call.
145 // Therefore we need to rebuild it in case UI units have changed since the last call.
146 if( m_dialog && m_dialog->GetUserUnits() != editFrame->GetUserUnits() )
147 {
148 m_dialog->Destroy();
149 m_dialog = nullptr;
150 }
151
152 if( !m_dialog )
153 m_dialog = new DIALOG_POSITION_RELATIVE( editFrame );
154
155 m_dialog->Show( true );
156
157 return 0;
158}
159
161{
163 return false;
164
166
167 // First, acquire the selection that we will be moving after
168 // we have the new offset vector.
171 !m_isFootprintEditor /* prompt user regarding locked items */ );
172
173 if( selection.Empty() )
174 return 0;
175
176 if( m_isFootprintEditor && !frame()->GetModel() )
177 return 0;
178
179 if( frame()->IsCurrentTool( ACTIONS::measureTool ) )
180 return 0;
181
182 auto& view = *getView();
183 auto& controls = *getViewControls();
184
185 frame()->PushTool( aEvent );
186
187 bool invertXAxis = displayOptions().m_DisplayInvertXAxis;
188 bool invertYAxis = displayOptions().m_DisplayInvertYAxis;
189
191 {
192 invertXAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertXAxis;
193 invertYAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertYAxis;
194 }
195
197 PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
198 bool originSet = false;
199 EDA_UNITS units = frame()->GetUserUnits();
200 KIGFX::PREVIEW::RULER_ITEM ruler( twoPtMgr, pcbIUScale, units, invertXAxis, invertYAxis );
201 STATUS_TEXT_POPUP statusPopup( frame() );
202
203 // Some colour to make it obviously not just a ruler
205 ruler.SetShowTicks( false );
206 ruler.SetShowEndArrowHead( true );
207
208 view.Add( &ruler );
209 view.SetVisible( &ruler, false );
210
211 auto setCursor =
212 [&]()
213 {
214 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
215 };
216
217 auto cleanup =
218 [&] ()
219 {
220 view.SetVisible( &ruler, false );
221 controls.SetAutoPan( false );
222 controls.CaptureCursor( false );
224 originSet = false;
225 };
226
227 const auto applyVector = [&]( const VECTOR2I& aMoveVec )
228 {
229 BOARD_COMMIT commit( frame() );
230 moveSelectionBy( selection, aMoveVec, commit );
231 commit.Push( _( "Set Relative Position Interactively" ) );
232 };
233
234 Activate();
235 // Must be done after Activate() so that it gets set into the correct context
236 controls.ShowCursor( true );
237 controls.SetAutoPan( false );
238 controls.CaptureCursor( false );
240
241 // Set initial cursor
242 setCursor();
243
244 const auto setInitialMsg = [&]()
245 {
246 statusPopup.SetText( _( "Select the reference point on the item to move." ) );
247 };
248
249 const auto setDragMsg = [&]()
250 {
251 statusPopup.SetText( _( "Select the point to define the new offset from." ) );
252 };
253
254 const auto setPopupPosition = [&]()
255 {
256 statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
257 };
258
259 setInitialMsg();
260
261 setPopupPosition();
262 statusPopup.Popup();
263 canvas()->SetStatusPopup( statusPopup.GetPanel() );
264
265 while( TOOL_EVENT* evt = Wait() )
266 {
267 setCursor();
268 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
269 grid.SetUseGrid( view.GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
270 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : controls.GetMousePosition();
271 cursorPos = grid.BestSnapAnchor( cursorPos, nullptr );
272 controls.ForceCursorPosition( true, cursorPos );
273 setPopupPosition();
274
275 if( evt->IsCancelInteractive() )
276 {
277 if( originSet )
278 {
279 cleanup();
280 }
281 else
282 {
283 frame()->PopTool( aEvent );
284 break;
285 }
286 }
287 else if( evt->IsActivate() )
288 {
289 if( originSet )
290 cleanup();
291
292 frame()->PopTool( aEvent );
293 break;
294 }
295 // click or drag starts
296 else if( !originSet && ( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
297 {
298 twoPtMgr.SetOrigin( cursorPos );
299 twoPtMgr.SetEnd( cursorPos );
300
301 setDragMsg();
302
303 controls.CaptureCursor( true );
304 controls.SetAutoPan( true );
305
306 originSet = true;
307 }
308 // second click or mouse up after drag ends
309 else if( originSet && ( evt->IsClick( BUT_LEFT ) || evt->IsMouseUp( BUT_LEFT ) ) )
310 {
311 // Hide the popup text so it doesn't get in the way
312 statusPopup.Hide();
313
314 // This is the forward vector from the ruler item
315 const VECTOR2I origVector = twoPtMgr.GetEnd() - twoPtMgr.GetOrigin();
316 VECTOR2I offsetVector = origVector;
317 // Start with the value of that vector in the dialog (will match the rule HUD)
318 DIALOG_SET_OFFSET dlg( *frame(), offsetVector, false );
319
320 int ret = dlg.ShowModal();
321
322 if( ret == wxID_OK )
323 {
324 const VECTOR2I move = origVector - offsetVector;
325
326 applyVector( move );
327
328 // Leave the arrow in place but update it
329 twoPtMgr.SetOrigin( twoPtMgr.GetOrigin() + move );
330 view.Update( &ruler, KIGFX::GEOMETRY );
331 canvas()->Refresh();
332 }
333
334 originSet = false;
335
336 setInitialMsg();
337
338 controls.SetAutoPan( false );
339 controls.CaptureCursor( false );
340
341 statusPopup.Popup();
342 }
343 // move or drag when origin set updates rules
344 else if( originSet && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
345 {
347 bool force45Deg;
348
349 if( frame()->IsType( FRAME_PCB_EDITOR ) )
350 force45Deg = mgr.GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_Use45DegreeLimit;
351 else
352 force45Deg = mgr.GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_Use45Limit;
353
354 twoPtMgr.SetAngleSnap( force45Deg );
355 twoPtMgr.SetEnd( cursorPos );
356
357 view.SetVisible( &ruler, true );
358 view.Update( &ruler, KIGFX::GEOMETRY );
359 }
360 else if( evt->IsAction( &ACTIONS::updateUnits ) )
361 {
362 if( frame()->GetUserUnits() != units )
363 {
364 units = frame()->GetUserUnits();
365 ruler.SwitchUnits( units );
366 view.Update( &ruler, KIGFX::GEOMETRY );
367 canvas()->ForceRefresh();
368 }
369
370 evt->SetPassEvent();
371 }
372 else if( evt->IsAction( &ACTIONS::updatePreferences ) )
373 {
374 invertXAxis = displayOptions().m_DisplayInvertXAxis;
375 invertYAxis = displayOptions().m_DisplayInvertYAxis;
376
378 {
379 invertXAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertXAxis;
380 invertYAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertYAxis;
381 }
382
383 ruler.UpdateDir( invertXAxis, invertYAxis );
384
385 view.Update( &ruler, KIGFX::GEOMETRY );
386 canvas()->Refresh();
387 evt->SetPassEvent();
388 }
389 else if( evt->IsClick( BUT_RIGHT ) )
390 {
391 // TODO: This does not work
394 picker->GetToolMenu().ShowContextMenu( dummy );
395 }
396 else if( !evt->IsMouseAction() )
397 {
398 // Often this will end up changing the items we just moved, so the ruler will be
399 // in the wrong place. Clear it away and the user can restart
400 twoPtMgr.Reset();
401 view.SetVisible( &ruler, false );
402 view.Update( &ruler, KIGFX::GEOMETRY );
403
404 evt->SetPassEvent();
405 }
406 else
407 {
408 evt->SetPassEvent();
409 }
410 }
411
412 view.SetVisible( &ruler, false );
413 view.Remove( &ruler );
414
415 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
416 controls.SetAutoPan( false );
417 controls.CaptureCursor( false );
419
420 canvas()->SetStatusPopup( nullptr );
421 return 0;
422}
423
424
426 const VECTOR2I& aTranslation )
427{
428 VECTOR2I aggregateTranslation = aPosAnchor + aTranslation - GetSelectionAnchorPosition();
429 moveSelectionBy( m_selection, aggregateTranslation, *m_commit );
430 m_commit->Push( _( "Position Relative" ) );
431
432 if( m_selection.IsHover() )
434
436
437 canvas()->Refresh();
438 return 0;
439}
440
441
443{
444 // clang-format off
447 // clang-format on
448}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
static TOOL_ACTION updatePreferences
Definition: actions.h:223
static TOOL_ACTION updateUnits
Definition: actions.h:196
static TOOL_ACTION measureTool
Definition: actions.h:203
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:344
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Modify a given item in the model.
Definition: commit.h:108
Dialog that invites the user to enter some kind of offset.
bool Show(bool show) override
EDA_UNITS GetUserUnits() const
Definition: dialog_shim.h:138
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:89
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:244
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:299
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:202
bool GetGridSnapping() const
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const override
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: pcb_view.cpp:91
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1) override
Add a VIEW_ITEM to the view.
Definition: pcb_view.cpp:57
virtual void Remove(VIEW_ITEM *aItem) override
Remove a VIEW_ITEM from the view.
Definition: pcb_view.cpp:74
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.
const COLOR4D & GetLayerColor(int aLayer) const
Return the color used to draw a layer.
virtual void CaptureCursor(bool aEnabled)
Force the cursor to stay within the drawing panel area.
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 VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const =0
Return the current mouse pointer position.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
GAL * GetGAL() const
Return the #GAL this view is using to draw graphical primitives.
Definition: view.h:198
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition: view.h:216
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition: view.cpp:1600
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:68
static TOOL_ACTION positionRelativeInteractively
Definition: pcb_actions.h:328
static TOOL_ACTION positionRelative
Definition: pcb_actions.h:327
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
Generic tool for picking an item.
The selection tool: currently supports:
void FilterCollectorForMarkers(GENERAL_COLLECTOR &aCollector) const
Drop any PCB_MARKERs from the collector.
PCB_SELECTION & RequestSelection(CLIENT_SELECTION_FILTER aClientFilter, bool aConfirmLockedItems=false)
Return the current selection, filtered according to aClientFilter.
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.
EDA_ITEM * GetTopLeftItem(bool aFootprintsOnly=false) const override
T * frame() const
KIGFX::PCB_VIEW * view() const
KIGFX::VIEW_CONTROLS * controls() const
PCB_DRAW_PANEL_GAL * canvas() const
PCBNEW_SETTINGS::DISPLAY_OPTIONS & displayOptions() const
bool m_isFootprintEditor
const PCB_SELECTION & selection() const
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition: pgm_base.h:125
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
bool IsHover() const
Definition: selection.h:84
bool HasType(KICAD_T aType) const
Checks if there is at least one item of requested kind.
Definition: selection.cpp:143
std::deque< EDA_ITEM * > & Items()
Definition: selection.h:177
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:110
T * GetAppSettings(const wxString &aFilename)
Return a handle to the a given settings by type.
wxWindow * GetPanel()
Definition: status_popup.h:62
virtual void Popup(wxWindow *aFocus=nullptr)
virtual void Move(const wxPoint &aWhere)
Extension of STATUS_POPUP for displaying a single line text.
Definition: status_popup.h:83
void SetText(const wxString &aText)
Display a text.
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:168
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_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).
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
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
EDA_UNITS GetUserUnits() const
#define _(s)
EDA_UNITS
Definition: eda_units.h:46
@ FRAME_PCB_EDITOR
Definition: frame_type.h:42
@ 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:677
Class to handle a set of BOARD_ITEMs.
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:1073
see class PGM_BASE
static void positionRelativeClientSelectionFilter(const VECTOR2I &aPt, GENERAL_COLLECTOR &aCollector, PCB_SELECTION_TOOL *sTool)
Position relative tools all use the same filter for selecting items.
static void moveSelectionBy(const PCB_SELECTION &aSelection, const VECTOR2I &aMoveVec, BOARD_COMMIT &commit)
Move each item in the selection by the given vector.
std::vector< FAB_LAYER_COLOR > dummy
@ MD_SHIFT
Definition: tool_event.h:143
@ BUT_LEFT
Definition: tool_event.h:132
@ BUT_RIGHT
Definition: tool_event.h:133
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87