KiCad PCB EDA Suite
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 (C) 2019-2021 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, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include <ee_actions.h>
26 #include <symbol_edit_frame.h>
29 #include <tools/ee_grid_helper.h>
30 #include <bitmaps.h>
31 #include <lib_text.h>
33 #include <lib_shape.h>
34 #include <pgm_base.h>
37 #include <string_utils.h>
38 
39 static void* g_lastPinWeakPtr;
40 
41 
43  EE_TOOL_BASE<SYMBOL_EDIT_FRAME>( "eeschema.SymbolDrawing" ),
44  m_lastTextAngle( 0.0 ),
45  m_lastFillStyle( FILL_T::NO_FILL ),
46  m_drawSpecificConvert( true ),
47  m_drawSpecificUnit( false )
48 {
49 }
50 
51 
53 {
55 
56  auto isDrawingCondition =
57  [] ( const SELECTION& aSel )
58  {
59  LIB_ITEM* item = (LIB_ITEM*) aSel.Front();
60  return item && item->IsNew();
61  };
62 
63  m_menu.GetMenu().AddItem( EE_ACTIONS::finishDrawing, isDrawingCondition, 2 );
64 
65  return true;
66 }
67 
68 
70 {
71  KICAD_T type = aEvent.Parameter<KICAD_T>();
72  auto* settings = Pgm().GetSettingsManager().GetAppSettings<SYMBOL_EDITOR_SETTINGS>();
73  auto* pinTool = type == LIB_PIN_T ? m_toolMgr->GetTool<SYMBOL_EDITOR_PIN_TOOL>() : nullptr;
74 
77  VECTOR2I cursorPos;
78  bool ignorePrimePosition = false;
79  LIB_ITEM* item = nullptr;
80  bool isText = aEvent.IsAction( &EE_ACTIONS::placeSymbolText );
81 
83 
84  std::string tool = aEvent.GetCommandStr().get();
85  m_frame->PushTool( tool );
86 
87  auto setCursor =
88  [&]()
89  {
90  if( item )
92  else if( isText )
94  else
96  };
97 
98  Activate();
99  // Must be done after Activate() so that it gets set into the correct context
100  controls->ShowCursor( true );
101  // Set initial cursor
102  setCursor();
103 
104  // Prime the pump
105  if( aEvent.HasPosition() )
106  {
107  m_toolMgr->PrimeTool( aEvent.Position() );
108  }
109  else if( !aEvent.IsReactivate() && isText )
110  {
111  m_toolMgr->PrimeTool( { 0, 0 } );
112  ignorePrimePosition = true;
113  }
114 
115  // Main loop: keep receiving events
116  while( TOOL_EVENT* evt = Wait() )
117  {
118  setCursor();
119  grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
120  grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
121 
122  cursorPos = grid.Align( controls->GetMousePosition() );
123  controls->ForceCursorPosition( true, cursorPos );
124 
125  auto cleanup =
126  [&] ()
127  {
129  m_view->ClearPreview();
130  delete item;
131  item = nullptr;
132  };
133 
134  if( evt->IsCancelInteractive() )
135  {
136  m_frame->GetInfoBar()->Dismiss();
137 
138  if( item )
139  {
140  cleanup();
141  }
142  else
143  {
144  m_frame->PopTool( tool );
145  break;
146  }
147  }
148  else if( evt->IsActivate() )
149  {
150  if( item && evt->IsMoveTool() )
151  {
152  // we're already moving our own item; ignore the move tool
153  evt->SetPassEvent( false );
154  continue;
155  }
156 
157  if( item )
158  {
159  m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel item creation." ) );
160  evt->SetPassEvent( false );
161  continue;
162  }
163 
164  if( evt->IsPointEditor() )
165  {
166  // don't exit (the point editor runs in the background)
167  }
168  else if( evt->IsMoveTool() )
169  {
170  break;
171  }
172  else
173  {
174  m_frame->PopTool( tool );
175  break;
176  }
177  }
178  else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
179  {
180  LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
181 
182  if( !symbol )
183  continue;
184 
185  // First click creates...
186  if( !item )
187  {
189 
190  switch( type )
191  {
192  case LIB_PIN_T:
193  {
194  item = pinTool->CreatePin( wxPoint( cursorPos.x, -cursorPos.y ), symbol );
195  g_lastPinWeakPtr = item;
196  break;
197  }
198  case LIB_TEXT_T:
199  {
200  LIB_TEXT* text = new LIB_TEXT( symbol );
201 
202  text->SetPosition( wxPoint( cursorPos.x, -cursorPos.y ) );
203  text->SetTextSize( wxSize( Mils2iu( settings->m_Defaults.text_size ),
204  Mils2iu( settings->m_Defaults.text_size ) ) );
205  text->SetTextAngle( m_lastTextAngle );
206 
208 
209  if( dlg.ShowModal() != wxID_OK || NoPrintableChars( text->GetText() ) )
210  delete text;
211  else
212  item = text;
213 
214  break;
215  }
216  default:
217  wxFAIL_MSG( wxT( "TwoClickPlace(): unknown type" ) );
218  }
219 
220  // If we started with a click on a tool button or menu then continue with the
221  // current mouse position. Otherwise warp back to the original click position.
222  if( evt->IsPrime() && ignorePrimePosition )
223  cursorPos = grid.Align( controls->GetMousePosition() );
224  else
225  controls->WarpCursor( cursorPos, true );
226 
227  if( item )
228  {
229  item->SetPosition( wxPoint( cursorPos.x, -cursorPos.y ) );
230 
231  item->SetFlags( IS_NEW | IS_MOVING );
232  m_view->ClearPreview();
233  m_view->AddToPreview( item->Clone() );
234  m_selectionTool->AddItemToSel( item );
235 
236  // update the cursor so it looks correct before another event
237  setCursor();
238  }
239 
240  controls->SetCursorPosition( cursorPos, false );
241  }
242  // ... and second click places:
243  else
244  {
245  m_frame->SaveCopyInUndoList( symbol );
246 
247  switch( item->Type() )
248  {
249  case LIB_PIN_T:
250  pinTool->PlacePin( (LIB_PIN*) item );
251  break;
252  case LIB_TEXT_T:
253  symbol->AddDrawItem( (LIB_TEXT*) item );
254  break;
255  default:
256  wxFAIL_MSG( wxT( "TwoClickPlace(): unknown type" ) );
257  }
258 
259  item->ClearEditFlags();
260  item = nullptr;
261  m_view->ClearPreview();
262 
263  m_frame->RebuildView();
264  m_frame->OnModify();
265  }
266  }
267  else if( evt->IsClick( BUT_RIGHT ) )
268  {
269  // Warp after context menu only if dragging...
270  if( !item )
272 
274  }
275  else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
276  {
277  item->SetPosition( wxPoint( cursorPos.x, -cursorPos.y ) );
278  m_view->ClearPreview();
279  m_view->AddToPreview( item->Clone() );
280  }
281  else
282  {
283  evt->SetPassEvent();
284  }
285 
286  // Enable autopanning and cursor capture only when there is an item to be placed
287  controls->SetAutoPan( item != nullptr );
288  controls->CaptureCursor( item != nullptr );
289  }
290 
291  controls->SetAutoPan( false );
292  controls->CaptureCursor( false );
293  controls->ForceCursorPosition( false );
295  return 0;
296 }
297 
298 
300 {
301  SETTINGS_MANAGER& settingsMgr = Pgm().GetSettingsManager();
303  SHAPE_T type = aEvent.Parameter<SHAPE_T>();
304  LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
305  LIB_ITEM* item = nullptr;
306 
307  // We might be running as the same shape in another co-routine. Make sure that one
308  // gets whacked.
310 
312 
313  std::string tool = aEvent.GetCommandStr().get();
314  m_frame->PushTool( tool );
315 
316  auto setCursor =
317  [&]()
318  {
320  };
321 
322  auto cleanup =
323  [&] ()
324  {
326  m_view->ClearPreview();
327  delete item;
328  item = nullptr;
329  };
330 
331  Activate();
332  // Must be done after Activate() so that it gets set into the correct context
333  getViewControls()->ShowCursor( true );
334  // Set initial cursor
335  setCursor();
336 
337  // Prime the pump
338  if( aEvent.HasPosition() )
340 
341  // Main loop: keep receiving events
342  while( TOOL_EVENT* evt = Wait() )
343  {
344  setCursor();
345 
346  VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
347 
348  if( evt->IsCancelInteractive() )
349  {
350  if( item )
351  cleanup();
352  else
353  {
354  m_frame->PopTool( tool );
355  break;
356  }
357  }
358  else if( evt->IsActivate() )
359  {
360  if( item )
361  cleanup();
362 
363  if( evt->IsPointEditor() )
364  {
365  // don't exit (the point editor runs in the background)
366  }
367  else if( evt->IsMoveTool() )
368  {
369  // leave ourselves on the stack so we come back after the move
370  break;
371  }
372  else
373  {
374  m_frame->PopTool( tool );
375  break;
376  }
377  }
378  else if( evt->IsClick( BUT_LEFT ) && !item )
379  {
380  // Update in case the symbol was changed while the tool was running
381  symbol = m_frame->GetCurSymbol();
382 
383  if( !symbol )
384  continue;
385 
387 
388  int lineWidth = Mils2iu( settings->m_Defaults.line_width );
389 
390  item = new LIB_SHAPE( symbol, type, lineWidth, m_lastFillStyle );
391  item->SetFlags( IS_NEW );
392  item->BeginEdit( wxPoint( cursorPos.x, -cursorPos.y ) );
393 
394  if( m_drawSpecificUnit )
395  item->SetUnit( m_frame->GetUnit() );
396 
398  item->SetConvert( m_frame->GetConvert() );
399 
400  m_selectionTool->AddItemToSel( item );
401  }
402  else if( item && ( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
403  || evt->IsAction( &EE_ACTIONS::finishDrawing ) ) )
404  {
405  if( symbol != m_frame->GetCurSymbol() )
406  {
407  symbol = m_frame->GetCurSymbol();
408  item->SetParent( symbol );
409  }
410 
411  if( evt->IsDblClick( BUT_LEFT ) || evt->IsAction( &EE_ACTIONS::finishDrawing )
412  || !item->ContinueEdit( wxPoint( cursorPos.x, -cursorPos.y ) ) )
413  {
414  item->EndEdit();
415  item->ClearEditFlags();
416  m_view->ClearPreview();
417 
418  m_frame->SaveCopyInUndoList( symbol );
419  symbol->AddDrawItem( item );
420  item = nullptr;
421 
422  m_frame->RebuildView();
423  m_frame->OnModify();
425  }
426  }
427  else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
428  {
429  item->CalcEdit( wxPoint( cursorPos.x, -cursorPos.y) );
430  m_view->ClearPreview();
431  m_view->AddToPreview( item->Clone() );
432  }
433  else if( evt->IsDblClick( BUT_LEFT ) && !item )
434  {
436  }
437  else if( evt->IsClick( BUT_RIGHT ) )
438  {
439  // Warp after context menu only if dragging...
440  if( !item )
442 
444  }
445  else
446  {
447  evt->SetPassEvent();
448  }
449 
450  // Enable autopanning and cursor capture only when there is a shape being drawn
451  getViewControls()->SetAutoPan( item != nullptr );
452  getViewControls()->CaptureCursor( item != nullptr );
453  }
454 
455  getViewControls()->SetAutoPan( false );
456  getViewControls()->CaptureCursor( false );
458  return 0;
459 }
460 
461 
463 {
464  std::string tool = aEvent.GetCommandStr().get();
465  m_frame->PushTool( tool );
466 
467  auto setCursor =
468  [&]()
469  {
471  };
472 
473  Activate();
474  // Must be done after Activate() so that it gets set into the correct context
475  getViewControls()->ShowCursor( true );
476  // Set initial cursor
477  setCursor();
478 
479  // Main loop: keep receiving events
480  while( TOOL_EVENT* evt = Wait() )
481  {
482  setCursor();
483 
484  if( evt->IsCancelInteractive() )
485  {
486  m_frame->PopTool( tool );
487  break;
488  }
489  else if( evt->IsActivate() )
490  {
491  m_frame->PopTool( tool );
492  break;
493  }
494  else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
495  {
496  LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
497 
498  if( !symbol )
499  continue;
500 
501  VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
502  wxPoint offset( -cursorPos.x, cursorPos.y );
503 
504  symbol->SetOffset( offset );
505 
506  // Refresh the view without changing the viewport
507  auto center = m_view->GetCenter();
508  center.x += offset.x;
509  center.y -= offset.y;
510  m_view->SetCenter( center );
512  m_frame->OnModify();
513  }
514  else if( evt->IsClick( BUT_RIGHT ) )
515  {
517  }
518  else
519  {
520  evt->SetPassEvent();
521  }
522  }
523 
525  return 0;
526 }
527 
528 
530 {
532  LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
533  LIB_PIN* sourcePin = nullptr;
534 
535  if( !symbol )
536  return 0;
537 
538  // See if we have a pin matching our weak ptr
539  for( LIB_PIN* test = symbol->GetNextPin(); test; test = symbol->GetNextPin( test ) )
540  {
541  if( (void*) test == g_lastPinWeakPtr )
542  sourcePin = test;
543  }
544 
545  if( sourcePin )
546  {
547  LIB_PIN* pin = pinTool->RepeatPin( sourcePin );
549 
551 
552  if( pin )
554  }
555 
556  return 0;
557 }
558 
559 
561 {
570 }
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag of the current symbol.
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
static TOOL_ACTION properties
Definition: ee_actions.h:121
LIB_PIN * RepeatPin(const LIB_PIN *aSourcePin)
static TOOL_ACTION placeSymbolText
Definition: ee_actions.h:102
static TOOL_ACTION drawSymbolLines
Definition: ee_actions.h:106
TOOL_MENU m_menu
The functions below are not yet implemented - their interface may change.
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
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.
static TOOL_ACTION activatePointEditor
Definition: actions.h:168
#define IS_NEW
New item, just created.
Define a symbol library graphical text item.
Definition: lib_text.h:39
void SetOffset(const wxPoint &aOffset)
Move the symbol aOffset.
const VECTOR2D & GetCenter() const
Return the center point of this VIEW (in world space coordinates).
Definition: view.h:334
void AddToPreview(EDA_ITEM *aItem, bool aTakeOwnership=true)
Definition: view.cpp:1614
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:46
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:214
void RecacheAllItems()
Rebuild GAL display lists.
Definition: view.cpp:1385
virtual void PushTool(const std::string &actionName)
NB: the definition of "tool" is different at the user level.
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:152
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:143
void PrimeTool(const VECTOR2D &aPosition)
"Prime" a tool by sending a cursor left-click event with the mouse position set to the passed in posi...
virtual void CalcEdit(const wxPoint &aPosition)
Calculate the attributes of an item at aPosition when it is being edited.
Definition: lib_item.h:135
Define a library symbol object.
Definition: lib_symbol.h:96
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
int PlaceAnchor(const TOOL_EVENT &aEvent)
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
void DeactivateTool()
Deactivate the currently active tool.
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:77
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:115
FILL_T
Definition: eda_shape.h:53
virtual EDA_ITEM * Clone() const
Create a duplicate of this item with linked list members set to NULL.
Definition: eda_item.cpp:83
The base class for drawable items used by schematic library symbols.
Definition: lib_item.h:61
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
Definition: tool_event.cpp:88
bool IsNew() const
Definition: eda_item.h:118
static TOOL_ACTION drawSymbolRectangle
Definition: ee_actions.h:103
bool Init() override
Init() is called once upon a registration of the tool.
Definition: ee_tool_base.h:66
EE_SELECTION & GetSelection()
Return the set of currently selected items.
int RepeatDrawItem(const TOOL_EVENT &aEvent)
void ShowInfoBarMsg(const wxString &aMsg, bool aShowCloseButton=false)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an info icon on the left of...
LIB_SYMBOL * GetCurSymbol() const
Return the current symbol being edited or NULL if none selected.
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
Definition: infobar.cpp:175
static TOOL_ACTION placeSymbolPin
Definition: ee_actions.h:101
void SetCenter(const VECTOR2D &aCenter)
Set the center point of the VIEW (i.e.
Definition: view.cpp:578
#define IS_MOVING
Item being moved.
static TOOL_ACTION addItemToSel
Selects an item (specified as the event parameter).
Definition: ee_actions.h:59
virtual void PopTool(const std::string &actionName)
SHAPE_T
Definition: eda_shape.h:40
virtual void CaptureCursor(bool aEnabled)
Force the cursor to stay within the drawing panel area.
T Parameter() const
Return a non-standard parameter assigned to the event.
Definition: tool_event.h:432
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
void SaveCopyInUndoList(EDA_ITEM *aItem, UNDO_REDO aUndoType=UNDO_REDO::LIBEDIT, bool aAppend=false)
Create a copy of the current symbol, and save it in the undo list.
Generic, UI-independent tool event.
Definition: tool_event.h:152
int DrawShape(const TOOL_EVENT &aEvent)
T * GetAppSettings(bool aLoadNow=true)
Returns a handle to the a given settings by type If the settings have already been loaded,...
void ClearPreview()
Definition: view.cpp:1592
static TOOL_ACTION repeatDrawItem
Definition: ee_actions.h:116
static TOOL_ACTION drawSymbolCircle
Definition: ee_actions.h:104
An interface for classes handling user events controlling the view behavior such as zooming,...
#define _(s)
EE_SELECTION_TOOL * m_selectionTool
Definition: ee_tool_base.h:179
void AddDrawItem(LIB_ITEM *aItem, bool aSort=true)
Add a new draw aItem to the draw object list and sort according to aSort.
Definition: lib_symbol.cpp:716
static TOOL_ACTION clearSelection
Clears the current selection.
Definition: ee_actions.h:56
static TOOL_ACTION finishDrawing
Definition: ee_actions.h:108
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
int TwoClickPlace(const TOOL_EVENT &aEvent)
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
void SetConvert(int aConvert)
Definition: lib_item.h:268
OPT< std::string > GetCommandStr() const
Definition: tool_event.h:460
static void * g_lastPinWeakPtr
void SetUnit(int aUnit)
Definition: lib_item.h:265
virtual bool ContinueEdit(const wxPoint &aPosition)
Continue an edit in progress at aPosition.
Definition: lib_item.h:117
see class PGM_BASE
int AddItemToSel(const TOOL_EVENT &aEvent)
virtual void BeginEdit(const wxPoint &aPosition)
Begin drawing a symbol library draw item at aPosition.
Definition: lib_item.h:105
void VetoContextMenuMouseWarp()
Disable mouse warping after the current context menu is closed.
Definition: tool_manager.h:422
static TOOL_ACTION drawSymbolArc
Definition: ee_actions.h:105
bool IsReactivate() const
Definition: tool_event.h:252
void Activate()
Run the tool.
WX_INFOBAR * GetInfoBar()
void SetPosition(const wxPoint &aPosition) override
Definition: lib_item.h:230
A foundation class for a tool operating on a schematic or symbol.
Definition: ee_tool_base.h:49
bool HasPosition() const
Definition: tool_event.h:240
bool Init() override
Init() is called once upon a registration of the tool.
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
void ClearEditFlags()
Definition: eda_item.h:171
virtual void EndEdit()
End an object editing action.
Definition: lib_item.h:124
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 ShowContextMenu(SELECTION &aSelection)
Helper function to set and immediately show a CONDITIONAL_MENU in concert with the given SELECTION.
Definition: tool_menu.cpp:59
bool NoPrintableChars(const wxString &aString)
Return true if the string is empty or contains only whitespace.
const VECTOR2D Position() const
Returns the point where dragging has started.
Definition: tool_event.h:263
static TOOL_ACTION placeSymbolAnchor
Definition: ee_actions.h:107
static TOOL_ACTION refreshPreview
Definition: actions.h:106
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
static TOOL_ACTION cursorClick
Definition: actions.h:123
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:112
The symbol library editor main window.