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