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{
89}
90
91
93{
94 if( aReason != RUN )
95 m_commit = std::make_unique<BOARD_COMMIT>( this );
96}
97
98
100{
101 // Find the selection tool, so they can cooperate
103
104 return m_selectionTool != nullptr;
105}
106
107
109{
110 PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
111
114 !m_isFootprintEditor /* prompt user regarding locked items */ );
115
116 if( selection.Empty() )
117 return 0;
118
120
121 // We prefer footprints, then pads, then anything else here.
122 EDA_ITEM* preferredItem = m_selection.GetTopLeftItem( true );
123
124 if( !preferredItem && m_selection.HasType( PCB_PAD_T ) )
125 {
126 PCB_SELECTION padsOnly = m_selection;
127 std::deque<EDA_ITEM*>& items = padsOnly.Items();
128 items.erase( std::remove_if( items.begin(), items.end(),
129 []( const EDA_ITEM* aItem )
130 {
131 return aItem->Type() != PCB_PAD_T;
132 } ), items.end() );
133
134 preferredItem = padsOnly.GetTopLeftItem();
135 }
136
137 if( preferredItem )
138 m_selectionAnchor = preferredItem->GetPosition();
139 else
141
142 // The dialog is not modal and not deleted between calls.
143 // It means some options can have changed since the last call.
144 // Therefore we need to rebuild it in case UI units have changed since the last call.
145 if( m_dialog && m_dialog->GetUserUnits() != editFrame->GetUserUnits() )
146 {
147 m_dialog->Destroy();
148 m_dialog = nullptr;
149 }
150
151 if( !m_dialog )
152 m_dialog = new DIALOG_POSITION_RELATIVE( editFrame );
153
154 m_dialog->Show( true );
155
156 return 0;
157}
158
160{
161 // First, acquire the selection that we will be moving after
162 // we have the new offset vector.
165 !m_isFootprintEditor /* prompt user regarding locked items */ );
166
167 if( m_isFootprintEditor && !frame()->GetModel() )
168 return 0;
169
170 if( frame()->IsCurrentTool( ACTIONS::measureTool ) )
171 return 0;
172
173 auto& view = *getView();
174 auto& controls = *getViewControls();
175
176 frame()->PushTool( aEvent );
177
178 bool invertXAxis = displayOptions().m_DisplayInvertXAxis;
179 bool invertYAxis = displayOptions().m_DisplayInvertYAxis;
180
182 {
183 invertXAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertXAxis;
184 invertYAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertYAxis;
185 }
186
188 PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
189 bool originSet = false;
190 EDA_UNITS units = frame()->GetUserUnits();
191 KIGFX::PREVIEW::RULER_ITEM ruler( twoPtMgr, pcbIUScale, units, invertXAxis, invertYAxis );
192 STATUS_TEXT_POPUP statusPopup( frame() );
193
194 // Some colour to make it obviously not just a ruler
196 ruler.SetShowTicks( false );
197 ruler.SetShowEndArrowHead( true );
198
199 view.Add( &ruler );
200 view.SetVisible( &ruler, false );
201
202 auto setCursor =
203 [&]()
204 {
205 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
206 };
207
208 auto cleanup =
209 [&] ()
210 {
211 view.SetVisible( &ruler, false );
212 controls.SetAutoPan( false );
213 controls.CaptureCursor( false );
215 originSet = false;
216 };
217
218 const auto applyVector = [&]( const VECTOR2I& aMoveVec )
219 {
220 BOARD_COMMIT commit( frame() );
221 moveSelectionBy( selection, aMoveVec, commit );
222 commit.Push( _( "Set Relative Position Interactively" ) );
223 };
224
225 Activate();
226 // Must be done after Activate() so that it gets set into the correct context
227 controls.ShowCursor( true );
228 controls.SetAutoPan( false );
229 controls.CaptureCursor( false );
231
232 // Set initial cursor
233 setCursor();
234
235 const auto setInitialMsg = [&]()
236 {
237 statusPopup.SetText( _( "Select the reference point on the item to move." ) );
238 };
239
240 const auto setDragMsg = [&]()
241 {
242 statusPopup.SetText( _( "Select the point to define the new offset from." ) );
243 };
244
245 const auto setPopupPosition = [&]()
246 {
247 statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
248 };
249
250 setInitialMsg();
251
252 setPopupPosition();
253 statusPopup.Popup();
254 canvas()->SetStatusPopup( statusPopup.GetPanel() );
255
256 while( TOOL_EVENT* evt = Wait() )
257 {
258 setCursor();
259 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
260 grid.SetUseGrid( view.GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
261 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : controls.GetMousePosition();
262 cursorPos = grid.BestSnapAnchor( cursorPos, nullptr );
263 controls.ForceCursorPosition( true, cursorPos );
264 setPopupPosition();
265
266 if( evt->IsCancelInteractive() )
267 {
268 if( originSet )
269 {
270 cleanup();
271 }
272 else
273 {
274 frame()->PopTool( aEvent );
275 break;
276 }
277 }
278 else if( evt->IsActivate() )
279 {
280 if( originSet )
281 cleanup();
282
283 frame()->PopTool( aEvent );
284 break;
285 }
286 // click or drag starts
287 else if( !originSet && ( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
288 {
289 twoPtMgr.SetOrigin( cursorPos );
290 twoPtMgr.SetEnd( cursorPos );
291
292 setDragMsg();
293
294 controls.CaptureCursor( true );
295 controls.SetAutoPan( true );
296
297 originSet = true;
298 }
299 // second click or mouse up after drag ends
300 else if( originSet && ( evt->IsClick( BUT_LEFT ) || evt->IsMouseUp( BUT_LEFT ) ) )
301 {
302 // This is the forward vector from the ruler item
303 const VECTOR2I origVector = twoPtMgr.GetEnd() - twoPtMgr.GetOrigin();
304 VECTOR2I offsetVector = origVector;
305 // Start with the value of that vector in the dialog (will match the rule HUD)
306 DIALOG_SET_OFFSET dlg( *frame(), offsetVector, false );
307
308 int ret = dlg.ShowModal();
309
310 if( ret == wxID_OK )
311 {
312 const VECTOR2I move = origVector - offsetVector;
313
314 applyVector( move );
315
316 // Leave the arrow in place but update it
317 twoPtMgr.SetOrigin( twoPtMgr.GetOrigin() + move );
318 view.Update( &ruler, KIGFX::GEOMETRY );
319 canvas()->Refresh();
320 }
321
322 originSet = false;
323
324 setInitialMsg();
325
326 controls.SetAutoPan( false );
327 controls.CaptureCursor( false );
328 }
329 // move or drag when origin set updates rules
330 else if( originSet && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
331 {
333 bool force45Deg;
334
335 if( frame()->IsType( FRAME_PCB_EDITOR ) )
336 force45Deg = mgr.GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_Use45DegreeLimit;
337 else
338 force45Deg = mgr.GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_Use45Limit;
339
340 twoPtMgr.SetAngleSnap( force45Deg );
341 twoPtMgr.SetEnd( cursorPos );
342
343 view.SetVisible( &ruler, true );
344 view.Update( &ruler, KIGFX::GEOMETRY );
345 }
346 else if( evt->IsAction( &ACTIONS::updateUnits ) )
347 {
348 if( frame()->GetUserUnits() != units )
349 {
350 units = frame()->GetUserUnits();
351 ruler.SwitchUnits( units );
352 view.Update( &ruler, KIGFX::GEOMETRY );
353 canvas()->ForceRefresh();
354 }
355
356 evt->SetPassEvent();
357 }
358 else if( evt->IsAction( &ACTIONS::updatePreferences ) )
359 {
360 invertXAxis = displayOptions().m_DisplayInvertXAxis;
361 invertYAxis = displayOptions().m_DisplayInvertYAxis;
362
364 {
365 invertXAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertXAxis;
366 invertYAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertYAxis;
367 }
368
369 ruler.UpdateDir( invertXAxis, invertYAxis );
370
371 view.Update( &ruler, KIGFX::GEOMETRY );
372 canvas()->Refresh();
373 evt->SetPassEvent();
374 }
375 else if( evt->IsClick( BUT_RIGHT ) )
376 {
377 // TODO: This does not work
380 picker->GetToolMenu().ShowContextMenu( dummy );
381 }
382 else
383 {
384 evt->SetPassEvent();
385 }
386 }
387
388 view.SetVisible( &ruler, false );
389 view.Remove( &ruler );
390
391 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
392 controls.SetAutoPan( false );
393 controls.CaptureCursor( false );
395
396 canvas()->SetStatusPopup( nullptr );
397 return 0;
398}
399
400
402 const VECTOR2I& aTranslation )
403{
404 VECTOR2I aggregateTranslation = aPosAnchor + aTranslation - GetSelectionAnchorPosition();
405 moveSelectionBy( m_selection, aggregateTranslation, *m_commit );
406 m_commit->Push( _( "Position Relative" ) );
407
408 if( m_selection.IsHover() )
410
412
413 canvas()->Refresh();
414 return 0;
415}
416
417
419{
420 // clang-format off
423 // clang-format on
424}
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:130
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 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:145
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:42
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:36
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:210
@ 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