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_arc.h>
33 #include <lib_circle.h>
34 #include <lib_polyline.h>
35 #include <lib_rectangle.h>
36 #include <pgm_base.h>
39 #include <kicad_string.h>
40 
41 static void* g_lastPinWeakPtr;
42 
43 
45  EE_TOOL_BASE<SYMBOL_EDIT_FRAME>( "eeschema.SymbolDrawing" ),
46  m_lastTextAngle( 0.0 ),
47  m_lastFillStyle( FILL_TYPE::NO_FILL ),
48  m_drawSpecificConvert( true ),
49  m_drawSpecificUnit( false )
50 {
51 }
52 
53 
55 {
57 
58  auto isDrawingCondition =
59  [] ( const SELECTION& aSel )
60  {
61  LIB_ITEM* item = (LIB_ITEM*) aSel.Front();
62  return item && item->IsNew();
63  };
64 
65  m_menu.GetMenu().AddItem( EE_ACTIONS::finishDrawing, isDrawingCondition, 2 );
66 
67  return true;
68 }
69 
70 
72 {
73  KICAD_T type = aEvent.Parameter<KICAD_T>();
74  auto* settings = Pgm().GetSettingsManager().GetAppSettings<SYMBOL_EDITOR_SETTINGS>();
75  auto* pinTool = type == LIB_PIN_T ? m_toolMgr->GetTool<SYMBOL_EDITOR_PIN_TOOL>() : nullptr;
76  VECTOR2I cursorPos;
77  EDA_ITEM* item = nullptr;
78  bool isText = aEvent.IsAction( &EE_ACTIONS::placeSymbolText );
79 
81  getViewControls()->ShowCursor( true );
82 
83  std::string tool = aEvent.GetCommandStr().get();
84  m_frame->PushTool( tool );
85  Activate();
86 
87  // Prime the pump
88  if( aEvent.HasPosition() || ( isText && !aEvent.IsReactivate() ) )
90 
91  auto setCursor =
92  [&]()
93  {
94  if( item )
96  else if( isText )
98  else
100  };
101 
102  // Set initial cursor
103  setCursor();
104 
105  // Main loop: keep receiving events
106  while( TOOL_EVENT* evt = Wait() )
107  {
108  setCursor();
109 
110  cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
111 
112  auto cleanup =
113  [&] ()
114  {
116  m_view->ClearPreview();
117  delete item;
118  item = nullptr;
119  };
120 
121  if( evt->IsCancelInteractive() )
122  {
123  if( item )
124  {
125  cleanup();
126  }
127  else
128  {
129  m_frame->PopTool( tool );
130  break;
131  }
132  }
133  else if( evt->IsActivate() )
134  {
135  if( item )
136  cleanup();
137 
138  if( evt->IsMoveTool() )
139  {
140  // leave ourselves on the stack so we come back after the move
141  break;
142  }
143  else
144  {
145  m_frame->PopTool( tool );
146  break;
147  }
148  }
149  else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
150  {
151  LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
152 
153  if( !symbol )
154  continue;
155 
156  // First click creates...
157  if( !item )
158  {
160 
161  switch( type )
162  {
163  case LIB_PIN_T:
164  {
165  item = pinTool->CreatePin( wxPoint( cursorPos.x, -cursorPos.y ), symbol );
166  g_lastPinWeakPtr = item;
167  break;
168  }
169  case LIB_TEXT_T:
170  {
171  LIB_TEXT* text = new LIB_TEXT( symbol );
172 
173  text->SetPosition( wxPoint( cursorPos.x, -cursorPos.y ) );
174  text->SetTextSize( wxSize( Mils2iu( settings->m_Defaults.text_size ),
175  Mils2iu( settings->m_Defaults.text_size ) ) );
176  text->SetTextAngle( m_lastTextAngle );
177 
179 
180  if( dlg.ShowModal() != wxID_OK || NoPrintableChars( text->GetText() ) )
181  delete text;
182  else
183  item = text;
184 
185  break;
186  }
187  default:
188  wxFAIL_MSG( "TwoClickPlace(): unknown type" );
189  }
190 
191  // Restore cursor after dialog
192  getViewControls()->WarpCursor( getViewControls()->GetCursorPosition(), true );
193 
194  if( item )
195  {
196  item->SetFlags( IS_NEW | IS_MOVING );
197  m_view->ClearPreview();
198  m_view->AddToPreview( item->Clone() );
199  m_selectionTool->AddItemToSel( item );
200 
201  // update the cursor so it looks correct before another event
202  setCursor();
203  }
204 
205  getViewControls()->SetCursorPosition( cursorPos, false );
206  }
207  // ... and second click places:
208  else
209  {
210  m_frame->SaveCopyInUndoList( symbol );
211 
212  switch( item->Type() )
213  {
214  case LIB_PIN_T:
215  pinTool->PlacePin( (LIB_PIN*) item );
216  break;
217  case LIB_TEXT_T:
218  symbol->AddDrawItem( (LIB_TEXT*) item );
219  break;
220  default:
221  wxFAIL_MSG( "TwoClickPlace(): unknown type" );
222  }
223 
224  item->ClearEditFlags();
225  item = nullptr;
226  m_view->ClearPreview();
227 
228  m_frame->RebuildView();
229  m_frame->OnModify();
230  }
231  }
232  else if( evt->IsClick( BUT_RIGHT ) )
233  {
234  // Warp after context menu only if dragging...
235  if( !item )
237 
239  }
240  else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
241  {
242  static_cast<LIB_ITEM*>( item )->SetPosition( wxPoint( cursorPos.x, -cursorPos.y ) );
243  m_view->ClearPreview();
244  m_view->AddToPreview( item->Clone() );
245  }
246  else
247  {
248  evt->SetPassEvent();
249  }
250 
251  // Enable autopanning and cursor capture only when there is an item to be placed
252  getViewControls()->SetAutoPan( item != nullptr );
253  getViewControls()->CaptureCursor( item != nullptr );
254  }
255 
256  getViewControls()->SetAutoPan( false );
257  getViewControls()->CaptureCursor( false );
259  return 0;
260 }
261 
262 
264 {
265  SYMBOL_EDITOR_SETTINGS* settings = Pgm().GetSettingsManager().GetAppSettings<SYMBOL_EDITOR_SETTINGS>();
266  KICAD_T type = aEvent.Parameter<KICAD_T>();
267 
268  // We might be running as the same shape in another co-routine. Make sure that one
269  // gets whacked.
271 
273  getViewControls()->ShowCursor( true );
274 
275  std::string tool = aEvent.GetCommandStr().get();
276  m_frame->PushTool( tool );
277  Activate();
278 
279  LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
280  LIB_ITEM* item = nullptr;
281 
282  // Prime the pump
283  if( aEvent.HasPosition() )
285 
286  auto setCursor =
287  [&]()
288  {
290  };
291 
292  // Set initial cursor
293  setCursor();
294 
295  // Main loop: keep receiving events
296  while( TOOL_EVENT* evt = Wait() )
297  {
298  setCursor();
299 
300  VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
301 
302  auto cleanup =
303  [&] ()
304  {
306  m_view->ClearPreview();
307  delete item;
308  item = nullptr;
309  };
310 
311  if( evt->IsCancelInteractive() )
312  {
313  if( item )
314  cleanup();
315  else
316  {
317  m_frame->PopTool( tool );
318  break;
319  }
320  }
321  else if( evt->IsActivate() )
322  {
323  if( item )
324  cleanup();
325 
326  if( evt->IsPointEditor() )
327  {
328  // don't exit (the point editor runs in the background)
329  }
330  else if( evt->IsMoveTool() )
331  {
332  // leave ourselves on the stack so we come back after the move
333  break;
334  }
335  else
336  {
337  m_frame->PopTool( tool );
338  break;
339  }
340  }
341  else if( evt->IsClick( BUT_LEFT ) && !item )
342  {
343  if( !symbol )
344  continue;
345 
347 
348  switch( type )
349  {
350  case LIB_ARC_T: item = new LIB_ARC( symbol ); break;
351  case LIB_CIRCLE_T: item = new LIB_CIRCLE( symbol ); break;
352  case LIB_POLYLINE_T: item = new LIB_POLYLINE( symbol ); break;
353  case LIB_RECTANGLE_T: item = new LIB_RECTANGLE( symbol ); break;
354  default: break; // keep compiler quiet
355  }
356 
357  wxCHECK( item, 0 );
358 
359  item->SetWidth( Mils2iu( settings->m_Defaults.line_width ) );
360  item->SetFillMode( m_lastFillStyle );
361  item->SetFlags( IS_NEW );
362  item->BeginEdit( wxPoint( cursorPos.x, -cursorPos.y ) );
363 
364  if( m_drawSpecificUnit )
365  item->SetUnit( m_frame->GetUnit() );
366 
368  item->SetConvert( m_frame->GetConvert() );
369 
370  m_selectionTool->AddItemToSel( item );
371  }
372  else if( item && ( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
373  || evt->IsAction( &EE_ACTIONS::finishDrawing ) ) )
374  {
375  if( evt->IsDblClick( BUT_LEFT ) || evt->IsAction( &EE_ACTIONS::finishDrawing )
376  || !item->ContinueEdit( wxPoint( cursorPos.x, -cursorPos.y ) ) )
377  {
378  item->EndEdit();
379  item->ClearEditFlags();
380  m_view->ClearPreview();
381 
382  m_frame->SaveCopyInUndoList( symbol );
383  symbol->AddDrawItem( item );
384  item = nullptr;
385 
386  m_frame->RebuildView();
387  m_frame->OnModify();
389  }
390  }
391  else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
392  {
393  item->CalcEdit( wxPoint( cursorPos.x, -cursorPos.y) );
394  m_view->ClearPreview();
395  m_view->AddToPreview( item->Clone() );
396  }
397  else if( evt->IsDblClick( BUT_LEFT ) && !item )
398  {
400  }
401  else if( evt->IsClick( BUT_RIGHT ) )
402  {
403  // Warp after context menu only if dragging...
404  if( !item )
406 
408  }
409  else
410  {
411  evt->SetPassEvent();
412  }
413 
414  // Enable autopanning and cursor capture only when there is a shape being drawn
415  getViewControls()->SetAutoPan( item != nullptr );
416  getViewControls()->CaptureCursor( item != nullptr );
417  }
418 
419  getViewControls()->SetAutoPan( false );
420  getViewControls()->CaptureCursor( false );
422  return 0;
423 }
424 
425 
427 {
428  getViewControls()->ShowCursor( true );
429 
430  std::string tool = aEvent.GetCommandStr().get();
431  m_frame->PushTool( tool );
432  Activate();
433 
434  auto setCursor =
435  [&]()
436  {
438  };
439 
440  // Set initial cursor
441  setCursor();
442 
443  // Main loop: keep receiving events
444  while( TOOL_EVENT* evt = Wait() )
445  {
446  setCursor();
447 
448  if( evt->IsCancelInteractive() )
449  {
450  m_frame->PopTool( tool );
451  break;
452  }
453  else if( evt->IsActivate() )
454  {
455  m_frame->PopTool( tool );
456  break;
457  }
458  else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
459  {
460  LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
461 
462  if( !symbol )
463  continue;
464 
465  VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
466  wxPoint offset( -cursorPos.x, cursorPos.y );
467 
468  symbol->SetOffset( offset );
469 
470  // Refresh the view without changing the viewport
471  auto center = m_view->GetCenter();
472  center.x += offset.x;
473  center.y -= offset.y;
474  m_view->SetCenter( center );
476  m_frame->OnModify();
477  }
478  else if( evt->IsClick( BUT_RIGHT ) )
479  {
481  }
482  else
483  {
484  evt->SetPassEvent();
485  }
486  }
487 
489  return 0;
490 }
491 
492 
494 {
496  LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
497  LIB_PIN* sourcePin = nullptr;
498 
499  if( !symbol )
500  return 0;
501 
502  // See if we have a pin matching our weak ptr
503  for( LIB_PIN* test = symbol->GetNextPin(); test; test = symbol->GetNextPin( test ) )
504  {
505  if( (void*) test == g_lastPinWeakPtr )
506  sourcePin = test;
507  }
508 
509  if( sourcePin )
510  {
511  LIB_PIN* pin = pinTool->RepeatPin( sourcePin );
513 
515 
516  if( pin )
518  }
519 
520  return 0;
521 }
522 
523 
525 {
534 }
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:117
LIB_PIN * RepeatPin(const LIB_PIN *aSourcePin)
static TOOL_ACTION placeSymbolText
Definition: ee_actions.h:98
static TOOL_ACTION drawSymbolLines
Definition: ee_actions.h:102
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.
FILL_TYPE
The set of fill types used in plotting or drawing enclosed areas.
Definition: fill_type.h:28
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:333
void AddToPreview(EDA_ITEM *aItem, bool aTakeOwnership=true)
Definition: view.cpp:1547
virtual void SetWidth(int aWidth)=0
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:46
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:214
void SetFillMode(FILL_TYPE aFillMode)
Definition: lib_item.h:264
void RecacheAllItems()
Rebuild GAL display lists.
Definition: view.cpp:1380
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:128
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 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:99
bool NoPrintableChars(const wxString &aString)
Return true if the string is empty or contains only whitespace.
Definition: string.cpp:466
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:97
void SetCenter(const VECTOR2D &aCenter)
Set the center point of the VIEW (i.e.
Definition: view.cpp:579
#define IS_MOVING
Item being moved.
static TOOL_ACTION addItemToSel
Selects an item (specified as the event parameter).
Definition: ee_actions.h:56
virtual void PopTool(const std::string &actionName)
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:427
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)
void ClearPreview()
Definition: view.cpp:1525
static TOOL_ACTION repeatDrawItem
Definition: ee_actions.h:112
static TOOL_ACTION drawSymbolCircle
Definition: ee_actions.h:100
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:653
static TOOL_ACTION clearSelection
Clears the current selection.
Definition: ee_actions.h:53
static TOOL_ACTION finishDrawing
Definition: ee_actions.h:104
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:261
OPT< std::string > GetCommandStr() const
Definition: tool_event.h:455
static void * g_lastPinWeakPtr
void SetUnit(int aUnit)
Definition: lib_item.h:258
virtual bool ContinueEdit(const wxPoint &aPosition)
Continue an edit in progress at aPosition.
Definition: lib_item.h:110
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:98
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:101
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:117
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
static TOOL_ACTION placeSymbolAnchor
Definition: ee_actions.h:103
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.