KiCad PCB EDA Suite
Loading...
Searching...
No Matches
symbol_editor_drawing_tools.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 (C) 2019 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <tool/tool_manager.h>
23#include <sch_actions.h>
24#include <optional>
25#include <symbol_edit_frame.h>
26#include <widgets/wx_infobar.h>
27#include <sch_commit.h>
33#include <sch_shape.h>
34#include <sch_textbox.h>
35#include <pgm_base.h>
36#include <view/view_controls.h>
39#include <string_utils.h>
40#include <wx/msgdlg.h>
42
43
45
46
58
59
61{
63
64 auto isDrawingCondition =
65 [] ( const SELECTION& aSel )
66 {
67 SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aSel.Front() );
68 return item && item->IsNew();
69 };
70
71 m_menu->GetMenu().AddItem( ACTIONS::finishInteractive, isDrawingCondition, 2 );
72
73 return true;
74}
75
76
78{
79 KICAD_T type = aEvent.Parameter<KICAD_T>();
82 : nullptr;
83
85 return 0;
86
88
91 VECTOR2I cursorPos;
92 bool ignorePrimePosition = false;
93 SCH_ITEM* item = nullptr;
94 bool isText = aEvent.IsAction( &SCH_ACTIONS::placeSymbolText );
95 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
96
98
99 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
100 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
101
102 auto setCursor =
103 [&]()
104 {
105 if( item )
106 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PLACE );
107 else if( isText )
108 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
109 else
110 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
111 };
112
113 auto cleanup =
114 [&] ()
115 {
117 m_view->ClearPreview();
118 delete item;
119 item = nullptr;
120 };
121
122 Activate();
123 // Must be done after Activate() so that it gets set into the correct context
124 controls->ShowCursor( true );
125 // Set initial cursor
126 setCursor();
127
128 if( aEvent.HasPosition() )
129 {
130 m_toolMgr->PrimeTool( aEvent.Position() );
131 }
132 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
133 {
134 m_toolMgr->PrimeTool( { 0, 0 } );
135 ignorePrimePosition = true;
136 }
137
138 SCH_COMMIT commit( m_toolMgr );
139
140 // Main loop: keep receiving events
141 while( TOOL_EVENT* evt = Wait() )
142 {
143 setCursor();
144 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
145 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
146
147 cursorPos = grid.Align( controls->GetMousePosition(), grid.GetItemGrid( item ) );
148 controls->ForceCursorPosition( true, cursorPos );
149
150 // The tool hotkey is interpreted as a click when drawing
151 bool isSyntheticClick = item && evt->IsActivate() && evt->HasPosition()
152 && evt->Matches( aEvent );
153
154 if( evt->IsCancelInteractive() )
155 {
156 m_frame->GetInfoBar()->Dismiss();
157
158 if( item )
159 cleanup();
160 else
161 break;
162 }
163 else if( evt->IsActivate() && !isSyntheticClick )
164 {
165 if( item && evt->IsMoveTool() )
166 {
167 // we're already moving our own item; ignore the move tool
168 evt->SetPassEvent( false );
169 continue;
170 }
171
172 if( item )
173 {
174 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel item creation." ) );
175 evt->SetPassEvent( false );
176 continue;
177 }
178
179 if( evt->IsPointEditor() )
180 {
181 // don't exit (the point editor runs in the background)
182 continue;
183 }
184
185 if( evt->IsMoveTool() )
186 {
187 // Make sure we come back after the move tool runs
188 frame()->PushTool( originalEvent );
189 }
190
191 break;
192 }
193 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
194 || isSyntheticClick
195 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
196 {
197 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
198
199 if( !symbol )
200 continue;
201
202 // First click creates...
203 if( !item )
204 {
206
207 switch( type )
208 {
209 case SCH_PIN_T:
210 item = pinTool->CreatePin( cursorPos, symbol );
211
212 if( item )
213 g_lastPin = item->m_Uuid;
214
215 break;
216
217 case SCH_TEXT_T:
218 {
219 SCH_TEXT* text = new SCH_TEXT( cursorPos, wxEmptyString, LAYER_DEVICE );
220
221 text->SetParent( symbol );
222
223 if( m_frame->GetDrawSpecificUnit() )
224 text->SetUnit( m_frame->GetUnit() );
225
226 if( m_frame->GetDrawSpecificBodyStyle() )
227 text->SetBodyStyle( m_frame->GetBodyStyle() );
228
229 if( cfg )
230 {
231 text->SetTextSize( VECTOR2I( schIUScale.MilsToIU( cfg->m_Defaults.text_size ),
232 schIUScale.MilsToIU( cfg->m_Defaults.text_size ) ) );
233 }
234
235 text->SetTextAngle( m_lastTextAngle );
236
238
239 if( dlg.ShowModal() != wxID_OK || NoPrintableChars( text->GetText() ) )
240 delete text;
241 else
242 item = text;
243
244 break;
245 }
246
247 default:
248 wxFAIL_MSG( "TwoClickPlace(): unknown type" );
249 }
250
251 // If we started with a hotkey which has a position then warp back to that.
252 // Otherwise update to the current mouse position pinned inside the autoscroll
253 // boundaries.
254 if( evt->IsPrime() && !ignorePrimePosition )
255 {
256 cursorPos = grid.Align( evt->Position(), grid.GetItemGrid( item ) );
257 getViewControls()->WarpMouseCursor( cursorPos, true );
258 }
259 else
260 {
262 cursorPos = grid.Align( getViewControls()->GetMousePosition(), grid.GetItemGrid( item ) );
263 }
264
265 if( item )
266 {
267 item->SetPosition( VECTOR2I( cursorPos.x, -cursorPos.y ) );
268
269 item->SetFlags( IS_NEW | IS_MOVING );
270 m_view->ClearPreview();
271 m_view->AddToPreview( item, false );
272 m_selectionTool->AddItemToSel( item );
273
274 // update the cursor so it looks correct before another event
275 setCursor();
276 }
277
278 if( m_frame->GetMoveWarpsCursor() )
279 controls->SetCursorPosition( cursorPos, false );
280
281 m_toolMgr->PostAction( ACTIONS::refreshPreview );
282 }
283 // ... and second click places:
284 else
285 {
286 commit.Modify( symbol, m_frame->GetScreen() );
287
288 switch( item->Type() )
289 {
290 case SCH_PIN_T:
291 pinTool->PlacePin( &commit, static_cast<SCH_PIN*>( item ) );
292 item->ClearEditFlags();
293 commit.Push( _( "Place Pin" ) );
294 break;
295
296 case SCH_TEXT_T:
297 symbol->AddDrawItem( static_cast<SCH_TEXT*>( item ) );
298 item->ClearEditFlags();
299 commit.Push( _( "Draw Text" ) );
300 break;
301
302 default:
303 wxFAIL_MSG( "TwoClickPlace(): unknown type" );
304 }
305
306 item = nullptr;
307 m_view->ClearPreview();
308 m_frame->RebuildView();
309 }
310 }
311 else if( evt->IsClick( BUT_RIGHT ) )
312 {
313 // Warp after context menu only if dragging...
314 if( !item )
315 m_toolMgr->VetoContextMenuMouseWarp();
316
317 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
318 }
319 else if( evt->IsAction( &ACTIONS::increment ) )
320 {
321 if( evt->HasParameter() )
322 m_toolMgr->RunSynchronousAction( ACTIONS::increment, &commit, evt->Parameter<ACTIONS::INCREMENT>() );
323 else
324 m_toolMgr->RunSynchronousAction( ACTIONS::increment, &commit, ACTIONS::INCREMENT { 1, 0 } );
325 }
326 else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
327 {
328 item->SetPosition( VECTOR2I( cursorPos.x, cursorPos.y ) );
329 m_view->ClearPreview();
330 m_view->AddToPreview( item, false );
331 }
332 else
333 {
334 evt->SetPassEvent();
335 }
336
337 // Enable autopanning and cursor capture only when there is an item to be placed
338 controls->SetAutoPan( item != nullptr );
339 controls->CaptureCursor( item != nullptr );
340 }
341
342 controls->SetAutoPan( false );
343 controls->CaptureCursor( false );
344 controls->ForceCursorPosition( false );
345 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
346 return 0;
347}
348
349
351{
352 if( m_inPlaceAnchor )
353 return 0;
354
356 SCOPED_TOOL_PUSHER raii( m_frame, aEvent );
357
358 auto setCursor =
359 [&]()
360 {
361 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
362 };
363
364 Activate();
365 // Must be done after Activate() so that it gets set into the correct context
366 getViewControls()->ShowCursor( true );
367 // Set initial cursor
368 setCursor();
369
370 // Main loop: keep receiving events
371 while( TOOL_EVENT* evt = Wait() )
372 {
373 setCursor();
374
375 if( evt->IsCancelInteractive() || evt->IsActivate() )
376 {
377 break;
378 }
379 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
380 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
381 {
382 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
383
384 if( !symbol )
385 continue;
386
387 VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
388
389 symbol->Move( -cursorPos );
390
391 // Refresh the view without changing the viewport
392 m_view->SetCenter( m_view->GetCenter() + cursorPos );
393 m_view->RecacheAllItems();
394 m_frame->OnModify();
395 }
396 else if( evt->IsClick( BUT_RIGHT ) )
397 {
398 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
399 }
400 else
401 {
402 evt->SetPassEvent();
403 }
404 }
405
406 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
407 return 0;
408}
409
410
412{
414 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
415 SCH_PIN* sourcePin = nullptr;
416
417 if( !symbol )
418 return 0;
419
420 for( SCH_PIN* test : symbol->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ) )
421 {
422 if( test->m_Uuid == g_lastPin )
423 {
424 sourcePin = test;
425 break;
426 }
427 }
428
429 if( sourcePin )
430 {
431 SCH_PIN* pin = pinTool->RepeatPin( sourcePin );
432
433 if( pin )
434 g_lastPin = pin->m_Uuid;
435
437
438 if( pin )
439 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, pin );
440 }
441
442 return 0;
443}
444
445
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition actions.h:223
static TOOL_ACTION cursorDblClick
Definition actions.h:177
static TOOL_ACTION cursorClick
Definition actions.h:176
static TOOL_ACTION increment
Definition actions.h:90
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
static TOOL_ACTION refreshPreview
Definition actions.h:155
static TOOL_ACTION finishInteractive
Definition actions.h:69
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:102
int ShowModal() override
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
virtual void ClearEditFlags()
Definition eda_item.h:178
virtual void SetPosition(const VECTOR2I &aPos)
Definition eda_item.h:349
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
const KIID m_Uuid
Definition eda_item.h:599
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
virtual bool Matches(const EDA_SEARCH_DATA &aSearchData, void *aAuxData) const
Compare the item against the search criteria in aSearchData.
Definition eda_item.h:484
bool IsNew() const
Definition eda_item.h:131
An interface for classes handling user events controlling the view behavior such as zooming,...
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 WarpMouseCursor(const VECTOR2D &aPosition, bool aWorldCoordinates=false, bool aWarpView=false)=0
If enabled (.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
virtual VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const =0
Return the current mouse pointer position.
virtual void SetCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true, bool aTriggeredByArrows=false, long aArrowCommand=0)=0
Move cursor to the requested position expressed in world coordinates.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
virtual void PinCursorInsideNonAutoscrollArea(bool aWarpMouseCursor)=0
Definition kiid.h:46
Define a library symbol object.
Definition lib_symbol.h:114
void Move(const VECTOR2I &aOffset) override
Move the symbol aOffset.
void AddDrawItem(SCH_ITEM *aItem, bool aSort=true)
Add a new draw aItem to the draw object list and sort according to aSort.
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:546
static TOOL_ACTION placeSymbolPin
static TOOL_ACTION placeSymbolAnchor
static TOOL_ACTION placeSymbolText
static TOOL_ACTION repeatDrawItem
Commit for the schematic and symbol editors.
Definition sch_commit.h:54
void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
bool Init() override
Init() is called once upon a registration of the tool.
SCH_TOOL_BASE(const std::string &aName)
SCH_SELECTION_TOOL * m_selectionTool
int PlaceAnchor(const TOOL_EVENT &aEvent)
bool Init() override
Init() is called once upon a registration of the tool.
int RepeatDrawItem(const TOOL_EVENT &aEvent)
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
int TwoClickPlace(const TOOL_EVENT &aEvent)
GR_TEXT_H_ALIGN_T m_lastTextJust
Re-entrancy guards.
SCH_PIN * RepeatPin(const SCH_PIN *aSourcePin)
SCH_PIN * CreatePin(const VECTOR2I &aPosition, LIB_SYMBOL *aSymbol)
bool PlacePin(SCH_COMMIT *aCommit, SCH_PIN *aPin)
The symbol library editor main window.
KIGFX::VIEW_CONTROLS * getViewControls() const
Definition tool_base.cpp:40
KIGFX::VIEW * getView() const
Definition tool_base.cpp:34
Generic, UI-independent tool event.
Definition tool_event.h:167
bool HasPosition() const
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition tool_event.h:256
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:289
bool IsReactivate() const
Control whether the tool is first being pushed to the stack or being reactivated after a pause.
Definition tool_event.h:269
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:484
void Go(int(SYMBOL_EDIT_FRAME::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
std::unique_ptr< TOOL_MENU > m_menu
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
@ PLACE
Definition cursors.h:94
@ ARROW
Definition cursors.h:42
@ BULLSEYE
Definition cursors.h:54
@ PENCIL
Definition cursors.h:48
#define _(s)
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:444
#define IS_NEW
New item, just created.
#define IS_MOVING
Item being moved.
@ LAYER_DEVICE
Definition layer_ids.h:488
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
T * GetAppSettings(const char *aFilename)
bool NoPrintableChars(const wxString &aString)
Return true if the string is empty or contains only whitespace.
Increment event parameters.
Definition actions.h:328
#define ALL_UNITS
Definition symbol.h:150
#define ALL_BODY_STYLES
Definition symbol.h:151
KIBIS_PIN * pin
@ GR_TEXT_H_ALIGN_LEFT
@ MD_SHIFT
Definition tool_event.h:139
@ BUT_LEFT
Definition tool_event.h:128
@ BUT_RIGHT
Definition tool_event.h:129
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:70
@ SCH_TEXT_T
Definition typeinfo.h:147
@ SCH_PIN_T
Definition typeinfo.h:149
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708