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
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 const auto setInitialMsg =
218 [&]()
219 {
220 statusPopup.SetText( _( "Select the reference point on the item to move." ) );
221 };
222
223 const auto setDragMsg =
224 [&]()
225 {
226 statusPopup.SetText( _( "Select the point to define the new offset from." ) );
227 };
228
229 const auto setPopupPosition =
230 [&]()
231 {
232 statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
233 };
234
235 auto cleanup =
236 [&] ()
237 {
238 view.SetVisible( &ruler, false );
239 controls.SetAutoPan( false );
240 controls.CaptureCursor( false );
242 originSet = false;
243 setInitialMsg();
244 };
245
246 const auto applyVector =
247 [&]( const VECTOR2I& aMoveVec )
248 {
249 BOARD_COMMIT commit( frame() );
250 moveSelectionBy( selection, aMoveVec, commit );
251 commit.Push( _( "Set Relative Position Interactively" ) );
252 };
253
254 Activate();
255 // Must be done after Activate() so that it gets set into the correct context
256 controls.ShowCursor( true );
257 controls.SetAutoPan( false );
258 controls.CaptureCursor( false );
260
261 // Set initial cursor
262 setCursor();
263
264 setInitialMsg();
265
266 setPopupPosition();
267 statusPopup.Popup();
268 canvas()->SetStatusPopup( statusPopup.GetPanel() );
269
270 while( TOOL_EVENT* evt = Wait() )
271 {
272 setCursor();
273 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
274 grid.SetUseGrid( view.GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
275 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : controls.GetMousePosition();
276 cursorPos = grid.BestSnapAnchor( cursorPos, nullptr );
277 controls.ForceCursorPosition( true, cursorPos );
278 setPopupPosition();
279
280 if( evt->IsCancelInteractive() )
281 {
282 if( originSet )
283 {
284 cleanup();
285 }
286 else
287 {
288 frame()->PopTool( aEvent );
289 break;
290 }
291 }
292 else if( evt->IsActivate() )
293 {
294 if( originSet )
295 cleanup();
296
297 frame()->PopTool( aEvent );
298 break;
299 }
300 // click or drag starts
301 else if( !originSet && ( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
302 {
303 twoPtMgr.SetOrigin( cursorPos );
304 twoPtMgr.SetEnd( cursorPos );
305
306 setDragMsg();
307
308 controls.CaptureCursor( true );
309 controls.SetAutoPan( true );
310
311 originSet = true;
312 }
313 // second click or mouse up after drag ends
314 else if( originSet && ( evt->IsClick( BUT_LEFT ) || evt->IsMouseUp( BUT_LEFT ) ) )
315 {
316 // Hide the popup text so it doesn't get in the way
317 statusPopup.Hide();
318
319 // This is the forward vector from the ruler item
320 const VECTOR2I origVector = twoPtMgr.GetEnd() - twoPtMgr.GetOrigin();
321 VECTOR2I offsetVector = origVector;
322 // Start with the value of that vector in the dialog (will match the rule HUD)
323 DIALOG_SET_OFFSET dlg( *frame(), offsetVector, false );
324
325 int ret = dlg.ShowModal();
326
327 if( ret == wxID_OK )
328 {
329 const VECTOR2I move = origVector - offsetVector;
330
331 applyVector( move );
332
333 // Leave the arrow in place but update it
334 twoPtMgr.SetOrigin( twoPtMgr.GetOrigin() + move );
335 view.Update( &ruler, KIGFX::GEOMETRY );
336 canvas()->Refresh();
337 }
338
339 originSet = false;
340
341 setInitialMsg();
342
343 controls.SetAutoPan( false );
344 controls.CaptureCursor( false );
345
346 statusPopup.Popup();
347 }
348 // move or drag when origin set updates rules
349 else if( originSet && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
350 {
351 bool force45Deg;
352
353 if( frame()->IsType( FRAME_PCB_EDITOR ) )
354 force45Deg = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_Use45DegreeLimit;
355 else
356 force45Deg = GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_Use45Limit;
357
358 twoPtMgr.SetAngleSnap( force45Deg );
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->IsClick( BUT_RIGHT ) )
394 {
395 // TODO: This does not work
398 picker->GetToolMenu().ShowContextMenu( dummy );
399 }
400 else if( !evt->IsMouseAction() )
401 {
402 // Often this will end up changing the items we just moved, so the ruler will be
403 // in the wrong place. Clear it away and the user can restart
404 twoPtMgr.Reset();
405 view.SetVisible( &ruler, false );
406 view.Update( &ruler, KIGFX::GEOMETRY );
407
408 evt->SetPassEvent();
409 }
410 else
411 {
412 evt->SetPassEvent();
413 }
414 }
415
416 view.SetVisible( &ruler, false );
417 view.Remove( &ruler );
418
419 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
420 controls.SetAutoPan( false );
421 controls.CaptureCursor( false );
423
424 canvas()->SetStatusPopup( nullptr );
425 return 0;
426}
427
428
430 const VECTOR2I& aTranslation )
431{
432 VECTOR2I aggregateTranslation = aPosAnchor + aTranslation - GetSelectionAnchorPosition();
433 moveSelectionBy( m_selection, aggregateTranslation, *m_commit );
434 m_commit->Push( _( "Position Relative" ) );
435
436 if( m_selection.IsHover() )
438
440
441 canvas()->Refresh();
442 return 0;
443}
444
445
447{
448 // clang-format off
451 // clang-format on
452}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:112
static TOOL_ACTION updatePreferences
Definition: actions.h:269
static TOOL_ACTION updateUnits
Definition: actions.h:204
static TOOL_ACTION measureTool
Definition: actions.h:245
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: actions.h:217
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:342
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:107
Dialog that invites the user to enter some kind of offset.
bool Show(bool show) override
EDA_UNITS GetUserUnits() const
Definition: dialog_shim.h:102
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:97
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:259
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:345
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:1602
static TOOL_ACTION positionRelativeInteractively
Definition: pcb_actions.h:307
static TOOL_ACTION positionRelative
Definition: pcb_actions.h:306
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
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:89
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:182
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:115
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)
@ RECURSE
Definition: eda_item.h:50
EDA_UNITS
Definition: eda_units.h:48
@ 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.
int GetUserUnits()
Return the currently selected user unit value for the interface.
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