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( m_isFootprintEditor && !frame()->GetModel() )
174 return 0;
175
176 if( frame()->IsCurrentTool( ACTIONS::measureTool ) )
177 return 0;
178
179 auto& view = *getView();
180 auto& controls = *getViewControls();
181
182 frame()->PushTool( aEvent );
183
184 bool invertXAxis = displayOptions().m_DisplayInvertXAxis;
185 bool invertYAxis = displayOptions().m_DisplayInvertYAxis;
186
188 {
189 invertXAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertXAxis;
190 invertYAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertYAxis;
191 }
192
194 PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
195 bool originSet = false;
196 EDA_UNITS units = frame()->GetUserUnits();
197 KIGFX::PREVIEW::RULER_ITEM ruler( twoPtMgr, pcbIUScale, units, invertXAxis, invertYAxis );
198 STATUS_TEXT_POPUP statusPopup( frame() );
199
200 // Some colour to make it obviously not just a ruler
202 ruler.SetShowTicks( false );
203 ruler.SetShowEndArrowHead( true );
204
205 view.Add( &ruler );
206 view.SetVisible( &ruler, false );
207
208 auto setCursor =
209 [&]()
210 {
211 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
212 };
213
214 auto cleanup =
215 [&] ()
216 {
217 view.SetVisible( &ruler, false );
218 controls.SetAutoPan( false );
219 controls.CaptureCursor( false );
221 originSet = false;
222 };
223
224 const auto applyVector = [&]( const VECTOR2I& aMoveVec )
225 {
226 BOARD_COMMIT commit( frame() );
227 moveSelectionBy( selection, aMoveVec, commit );
228 commit.Push( _( "Set Relative Position Interactively" ) );
229 };
230
231 Activate();
232 // Must be done after Activate() so that it gets set into the correct context
233 controls.ShowCursor( true );
234 controls.SetAutoPan( false );
235 controls.CaptureCursor( false );
237
238 // Set initial cursor
239 setCursor();
240
241 const auto setInitialMsg = [&]()
242 {
243 statusPopup.SetText( _( "Select the reference point on the item to move." ) );
244 };
245
246 const auto setDragMsg = [&]()
247 {
248 statusPopup.SetText( _( "Select the point to define the new offset from." ) );
249 };
250
251 const auto setPopupPosition = [&]()
252 {
253 statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
254 };
255
256 setInitialMsg();
257
258 setPopupPosition();
259 statusPopup.Popup();
260 canvas()->SetStatusPopup( statusPopup.GetPanel() );
261
262 while( TOOL_EVENT* evt = Wait() )
263 {
264 setCursor();
265 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
266 grid.SetUseGrid( view.GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
267 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : controls.GetMousePosition();
268 cursorPos = grid.BestSnapAnchor( cursorPos, nullptr );
269 controls.ForceCursorPosition( true, cursorPos );
270 setPopupPosition();
271
272 if( evt->IsCancelInteractive() )
273 {
274 if( originSet )
275 {
276 cleanup();
277 }
278 else
279 {
280 frame()->PopTool( aEvent );
281 break;
282 }
283 }
284 else if( evt->IsActivate() )
285 {
286 if( originSet )
287 cleanup();
288
289 frame()->PopTool( aEvent );
290 break;
291 }
292 // click or drag starts
293 else if( !originSet && ( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
294 {
295 twoPtMgr.SetOrigin( cursorPos );
296 twoPtMgr.SetEnd( cursorPos );
297
298 setDragMsg();
299
300 controls.CaptureCursor( true );
301 controls.SetAutoPan( true );
302
303 originSet = true;
304 }
305 // second click or mouse up after drag ends
306 else if( originSet && ( evt->IsClick( BUT_LEFT ) || evt->IsMouseUp( BUT_LEFT ) ) )
307 {
308 // Hide the popup text so it doesn't get in the way
309 statusPopup.Hide();
310
311 // This is the forward vector from the ruler item
312 const VECTOR2I origVector = twoPtMgr.GetEnd() - twoPtMgr.GetOrigin();
313 VECTOR2I offsetVector = origVector;
314 // Start with the value of that vector in the dialog (will match the rule HUD)
315 DIALOG_SET_OFFSET dlg( *frame(), offsetVector, false );
316
317 int ret = dlg.ShowModal();
318
319 if( ret == wxID_OK )
320 {
321 const VECTOR2I move = origVector - offsetVector;
322
323 applyVector( move );
324
325 // Leave the arrow in place but update it
326 twoPtMgr.SetOrigin( twoPtMgr.GetOrigin() + move );
327 view.Update( &ruler, KIGFX::GEOMETRY );
328 canvas()->Refresh();
329 }
330
331 originSet = false;
332
333 setInitialMsg();
334
335 controls.SetAutoPan( false );
336 controls.CaptureCursor( false );
337
338 statusPopup.Popup();
339 }
340 // move or drag when origin set updates rules
341 else if( originSet && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
342 {
344 bool force45Deg;
345
346 if( frame()->IsType( FRAME_PCB_EDITOR ) )
347 force45Deg = mgr.GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_Use45DegreeLimit;
348 else
349 force45Deg = mgr.GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_Use45Limit;
350
351 twoPtMgr.SetAngleSnap( force45Deg );
352 twoPtMgr.SetEnd( cursorPos );
353
354 view.SetVisible( &ruler, true );
355 view.Update( &ruler, KIGFX::GEOMETRY );
356 }
357 else if( evt->IsAction( &ACTIONS::updateUnits ) )
358 {
359 if( frame()->GetUserUnits() != units )
360 {
361 units = frame()->GetUserUnits();
362 ruler.SwitchUnits( units );
363 view.Update( &ruler, KIGFX::GEOMETRY );
364 canvas()->ForceRefresh();
365 }
366
367 evt->SetPassEvent();
368 }
369 else if( evt->IsAction( &ACTIONS::updatePreferences ) )
370 {
371 invertXAxis = displayOptions().m_DisplayInvertXAxis;
372 invertYAxis = displayOptions().m_DisplayInvertYAxis;
373
375 {
376 invertXAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertXAxis;
377 invertYAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertYAxis;
378 }
379
380 ruler.UpdateDir( invertXAxis, invertYAxis );
381
382 view.Update( &ruler, KIGFX::GEOMETRY );
383 canvas()->Refresh();
384 evt->SetPassEvent();
385 }
386 else if( evt->IsClick( BUT_RIGHT ) )
387 {
388 // TODO: This does not work
391 picker->GetToolMenu().ShowContextMenu( dummy );
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 );
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
440{
441 // clang-format off
444 // clang-format on
445}
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: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