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 <bitmaps.h>
30 #include <lib_text.h>
32 #include <lib_shape.h>
33 #include <pgm_base.h>
36 #include <string_utils.h>
37 
38 static void* g_lastPinWeakPtr;
39 
40 
42  EE_TOOL_BASE<SYMBOL_EDIT_FRAME>( "eeschema.SymbolDrawing" ),
43  m_lastTextAngle( 0.0 ),
44  m_lastFillStyle( FILL_T::NO_FILL ),
45  m_drawSpecificConvert( true ),
46  m_drawSpecificUnit( false )
47 {
48 }
49 
50 
52 {
54 
55  auto isDrawingCondition =
56  [] ( const SELECTION& aSel )
57  {
58  LIB_ITEM* item = (LIB_ITEM*) aSel.Front();
59  return item && item->IsNew();
60  };
61 
62  m_menu.GetMenu().AddItem( EE_ACTIONS::finishDrawing, isDrawingCondition, 2 );
63 
64  return true;
65 }
66 
67 
69 {
70  KICAD_T type = aEvent.Parameter<KICAD_T>();
71  auto* settings = Pgm().GetSettingsManager().GetAppSettings<SYMBOL_EDITOR_SETTINGS>();
72  auto* pinTool = type == LIB_PIN_T ? m_toolMgr->GetTool<SYMBOL_EDITOR_PIN_TOOL>() : nullptr;
73  VECTOR2I cursorPos;
74  EDA_ITEM* item = nullptr;
75  bool isText = aEvent.IsAction( &EE_ACTIONS::placeSymbolText );
76 
78 
79  std::string tool = aEvent.GetCommandStr().get();
80  m_frame->PushTool( tool );
81 
82  auto setCursor =
83  [&]()
84  {
85  if( item )
87  else if( isText )
89  else
91  };
92 
93  Activate();
94  // Must be done after Activate() so that it gets set into the correct context
95  getViewControls()->ShowCursor( true );
96  // Set initial cursor
97  setCursor();
98 
99  // Prime the pump
100  if( aEvent.HasPosition() || ( isText && !aEvent.IsReactivate() ) )
102 
103  // Main loop: keep receiving events
104  while( TOOL_EVENT* evt = Wait() )
105  {
106  setCursor();
107 
108  cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
109 
110  auto cleanup =
111  [&] ()
112  {
114  m_view->ClearPreview();
115  delete item;
116  item = nullptr;
117  };
118 
119  if( evt->IsCancelInteractive() )
120  {
121  if( item )
122  {
123  cleanup();
124  }
125  else
126  {
127  m_frame->PopTool( tool );
128  break;
129  }
130  }
131  else if( evt->IsActivate() )
132  {
133  if( item )
134  cleanup();
135 
136  if( evt->IsMoveTool() )
137  {
138  // leave ourselves on the stack so we come back after the move
139  break;
140  }
141  else
142  {
143  m_frame->PopTool( tool );
144  break;
145  }
146  }
147  else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
148  {
149  LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
150 
151  if( !symbol )
152  continue;
153 
154  // First click creates...
155  if( !item )
156  {
158 
159  switch( type )
160  {
161  case LIB_PIN_T:
162  {
163  item = pinTool->CreatePin( wxPoint( cursorPos.x, -cursorPos.y ), symbol );
164  g_lastPinWeakPtr = item;
165  break;
166  }
167  case LIB_TEXT_T:
168  {
169  LIB_TEXT* text = new LIB_TEXT( symbol );
170 
171  text->SetPosition( wxPoint( cursorPos.x, -cursorPos.y ) );
172  text->SetTextSize( wxSize( Mils2iu( settings->m_Defaults.text_size ),
173  Mils2iu( settings->m_Defaults.text_size ) ) );
174  text->SetTextAngle( m_lastTextAngle );
175 
177 
178  if( dlg.ShowModal() != wxID_OK || NoPrintableChars( text->GetText() ) )
179  delete text;
180  else
181  item = text;
182 
183  break;
184  }
185  default:
186  wxFAIL_MSG( "TwoClickPlace(): unknown type" );
187  }
188 
189  // Restore cursor after dialog
190  getViewControls()->WarpCursor( getViewControls()->GetCursorPosition(), true );
191 
192  if( item )
193  {
194  item->SetFlags( IS_NEW | IS_MOVING );
195  m_view->ClearPreview();
196  m_view->AddToPreview( item->Clone() );
197  m_selectionTool->AddItemToSel( item );
198 
199  // update the cursor so it looks correct before another event
200  setCursor();
201  }
202 
203  getViewControls()->SetCursorPosition( cursorPos, false );
204  }
205  // ... and second click places:
206  else
207  {
208  m_frame->SaveCopyInUndoList( symbol );
209 
210  switch( item->Type() )
211  {
212  case LIB_PIN_T:
213  pinTool->PlacePin( (LIB_PIN*) item );
214  break;
215  case LIB_TEXT_T:
216  symbol->AddDrawItem( (LIB_TEXT*) item );
217  break;
218  default:
219  wxFAIL_MSG( "TwoClickPlace(): unknown type" );
220  }
221 
222  item->ClearEditFlags();
223  item = nullptr;
224  m_view->ClearPreview();
225 
226  m_frame->RebuildView();
227  m_frame->OnModify();
228  }
229  }
230  else if( evt->IsClick( BUT_RIGHT ) )
231  {
232  // Warp after context menu only if dragging...
233  if( !item )
235 
237  }
238  else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
239  {
240  static_cast<LIB_ITEM*>( item )->SetPosition( wxPoint( cursorPos.x, -cursorPos.y ) );
241  m_view->ClearPreview();
242  m_view->AddToPreview( item->Clone() );
243  }
244  else
245  {
246  evt->SetPassEvent();
247  }
248 
249  // Enable autopanning and cursor capture only when there is an item to be placed
250  getViewControls()->SetAutoPan( item != nullptr );
251  getViewControls()->CaptureCursor( item != nullptr );
252  }
253 
254  getViewControls()->SetAutoPan( false );
255  getViewControls()->CaptureCursor( false );
257  return 0;
258 }
259 
260 
262 {
263  SETTINGS_MANAGER& settingsMgr = Pgm().GetSettingsManager();
265  SHAPE_T type = aEvent.Parameter<SHAPE_T>();
266  LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
267  LIB_ITEM* item = nullptr;
268 
269  // We might be running as the same shape in another co-routine. Make sure that one
270  // gets whacked.
272 
274 
275  std::string tool = aEvent.GetCommandStr().get();
276  m_frame->PushTool( tool );
277 
278  auto setCursor =
279  [&]()
280  {
282  };
283 
284  auto cleanup =
285  [&] ()
286  {
288  m_view->ClearPreview();
289  delete item;
290  item = nullptr;
291  };
292 
293  Activate();
294  // Must be done after Activate() so that it gets set into the correct context
295  getViewControls()->ShowCursor( true );
296  // Set initial cursor
297  setCursor();
298 
299  // Prime the pump
300  if( aEvent.HasPosition() )
302 
303  // Main loop: keep receiving events
304  while( TOOL_EVENT* evt = Wait() )
305  {
306  setCursor();
307 
308  VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
309 
310  if( evt->IsCancelInteractive() )
311  {
312  if( item )
313  cleanup();
314  else
315  {
316  m_frame->PopTool( tool );
317  break;
318  }
319  }
320  else if( evt->IsActivate() )
321  {
322  if( item )
323  cleanup();
324 
325  if( evt->IsPointEditor() )
326  {
327  // don't exit (the point editor runs in the background)
328  }
329  else if( evt->IsMoveTool() )
330  {
331  // leave ourselves on the stack so we come back after the move
332  break;
333  }
334  else
335  {
336  m_frame->PopTool( tool );
337  break;
338  }
339  }
340  else if( evt->IsClick( BUT_LEFT ) && !item )
341  {
342  // Update in case the symbol was changed while the tool was running
343  symbol = m_frame->GetCurSymbol();
344 
345  if( !symbol )
346  continue;
347 
349 
350  int lineWidth = Mils2iu( settings->m_Defaults.line_width );
351 
352  item = new LIB_SHAPE( symbol, type, lineWidth, m_lastFillStyle );
353  item->SetFlags( IS_NEW );
354  item->BeginEdit( wxPoint( cursorPos.x, -cursorPos.y ) );
355 
356  if( m_drawSpecificUnit )
357  item->SetUnit( m_frame->GetUnit() );
358 
360  item->SetConvert( m_frame->GetConvert() );
361 
362  m_selectionTool->AddItemToSel( item );
363  }
364  else if( item && ( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
365  || evt->IsAction( &EE_ACTIONS::finishDrawing ) ) )
366  {
367  if( symbol != m_frame->GetCurSymbol() )
368  {
369  symbol = m_frame->GetCurSymbol();
370  item->SetParent( symbol );
371  }
372 
373  if( evt->IsDblClick( BUT_LEFT ) || evt->IsAction( &EE_ACTIONS::finishDrawing )
374  || !item->ContinueEdit( wxPoint( cursorPos.x, -cursorPos.y ) ) )
375  {
376  item->EndEdit();
377  item->ClearEditFlags();
378  m_view->ClearPreview();
379 
380  m_frame->SaveCopyInUndoList( symbol );
381  symbol->AddDrawItem( item );
382  item = nullptr;
383 
384  m_frame->RebuildView();
385  m_frame->OnModify();
387  }
388  }
389  else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
390  {
391  item->CalcEdit( wxPoint( cursorPos.x, -cursorPos.y) );
392  m_view->ClearPreview();
393  m_view->AddToPreview( item->Clone() );
394  }
395  else if( evt->IsDblClick( BUT_LEFT ) && !item )
396  {
398  }
399  else if( evt->IsClick( BUT_RIGHT ) )
400  {
401  // Warp after context menu only if dragging...
402  if( !item )
404 
406  }
407  else
408  {
409  evt->SetPassEvent();
410  }
411 
412  // Enable autopanning and cursor capture only when there is a shape being drawn
413  getViewControls()->SetAutoPan( item != nullptr );
414  getViewControls()->CaptureCursor( item != nullptr );
415  }
416 
417  getViewControls()->SetAutoPan( false );
418  getViewControls()->CaptureCursor( false );
420  return 0;
421 }
422 
423 
425 {
426  std::string tool = aEvent.GetCommandStr().get();
427  m_frame->PushTool( tool );
428 
429  auto setCursor =
430  [&]()
431  {
433  };
434 
435  Activate();
436  // Must be done after Activate() so that it gets set into the correct context
437  getViewControls()->ShowCursor( true );
438  // Set initial cursor
439  setCursor();
440 
441  // Main loop: keep receiving events
442  while( TOOL_EVENT* evt = Wait() )
443  {
444  setCursor();
445 
446  if( evt->IsCancelInteractive() )
447  {
448  m_frame->PopTool( tool );
449  break;
450  }
451  else if( evt->IsActivate() )
452  {
453  m_frame->PopTool( tool );
454  break;
455  }
456  else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
457  {
458  LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
459 
460  if( !symbol )
461  continue;
462 
463  VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
464  wxPoint offset( -cursorPos.x, cursorPos.y );
465 
466  symbol->SetOffset( offset );
467 
468  // Refresh the view without changing the viewport
469  auto center = m_view->GetCenter();
470  center.x += offset.x;
471  center.y -= offset.y;
472  m_view->SetCenter( center );
474  m_frame->OnModify();
475  }
476  else if( evt->IsClick( BUT_RIGHT ) )
477  {
479  }
480  else
481  {
482  evt->SetPassEvent();
483  }
484  }
485 
487  return 0;
488 }
489 
490 
492 {
494  LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
495  LIB_PIN* sourcePin = nullptr;
496 
497  if( !symbol )
498  return 0;
499 
500  // See if we have a pin matching our weak ptr
501  for( LIB_PIN* test = symbol->GetNextPin(); test; test = symbol->GetNextPin( test ) )
502  {
503  if( (void*) test == g_lastPinWeakPtr )
504  sourcePin = test;
505  }
506 
507  if( sourcePin )
508  {
509  LIB_PIN* pin = pinTool->RepeatPin( sourcePin );
511 
513 
514  if( pin )
516  }
517 
518  return 0;
519 }
520 
521 
523 {
532 }
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:120
LIB_PIN * RepeatPin(const LIB_PIN *aSourcePin)
static TOOL_ACTION placeSymbolText
Definition: ee_actions.h:101
static TOOL_ACTION drawSymbolLines
Definition: ee_actions.h:105
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
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 CalcEdit(const wxPoint &aPosition)
Calculate the attributes of an item at aPosition when it is being edited.
Definition: lib_item.h:127
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:102
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)
virtual void WarpCursor(const VECTOR2D &aPosition, bool aWorldCoordinates=false, bool aWarpView=false)=0
If enabled (.
LIB_SYMBOL * GetCurSymbol() const
Return the current symbol being edited or NULL if none selected.
static TOOL_ACTION placeSymbolPin
Definition: ee_actions.h:100
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:115
static TOOL_ACTION drawSymbolCircle
Definition: ee_actions.h:103
EE_SELECTION_TOOL * m_selectionTool
Definition: ee_tool_base.h:177
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:701
static TOOL_ACTION clearSelection
Clears the current selection.
Definition: ee_actions.h:56
static TOOL_ACTION finishDrawing
Definition: ee_actions.h:107
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:260
OPT< std::string > GetCommandStr() const
Definition: tool_event.h:460
static void * g_lastPinWeakPtr
void SetUnit(int aUnit)
Definition: lib_item.h:257
virtual bool ContinueEdit(const wxPoint &aPosition)
Continue an edit in progress at aPosition.
Definition: lib_item.h:109
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:97
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:104
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:99
bool IsReactivate() const
Definition: tool_event.h:252
void Activate()
Run the tool.
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:116
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.
static TOOL_ACTION placeSymbolAnchor
Definition: ee_actions.h:106
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.