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 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
198 {
199 if( !newItem )
200 {
201 // create the item if possible
202 makeNewItem( cursorPos );
203
204 // no item created, so wait for another click
205 if( !newItem )
206 continue;
207
208 controls()->CaptureCursor( true );
209 controls()->SetAutoPan( true );
210 }
211 else
212 {
213 BOARD_ITEM* newBoardItem = newItem.release();
214 EDA_ITEM_FLAGS oldFlags = newBoardItem->GetFlags();
215
216 newBoardItem->ClearFlags();
217
218 if( !aPlacer->PlaceItem( newBoardItem, commit ) )
219 {
220 newBoardItem->SetFlags( oldFlags );
221 newItem.reset( newBoardItem );
222 continue;
223 }
224
225 preview.Clear();
226 commit.Push( aCommitMessage );
227
228 controls()->CaptureCursor( false );
229 controls()->SetAutoPan( false );
230 controls()->ShowCursor( true );
231
232 if( !( aOptions & IPO_REPEAT ) )
233 break;
234
235 if( aOptions & IPO_SINGLE_CLICK )
236 makeNewItem( controls()->GetCursorPosition() );
237
238 setCursor();
239 }
240 }
241 else if( evt->IsClick( BUT_RIGHT ) )
242 {
243 m_menu->ShowContextMenu( selection() );
244 }
245 else if( evt->IsAction( &PCB_ACTIONS::trackViaSizeChanged ) )
246 {
247 m_toolMgr->PostAction( ACTIONS::refreshPreview );
248 }
249 else if( newItem && evt->Category() == TC_COMMAND )
250 {
251 /*
252 * Handle any events that can affect the item as we move it around
253 */
254 if( TOOL_EVT_UTILS::IsRotateToolEvt( *evt ) && ( aOptions & IPO_ROTATE ) )
255 {
256 EDA_ANGLE rotationAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *frame(), *evt );
257 newItem->Rotate( newItem->GetPosition(), rotationAngle );
258 view()->Update( &preview );
259 }
260 else if( evt->IsAction( &PCB_ACTIONS::flip ) && ( aOptions & IPO_FLIP ) )
261 {
262 newItem->Flip( newItem->GetPosition(), frame()->GetPcbNewSettings()->m_FlipDirection );
263 view()->Update( &preview );
264 }
265 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
266 {
267 frame()->OnEditItemRequest( newItem.get() );
268
269 // Notify other tools of the changes
271 }
272 else if( evt->IsAction( &ACTIONS::refreshPreview ) )
273 {
274 preview.Clear();
275 newItem.reset();
276
277 makeNewItem( cursorPos );
278 aPlacer->SnapItem( newItem.get() );
279 view()->Update( &preview );
280 }
281 else
282 {
283 evt->SetPassEvent();
284 }
285 }
286 else if( newItem && ( evt->IsMotion()
287 || evt->IsAction( &ACTIONS::refreshPreview ) ) )
288 {
289 // track the cursor
290 newItem->SetPosition( cursorPos );
291 aPlacer->SnapItem( newItem.get() );
292
293 // Show a preview of the item
294 view()->Update( &preview );
295 }
296 else
297 {
298 evt->SetPassEvent();
299 }
300 }
301
302 view()->Remove( &preview );
303 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
304 controls()->SetAutoPan( false );
305 controls()->CaptureCursor( false );
306 controls()->ForceCursorPosition( false );
307
308 if( restoreAngleSnapMode )
309 {
310 *angleSnapMode = savedAngleSnapMode;
312 }
313}
314
315
317{
318 // A basic context manu. Many (but not all) tools will choose to override this.
319 CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu();
320
321 // cancel current tool goes in main context menu at the top if present
323 ctxMenu.AddSeparator( 1 );
324
325 // Finally, add the standard zoom/grid items
326 getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( *m_menu.get() );
327
328 return true;
329}
330
331
333{
334}
335
336
340
341
343{
344 return frame<PCB_BASE_FRAME>()->GetPcbNewSettings()->m_Display;
345}
346
348{
349 return static_cast<PCB_DRAW_PANEL_GAL*>( frame<PCB_BASE_FRAME>()->GetCanvas() );
350}
351
352
354{
355 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
356
357 return selTool->GetSelection();
358}
359
360
362{
363 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
364
365 return selTool->GetSelection();
366}
367
368
373
375{
377}
378
380{
381 if( frame<PCB_BASE_FRAME>()->IsType( FRAME_PCB_EDITOR ) )
382 return GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_AngleSnapMode;
383 else
384 return GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode;
385}
386
387
389{
390 if( frame<PCB_BASE_FRAME>()->IsType( FRAME_PCB_EDITOR ) )
391 return GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_AutoConstraints;
392 else
393 return GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AutoConstraints;
394}
395
396
398{
399 // Base implementation performs no snapping
400}
401
402
404{
405 aCommit.Add( aItem );
406 return true;
407}
static TOOL_ACTION cancelInteractive
Definition actions.h:68
static TOOL_ACTION cursorDblClick
Definition actions.h:177
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
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 item had a property changed (except movement)
Definition actions.h:352
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
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
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:708