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>
41
42
44
45
57
58
60{
62
63 auto isDrawingCondition =
64 [] ( const SELECTION& aSel )
65 {
66 SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aSel.Front() );
67 return item && item->IsNew();
68 };
69
70 m_menu->GetMenu().AddItem( ACTIONS::finishInteractive, isDrawingCondition, 2 );
71
72 return true;
73}
74
75
77{
78 KICAD_T type = aEvent.Parameter<KICAD_T>();
81 : nullptr;
82
84 return 0;
85
87
90 VECTOR2I cursorPos;
91 bool ignorePrimePosition = false;
92 SCH_ITEM* item = nullptr;
93 bool isText = aEvent.IsAction( &SCH_ACTIONS::placeSymbolText );
94 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
95
97
98 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
99 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
100
101 auto setCursor =
102 [&]()
103 {
104 if( item )
105 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PLACE );
106 else if( isText )
107 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
108 else
109 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
110 };
111
112 auto cleanup =
113 [&] ()
114 {
116 m_view->ClearPreview();
117 delete item;
118 item = nullptr;
119 };
120
121 Activate();
122 // Must be done after Activate() so that it gets set into the correct context
123 controls->ShowCursor( true );
124 // Set initial cursor
125 setCursor();
126
127 if( aEvent.HasPosition() )
128 {
129 m_toolMgr->PrimeTool( aEvent.Position() );
130 }
131 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
132 {
133 m_toolMgr->PrimeTool( { 0, 0 } );
134 ignorePrimePosition = true;
135 }
136
137 SCH_COMMIT commit( m_toolMgr );
138
139 // Main loop: keep receiving events
140 while( TOOL_EVENT* evt = Wait() )
141 {
142 setCursor();
143 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
144 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
145
146 cursorPos = grid.Align( controls->GetMousePosition(), grid.GetItemGrid( item ) );
147 controls->ForceCursorPosition( true, cursorPos );
148
149 // The tool hotkey is interpreted as a click when drawing
150 bool isSyntheticClick = item && evt->IsActivate() && evt->HasPosition()
151 && evt->Matches( aEvent );
152
153 if( evt->IsCancelInteractive() )
154 {
155 m_frame->GetInfoBar()->Dismiss();
156
157 if( item )
158 cleanup();
159 else
160 break;
161 }
162 else if( evt->IsActivate() && !isSyntheticClick )
163 {
164 if( item && evt->IsMoveTool() )
165 {
166 // we're already moving our own item; ignore the move tool
167 evt->SetPassEvent( false );
168 continue;
169 }
170
171 if( item )
172 {
173 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel item creation." ) );
174 evt->SetPassEvent( false );
175 continue;
176 }
177
178 if( evt->IsPointEditor() )
179 {
180 // don't exit (the point editor runs in the background)
181 continue;
182 }
183
184 if( evt->IsMoveTool() )
185 {
186 // Make sure we come back after the move tool runs
187 frame()->PushTool( originalEvent );
188 }
189
190 break;
191 }
192 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
193 || isSyntheticClick
194 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
195 {
196 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
197
198 if( !symbol )
199 continue;
200
201 // First click creates...
202 if( !item )
203 {
205
206 switch( type )
207 {
208 case SCH_PIN_T:
209 item = pinTool->CreatePin( cursorPos, symbol );
210
211 if( item )
212 g_lastPin = item->m_Uuid;
213
214 break;
215
216 case SCH_TEXT_T:
217 {
218 SCH_TEXT* text = new SCH_TEXT( cursorPos, wxEmptyString, LAYER_DEVICE );
219
220 text->SetParent( symbol );
221
222 if( m_frame->GetDrawSpecificUnit() )
223 text->SetUnit( m_frame->GetUnit() );
224
225 if( m_frame->GetDrawSpecificBodyStyle() )
226 text->SetBodyStyle( m_frame->GetBodyStyle() );
227
228 if( cfg )
229 {
230 text->SetTextSize( VECTOR2I( schIUScale.MilsToIU( cfg->m_Defaults.text_size ),
231 schIUScale.MilsToIU( cfg->m_Defaults.text_size ) ) );
232 }
233
234 text->SetTextAngle( m_lastTextAngle );
235
237
238 if( dlg.ShowModal() != wxID_OK || NoPrintableChars( text->GetText() ) )
239 delete text;
240 else
241 item = text;
242
243 break;
244 }
245
246 default:
247 wxFAIL_MSG( "TwoClickPlace(): unknown type" );
248 }
249
250 // If we started with a hotkey which has a position then warp back to that.
251 // Otherwise update to the current mouse position pinned inside the autoscroll
252 // boundaries.
253 if( evt->IsPrime() && !ignorePrimePosition )
254 {
255 cursorPos = grid.Align( evt->Position(), grid.GetItemGrid( item ) );
256 getViewControls()->WarpMouseCursor( cursorPos, true );
257 }
258 else
259 {
261 cursorPos = grid.Align( getViewControls()->GetMousePosition(), grid.GetItemGrid( item ) );
262 }
263
264 if( item )
265 {
266 item->SetPosition( VECTOR2I( cursorPos.x, -cursorPos.y ) );
267
268 item->SetFlags( IS_NEW | IS_MOVING );
269 m_view->ClearPreview();
270 m_view->AddToPreview( item, false );
271 m_selectionTool->AddItemToSel( item );
272
273 // update the cursor so it looks correct before another event
274 setCursor();
275 }
276
277 if( m_frame->GetMoveWarpsCursor() )
278 controls->SetCursorPosition( cursorPos, false );
279
280 m_toolMgr->PostAction( ACTIONS::refreshPreview );
281 }
282 // ... and second click places:
283 else
284 {
285 commit.Modify( symbol, m_frame->GetScreen() );
286
287 switch( item->Type() )
288 {
289 case SCH_PIN_T:
290 pinTool->PlacePin( &commit, static_cast<SCH_PIN*>( item ) );
291 item->ClearEditFlags();
292 commit.Push( _( "Place Pin" ) );
293 break;
294
295 case SCH_TEXT_T:
296 symbol->AddDrawItem( static_cast<SCH_TEXT*>( item ) );
297 item->ClearEditFlags();
298 commit.Push( _( "Draw Text" ) );
299 break;
300
301 default:
302 wxFAIL_MSG( "TwoClickPlace(): unknown type" );
303 }
304
305 item = nullptr;
306 m_view->ClearPreview();
307 m_frame->RebuildView();
308 }
309 }
310 else if( evt->IsClick( BUT_RIGHT ) )
311 {
312 // Warp after context menu only if dragging...
313 if( !item )
314 m_toolMgr->VetoContextMenuMouseWarp();
315
316 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
317 }
318 else if( evt->IsAction( &ACTIONS::increment ) )
319 {
320 if( evt->HasParameter() )
321 m_toolMgr->RunSynchronousAction( ACTIONS::increment, &commit, evt->Parameter<ACTIONS::INCREMENT>() );
322 else
323 m_toolMgr->RunSynchronousAction( ACTIONS::increment, &commit, ACTIONS::INCREMENT { 1, 0 } );
324 }
325 else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
326 {
327 item->SetPosition( VECTOR2I( cursorPos.x, cursorPos.y ) );
328 m_view->ClearPreview();
329 m_view->AddToPreview( item, false );
330 }
331 else
332 {
333 evt->SetPassEvent();
334 }
335
336 // Enable autopanning and cursor capture only when there is an item to be placed
337 controls->SetAutoPan( item != nullptr );
338 controls->CaptureCursor( item != nullptr );
339 }
340
341 controls->SetAutoPan( false );
342 controls->CaptureCursor( false );
343 controls->ForceCursorPosition( false );
344 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
345 return 0;
346}
347
348
350{
351 if( m_inPlaceAnchor )
352 return 0;
353
355 SCOPED_TOOL_PUSHER raii( m_frame, aEvent );
356
357 auto setCursor =
358 [&]()
359 {
360 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
361 };
362
363 Activate();
364 // Must be done after Activate() so that it gets set into the correct context
365 getViewControls()->ShowCursor( true );
366 // Set initial cursor
367 setCursor();
368
369 // Main loop: keep receiving events
370 while( TOOL_EVENT* evt = Wait() )
371 {
372 setCursor();
373
374 if( evt->IsCancelInteractive() || evt->IsActivate() )
375 {
376 break;
377 }
378 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
379 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
380 {
381 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
382
383 if( !symbol )
384 continue;
385
386 VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
387
388 symbol->Move( -cursorPos );
389
390 // Refresh the view without changing the viewport
391 m_view->SetCenter( m_view->GetCenter() + cursorPos );
392 m_view->RecacheAllItems();
393 m_frame->OnModify();
394 }
395 else if( evt->IsClick( BUT_RIGHT ) )
396 {
397 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
398 }
399 else
400 {
401 evt->SetPassEvent();
402 }
403 }
404
405 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
406 return 0;
407}
408
409
411{
413 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
414 SCH_PIN* sourcePin = nullptr;
415
416 if( !symbol )
417 return 0;
418
419 for( SCH_PIN* test : symbol->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ) )
420 {
421 if( test->m_Uuid == g_lastPin )
422 {
423 sourcePin = test;
424 break;
425 }
426 }
427
428 if( sourcePin )
429 {
430 SCH_PIN* pin = pinTool->RepeatPin( sourcePin );
431
432 if( pin )
433 g_lastPin = pin->m_Uuid;
434
436
437 if( pin )
438 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, pin );
439 }
440
441 return 0;
442}
443
444
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:562
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:95
@ ARROW
Definition cursors.h:43
@ BULLSEYE
Definition cursors.h:55
@ PENCIL
Definition cursors.h:49
#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:494
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:329
#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