KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_tool_base.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
20#include "pcb_tool_base.h"
21
22#include <tool/tool_manager.h>
23#include <board_commit.h>
25#include <footprint.h>
26#include <pcb_draw_panel_gal.h>
27#include <pgm_base.h>
29#include <pcbnew_settings.h>
33#include <tools/pcb_actions.h>
36#include <view/view_controls.h>
37
38
41 const wxString& aCommitMessage, int aOptions )
42{
43 using namespace std::placeholders;
44 std::unique_ptr<BOARD_ITEM> newItem;
45
46 TOOL_EVENT originalEvent = aTool; // This can change out from under us when the event loop runs
47 SCOPED_TOOL_PUSHER raii( frame(), originalEvent );
48
49 BOARD_COMMIT commit( frame() );
50
51 LEADER_MODE* angleSnapMode = nullptr;
52 LEADER_MODE savedAngleSnapMode = LEADER_MODE::DIRECT;
53 bool restoreAngleSnapMode = false;
54
55 if( frame()->IsType( FRAME_PCB_EDITOR ) )
56 {
57 angleSnapMode = &GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_AngleSnapMode;
58 }
59 else if( frame()->IsType( FRAME_FOOTPRINT_EDITOR ) )
60 {
61 angleSnapMode = &GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode;
62 }
63
64 if( !angleSnapMode )
65 {
66 PCB_VIEWERS_SETTINGS_BASE* viewerSettings = frame()->GetViewerSettingsBase();
67
68 if( viewerSettings )
69 angleSnapMode = &viewerSettings->m_ViewersDisplay.m_AngleSnapMode;
70 }
71
72 if( angleSnapMode && *angleSnapMode != LEADER_MODE::DIRECT )
73 {
74 savedAngleSnapMode = *angleSnapMode;
75 *angleSnapMode = LEADER_MODE::DIRECT;
76 restoreAngleSnapMode = true;
78 }
79
81
82 Activate();
83 // Must be done after Activate() so that it gets set into the correct context
84 controls()->ShowCursor( true );
85 controls()->ForceCursorPosition( false );
86 // do not capture or auto-pan until we start placing an item
87
88 PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
89
90 // Add a VIEW_GROUP that serves as a preview for the new item
91 PCB_SELECTION preview;
92 view()->Add( &preview );
93
94 aPlacer->m_board = board();
95 aPlacer->m_frame = frame();
96 aPlacer->m_modifiers = 0;
97
98 auto makeNewItem =
99 [&]( const VECTOR2I& aPosition )
100 {
101 if( frame()->GetModel() )
102 newItem = aPlacer->CreateItem();
103
104 if( newItem )
105 {
106 newItem->SetPosition( aPosition );
107 preview.Add( newItem.get() );
108
109 // footprints have more drawable parts
110 if( FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( newItem.get() ) )
111 {
112 fp->RunOnChildren( std::bind( &KIGFX::VIEW_GROUP::Add, &preview, _1 ),
114 }
115 }
116 };
117
118 if( aOptions & IPO_SINGLE_CLICK )
119 makeNewItem( controls()->GetCursorPosition() );
120
121 auto setCursor =
122 [&]()
123 {
124 if( !newItem )
125 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
126 else
127 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::PLACE );
128 };
129
130 // Set initial cursor
131 setCursor();
132
133 // Main loop: keep receiving events
134 while( TOOL_EVENT* evt = Wait() )
135 {
136 setCursor();
137
138 grid.SetSnap( false ); // Interactive placement tools need to set their own item snaps
139 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
140 VECTOR2I cursorPos = controls()->GetMousePosition();
141
142 if( !evt->IsActivate() && !evt->IsCancelInteractive() )
143 cursorPos = grid.ResolveSnap( cursorPos, nullptr ).position;
144 else
145 grid.FullReset();
146
147 aPlacer->m_modifiers = evt->Modifier();
148
149 auto cleanup =
150 [&] ()
151 {
152 newItem = nullptr;
153 preview.Clear();
154 view()->Update( &preview );
155 controls()->SetAutoPan( false );
156 controls()->CaptureCursor( false );
157 controls()->ShowCursor( true );
158 controls()->ForceCursorPosition( false );
159 };
160
161 if( evt->IsCancelInteractive() )
162 {
163 if( aOptions & IPO_SINGLE_CLICK )
164 {
165 cleanup();
166 break;
167 }
168 else if( newItem )
169 {
170 cleanup();
171 }
172 else
173 {
174 break;
175 }
176 }
177 else if( evt->IsActivate() )
178 {
179 if( newItem )
180 cleanup();
181
182 if( evt->IsPointEditor() )
183 {
184 // don't exit (the point editor runs in the background)
185 continue;
186 }
187
188 if( evt->IsMoveTool() )
189 {
190 // Make sure we come back after the move tool is done
191 frame()->PushTool( originalEvent );
192 }
193
194 break;
195 }
196 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
197 {
198 if( !newItem )
199 {
200 // create the item if possible
201 makeNewItem( cursorPos );
202
203 // no item created, so wait for another click
204 if( !newItem )
205 continue;
206
207 controls()->CaptureCursor( true );
208 controls()->SetAutoPan( true );
209 }
210 else
211 {
212 BOARD_ITEM* newBoardItem = newItem.release();
213 EDA_ITEM_FLAGS oldFlags = newBoardItem->GetFlags();
214
215 newBoardItem->ClearFlags();
216
217 if( !aPlacer->PlaceItem( newBoardItem, commit ) )
218 {
219 newBoardItem->SetFlags( oldFlags );
220 newItem.reset( newBoardItem );
221 continue;
222 }
223
224 preview.Clear();
225 commit.Push( aCommitMessage );
226
227 controls()->CaptureCursor( false );
228 controls()->SetAutoPan( false );
229 controls()->ShowCursor( true );
230
231 if( !( aOptions & IPO_REPEAT ) )
232 break;
233
234 if( aOptions & IPO_SINGLE_CLICK )
235 makeNewItem( controls()->GetCursorPosition() );
236
237 setCursor();
238 }
239 }
240 else if( evt->IsClick( BUT_RIGHT ) )
241 {
242 m_menu->ShowContextMenu( selection() );
243 }
244 else if( evt->IsAction( &PCB_ACTIONS::trackViaSizeChanged ) )
245 {
246 m_toolMgr->PostAction( ACTIONS::refreshPreview );
247 }
248 else if( newItem && evt->Category() == TC_COMMAND )
249 {
250 /*
251 * Handle any events that can affect the item as we move it around
252 */
253 if( TOOL_EVT_UTILS::IsRotateToolEvt( *evt ) && ( aOptions & IPO_ROTATE ) )
254 {
255 EDA_ANGLE rotationAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *frame(), *evt );
256 newItem->Rotate( newItem->GetPosition(), rotationAngle );
257 view()->Update( &preview );
258 }
259 else if( evt->IsAction( &PCB_ACTIONS::flip ) && ( aOptions & IPO_FLIP ) )
260 {
261 newItem->Flip( newItem->GetPosition(), frame()->GetPcbNewSettings()->m_FlipDirection );
262 view()->Update( &preview );
263 }
264 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
265 {
266 frame()->OnEditItemRequest( newItem.get() );
267
268 // Notify other tools of the changes
270 }
271 else if( evt->IsAction( &ACTIONS::refreshPreview ) )
272 {
273 preview.Clear();
274 newItem.reset();
275
276 makeNewItem( cursorPos );
277 aPlacer->SnapItem( newItem.get() );
278 view()->Update( &preview );
279 }
280 else
281 {
282 evt->SetPassEvent();
283 }
284 }
285 else if( newItem && evt->IsMotion() )
286 {
287 // track the cursor
288 newItem->SetPosition( cursorPos );
289 aPlacer->SnapItem( newItem.get() );
290
291 // Show a preview of the item
292 view()->Update( &preview );
293 }
294 else
295 {
296 evt->SetPassEvent();
297 }
298 }
299
300 view()->Remove( &preview );
301 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
302 controls()->SetAutoPan( false );
303 controls()->CaptureCursor( false );
304 controls()->ForceCursorPosition( false );
305
306 if( restoreAngleSnapMode )
307 {
308 *angleSnapMode = savedAngleSnapMode;
310 }
311}
312
313
315{
316 // A basic context manu. Many (but not all) tools will choose to override this.
317 CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu();
318
319 // cancel current tool goes in main context menu at the top if present
321 ctxMenu.AddSeparator( 1 );
322
323 // Finally, add the standard zoom/grid items
324 getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( *m_menu.get() );
325
326 return true;
327}
328
329
331{
332}
333
334
338
339
341{
342 return frame<PCB_BASE_FRAME>()->GetPcbNewSettings()->m_Display;
343}
344
346{
347 return static_cast<PCB_DRAW_PANEL_GAL*>( frame<PCB_BASE_FRAME>()->GetCanvas() );
348}
349
350
352{
353 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
354
355 return selTool->GetSelection();
356}
357
358
360{
361 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
362
363 return selTool->GetSelection();
364}
365
366
371
373{
375}
376
378{
379 if( frame<PCB_BASE_FRAME>()->IsType( FRAME_PCB_EDITOR ) )
380 return GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_AngleSnapMode;
381 else
382 return GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode;
383}
384
385
387{
388 if( frame<PCB_BASE_FRAME>()->IsType( FRAME_PCB_EDITOR ) )
389 return GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_AutoConstraints;
390 else
391 return GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AutoConstraints;
392}
393
394
396{
397 // Base implementation performs no snapping
398}
399
400
402{
403 aCommit.Add( aItem );
404 return true;
405}
static TOOL_ACTION cancelInteractive
Definition actions.h:68
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
static TOOL_ACTION refreshPreview
Definition actions.h:155
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:84
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:74
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
void AddSeparator(int aOrder=ANY_ORDER)
Add a separator to the menu.
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:160
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:167
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:350
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:87
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1) override
Add a VIEW_ITEM to the view.
Definition pcb_view.cpp:53
virtual void Remove(VIEW_ITEM *aItem) override
Remove a VIEW_ITEM from the view.
Definition pcb_view.cpp:70
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.
virtual void Add(VIEW_ITEM *aItem)
Add an item to the group.
static TOOL_ACTION trackViaSizeChanged
static TOOL_ACTION properties
Activation of the edit tool.
static TOOL_ACTION angleSnapModeChanged
Notification event when angle mode changes.
static TOOL_ACTION flip
Flipping of selected objects.
The selection tool: currently supports:
PCB_SELECTION & GetSelection()
T * frame() const
KIGFX::PCB_VIEW * view() const
virtual bool Is45Limited() const
Should the tool use its 45° mode option?
LEADER_MODE GetAngleSnapMode() const
Get the current angle snapping mode.
bool GetAutoConstraints() const
Should drawing tools author constraints automatically?
KIGFX::VIEW_CONTROLS * controls() const
BOARD * board() const
PCB_DRAW_PANEL_GAL * canvas() const
PCBNEW_SETTINGS::DISPLAY_OPTIONS & displayOptions() const
virtual void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
@ IPO_FLIP
Handle flip action in the loop by calling the item's flip method.
@ IPO_ROTATE
Handle the rotate action in the loop by calling the item's rotate method.
@ IPO_SINGLE_CLICK
Create an item immediately on placement starting, otherwise show the pencil cursor until the item is ...
@ IPO_REPEAT
Allow repeat placement of the item.
virtual bool Init() override
Init() is called once upon a registration of the tool.
virtual void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
void doInteractiveItemPlacement(const TOOL_EVENT &aTool, INTERACTIVE_PLACER_BASE *aPlacer, const wxString &aCommitMessage, int aOptions=IPO_ROTATE|IPO_FLIP|IPO_REPEAT)
Helper function for performing a common interactive idiom: wait for a left click, place an item there...
virtual bool Is90Limited() const
Should the tool limit drawing to horizontal and vertical only?
const PCB_SELECTION & selection() const
VIEWERS_DISPLAY_OPTIONS m_ViewersDisplay
static bool ShowAlways(const SELECTION &aSelection)
The default condition function (always returns true).
virtual void Add(EDA_ITEM *aItem)
A null aItem is ignored; the selection never holds null members.
Definition selection.cpp:38
virtual void Clear() override
Remove all the stored items from the group.
Definition selection.h:97
TOOL_MANAGER * GetManager() const
Return the instance of TOOL_MANAGER that takes care of the tool.
Definition tool_base.h:142
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:182
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
Generic, UI-independent tool event.
Definition tool_event.h:167
std::unique_ptr< TOOL_MENU > m_menu
The functions below are not yet implemented - their interface may change.
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 RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
@ PLACE
Definition cursors.h:94
@ ARROW
Definition cursors.h:42
@ PENCIL
Definition cursors.h:48
@ NO_RECURSE
Definition eda_item.h:52
std::uint32_t EDA_ITEM_FLAGS
@ FRAME_PCB_EDITOR
Definition frame_type.h:38
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:39
LEADER_MODE
The kind of the leader line.
@ DIRECT
Unconstrained point-to-point.
@ DEG90
90 Degree only
EDA_ANGLE GetEventRotationAngle(const PCB_BASE_EDIT_FRAME &aFrame, const TOOL_EVENT &aEvent)
Function getEventRotationAngle()
bool IsRotateToolEvt(const TOOL_EVENT &aEvt)
Function isRotateToolEvt()
see class PGM_BASE
T * GetAppSettings(const char *aFilename)
virtual void SnapItem(BOARD_ITEM *aItem)
PCB_BASE_EDIT_FRAME * m_frame
virtual std::unique_ptr< BOARD_ITEM > CreateItem()=0
virtual bool PlaceItem(BOARD_ITEM *aItem, BOARD_COMMIT &aCommit)
@ TC_COMMAND
Definition tool_event.h:53
@ BUT_LEFT
Definition tool_event.h:128
@ BUT_RIGHT
Definition tool_event.h:129
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683