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, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <set>
23
24#include <board_commit.h>
25#include <collectors.h>
28#include <footprint.h>
31#include <kiplatform/ui.h>
32#include <pad.h>
33#include <pcb_group.h>
36#include <pcb_painter.h>
37#include <pgm_base.h>
39#include <render_settings.h>
41#include <status_popup.h>
42#include <tools/pcb_actions.h>
46#include <view/view_controls.h>
47
48
61static void moveSelectionBy( const PCB_SELECTION& aSelection, const VECTOR2I& aMoveVec,
62 BOARD_COMMIT& aCommit, bool aAllowFreePads )
63{
64 std::set<BOARD_ITEM*> moved;
65
66 for( EDA_ITEM* item : aSelection )
67 {
68 if( !item->IsBOARD_ITEM() )
69 continue;
70
71 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
72
73 if( boardItem->Type() == PCB_PAD_T && !aAllowFreePads )
74 boardItem = boardItem->GetParent();
75
76 if( !moved.insert( boardItem ).second )
77 continue;
78
79 aCommit.Modify( boardItem, nullptr, RECURSE_MODE::RECURSE );
80 boardItem->Move( aMoveVec );
81 }
82}
83
84
86 PCB_TOOL_BASE( "pcbnew.PositionRelative" ),
87 m_dialog( nullptr ),
88 m_selectionTool( nullptr ),
90{
91}
92
93
95{
96 if( aReason != RUN )
97 m_commit = std::make_unique<BOARD_COMMIT>( this );
98}
99
100
102{
103 // Find the selection tool, so they can cooperate
105
106 return m_selectionTool != nullptr;
107}
108
109
111{
113
114 const auto& selection = m_selectionTool->RequestSelection(
115 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
116 {
117 sTool->FilterCollectorForHierarchy( aCollector, true );
118 sTool->FilterCollectorForMarkers( aCollector );
119 sTool->FilterCollectorForLockedItems( aCollector );
120 } );
121
122 if( m_selectionTool->ReportFilteredLockedItems() )
123 return 0;
124
125 if( selection.Empty() )
126 return 0;
127
129
130 // We prefer footprints, then pads, then anything else here.
131 EDA_ITEM* preferredItem = m_selection.GetTopLeftItem( true );
132
133 if( !preferredItem && m_selection.HasType( PCB_PAD_T ) )
134 {
135 PCB_SELECTION padsOnly = m_selection;
136 std::deque<EDA_ITEM*>& items = padsOnly.Items();
137 items.erase( std::remove_if( items.begin(), items.end(),
138 []( const EDA_ITEM* aItem )
139 {
140 return aItem->Type() != PCB_PAD_T;
141 } ), items.end() );
142
143 preferredItem = padsOnly.GetTopLeftItem();
144 }
145
146 if( preferredItem )
147 m_selectionAnchor = preferredItem->GetPosition();
148 else
149 m_selectionAnchor = m_selection.GetTopLeftItem()->GetPosition();
150
151 // The dialog is not modal and not deleted between calls.
152 // It means some options can have changed since the last call.
153 // Therefore we need to rebuild it in case UI units have changed since the last call.
154 if( m_dialog && m_dialog->GetUserUnits() != editFrame->GetUserUnits() )
155 {
156 m_dialog->Destroy();
157 m_dialog = nullptr;
158 }
159
160 if( !m_dialog )
161 m_dialog = new DIALOG_POSITION_RELATIVE( editFrame );
162
163 m_dialog->Show( true );
164
165 return 0;
166}
167
168
170{
172 return false;
173
175
176 // First, acquire the selection that we will be moving after we have the new offset vector.
177 const PCB_SELECTION& selection = m_selectionTool->RequestSelection(
178 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
179 {
180 sTool->FilterCollectorForHierarchy( aCollector, true );
181 sTool->FilterCollectorForMarkers( aCollector );
182 sTool->FilterCollectorForLockedItems( aCollector );
183 } );
184
185 if( m_selectionTool->ReportFilteredLockedItems() )
186 return 0;
187
188 if( selection.Empty() )
189 return 0;
190
191 if( m_isFootprintEditor && !frame()->GetModel() )
192 return 0;
193
194 if( frame()->IsCurrentTool( ACTIONS::measureTool ) )
195 return 0;
196
199 SCOPED_TOOL_PUSHER raii( frame(), aEvent );
200
201 bool invertXAxis = displayOptions().m_DisplayInvertXAxis;
202 bool invertYAxis = displayOptions().m_DisplayInvertYAxis;
203
205 {
206 invertXAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertXAxis;
207 invertYAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertYAxis;
208 }
209
211 PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
212 bool originSet = false;
213 EDA_UNITS units = frame()->GetUserUnits();
214 KIGFX::PREVIEW::RULER_ITEM ruler( twoPtMgr, pcbIUScale, units, invertXAxis, invertYAxis );
215 STATUS_TEXT_POPUP statusPopup( frame() );
216
217 bool allowFreePads = m_isFootprintEditor
218 || ( frame()->GetPcbNewSettings()
219 && frame()->GetPcbNewSettings()->m_AllowFreePads );
220
221 // Some colour to make it obviously not just a ruler
222 ruler.SetColor( view->GetPainter()->GetSettings()->GetLayerColor( LAYER_ANCHOR ) );
223 ruler.SetShowTicks( false );
224 ruler.SetShowEndArrowHead( true );
225
226 view->Add( &ruler );
227 view->SetVisible( &ruler, false );
228
229 auto setCursor =
230 [&]()
231 {
232 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
233 };
234
235 const auto setInitialMsg =
236 [&]()
237 {
238 statusPopup.SetText( _( "Select the reference point on the item to move." ) );
239 };
240
241 const auto setDragMsg =
242 [&]()
243 {
244 statusPopup.SetText( _( "Select the point to define the new offset from." ) );
245 };
246
247 const auto setPopupPosition =
248 [&]()
249 {
250 statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
251 };
252
253 auto cleanup =
254 [&] ()
255 {
256 view->SetVisible( &ruler, false );
257 controls->SetAutoPan( false );
258 controls->CaptureCursor( false );
259 controls->ForceCursorPosition( false );
260 originSet = false;
261 setInitialMsg();
262 };
263
264 const auto applyVector =
265 [&]( const VECTOR2I& aMoveVec )
266 {
267 BOARD_COMMIT commit( frame() );
268 moveSelectionBy( selection, aMoveVec, commit, allowFreePads );
269 commit.Push( _( "Set Relative Position Interactively" ) );
270 };
271
272 Activate();
273 // Must be done after Activate() so that it gets set into the correct context
274 controls->ShowCursor( true );
275 controls->SetAutoPan( false );
276 controls->CaptureCursor( false );
277 controls->ForceCursorPosition( false );
278
279 // Set initial cursor
280 setCursor();
281
282 setInitialMsg();
283
284 setPopupPosition();
285 statusPopup.Popup();
286 canvas()->SetStatusPopup( statusPopup.GetPanel() );
287
288 while( TOOL_EVENT* evt = Wait() )
289 {
290 setCursor();
291 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
292 grid.SetUseGrid( view->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
293 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : controls->GetMousePosition();
294 setPopupPosition();
295
296 if( !evt->IsActivate() && !evt->IsCancelInteractive() )
297 {
298 // If we are switching, the canvas may not be valid any more
299 cursorPos = grid.ResolveSnap( cursorPos, nullptr ).position;
300 controls->ForceCursorPosition( true, cursorPos );
301 }
302 else
303 {
304 grid.FullReset();
305 }
306
307 if( evt->IsCancelInteractive() )
308 {
309 if( originSet )
310 cleanup();
311 else
312 break;
313 }
314 else if( evt->IsActivate() )
315 {
316 if( originSet )
317 cleanup();
318
319 break;
320 }
321 // click or drag starts
322 else if( !originSet && ( evt->IsDrag( BUT_LEFT )
323 || evt->IsClick( BUT_LEFT )
324 || evt->IsAction( &ACTIONS::cursorClick ) ) )
325 {
326 twoPtMgr.SetOrigin( cursorPos );
327 twoPtMgr.SetEnd( cursorPos );
328
329 setDragMsg();
330
331 controls->CaptureCursor( true );
332 controls->SetAutoPan( true );
333
334 originSet = true;
335 }
336 // second click or mouse up after drag ends
337 else if( originSet && ( evt->IsClick( BUT_LEFT )
338 || evt->IsAction( &ACTIONS::cursorClick )
339 || evt->IsMouseUp( BUT_LEFT ) ) )
340 {
341 // Hide the popup text so it doesn't get in the way
342 statusPopup.Hide();
343
344 // This is the forward vector from the ruler item
345 VECTOR2I offsetVector = twoPtMgr.GetEnd() - twoPtMgr.GetOrigin();
346 const VECTOR2I toReferencePtVector = twoPtMgr.GetOrigin() - twoPtMgr.GetEnd();
347
348 // Start with the value of that vector in the dialog (will match the rule HUD)
349 DIALOG_OFFSET_ITEM dlg( *frame(), offsetVector );
350
351 if( dlg.ShowModal() == wxID_OK )
352 {
353 const VECTOR2I move = toReferencePtVector + offsetVector;
354
355 applyVector( move );
356
357 // Leave the arrow in place but update it
358 twoPtMgr.SetEnd( twoPtMgr.GetOrigin() + offsetVector );
359 view->Update( &ruler, KIGFX::GEOMETRY );
360 canvas()->Refresh();
361 }
362
363 originSet = false;
364
365 setInitialMsg();
366
367 controls->SetAutoPan( false );
368 controls->CaptureCursor( false );
369
370 statusPopup.Popup();
371 }
372 // move or drag when origin set updates rules
373 else if( originSet && ( evt->IsMotion()
374 || evt->IsAction( &ACTIONS::refreshPreview )
375 || evt->IsDrag( BUT_LEFT ) ) )
376 {
377 auto snap = LEADER_MODE::DIRECT;
378
379 if( frame()->IsType( FRAME_PCB_EDITOR ) )
380 snap = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_AngleSnapMode;
381 else
382 snap = GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode;
383
384 twoPtMgr.SetAngleSnap( snap );
385 // The end is fixed; we must update the origin
386 twoPtMgr.SetOrigin( cursorPos );
387
388 view->SetVisible( &ruler, true );
389 view->Update( &ruler, KIGFX::GEOMETRY );
390 }
391 else if( evt->IsAction( &ACTIONS::updateUnits ) )
392 {
393 if( frame()->GetUserUnits() != units )
394 {
395 units = frame()->GetUserUnits();
396 ruler.SwitchUnits( units );
397 view->Update( &ruler, KIGFX::GEOMETRY );
398 canvas()->ForceRefresh();
399 }
400
401 evt->SetPassEvent();
402 }
403 else if( evt->IsAction( &ACTIONS::updatePreferences ) )
404 {
405 invertXAxis = displayOptions().m_DisplayInvertXAxis;
406 invertYAxis = displayOptions().m_DisplayInvertYAxis;
407
409 {
410 invertXAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertXAxis;
411 invertYAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertYAxis;
412 }
413
414 ruler.UpdateDir( invertXAxis, invertYAxis );
415
416 view->Update( &ruler, KIGFX::GEOMETRY );
417 canvas()->Refresh();
418 evt->SetPassEvent();
419 }
420 else if( !evt->IsMouseAction() )
421 {
422 // Often this will end up changing the items we just moved, so the ruler will be
423 // in the wrong place. Clear it away and the user can restart
424 twoPtMgr.Reset();
425 view->SetVisible( &ruler, false );
426 view->Update( &ruler, KIGFX::GEOMETRY );
427
428 evt->SetPassEvent();
429 }
430 else
431 {
432 evt->SetPassEvent();
433 }
434 }
435
436 view->SetVisible( &ruler, false );
437 view->Remove( &ruler );
438
439 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
440 controls->SetAutoPan( false );
441 controls->CaptureCursor( false );
442 controls->ForceCursorPosition( false );
443
444 canvas()->SetStatusPopup( nullptr );
445 return 0;
446}
447
448
450 const VECTOR2I& aTranslation )
451{
452 VECTOR2I aggregateTranslation = aPosAnchor + aTranslation - GetSelectionAnchorPosition();
453
454 bool allowFreePads = m_isFootprintEditor
455 || ( frame()->GetPcbNewSettings()
456 && frame()->GetPcbNewSettings()->m_AllowFreePads );
457
458 moveSelectionBy( m_selection, aggregateTranslation, *m_commit, allowFreePads );
459 m_commit->Push( _( "Position Relative" ) );
460
461 if( m_selection.IsHover() )
463
465
466 canvas()->Refresh();
467 return 0;
468}
469
470
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
static TOOL_ACTION updatePreferences
Definition actions.h:275
static TOOL_ACTION updateUnits
Definition actions.h:203
static TOOL_ACTION measureTool
Definition actions.h:248
static TOOL_ACTION cursorClick
Definition actions.h:176
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
static TOOL_ACTION refreshPreview
Definition actions.h:155
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:84
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition board_item.h:436
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:266
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:102
Dialog that invites the user to enter some kind of offset.
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:98
virtual VECTOR2I GetPosition() const
Definition eda_item.h:348
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
static const TOOL_EVENT SelectedItemsModified
Selected item had a property changed (except movement)
Definition actions.h:352
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:203
A drawn ruler item for showing the distance between two points.
Definition ruler_item.h:42
void SetShowEndArrowHead(bool aShow)
Definition ruler_item.h:59
void SwitchUnits(EDA_UNITS aUnits)
Switch the ruler units.
Definition ruler_item.h:87
void UpdateDir(bool aFlipX, bool aFlipY)
Definition ruler_item.h:89
void SetColor(const COLOR4D &aColor)
Definition ruler_item.h:55
void SetShowTicks(bool aShow)
Definition ruler_item.h:57
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.
An interface for classes handling user events controlling the view behavior such as zooming,...
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:63
static TOOL_ACTION interactiveOffsetTool
static TOOL_ACTION positionRelative
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
The selection tool: currently supports:
void FilterCollectorForMarkers(GENERAL_COLLECTOR &aCollector) const
Drop any PCB_MARKERs from the collector.
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.
void FilterCollectorForLockedItems(GENERAL_COLLECTOR &aCollector)
In the PCB editor strip out any locked items unless the OverrideLocks checkbox is set.
EDA_ITEM * GetTopLeftItem(bool aFootprintsOnly=false) const override
T * frame() const
KIGFX::PCB_VIEW * view() const
KIGFX::VIEW_CONTROLS * controls() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
PCB_DRAW_PANEL_GAL * canvas() const
PCBNEW_SETTINGS::DISPLAY_OPTIONS & displayOptions() const
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.
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.
int InteractiveOffset(const TOOL_EVENT &aEvent)
Draw a line connecting two points and allow the user to enter what it should be.
PCB_SELECTION_TOOL * m_selectionTool
std::deque< EDA_ITEM * > & Items()
Definition selection.h:181
wxWindow * GetPanel()
virtual void Popup(wxWindow *aFocus=nullptr)
virtual void Move(const wxPoint &aWhere)
Extension of STATUS_POPUP for displaying a single line text.
void SetText(const wxString &aText)
Display a text.
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:182
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:40
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
KIGFX::VIEW * getView() const
Return the instance of KIGFX::VIEW object used in the application.
Definition tool_base.cpp:34
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:74
@ RUN
Tool is invoked after being inactive.
Definition tool_base.h:75
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_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.
EDA_UNITS GetUserUnits() const
@ MEASURE
Definition cursors.h:64
@ ARROW
Definition cursors.h:42
#define _(s)
@ RECURSE
Definition eda_item.h:51
EDA_UNITS
Definition eda_units.h:44
@ FRAME_PCB_EDITOR
Definition frame_type.h:38
@ DIRECT
Unconstrained point-to-point.
@ LAYER_ANCHOR
Anchor of items having an anchor point (texts, footprints).
Definition layer_ids.h:244
@ GEOMETRY
Position or shape has changed.
Definition view_item.h:51
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition wxgtk/ui.cpp:851
Class to handle a set of BOARD_ITEMs.
see class PGM_BASE
static void moveSelectionBy(const PCB_SELECTION &aSelection, const VECTOR2I &aMoveVec, BOARD_COMMIT &aCommit, bool aAllowFreePads)
Move each item in the selection by the given vector.
T * GetAppSettings(const char *aFilename)
bool moved
@ MD_SHIFT
Definition tool_event.h:139
@ BUT_LEFT
Definition tool_event.h:128
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708