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
58static void moveSelectionBy( const PCB_SELECTION& aSelection, const VECTOR2I& aMoveVec,
59 BOARD_COMMIT& commit, bool aAllowFreePads )
60{
61 std::set<BOARD_ITEM*> moved;
62
63 for( EDA_ITEM* item : aSelection )
64 {
65 if( !item->IsBOARD_ITEM() )
66 continue;
67
68 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
69
70 if( boardItem->Type() == PCB_PAD_T && !aAllowFreePads )
71 boardItem = boardItem->GetParent();
72
73 if( !moved.insert( boardItem ).second )
74 continue;
75
76 commit.Modify( boardItem, nullptr, RECURSE_MODE::RECURSE );
77 boardItem->Move( aMoveVec );
78 }
79}
80
81
83 PCB_TOOL_BASE( "pcbnew.PositionRelative" ),
84 m_dialog( nullptr ),
85 m_selectionTool( nullptr ),
87{
88}
89
90
92{
93 if( aReason != RUN )
94 m_commit = std::make_unique<BOARD_COMMIT>( this );
95}
96
97
99{
100 // Find the selection tool, so they can cooperate
102
103 return m_selectionTool != nullptr;
104}
105
106
108{
110
111 const auto& selection = m_selectionTool->RequestSelection(
112 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
113 {
114 sTool->FilterCollectorForHierarchy( aCollector, true );
115 sTool->FilterCollectorForMarkers( aCollector );
116 sTool->FilterCollectorForLockedItems( aCollector );
117 } );
118
119 if( selection.Empty() )
120 return 0;
121
123
124 // We prefer footprints, then pads, then anything else here.
125 EDA_ITEM* preferredItem = m_selection.GetTopLeftItem( true );
126
127 if( !preferredItem && m_selection.HasType( PCB_PAD_T ) )
128 {
129 PCB_SELECTION padsOnly = m_selection;
130 std::deque<EDA_ITEM*>& items = padsOnly.Items();
131 items.erase( std::remove_if( items.begin(), items.end(),
132 []( const EDA_ITEM* aItem )
133 {
134 return aItem->Type() != PCB_PAD_T;
135 } ), items.end() );
136
137 preferredItem = padsOnly.GetTopLeftItem();
138 }
139
140 if( preferredItem )
141 m_selectionAnchor = preferredItem->GetPosition();
142 else
143 m_selectionAnchor = m_selection.GetTopLeftItem()->GetPosition();
144
145 // The dialog is not modal and not deleted between calls.
146 // It means some options can have changed since the last call.
147 // Therefore we need to rebuild it in case UI units have changed since the last call.
148 if( m_dialog && m_dialog->GetUserUnits() != editFrame->GetUserUnits() )
149 {
150 m_dialog->Destroy();
151 m_dialog = nullptr;
152 }
153
154 if( !m_dialog )
155 m_dialog = new DIALOG_POSITION_RELATIVE( editFrame );
156
157 m_dialog->Show( true );
158
159 return 0;
160}
161
162
164{
166 return false;
167
169
170 // First, acquire the selection that we will be moving after we have the new offset vector.
171 const PCB_SELECTION& selection = m_selectionTool->RequestSelection(
172 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
173 {
174 sTool->FilterCollectorForHierarchy( aCollector, true );
175 sTool->FilterCollectorForMarkers( aCollector );
176 sTool->FilterCollectorForLockedItems( aCollector );
177 } );
178
179 if( selection.Empty() )
180 return 0;
181
182 if( m_isFootprintEditor && !frame()->GetModel() )
183 return 0;
184
185 if( frame()->IsCurrentTool( ACTIONS::measureTool ) )
186 return 0;
187
188 auto& view = *getView();
189 auto& controls = *getViewControls();
190
191 frame()->PushTool( aEvent );
192
193 bool invertXAxis = displayOptions().m_DisplayInvertXAxis;
194 bool invertYAxis = displayOptions().m_DisplayInvertYAxis;
195
197 {
198 invertXAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertXAxis;
199 invertYAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertYAxis;
200 }
201
203 PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
204 bool originSet = false;
205 EDA_UNITS units = frame()->GetUserUnits();
206 KIGFX::PREVIEW::RULER_ITEM ruler( twoPtMgr, pcbIUScale, units, invertXAxis, invertYAxis );
207 STATUS_TEXT_POPUP statusPopup( frame() );
208
209 bool allowFreePads = m_isFootprintEditor
210 || ( frame()->GetPcbNewSettings()
211 && frame()->GetPcbNewSettings()->m_AllowFreePads );
212
213 // Some colour to make it obviously not just a ruler
214 ruler.SetColor( view.GetPainter()->GetSettings()->GetLayerColor( LAYER_ANCHOR ) );
215 ruler.SetShowTicks( false );
216 ruler.SetShowEndArrowHead( true );
217
218 view.Add( &ruler );
219 view.SetVisible( &ruler, false );
220
221 auto setCursor =
222 [&]()
223 {
224 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
225 };
226
227 const auto setInitialMsg =
228 [&]()
229 {
230 statusPopup.SetText( _( "Select the reference point on the item to move." ) );
231 };
232
233 const auto setDragMsg =
234 [&]()
235 {
236 statusPopup.SetText( _( "Select the point to define the new offset from." ) );
237 };
238
239 const auto setPopupPosition =
240 [&]()
241 {
242 statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
243 };
244
245 auto cleanup =
246 [&] ()
247 {
248 view.SetVisible( &ruler, false );
249 controls.SetAutoPan( false );
250 controls.CaptureCursor( false );
251 controls.ForceCursorPosition( false );
252 originSet = false;
253 setInitialMsg();
254 };
255
256 const auto applyVector =
257 [&]( const VECTOR2I& aMoveVec )
258 {
259 BOARD_COMMIT commit( frame() );
260 moveSelectionBy( selection, aMoveVec, commit, allowFreePads );
261 commit.Push( _( "Set Relative Position Interactively" ) );
262 };
263
264 Activate();
265 // Must be done after Activate() so that it gets set into the correct context
266 controls.ShowCursor( true );
267 controls.SetAutoPan( false );
268 controls.CaptureCursor( false );
269 controls.ForceCursorPosition( false );
270
271 // Set initial cursor
272 setCursor();
273
274 setInitialMsg();
275
276 setPopupPosition();
277 statusPopup.Popup();
278 canvas()->SetStatusPopup( statusPopup.GetPanel() );
279
280 while( TOOL_EVENT* evt = Wait() )
281 {
282 setCursor();
283 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
284 grid.SetUseGrid( view.GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
285 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : controls.GetMousePosition();
286 setPopupPosition();
287
288 if( !evt->IsActivate() && !evt->IsCancelInteractive() )
289 {
290 // If we are switching, the canvas may not be valid any more
291 cursorPos = grid.BestSnapAnchor( cursorPos, nullptr );
292 controls.ForceCursorPosition( true, cursorPos );
293 }
294 else
295 {
296 grid.FullReset();
297 }
298
299 if( evt->IsCancelInteractive() )
300 {
301 if( originSet )
302 {
303 cleanup();
304 }
305 else
306 {
307 frame()->PopTool( aEvent );
308 break;
309 }
310 }
311 else if( evt->IsActivate() )
312 {
313 if( originSet )
314 cleanup();
315
316 frame()->PopTool( aEvent );
317 break;
318 }
319 // click or drag starts
320 else if( !originSet && ( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
321 {
322 twoPtMgr.SetOrigin( cursorPos );
323 twoPtMgr.SetEnd( cursorPos );
324
325 setDragMsg();
326
327 controls.CaptureCursor( true );
328 controls.SetAutoPan( true );
329
330 originSet = true;
331 }
332 // second click or mouse up after drag ends
333 else if( originSet && ( evt->IsClick( BUT_LEFT ) || evt->IsMouseUp( BUT_LEFT ) ) )
334 {
335 // Hide the popup text so it doesn't get in the way
336 statusPopup.Hide();
337
338 // This is the forward vector from the ruler item
339 VECTOR2I offsetVector = twoPtMgr.GetEnd() - twoPtMgr.GetOrigin();
340 const VECTOR2I toReferencePtVector = twoPtMgr.GetOrigin() - twoPtMgr.GetEnd();
341
342 // Start with the value of that vector in the dialog (will match the rule HUD)
343 DIALOG_OFFSET_ITEM dlg( *frame(), offsetVector );
344
345 if( dlg.ShowModal() == wxID_OK )
346 {
347 const VECTOR2I move = toReferencePtVector + offsetVector;
348
349 applyVector( move );
350
351 // Leave the arrow in place but update it
352 twoPtMgr.SetEnd( twoPtMgr.GetOrigin() + offsetVector );
353 view.Update( &ruler, KIGFX::GEOMETRY );
354 canvas()->Refresh();
355 }
356
357 originSet = false;
358
359 setInitialMsg();
360
361 controls.SetAutoPan( false );
362 controls.CaptureCursor( false );
363
364 statusPopup.Popup();
365 }
366 // move or drag when origin set updates rules
367 else if( originSet && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
368 {
369 auto snap = LEADER_MODE::DIRECT;
370
371 if( frame()->IsType( FRAME_PCB_EDITOR ) )
372 snap = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_AngleSnapMode;
373 else
374 snap = GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode;
375
376 twoPtMgr.SetAngleSnap( snap );
377 // The end is fixed; we must update the origin
378 twoPtMgr.SetOrigin( cursorPos );
379
380 view.SetVisible( &ruler, true );
381 view.Update( &ruler, KIGFX::GEOMETRY );
382 }
383 else if( evt->IsAction( &ACTIONS::updateUnits ) )
384 {
385 if( frame()->GetUserUnits() != units )
386 {
387 units = frame()->GetUserUnits();
388 ruler.SwitchUnits( units );
389 view.Update( &ruler, KIGFX::GEOMETRY );
390 canvas()->ForceRefresh();
391 }
392
393 evt->SetPassEvent();
394 }
395 else if( evt->IsAction( &ACTIONS::updatePreferences ) )
396 {
397 invertXAxis = displayOptions().m_DisplayInvertXAxis;
398 invertYAxis = displayOptions().m_DisplayInvertYAxis;
399
401 {
402 invertXAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertXAxis;
403 invertYAxis = frame()->GetFootprintEditorSettings()->m_DisplayInvertYAxis;
404 }
405
406 ruler.UpdateDir( invertXAxis, invertYAxis );
407
408 view.Update( &ruler, KIGFX::GEOMETRY );
409 canvas()->Refresh();
410 evt->SetPassEvent();
411 }
412 else if( !evt->IsMouseAction() )
413 {
414 // Often this will end up changing the items we just moved, so the ruler will be
415 // in the wrong place. Clear it away and the user can restart
416 twoPtMgr.Reset();
417 view.SetVisible( &ruler, false );
418 view.Update( &ruler, KIGFX::GEOMETRY );
419
420 evt->SetPassEvent();
421 }
422 else
423 {
424 evt->SetPassEvent();
425 }
426 }
427
428 view.SetVisible( &ruler, false );
429 view.Remove( &ruler );
430
431 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
432 controls.SetAutoPan( false );
433 controls.CaptureCursor( false );
434 controls.ForceCursorPosition( false );
435
436 canvas()->SetStatusPopup( nullptr );
437 return 0;
438}
439
440
442 const VECTOR2I& aTranslation )
443{
444 VECTOR2I aggregateTranslation = aPosAnchor + aTranslation - GetSelectionAnchorPosition();
445
446 bool allowFreePads = m_isFootprintEditor
447 || ( frame()->GetPcbNewSettings()
448 && frame()->GetPcbNewSettings()->m_AllowFreePads );
449
450 moveSelectionBy( m_selection, aggregateTranslation, *m_commit, allowFreePads );
451 m_commit->Push( _( "Position Relative" ) );
452
453 if( m_selection.IsHover() )
455
457
458 canvas()->Refresh();
459 return 0;
460}
461
462
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
static TOOL_ACTION updatePreferences
Definition actions.h:272
static TOOL_ACTION updateUnits
Definition actions.h:203
static TOOL_ACTION measureTool
Definition actions.h:248
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
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:81
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition board_item.h:372
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:231
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:96
virtual VECTOR2I GetPosition() const
Definition eda_item.h:282
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:348
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:60
void SwitchUnits(EDA_UNITS aUnits)
Switch the ruler units.
Definition ruler_item.h:88
void UpdateDir(bool aFlipX, bool aFlipY)
Definition ruler_item.h:90
void SetColor(const COLOR4D &aColor)
Definition ruler_item.h:56
void SetShowTicks(bool aShow)
Definition ruler_item.h:58
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.
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:178
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
Returns the instance of #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:49
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:766
Class to handle a set of BOARD_ITEMs.
see class PGM_BASE
static void moveSelectionBy(const PCB_SELECTION &aSelection, const VECTOR2I &aMoveVec, BOARD_COMMIT &commit, 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:80
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683