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  if( !symbol )
343  continue;
344 
346 
347  int lineWidth = Mils2iu( settings->m_Defaults.line_width );
348 
349  item = new LIB_SHAPE( symbol, type, lineWidth, m_lastFillStyle );
350  item->SetFlags( IS_NEW );
351  item->BeginEdit( wxPoint( cursorPos.x, -cursorPos.y ) );
352 
353  if( m_drawSpecificUnit )
354  item->SetUnit( m_frame->GetUnit() );
355 
357  item->SetConvert( m_frame->GetConvert() );
358 
359  m_selectionTool->AddItemToSel( item );
360  }
361  else if( item && ( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
362  || evt->IsAction( &EE_ACTIONS::finishDrawing ) ) )
363  {
364  if( evt->IsDblClick( BUT_LEFT ) || evt->IsAction( &EE_ACTIONS::finishDrawing )
365  || !item->ContinueEdit( wxPoint( cursorPos.x, -cursorPos.y ) ) )
366  {
367  item->EndEdit();
368  item->ClearEditFlags();
369  m_view->ClearPreview();
370 
371  m_frame->SaveCopyInUndoList( symbol );
372  symbol->AddDrawItem( item );
373  item = nullptr;
374 
375  m_frame->RebuildView();
376  m_frame->OnModify();
378  }
379  }
380  else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
381  {
382  item->CalcEdit( wxPoint( cursorPos.x, -cursorPos.y) );
383  m_view->ClearPreview();
384  m_view->AddToPreview( item->Clone() );
385  }
386  else if( evt->IsDblClick( BUT_LEFT ) && !item )
387  {
389  }
390  else if( evt->IsClick( BUT_RIGHT ) )
391  {
392  // Warp after context menu only if dragging...
393  if( !item )
395 
397  }
398  else
399  {
400  evt->SetPassEvent();
401  }
402 
403  // Enable autopanning and cursor capture only when there is a shape being drawn
404  getViewControls()->SetAutoPan( item != nullptr );
405  getViewControls()->CaptureCursor( item != nullptr );
406  }
407 
408  getViewControls()->SetAutoPan( false );
409  getViewControls()->CaptureCursor( false );
411  return 0;
412 }
413 
414 
416 {
417  std::string tool = aEvent.GetCommandStr().get();
418  m_frame->PushTool( tool );
419 
420  auto setCursor =
421  [&]()
422  {
424  };
425 
426  Activate();
427  // Must be done after Activate() so that it gets set into the correct context
428  getViewControls()->ShowCursor( true );
429  // Set initial cursor
430  setCursor();
431 
432  // Main loop: keep receiving events
433  while( TOOL_EVENT* evt = Wait() )
434  {
435  setCursor();
436 
437  if( evt->IsCancelInteractive() )
438  {
439  m_frame->PopTool( tool );
440  break;
441  }
442  else if( evt->IsActivate() )
443  {
444  m_frame->PopTool( tool );
445  break;
446  }
447  else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
448  {
449  LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
450 
451  if( !symbol )
452  continue;
453 
454  VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
455  wxPoint offset( -cursorPos.x, cursorPos.y );
456 
457  symbol->SetOffset( offset );
458 
459  // Refresh the view without changing the viewport
460  auto center = m_view->GetCenter();
461  center.x += offset.x;
462  center.y -= offset.y;
463  m_view->SetCenter( center );
465  m_frame->OnModify();
466  }
467  else if( evt->IsClick( BUT_RIGHT ) )
468  {
470  }
471  else
472  {
473  evt->SetPassEvent();
474  }
475  }
476 
478  return 0;
479 }
480 
481 
483 {
485  LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
486  LIB_PIN* sourcePin = nullptr;
487 
488  if( !symbol )
489  return 0;
490 
491  // See if we have a pin matching our weak ptr
492  for( LIB_PIN* test = symbol->GetNextPin(); test; test = symbol->GetNextPin( test ) )
493  {
494  if( (void*) test == g_lastPinWeakPtr )
495  sourcePin = test;
496  }
497 
498  if( sourcePin )
499  {
500  LIB_PIN* pin = pinTool->RepeatPin( sourcePin );
502 
504 
505  if( pin )
507  }
508 
509  return 0;
510 }
511 
512 
514 {
523 }
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:1556
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:1389
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:153
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
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:119
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:576
#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:1534
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:666
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:100
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:172
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:113
The symbol library editor main window.