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 (C) 2017-2022 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
52 PCB_TOOL_BASE( "pcbnew.PositionRelative" ),
53 m_dialog( nullptr ),
54 m_selectionTool( nullptr )
55{
56}
57
58
60{
61 if( aReason != RUN )
62 m_commit = std::make_unique<BOARD_COMMIT>( this );
63}
64
65
67{
68 // Find the selection tool, so they can cooperate
70
71 return m_selectionTool != nullptr;
72}
73
74
76{
77 PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
78
80 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
81 {
82 sTool->FilterCollectorForHierarchy( aCollector, true );
83 sTool->FilterCollectorForMarkers( aCollector );
84 },
85 !m_isFootprintEditor /* prompt user regarding locked items */ );
86
87 if( selection.Empty() )
88 return 0;
89
91
92 // We prefer footprints, then pads, then anything else here.
93 EDA_ITEM* preferredItem = m_selection.GetTopLeftItem( true );
94
95 if( !preferredItem && m_selection.HasType( PCB_PAD_T ) )
96 {
97 PCB_SELECTION padsOnly = m_selection;
98 std::deque<EDA_ITEM*>& items = padsOnly.Items();
99 items.erase( std::remove_if( items.begin(), items.end(),
100 []( const EDA_ITEM* aItem )
101 {
102 return aItem->Type() != PCB_PAD_T;
103 } ), items.end() );
104
105 preferredItem = padsOnly.GetTopLeftItem();
106 }
107
108 if( preferredItem )
109 m_selectionAnchor = preferredItem->GetPosition();
110 else
112
113 // The dialog is not modal and not deleted between calls.
114 // It means some options can have changed since the last call.
115 // Therefore we need to rebuild it in case UI units have changed since the last call.
116 if( m_dialog && m_dialog->GetUserUnits() != editFrame->GetUserUnits() )
117 {
118 m_dialog->Destroy();
119 m_dialog = nullptr;
120 }
121
122 if( !m_dialog )
123 m_dialog = new DIALOG_POSITION_RELATIVE( editFrame );
124
125 m_dialog->Show( true );
126
127 return 0;
128}
129
130
132{
133 // First, acquire the selection that we will be moving after
134 // we have the new offset vector.
136 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
137 {
138 sTool->FilterCollectorForHierarchy( aCollector, true );
139 sTool->FilterCollectorForMarkers( aCollector );
140 },
141 !m_isFootprintEditor /* prompt user regarding locked items */ );
142
143 if( m_isFootprintEditor && !frame()->GetModel() )
144 return 0;
145
146 if( frame()->IsCurrentTool( ACTIONS::measureTool ) )
147 return 0;
148
149 auto& view = *getView();
150 auto& controls = *getViewControls();
151
152 frame()->PushTool( aEvent );
153
154 bool invertXAxis = displayOptions().m_DisplayInvertXAxis;
155 bool invertYAxis = displayOptions().m_DisplayInvertYAxis;
156
158 {
159 invertXAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertXAxis;
160 invertYAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertYAxis;
161 }
162
164 PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
165 bool originSet = false;
166 EDA_UNITS units = frame()->GetUserUnits();
167 KIGFX::PREVIEW::RULER_ITEM ruler( twoPtMgr, pcbIUScale, units, invertXAxis, invertYAxis );
168 STATUS_TEXT_POPUP statusPopup( frame() );
169
170 // Some colour to make it obviously not just a ruler
172 ruler.SetShowTicks( false );
173 ruler.SetShowEndArrowHead( true );
174
175 view.Add( &ruler );
176 view.SetVisible( &ruler, false );
177
178 auto setCursor =
179 [&]()
180 {
181 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
182 };
183
184 auto cleanup =
185 [&] ()
186 {
187 view.SetVisible( &ruler, false );
188 controls.SetAutoPan( false );
189 controls.CaptureCursor( false );
191 originSet = false;
192 };
193
194 const auto applyVector = [&]( const VECTOR2I& aMoveVec )
195 {
196 BOARD_COMMIT commit( frame() );
197
198 for( EDA_ITEM* item : selection )
199 {
200 if( !item->IsBOARD_ITEM() )
201 continue;
202
203 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
204
205 // Don't move a pad by itself unless editing the footprint
206 if( boardItem->Type() == PCB_PAD_T
207 && !frame()->GetPcbNewSettings()->m_AllowFreePads
208 && frame()->IsType( FRAME_PCB_EDITOR ) )
209 continue;
210
211 commit.Modify( boardItem );
212 boardItem->Move( aMoveVec );
213 }
214
215 commit.Push( _( "Set Relative Position Interactively" ) );
216 };
217
218 Activate();
219 // Must be done after Activate() so that it gets set into the correct context
220 controls.ShowCursor( true );
221 controls.SetAutoPan( false );
222 controls.CaptureCursor( false );
224
225 // Set initial cursor
226 setCursor();
227
228 const auto setInitialMsg = [&]()
229 {
230 statusPopup.SetText( _( "Select the reference point on the item to move." ) );
231 };
232
233 const auto setDragMsg = [&]()
234 {
235 statusPopup.SetText( _( "Select the point to define the new offset from." ) );
236 };
237
238 const auto setPopupPosition = [&]()
239 {
240 statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
241 };
242
243 setInitialMsg();
244
245 setPopupPosition();
246 statusPopup.Popup();
247 canvas()->SetStatusPopup( statusPopup.GetPanel() );
248
249 while( TOOL_EVENT* evt = Wait() )
250 {
251 setCursor();
252 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
253 grid.SetUseGrid( view.GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
254 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : controls.GetMousePosition();
255 cursorPos = grid.BestSnapAnchor( cursorPos, nullptr );
256 controls.ForceCursorPosition( true, cursorPos );
257 setPopupPosition();
258
259 if( evt->IsCancelInteractive() )
260 {
261 if( originSet )
262 {
263 cleanup();
264 }
265 else
266 {
267 frame()->PopTool( aEvent );
268 break;
269 }
270 }
271 else if( evt->IsActivate() )
272 {
273 if( originSet )
274 cleanup();
275
276 frame()->PopTool( aEvent );
277 break;
278 }
279 // click or drag starts
280 else if( !originSet && ( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
281 {
282 twoPtMgr.SetOrigin( cursorPos );
283 twoPtMgr.SetEnd( cursorPos );
284
285 setDragMsg();
286
287 controls.CaptureCursor( true );
288 controls.SetAutoPan( true );
289
290 originSet = true;
291 }
292 // second click or mouse up after drag ends
293 else if( originSet && ( evt->IsClick( BUT_LEFT ) || evt->IsMouseUp( BUT_LEFT ) ) )
294 {
295 // This is the forward vector from the ruler item
296 const VECTOR2I origVector = twoPtMgr.GetEnd() - twoPtMgr.GetOrigin();
297 VECTOR2I offsetVector = origVector;
298 // Start with the value of that vector in the dialog (will match the rule HUD)
299 DIALOG_SET_OFFSET dlg( *frame(), offsetVector, false );
300
301 int ret = dlg.ShowModal();
302
303 if( ret == wxID_OK )
304 {
305 const VECTOR2I move = origVector - offsetVector;
306
307 applyVector( move );
308
309 // Leave the arrow in place but update it
310 twoPtMgr.SetOrigin( twoPtMgr.GetOrigin() + move );
311 view.Update( &ruler, KIGFX::GEOMETRY );
312 canvas()->Refresh();
313 }
314
315 originSet = false;
316
317 setInitialMsg();
318
319 controls.SetAutoPan( false );
320 controls.CaptureCursor( false );
321 }
322 // move or drag when origin set updates rules
323 else if( originSet && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
324 {
326 bool force45Deg;
327
328 if( frame()->IsType( FRAME_PCB_EDITOR ) )
329 force45Deg = mgr.GetAppSettings<PCBNEW_SETTINGS>()->m_Use45DegreeLimit;
330 else
331 force45Deg = mgr.GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>()->m_Use45Limit;
332
333 twoPtMgr.SetAngleSnap( force45Deg );
334 twoPtMgr.SetEnd( cursorPos );
335
336 view.SetVisible( &ruler, true );
337 view.Update( &ruler, KIGFX::GEOMETRY );
338 }
339 else if( evt->IsAction( &ACTIONS::updateUnits ) )
340 {
341 if( frame()->GetUserUnits() != units )
342 {
343 units = frame()->GetUserUnits();
344 ruler.SwitchUnits( units );
345 view.Update( &ruler, KIGFX::GEOMETRY );
346 canvas()->ForceRefresh();
347 }
348
349 evt->SetPassEvent();
350 }
351 else if( evt->IsAction( &ACTIONS::updatePreferences ) )
352 {
353 invertXAxis = displayOptions().m_DisplayInvertXAxis;
354 invertYAxis = displayOptions().m_DisplayInvertYAxis;
355
357 {
358 invertXAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertXAxis;
359 invertYAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertYAxis;
360 }
361
362 ruler.UpdateDir( invertXAxis, invertYAxis );
363
364 view.Update( &ruler, KIGFX::GEOMETRY );
365 canvas()->Refresh();
366 evt->SetPassEvent();
367 }
368 else if( evt->IsClick( BUT_RIGHT ) )
369 {
370 // TODO: This does not work
373 picker->GetToolMenu().ShowContextMenu( dummy );
374 }
375 else
376 {
377 evt->SetPassEvent();
378 }
379 }
380
381 view.SetVisible( &ruler, false );
382 view.Remove( &ruler );
383
384 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
385 controls.SetAutoPan( false );
386 controls.CaptureCursor( false );
388
389 canvas()->SetStatusPopup( nullptr );
390 return 0;
391}
392
393
395 const VECTOR2I& aTranslation )
396{
397 VECTOR2I aggregateTranslation = aPosAnchor + aTranslation - GetSelectionAnchorPosition();
398
399 for( EDA_ITEM* item : m_selection )
400 {
401 if( !item->IsBOARD_ITEM() )
402 continue;
403
404 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
405
406 // Don't move a pad by itself unless editing the footprint
407 if( boardItem->Type() == PCB_PAD_T
408 && !frame()->GetPcbNewSettings()->m_AllowFreePads
409 && frame()->IsType( FRAME_PCB_EDITOR ) )
410 {
411 boardItem = boardItem->GetParent();
412 }
413
414 m_commit->Modify( boardItem );
415 boardItem->Move( aggregateTranslation );
416 }
417
418 m_commit->Push( _( "Position Relative" ) );
419
420 if( m_selection.IsHover() )
422
424
425 canvas()->Refresh();
426 return 0;
427}
428
429
431{
432 // clang-format off
435 // clang-format on
436}
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
Revert the commit by restoring the modified items state.
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:342
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:215
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:105
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:243
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
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:90
void UpdateDir(bool aFlipX, bool aFlipY)
Definition: ruler_item.h:92
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:203
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition: view.h:221
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition: view.cpp:1614
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 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:142
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()
Returns a handle to the a given settings by type If the settings have already been loaded,...
wxWindow * GetPanel()
Definition: status_popup.h:63
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:84
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:218
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:167
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:202
@ GEOMETRY
Position or shape has changed.
Definition: view_item.h:54
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition: wxgtk/ui.cpp:620
Class to handle a set of BOARD_ITEMs.
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: pgm_base.cpp:1060
see class PGM_BASE
std::vector< FAB_LAYER_COLOR > dummy
@ MD_SHIFT
Definition: tool_event.h:142
@ BUT_LEFT
Definition: tool_event.h:131
@ BUT_RIGHT
Definition: tool_event.h:132
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87