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 = grid.BestSnapAnchor( controls()->GetMousePosition(), nullptr );
142
143 aPlacer->m_modifiers = evt->Modifier();
144
145 auto cleanup =
146 [&] ()
147 {
148 newItem = nullptr;
149 preview.Clear();
150 view()->Update( &preview );
151 controls()->SetAutoPan( false );
152 controls()->CaptureCursor( false );
153 controls()->ShowCursor( true );
154 controls()->ForceCursorPosition( false );
155 };
156
157 if( evt->IsCancelInteractive() )
158 {
159 if( aOptions & IPO_SINGLE_CLICK )
160 {
161 cleanup();
162 frame()->PopTool( aTool );
163 break;
164 }
165 else if( newItem )
166 {
167 cleanup();
168 }
169 else
170 {
171 frame()->PopTool( aTool );
172 break;
173 }
174 }
175 else if( evt->IsActivate() )
176 {
177 if( newItem )
178 cleanup();
179
180 if( evt->IsPointEditor() )
181 {
182 // don't exit (the point editor runs in the background)
183 }
184 else if( evt->IsMoveTool() )
185 {
186 // leave ourselves on the stack so we come back after the move
187 break;
188 }
189 else
190 {
191 frame()->PopTool( aTool );
192 break;
193 }
194 }
195 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
196 {
197 if( !newItem )
198 {
199 // create the item if possible
200 makeNewItem( cursorPos );
201
202 // no item created, so wait for another click
203 if( !newItem )
204 continue;
205
206 controls()->CaptureCursor( true );
207 controls()->SetAutoPan( true );
208 }
209 else
210 {
211 BOARD_ITEM* newBoardItem = newItem.release();
212 EDA_ITEM_FLAGS oldFlags = newBoardItem->GetFlags();
213
214 newBoardItem->ClearFlags();
215
216 if( !aPlacer->PlaceItem( newBoardItem, commit ) )
217 {
218 newBoardItem->SetFlags( oldFlags );
219 newItem.reset( newBoardItem );
220 continue;
221 }
222
223 preview.Clear();
224 commit.Push( aCommitMessage );
225
226 controls()->CaptureCursor( false );
227 controls()->SetAutoPan( false );
228 controls()->ShowCursor( true );
229
230 if( !( aOptions & IPO_REPEAT ) )
231 break;
232
233 if( aOptions & IPO_SINGLE_CLICK )
234 makeNewItem( controls()->GetCursorPosition() );
235
236 setCursor();
237 }
238 }
239 else if( evt->IsClick( BUT_RIGHT ) )
240 {
241 m_menu->ShowContextMenu( selection() );
242 }
243 else if( evt->IsAction( &PCB_ACTIONS::trackViaSizeChanged ) )
244 {
245 m_toolMgr->PostAction( ACTIONS::refreshPreview );
246 }
247 else if( newItem && evt->Category() == TC_COMMAND )
248 {
249 /*
250 * Handle any events that can affect the item as we move it around
251 */
252 if( TOOL_EVT_UTILS::IsRotateToolEvt( *evt ) && ( aOptions & IPO_ROTATE ) )
253 {
254 EDA_ANGLE rotationAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *frame(), *evt );
255 newItem->Rotate( newItem->GetPosition(), rotationAngle );
256 view()->Update( &preview );
257 }
258 else if( evt->IsAction( &PCB_ACTIONS::flip ) && ( aOptions & IPO_FLIP ) )
259 {
260 newItem->Flip( newItem->GetPosition(), frame()->GetPcbNewSettings()->m_FlipDirection );
261 view()->Update( &preview );
262 }
263 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
264 {
265 frame()->OnEditItemRequest( newItem.get() );
266
267 // Notify other tools of the changes
269 }
270 else if( evt->IsAction( &ACTIONS::refreshPreview ) )
271 {
272 preview.Clear();
273 newItem.reset();
274
275 makeNewItem( cursorPos );
276 aPlacer->SnapItem( newItem.get() );
277 view()->Update( &preview );
278 }
279 else
280 {
281 evt->SetPassEvent();
282 }
283 }
284 else if( newItem && evt->IsMotion() )
285 {
286 // track the cursor
287 newItem->SetPosition( cursorPos );
288 aPlacer->SnapItem( newItem.get() );
289
290 // Show a preview of the item
291 view()->Update( &preview );
292 }
293 else
294 {
295 evt->SetPassEvent();
296 }
297 }
298
299 view()->Remove( &preview );
300 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
301 controls()->SetAutoPan( false );
302 controls()->CaptureCursor( false );
303 controls()->ForceCursorPosition( false );
304
305 if( restoreAngleSnapMode )
306 {
307 *angleSnapMode = savedAngleSnapMode;
309 }
310}
311
312
314{
315 // A basic context manu. Many (but not all) tools will choose to override this.
316 CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu();
317
318 // cancel current tool goes in main context menu at the top if present
320 ctxMenu.AddSeparator( 1 );
321
322 // Finally, add the standard zoom/grid items
323 getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( *m_menu.get() );
324
325 return true;
326}
327
328
330{
331}
332
333
337
338
340{
341 return frame<PCB_BASE_FRAME>()->GetPcbNewSettings()->m_Display;
342}
343
345{
346 return static_cast<PCB_DRAW_PANEL_GAL*>( frame<PCB_BASE_FRAME>()->GetCanvas() );
347}
348
349
351{
352 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
353
354 return selTool->GetSelection();
355}
356
357
359{
360 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
361
362 return selTool->GetSelection();
363}
364
365
370
372{
374}
375
377{
378 if( frame<PCB_BASE_FRAME>()->IsType( FRAME_PCB_EDITOR ) )
379 return GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_AngleSnapMode;
380 else
381 return GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode;
382}
383
384
386{
387 // Base implementation performs no snapping
388}
389
390
392{
393 aCommit.Add( aItem );
394 return true;
395}
static TOOL_ACTION cancelInteractive
Definition actions.h:72
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:223
static TOOL_ACTION refreshPreview
Definition actions.h:158
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:79
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:353
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 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