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-2020 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->Modifier( MD_ALT ) );
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_PART* part = m_frame->GetCurPart();
152 
153  if( !part )
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 ), part );
166  g_lastPinWeakPtr = item;
167  break;
168  }
169  case LIB_TEXT_T:
170  {
171  LIB_TEXT* text = new LIB_TEXT( part );
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 
178  DIALOG_LIB_EDIT_TEXT dlg( m_frame, text );
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_MOVED );
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( part );
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  part->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 
257  return 0;
258 }
259 
260 
262 {
263  SYMBOL_EDITOR_SETTINGS* settings = Pgm().GetSettingsManager().GetAppSettings<SYMBOL_EDITOR_SETTINGS>();
264  KICAD_T type = aEvent.Parameter<KICAD_T>();
265 
266  // We might be running as the same shape in another co-routine. Make sure that one
267  // gets whacked.
269 
271  getViewControls()->ShowCursor( true );
272 
273  std::string tool = aEvent.GetCommandStr().get();
274  m_frame->PushTool( tool );
275  Activate();
276 
277  LIB_PART* part = m_frame->GetCurPart();
278  LIB_ITEM* item = nullptr;
279 
280  // Prime the pump
281  if( aEvent.HasPosition() )
283 
284  auto setCursor =
285  [&]()
286  {
288  };
289 
290  // Set initial cursor
291  setCursor();
292 
293  // Main loop: keep receiving events
294  while( TOOL_EVENT* evt = Wait() )
295  {
296  setCursor();
297 
298  VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->Modifier( MD_ALT ) );
299 
300  auto cleanup =
301  [&] ()
302  {
304  m_view->ClearPreview();
305  delete item;
306  item = nullptr;
307  };
308 
309  if( evt->IsCancelInteractive() )
310  {
311  if( item )
312  cleanup();
313  else
314  {
315  m_frame->PopTool( tool );
316  break;
317  }
318  }
319  else if( evt->IsActivate() )
320  {
321  if( item )
322  cleanup();
323 
324  if( evt->IsPointEditor() )
325  {
326  // don't exit (the point editor runs in the background)
327  }
328  else if( evt->IsMoveTool() )
329  {
330  // leave ourselves on the stack so we come back after the move
331  break;
332  }
333  else
334  {
335  m_frame->PopTool( tool );
336  break;
337  }
338  }
339  else if( evt->IsClick( BUT_LEFT ) && !item )
340  {
341  if( !part )
342  continue;
343 
345 
346  switch( type )
347  {
348  case LIB_ARC_T: item = new LIB_ARC( part ); break;
349  case LIB_CIRCLE_T: item = new LIB_CIRCLE( part ); break;
350  case LIB_POLYLINE_T: item = new LIB_POLYLINE( part ); break;
351  case LIB_RECTANGLE_T: item = new LIB_RECTANGLE( part ); break;
352  default: break; // keep compiler quiet
353  }
354 
355  wxCHECK( item, 0 );
356 
357  item->SetWidth( settings->m_Defaults.line_width );
358  item->SetFillMode( m_lastFillStyle );
359  item->SetFlags( IS_NEW );
360  item->BeginEdit( wxPoint( cursorPos.x, -cursorPos.y ) );
361 
362  if( m_drawSpecificUnit )
363  item->SetUnit( m_frame->GetUnit() );
364 
366  item->SetConvert( m_frame->GetConvert() );
367 
368  m_selectionTool->AddItemToSel( item );
369  }
370  else if( item && ( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
371  || evt->IsAction( &EE_ACTIONS::finishDrawing ) ) )
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( part );
381  part->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 
418  return 0;
419 }
420 
421 
423 {
424  getViewControls()->ShowCursor( true );
425 
426  std::string tool = aEvent.GetCommandStr().get();
427  m_frame->PushTool( tool );
428  Activate();
429 
430  auto setCursor =
431  [&]()
432  {
434  };
435 
436  // Set initial cursor
437  setCursor();
438 
439  // Main loop: keep receiving events
440  while( TOOL_EVENT* evt = Wait() )
441  {
442  setCursor();
443 
444  if( evt->IsCancelInteractive() )
445  {
446  m_frame->PopTool( tool );
447  break;
448  }
449  else if( evt->IsActivate() )
450  {
451  m_frame->PopTool( tool );
452  break;
453  }
454  else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
455  {
456  LIB_PART* part = m_frame->GetCurPart();
457 
458  if( !part )
459  continue;
460 
461  VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->Modifier( MD_ALT ) );
462  wxPoint offset( -cursorPos.x, cursorPos.y );
463 
464  part->SetOffset( offset );
465 
466  // Refresh the view without changing the viewport
467  auto center = m_view->GetCenter();
468  center.x += offset.x;
469  center.y -= offset.y;
470  m_view->SetCenter( center );
472  m_frame->OnModify();
473  }
474  else if( evt->IsClick( BUT_RIGHT ) )
475  {
477  }
478  else
479  {
480  evt->SetPassEvent();
481  }
482  }
483 
485  return 0;
486 }
487 
488 
490 {
492  LIB_PART* part = m_frame->GetCurPart();
493  LIB_PIN* sourcePin = nullptr;
494 
495  if( !part )
496  return 0;
497 
498  // See if we have a pin matching our weak ptr
499  for( LIB_PIN* test = part->GetNextPin(); test; test = part->GetNextPin( test ) )
500  {
501  if( (void*) test == g_lastPinWeakPtr )
502  sourcePin = test;
503  }
504 
505  if( sourcePin )
506  {
507  LIB_PIN* pin = pinTool->RepeatPin( sourcePin );
508  g_lastPinWeakPtr = pin;
509 
511 
512  if( pin )
514  }
515 
516  return 0;
517 }
518 
519 
521 {
530 }
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:118
LIB_PIN * RepeatPin(const LIB_PIN *aSourcePin)
static TOOL_ACTION placeSymbolText
Definition: ee_actions.h:99
static TOOL_ACTION drawSymbolLines
Definition: ee_actions.h:103
TOOL_MENU m_menu
The functions below are not yet implemented - their interface may change.
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:171
Define a symbol library graphical text item.
Definition: lib_text.h:40
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:1552
virtual void SetWidth(int aWidth)=0
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:46
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:215
void SetFillMode(FILL_TYPE aFillMode)
Definition: lib_item.h:267
void RecacheAllItems()
Rebuild GAL display lists.
Definition: view.cpp:1372
virtual void PushTool(const std::string &actionName)
NB: the definition of "tool" is different at the user level.
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:141
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.
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:244
virtual void CalcEdit(const wxPoint &aPosition)
Calculates the attributes of an item at aPosition when it is being edited.
Definition: lib_item.h:131
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.
void SetCurrentCursor(KICURSOR cursor)
Set the current cursor shape for this panel.
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:97
The base class for drawable items used by schematic library components.
Definition: lib_item.h:62
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:70
bool IsNew() const
Definition: eda_item.h:168
void SetOffset(const wxPoint &aOffset)
Move the part aOffset.
Definition: lib_symbol.cpp:998
static TOOL_ACTION drawSymbolRectangle
Definition: ee_actions.h:100
bool Init() override
Init() is called once upon a registration of the tool.
Definition: ee_tool_base.h:65
void AddDrawItem(LIB_ITEM *aItem)
Add a new draw aItem to the draw object list.
Definition: lib_symbol.cpp:648
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 (.
static TOOL_ACTION placeSymbolPin
Definition: ee_actions.h:98
void SetCenter(const VECTOR2D &aCenter)
Set the center point of the VIEW (i.e.
Definition: view.cpp:579
void SetFlags(STATUS_FLAGS aMask)
Definition: eda_item.h:202
static TOOL_ACTION addItemToSel
Selects an item (specified as the event parameter).
Definition: ee_actions.h:57
virtual bool ContinueEdit(const wxPoint aPosition)
Continue an edit in progress at aPosition.
Definition: lib_item.h:113
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:443
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:173
int DrawShape(const TOOL_EVENT &aEvent)
void ClearPreview()
Definition: view.cpp:1530
static TOOL_ACTION repeatDrawItem
Definition: ee_actions.h:113
static TOOL_ACTION drawSymbolCircle
Definition: ee_actions.h:101
Define a library symbol object.
Definition: lib_symbol.h:93
EE_SELECTION_TOOL * m_selectionTool
Definition: ee_tool_base.h:176
static TOOL_ACTION clearSelection
Clears the current selection.
Definition: ee_actions.h:54
static TOOL_ACTION finishDrawing
Definition: ee_actions.h:105
#define IS_MOVED
Item being moved.
Definition: eda_item.h:105
LIB_PART * GetCurPart()
Return the current part being edited or NULL if none selected.
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:264
virtual void BeginEdit(const wxPoint aPosition)
Begin drawing a component library draw item at aPosition.
Definition: lib_item.h:101
OPT< std::string > GetCommandStr() const
Definition: tool_event.h:471
static void * g_lastPinWeakPtr
void SetUnit(int aUnit)
Definition: lib_item.h:261
see class PGM_BASE
int AddItemToSel(const TOOL_EVENT &aEvent)
void VetoContextMenuMouseWarp()
Disable mouse warping after the current context menu is closed.
Definition: tool_manager.h:428
static TOOL_ACTION drawSymbolArc
Definition: ee_actions.h:102
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:149
bool IsReactivate() const
Definition: tool_event.h:273
void Activate()
Run the tool.
void SetPosition(const wxPoint &aPosition) override
Definition: lib_item.h:215
A foundation class for a tool operating on a schematic or symbol.
Definition: ee_tool_base.h:48
bool HasPosition() const
Definition: tool_event.h:261
bool Init() override
Init() is called once upon a registration of the tool.
virtual void SetTextAngle(double aAngle)
Definition: eda_text.h:174
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
bool NoPrintableChars(wxString aString)
Return true if the string is empty or contains only whitespace.
Definition: string.cpp:370
void ClearEditFlags()
Definition: eda_item.h:221
virtual void EndEdit()
End an object editing action.
Definition: lib_item.h:120
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
#define IS_NEW
New item, just created.
Definition: eda_item.h:106
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:133
static TOOL_ACTION placeSymbolAnchor
Definition: ee_actions.h:104
static TOOL_ACTION refreshPreview
Definition: actions.h:109
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
static TOOL_ACTION cursorClick
Definition: actions.h:126
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:162
The symbol library editor main window.