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