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 m_frame->PushTool( aEvent );
98
99 auto setCursor =
100 [&]()
101 {
102 if( item )
103 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PLACE );
104 else if( isText )
105 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
106 else
107 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
108 };
109
110 auto cleanup =
111 [&] ()
112 {
114 m_view->ClearPreview();
115 delete item;
116 item = nullptr;
117 };
118
119 Activate();
120 // Must be done after Activate() so that it gets set into the correct context
121 controls->ShowCursor( true );
122 // Set initial cursor
123 setCursor();
124
125 if( aEvent.HasPosition() )
126 {
127 m_toolMgr->PrimeTool( aEvent.Position() );
128 }
129 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
130 {
131 m_toolMgr->PrimeTool( { 0, 0 } );
132 ignorePrimePosition = true;
133 }
134
135 SCH_COMMIT commit( m_toolMgr );
136
137 // Main loop: keep receiving events
138 while( TOOL_EVENT* evt = Wait() )
139 {
140 setCursor();
141 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
142 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
143
144 cursorPos = grid.Align( controls->GetMousePosition(), grid.GetItemGrid( item ) );
145 controls->ForceCursorPosition( true, cursorPos );
146
147 // The tool hotkey is interpreted as a click when drawing
148 bool isSyntheticClick = item && evt->IsActivate() && evt->HasPosition()
149 && evt->Matches( aEvent );
150
151 if( evt->IsCancelInteractive() )
152 {
153 m_frame->GetInfoBar()->Dismiss();
154
155 if( item )
156 {
157 cleanup();
158 }
159 else
160 {
161 m_frame->PopTool( aEvent );
162 break;
163 }
164 }
165 else if( evt->IsActivate() && !isSyntheticClick )
166 {
167 if( item && evt->IsMoveTool() )
168 {
169 // we're already moving our own item; ignore the move tool
170 evt->SetPassEvent( false );
171 continue;
172 }
173
174 if( item )
175 {
176 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel item creation." ) );
177 evt->SetPassEvent( false );
178 continue;
179 }
180
181 if( evt->IsPointEditor() )
182 {
183 // don't exit (the point editor runs in the background)
184 }
185 else if( evt->IsMoveTool() )
186 {
187 break;
188 }
189 else
190 {
191 m_frame->PopTool( aEvent );
192 break;
193 }
194 }
195 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) || isSyntheticClick )
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
357 m_frame->PushTool( aEvent );
358
359 auto setCursor =
360 [&]()
361 {
362 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
363 };
364
365 Activate();
366 // Must be done after Activate() so that it gets set into the correct context
367 getViewControls()->ShowCursor( true );
368 // Set initial cursor
369 setCursor();
370
371 // Main loop: keep receiving events
372 while( TOOL_EVENT* evt = Wait() )
373 {
374 setCursor();
375
376 if( evt->IsCancelInteractive() )
377 {
378 m_frame->PopTool( aEvent );
379 break;
380 }
381 else if( evt->IsActivate() )
382 {
383 m_frame->PopTool( aEvent );
384 break;
385 }
386 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
387 {
388 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
389
390 if( !symbol )
391 continue;
392
393 VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
394
395 symbol->Move( -cursorPos );
396
397 // Refresh the view without changing the viewport
398 m_view->SetCenter( m_view->GetCenter() + cursorPos );
399 m_view->RecacheAllItems();
400 m_frame->OnModify();
401 }
402 else if( evt->IsClick( BUT_RIGHT ) )
403 {
404 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
405 }
406 else
407 {
408 evt->SetPassEvent();
409 }
410 }
411
412 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
413 return 0;
414}
415
416
418{
420 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
421 SCH_PIN* sourcePin = nullptr;
422
423 if( !symbol )
424 return 0;
425
426 for( SCH_PIN* test : symbol->GetPins() )
427 {
428 if( test->m_Uuid == g_lastPin )
429 {
430 sourcePin = test;
431 break;
432 }
433 }
434
435 if( sourcePin )
436 {
437 SCH_PIN* pin = pinTool->RepeatPin( sourcePin );
438
439 if( pin )
440 g_lastPin = pin->m_Uuid;
441
443
444 if( pin )
445 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, pin );
446 }
447
448 return 0;
449}
450
451
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:96
virtual void ClearEditFlags()
Definition eda_item.h:166
virtual void SetPosition(const VECTOR2I &aPos)
Definition eda_item.h:283
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:152
const KIID m_Uuid
Definition eda_item.h:531
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
virtual bool Matches(const EDA_SEARCH_DATA &aSearchData, void *aAuxData) const
Compare the item against the search criteria in aSearchData.
Definition eda_item.h:416
bool IsNew() const
Definition eda_item.h:129
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
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:162
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:407
#define IS_NEW
New item, just created.
#define IS_MOVING
Item being moved.
@ LAYER_DEVICE
Definition layer_ids.h:472
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:71
@ SCH_TEXT_T
Definition typeinfo.h:148
@ SCH_PIN_T
Definition typeinfo.h:150
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683