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, 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
24#include "pcb_tool_base.h"
25
26#include <tool/tool_manager.h>
27#include <board_commit.h>
29#include <footprint.h>
30#include <pcb_draw_panel_gal.h>
31#include <pgm_base.h>
33#include <pcbnew_settings.h>
37#include <tools/pcb_actions.h>
40#include <view/view_controls.h>
41
42
45 const wxString& aCommitMessage, int aOptions )
46{
47 using namespace std::placeholders;
48 std::unique_ptr<BOARD_ITEM> newItem;
49
50 frame()->PushTool( aTool );
51
52 BOARD_COMMIT commit( frame() );
53
54 LEADER_MODE* angleSnapMode = nullptr;
55 LEADER_MODE savedAngleSnapMode = LEADER_MODE::DIRECT;
56 bool restoreAngleSnapMode = false;
57
58 if( frame()->IsType( FRAME_PCB_EDITOR ) )
59 {
60 angleSnapMode = &GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_AngleSnapMode;
61 }
62 else if( frame()->IsType( FRAME_FOOTPRINT_EDITOR ) )
63 {
64 angleSnapMode = &GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode;
65 }
66
67 if( !angleSnapMode )
68 {
69 PCB_VIEWERS_SETTINGS_BASE* viewerSettings = frame()->GetViewerSettingsBase();
70
71 if( viewerSettings )
72 angleSnapMode = &viewerSettings->m_ViewersDisplay.m_AngleSnapMode;
73 }
74
75 if( angleSnapMode && *angleSnapMode != LEADER_MODE::DIRECT )
76 {
77 savedAngleSnapMode = *angleSnapMode;
78 *angleSnapMode = LEADER_MODE::DIRECT;
79 restoreAngleSnapMode = true;
81 }
82
84
85 Activate();
86 // Must be done after Activate() so that it gets set into the correct context
87 controls()->ShowCursor( true );
88 controls()->ForceCursorPosition( false );
89 // do not capture or auto-pan until we start placing an item
90
91 PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
92
93 // Add a VIEW_GROUP that serves as a preview for the new item
94 PCB_SELECTION preview;
95 view()->Add( &preview );
96
97 aPlacer->m_board = board();
98 aPlacer->m_frame = frame();
99 aPlacer->m_modifiers = 0;
100
101 auto makeNewItem =
102 [&]( const VECTOR2I& aPosition )
103 {
104 if( frame()->GetModel() )
105 newItem = aPlacer->CreateItem();
106
107 if( newItem )
108 {
109 newItem->SetPosition( aPosition );
110 preview.Add( newItem.get() );
111
112 // footprints have more drawable parts
113 if( FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( newItem.get() ) )
114 fp->RunOnChildren( std::bind( &KIGFX::VIEW_GROUP::Add, &preview, _1 ),
116 }
117 };
118
119 if( aOptions & IPO_SINGLE_CLICK )
120 makeNewItem( controls()->GetCursorPosition() );
121
122 auto setCursor =
123 [&]()
124 {
125 if( !newItem )
126 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
127 else
128 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::PLACE );
129 };
130
131 // Set initial cursor
132 setCursor();
133
134 // Main loop: keep receiving events
135 while( TOOL_EVENT* evt = Wait() )
136 {
137 setCursor();
138
139 grid.SetSnap( false ); // Interactive placement tools need to set their own item snaps
140 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
141 VECTOR2I cursorPos = controls()->GetMousePosition();
142
143 if( !evt->IsActivate() && !evt->IsCancelInteractive() )
144 {
145 cursorPos = grid.BestSnapAnchor( cursorPos, nullptr );
146 }
147 else
148 {
149 grid.FullReset();
150 }
151
152 aPlacer->m_modifiers = evt->Modifier();
153
154 auto cleanup =
155 [&] ()
156 {
157 newItem = nullptr;
158 preview.Clear();
159 view()->Update( &preview );
160 controls()->SetAutoPan( false );
161 controls()->CaptureCursor( false );
162 controls()->ShowCursor( true );
163 controls()->ForceCursorPosition( false );
164 };
165
166 if( evt->IsCancelInteractive() )
167 {
168 if( aOptions & IPO_SINGLE_CLICK )
169 {
170 cleanup();
171 frame()->PopTool( aTool );
172 break;
173 }
174 else if( newItem )
175 {
176 cleanup();
177 }
178 else
179 {
180 frame()->PopTool( aTool );
181 break;
182 }
183 }
184 else if( evt->IsActivate() )
185 {
186 if( newItem )
187 cleanup();
188
189 if( evt->IsPointEditor() )
190 {
191 // don't exit (the point editor runs in the background)
192 }
193 else if( evt->IsMoveTool() )
194 {
195 // leave ourselves on the stack so we come back after the move
196 break;
197 }
198 else
199 {
200 frame()->PopTool( aTool );
201 break;
202 }
203 }
204 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
205 {
206 if( !newItem )
207 {
208 // create the item if possible
209 makeNewItem( cursorPos );
210
211 // no item created, so wait for another click
212 if( !newItem )
213 continue;
214
215 controls()->CaptureCursor( true );
216 controls()->SetAutoPan( true );
217 }
218 else
219 {
220 BOARD_ITEM* newBoardItem = newItem.release();
221 EDA_ITEM_FLAGS oldFlags = newBoardItem->GetFlags();
222
223 newBoardItem->ClearFlags();
224
225 if( !aPlacer->PlaceItem( newBoardItem, commit ) )
226 {
227 newBoardItem->SetFlags( oldFlags );
228 newItem.reset( newBoardItem );
229 continue;
230 }
231
232 preview.Clear();
233 commit.Push( aCommitMessage );
234
235 controls()->CaptureCursor( false );
236 controls()->SetAutoPan( false );
237 controls()->ShowCursor( true );
238
239 if( !( aOptions & IPO_REPEAT ) )
240 break;
241
242 if( aOptions & IPO_SINGLE_CLICK )
243 makeNewItem( controls()->GetCursorPosition() );
244
245 setCursor();
246 }
247 }
248 else if( evt->IsClick( BUT_RIGHT ) )
249 {
250 m_menu->ShowContextMenu( selection() );
251 }
252 else if( evt->IsAction( &PCB_ACTIONS::trackViaSizeChanged ) )
253 {
254 m_toolMgr->PostAction( ACTIONS::refreshPreview );
255 }
256 else if( newItem && evt->Category() == TC_COMMAND )
257 {
258 /*
259 * Handle any events that can affect the item as we move it around
260 */
261 if( TOOL_EVT_UTILS::IsRotateToolEvt( *evt ) && ( aOptions & IPO_ROTATE ) )
262 {
263 EDA_ANGLE rotationAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *frame(), *evt );
264 newItem->Rotate( newItem->GetPosition(), rotationAngle );
265 view()->Update( &preview );
266 }
267 else if( evt->IsAction( &PCB_ACTIONS::flip ) && ( aOptions & IPO_FLIP ) )
268 {
269 newItem->Flip( newItem->GetPosition(), frame()->GetPcbNewSettings()->m_FlipDirection );
270 view()->Update( &preview );
271 }
272 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
273 {
274 frame()->OnEditItemRequest( newItem.get() );
275
276 // Notify other tools of the changes
278 }
279 else if( evt->IsAction( &ACTIONS::refreshPreview ) )
280 {
281 preview.Clear();
282 newItem.reset();
283
284 makeNewItem( cursorPos );
285 aPlacer->SnapItem( newItem.get() );
286 view()->Update( &preview );
287 }
288 else
289 {
290 evt->SetPassEvent();
291 }
292 }
293 else if( newItem && evt->IsMotion() )
294 {
295 // track the cursor
296 newItem->SetPosition( cursorPos );
297 aPlacer->SnapItem( newItem.get() );
298
299 // Show a preview of the item
300 view()->Update( &preview );
301 }
302 else
303 {
304 evt->SetPassEvent();
305 }
306 }
307
308 view()->Remove( &preview );
309 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
310 controls()->SetAutoPan( false );
311 controls()->CaptureCursor( false );
312 controls()->ForceCursorPosition( false );
313
314 if( restoreAngleSnapMode )
315 {
316 *angleSnapMode = savedAngleSnapMode;
318 }
319}
320
321
323{
324 // A basic context manu. Many (but not all) tools will choose to override this.
325 CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu();
326
327 // cancel current tool goes in main context menu at the top if present
329 ctxMenu.AddSeparator( 1 );
330
331 // Finally, add the standard zoom/grid items
332 getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( *m_menu.get() );
333
334 return true;
335}
336
337
339{
340}
341
342
346
347
349{
350 return frame<PCB_BASE_FRAME>()->GetPcbNewSettings()->m_Display;
351}
352
354{
355 return static_cast<PCB_DRAW_PANEL_GAL*>( frame<PCB_BASE_FRAME>()->GetCanvas() );
356}
357
358
360{
361 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
362
363 return selTool->GetSelection();
364}
365
366
368{
369 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
370
371 return selTool->GetSelection();
372}
373
374
379
381{
383}
384
386{
387 if( frame<PCB_BASE_FRAME>()->IsType( FRAME_PCB_EDITOR ) )
388 return GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_AngleSnapMode;
389 else
390 return GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode;
391}
392
393
395{
396 // Base implementation performs no snapping
397}
398
399
401{
402 aCommit.Add( aItem );
403 return true;
404}
static TOOL_ACTION cancelInteractive
Definition actions.h:72
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:224
static TOOL_ACTION refreshPreview
Definition actions.h:159
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:83
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:78
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:142
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:144
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:145
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
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: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
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.
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)
Definition selection.cpp:42
virtual void Clear() override
Remove all the stored items from the group.
Definition selection.h:98
TOOL_MANAGER * GetManager() const
Return the instance of TOOL_MANAGER that takes care of the tool.
Definition tool_base.h:146
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:186
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
Generic, UI-independent tool event.
Definition tool_event.h:171
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:98
@ ARROW
Definition cursors.h:46
@ PENCIL
Definition cursors.h:52
@ NO_RECURSE
Definition eda_item.h:52
std::uint32_t EDA_ITEM_FLAGS
@ FRAME_PCB_EDITOR
Definition frame_type.h:42
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:43
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:57
@ BUT_LEFT
Definition tool_event.h:132
@ BUT_RIGHT
Definition tool_event.h:133
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695