KiCad PCB EDA Suite
pl_point_editor.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 <functional>
26 using namespace std::placeholders;
27 
28 #include <tool/tool_manager.h>
29 #include <tool/actions.h>
30 #include <view/view_controls.h>
32 #include <confirm.h>
33 #include <bitmaps.h>
34 #include <status_popup.h>
37 #include "pl_editor_frame.h"
38 #include "pl_point_editor.h"
39 #include "properties_frame.h"
41 
42 // Few constants to avoid using bare numbers for point indices
44 {
46 };
47 
49 {
51 };
52 
54 {
55 public:
56  static std::shared_ptr<EDIT_POINTS> Make( EDA_ITEM* aItem )
57  {
58  std::shared_ptr<EDIT_POINTS> points = std::make_shared<EDIT_POINTS>( aItem );
59 
60  if( !aItem )
61  return points;
62 
63  // Generate list of edit points based on the item type
64  switch( aItem->Type() )
65  {
66  case WSG_LINE_T:
67  {
68  DS_DRAW_ITEM_LINE* line = static_cast<DS_DRAW_ITEM_LINE*>( aItem );
69  points->AddPoint( line->GetStart() );
70  points->AddPoint( line->GetEnd() );
71  break;
72  }
73  case WSG_RECT_T:
74  {
75  DS_DRAW_ITEM_RECT* rect = static_cast<DS_DRAW_ITEM_RECT*>( aItem );
76  wxPoint topLeft = rect->GetStart();
77  wxPoint botRight = rect->GetEnd();
78 
79  if( topLeft.y > botRight.y )
80  std::swap( topLeft.y, botRight.y );
81 
82  if( topLeft.x > botRight.x )
83  std::swap( topLeft.x, botRight.x );
84 
85  points->AddPoint( (VECTOR2I) topLeft );
86  points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
87  points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
88  points->AddPoint( (VECTOR2I) botRight );
89  break;
90  }
91  default:
92  points.reset();
93  break;
94  }
95 
96  return points;
97  }
98 
99 private:
101 };
102 
103 
105  TOOL_INTERACTIVE( "plEditor.PointEditor" ),
106  m_frame( nullptr ),
107  m_selectionTool( nullptr ),
108  m_editedPoint( nullptr )
109 {
110 }
111 
112 
114 {
115  if( aReason == MODEL_RELOAD )
116  {
117  // Init variables used by every drawing tool
118  m_frame = getEditFrame<PL_EDITOR_FRAME>();
119  }
120 
121  m_editPoints.reset();
122 }
123 
124 
126 {
127  m_frame = getEditFrame<PL_EDITOR_FRAME>();
129  return true;
130 }
131 
132 
134 {
135  EDIT_POINT* point = m_editedPoint;
136 
137  if( aEvent.IsMotion() )
138  point = m_editPoints->FindPoint( aEvent.Position(), getView() );
139  else if( aEvent.IsDrag( BUT_LEFT ) )
140  point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
141  else
142  point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
143 
144  if( m_editedPoint != point )
145  setEditedPoint( point );
146 }
147 
148 
149 int PL_POINT_EDITOR::Main( const TOOL_EVENT& aEvent )
150 {
151  static KICAD_T pointTypes[] = { WSG_LINE_T, WSG_RECT_T, EOT };
152 
154  const PL_SELECTION& selection = m_selectionTool->GetSelection();
155 
156  if( selection.Size() != 1 || !selection.Front()->IsType( pointTypes ) )
157  return 0;
158 
159  EDA_ITEM* item = (EDA_ITEM*) selection.Front();
160 
161  // Wait till drawing tool is done
162  if( item->IsNew() )
163  return 0;
164 
165  Activate();
166  // Must be done after Activate() so that it gets set into the correct context
167  controls->ShowCursor( true );
168 
170 
171  if( !m_editPoints )
172  return 0;
173 
174  getView()->Add( m_editPoints.get() );
175  setEditedPoint( nullptr );
176  updateEditedPoint( aEvent );
177  bool inDrag = false;
178  bool modified = false;
179 
180  // Main loop: keep receiving events
181  while( TOOL_EVENT* evt = Wait() )
182  {
183  if( !m_editPoints || evt->IsSelectionEvent() )
184  break;
185 
186  if ( !inDrag )
187  updateEditedPoint( *evt );
188 
189  if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
190  {
191  if( !inDrag )
192  {
194  controls->ForceCursorPosition( false );
195  inDrag = true;
196  modified = true;
197  }
198 
199  m_editedPoint->SetPosition( controls->GetCursorPosition( !evt->DisableGridSnapping() ) );
200 
201  updateItem();
202  updatePoints();
203  }
204 
205  else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
206  {
207  controls->SetAutoPan( false );
208  inDrag = false;
209  }
210 
211  else if( evt->IsCancelInteractive() || evt->IsActivate() )
212  {
213  if( inDrag ) // Restore the last change
214  {
216  inDrag = false;
217  modified = false;
218  }
219  else if( evt->IsCancelInteractive() )
220  break;
221 
222  if( evt->IsActivate() && !evt->IsMoveTool() )
223  break;
224  }
225 
226  else
227  evt->SetPassEvent();
228 
229  controls->SetAutoPan( inDrag );
230  controls->CaptureCursor( inDrag );
231  }
232 
233  controls->SetAutoPan( false );
234  controls->CaptureCursor( false );
235 
236  if( m_editPoints )
237  {
238  getView()->Remove( m_editPoints.get() );
239 
240  if( modified )
241  m_frame->OnModify();
242 
243  m_editPoints.reset();
244  m_frame->GetCanvas()->Refresh();
245  }
246 
247  return 0;
248 }
249 
250 
251 void pinEditedCorner( int editedPointIndex, int minWidth, int minHeight, VECTOR2I& topLeft,
252  VECTOR2I& topRight, VECTOR2I& botLeft, VECTOR2I& botRight )
253 {
254  switch( editedPointIndex )
255  {
256  case RECT_TOPLEFT:
257  // pin edited point within opposite corner
258  topLeft.x = std::min( topLeft.x, botRight.x - minWidth );
259  topLeft.y = std::min( topLeft.y, botRight.y - minHeight );
260 
261  // push edited point edges to adjacent corners
262  topRight.y = topLeft.y;
263  botLeft.x = topLeft.x;
264 
265  break;
266 
267  case RECT_TOPRIGHT:
268  // pin edited point within opposite corner
269  topRight.x = std::max( topRight.x, botLeft.x + minWidth );
270  topRight.y = std::min( topRight.y, botLeft.y - minHeight );
271 
272  // push edited point edges to adjacent corners
273  topLeft.y = topRight.y;
274  botRight.x = topRight.x;
275 
276  break;
277 
278  case RECT_BOTLEFT:
279  // pin edited point within opposite corner
280  botLeft.x = std::min( botLeft.x, topRight.x - minWidth );
281  botLeft.y = std::max( botLeft.y, topRight.y + minHeight );
282 
283  // push edited point edges to adjacent corners
284  botRight.y = botLeft.y;
285  topLeft.x = botLeft.x;
286 
287  break;
288 
289  case RECT_BOTRIGHT:
290  // pin edited point within opposite corner
291  botRight.x = std::max( botRight.x, topLeft.x + minWidth );
292  botRight.y = std::max( botRight.y, topLeft.y + minHeight );
293 
294  // push edited point edges to adjacent corners
295  botLeft.y = botRight.y;
296  topRight.x = botRight.x;
297 
298  break;
299  }
300 }
301 
302 
304 {
305  EDA_ITEM* item = m_editPoints->GetParent();
306 
307  if( !item )
308  return;
309 
310  DS_DATA_ITEM* dataItem = static_cast<DS_DRAW_ITEM_BASE*>( item )->GetPeer();
311 
312  // The current item is perhaps not the main item if we have a set of repeated items.
313  // So we change the coordinate references in dataItem using move vectors of the start and
314  // end points that are the same for each repeated item.
315 
316  switch( item->Type() )
317  {
318  case WSG_LINE_T:
319  {
320  DS_DRAW_ITEM_LINE* line = static_cast<DS_DRAW_ITEM_LINE*>( item );
321 
322  wxPoint move_startpoint = (wxPoint) m_editPoints->Point( LINE_START ).GetPosition()
323  - line->GetStart();
324  wxPoint move_endpoint = (wxPoint) m_editPoints->Point( LINE_END ).GetPosition()
325  - line->GetEnd();
326 
327  dataItem->MoveStartPointToUi( dataItem->GetStartPosUi() + move_startpoint );
328  dataItem->MoveEndPointToUi( dataItem->GetEndPosUi() + move_endpoint );
329 
330  for( DS_DRAW_ITEM_BASE* draw_item : dataItem->GetDrawItems() )
331  {
332  DS_DRAW_ITEM_LINE* draw_line = static_cast<DS_DRAW_ITEM_LINE*>( draw_item );
333 
334  draw_line->SetStart( draw_line->GetStart() + move_startpoint );
335  draw_line->SetEnd( draw_line->GetEnd() + move_endpoint );
336  getView()->Update( draw_item );
337  }
338 
339  break;
340  }
341 
342  case WSG_RECT_T:
343  {
344  DS_DRAW_ITEM_RECT* rect = static_cast<DS_DRAW_ITEM_RECT*>( item );
345  VECTOR2I topLeft = m_editPoints->Point( RECT_TOPLEFT ).GetPosition();
346  VECTOR2I topRight = m_editPoints->Point( RECT_TOPRIGHT ).GetPosition();
347  VECTOR2I botLeft = m_editPoints->Point( RECT_BOTLEFT ).GetPosition();
348  VECTOR2I botRight = m_editPoints->Point( RECT_BOTRIGHT ).GetPosition();
349 
350  pinEditedCorner( getEditedPointIndex(), Mils2iu( 1 ), Mils2iu( 1 ),
351  topLeft, topRight, botLeft, botRight );
352 
353  wxPoint start_delta, end_delta;
354 
355  if( rect->GetStart().y > rect->GetEnd().y )
356  {
357  start_delta.y = botRight.y - rect->GetStart().y;
358  end_delta.y = topLeft.y - rect->GetEnd().y;
359  }
360  else
361  {
362  start_delta.y = topLeft.y - rect->GetStart().y;
363  end_delta.y = botRight.y - rect->GetEnd().y;
364  }
365 
366  if( rect->GetStart().x > rect->GetEnd().x )
367  {
368  start_delta.x = botRight.x - rect->GetStart().x;
369  end_delta.x = topLeft.x - rect->GetEnd().x;
370  }
371  else
372  {
373  start_delta.x = topLeft.x - rect->GetStart().x;
374  end_delta.x = botRight.x - rect->GetEnd().x;
375  }
376 
377  dataItem->MoveStartPointToUi( dataItem->GetStartPosUi() + start_delta );
378  dataItem->MoveEndPointToUi( dataItem->GetEndPosUi() + end_delta );
379 
380  for( DS_DRAW_ITEM_BASE* draw_item : dataItem->GetDrawItems() )
381  {
382  DS_DRAW_ITEM_RECT* draw_rect = static_cast<DS_DRAW_ITEM_RECT*>( draw_item );
383 
384  draw_rect->SetStart( draw_rect->GetStart() + start_delta );
385  draw_rect->SetEnd( draw_rect->GetEnd() + end_delta );
386  getView()->Update( draw_item );
387  }
388 
389  break;
390  }
391 
392  default:
393  break;
394  }
395 
396  m_frame->SetMsgPanel( item );
397 
398  // The Properties frame will be updated. Avoid flicker during update:
399  m_frame->GetPropertiesFrame()->Freeze();
401  m_frame->GetPropertiesFrame()->Thaw();
402 }
403 
404 
406 {
407  if( !m_editPoints )
408  return;
409 
410  EDA_ITEM* item = m_editPoints->GetParent();
411 
412  if( !item )
413  return;
414 
415  switch( item->Type() )
416  {
417  case WSG_LINE_T:
418  {
419  DS_DRAW_ITEM_LINE* line = static_cast<DS_DRAW_ITEM_LINE*>( item );
420 
421  m_editPoints->Point( LINE_START ).SetPosition( line->GetStart() );
422  m_editPoints->Point( LINE_END ).SetPosition( line->GetEnd() );
423  break;
424  }
425 
426  case WSG_RECT_T:
427  {
428  DS_DRAW_ITEM_RECT* rect = static_cast<DS_DRAW_ITEM_RECT*>( item );
429  wxPoint topLeft = rect->GetPosition();
430  wxPoint botRight = rect->GetEnd();
431 
432  if( topLeft.y > botRight.y )
433  std::swap( topLeft.y, botRight.y );
434 
435  if( topLeft.x > botRight.x )
436  std::swap( topLeft.x, botRight.x );
437 
438  m_editPoints->Point( RECT_TOPLEFT ).SetPosition( (VECTOR2I) topLeft );
439  m_editPoints->Point( RECT_TOPRIGHT ).SetPosition( VECTOR2I( botRight.x, topLeft.y ) );
440  m_editPoints->Point( RECT_BOTLEFT ).SetPosition( VECTOR2I( topLeft.x, botRight.y ) );
441  m_editPoints->Point( RECT_BOTRIGHT ).SetPosition( (VECTOR2I) botRight );
442  break;
443  }
444 
445  default:
446  break;
447  }
448 
449  getView()->Update( m_editPoints.get() );
450 }
451 
452 
454 {
456 
457  if( aPoint )
458  {
460  controls->ForceCursorPosition( true, aPoint->GetPosition() );
461  controls->ShowCursor( true );
462  }
463  else
464  {
465  if( m_frame->ToolStackIsEmpty() )
466  controls->ShowCursor( false );
467 
468  controls->ForceCursorPosition( false );
469  }
470 
471  m_editedPoint = aPoint;
472 }
473 
474 
476 {
477  updatePoints();
478  return 0;
479 }
480 
481 
483 {
487 }
488 
489 
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
void SetStart(const wxPoint &aPos)
Definition: ds_draw_item.h:220
void SetStart(const wxPoint &aPos)
Definition: ds_draw_item.h:134
PROPERTIES_FRAME * GetPropertiesFrame()
static const TOOL_EVENT SelectedEvent
Definition: actions.h:199
PL_EDITOR_FRAME * m_frame
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
PL_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
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
Model changes (required full reload)
Definition: tool_base.h:80
const wxPoint GetEndPosUi(int ii=0) const
This file is part of the common library.
void SetEnd(const wxPoint &aPos) override
Definition: ds_draw_item.h:222
static std::shared_ptr< EDIT_POINTS > Make(EDA_ITEM *aItem)
void MoveEndPointToUi(const wxPoint &aPosition)
Move the ending point of the item to a new position.
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:214
void updatePoints()
Update which point is being edited.
bool Init() override
Init() is called once upon a registration of the tool.
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:348
bool IsMotion() const
Definition: tool_event.h:300
PL_SELECTION_TOOL * m_selectionTool
Currently edited point, NULL if there is none.
std::shared_ptr< EDIT_POINTS > m_editPoints
void setEditedPoint(EDIT_POINT *aPoint)
Return true if aPoint is the currently modified point.
void pinEditedCorner(int editedPointIndex, int minWidth, int minHeight, VECTOR2I &topLeft, VECTOR2I &topRight, VECTOR2I &botLeft, VECTOR2I &botRight)
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
const wxPoint & GetStart() const
Definition: ds_draw_item.h:133
wxPoint GetPosition() const override
Definition: ds_draw_item.h:224
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).
const wxPoint & GetStart() const
Definition: ds_draw_item.h:219
PL_SELECTION & GetSelection()
Return the set of currently selected items.
search types array terminator (End Of Types)
Definition: typeinfo.h:81
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:77
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:204
bool IsNew() const
Definition: eda_item.h:119
void OnModify()
Must be called after a change in order to set the "modify" flag.
Base class to handle basic graphic items.
Definition: ds_draw_item.h:58
int getEditedPointIndex() const
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:285
virtual VECTOR2I GetPosition() const
Return coordinates of an EDIT_POINT.
Definition: edit_points.h:70
int Main(const TOOL_EVENT &aEvent)
void SetMsgPanel(const std::vector< MSG_PANEL_ITEM > &aList)
Clear the message panel and populates it with the contents of aList.
void updateEditedPoint(const TOOL_EVENT &aEvent)
Set the current point being edited. NULL means none.
virtual void CaptureCursor(bool aEnabled)
Force the cursor to stay within the drawing panel area.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
Generic, UI-independent tool event.
Definition: tool_event.h:152
void updateItem() const
< Update item's points with edit points.
bool ToolStackIsEmpty()
Definition: tools_holder.h:116
An interface for classes handling user events controlling the view behavior such as zooming,...
const VECTOR2D DragOrigin() const
Returns information about mouse buttons state.
Definition: tool_event.h:269
const std::vector< DS_DRAW_ITEM_BASE * > & GetDrawItems() const
Definition: ds_data_item.h:110
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
virtual bool IsType(const KICAD_T aScanTypes[]) const
Check whether the item is one of the listed types.
Definition: eda_item.h:183
LINE_POINTS
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
EDIT_POINT * m_editedPoint
Currently available edit points.
int modifiedSelection(const TOOL_EVENT &aEvent)
Non filled rectangle with thick segment.
Definition: ds_draw_item.h:205
void MoveStartPointToUi(const wxPoint &aPosition)
Move the starting point of the item to a new position.
Drawing sheet structure type definitions.
Definition: ds_data_item.h:95
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
Update the board display after modifying it by a python script (note: it is automatically called by a...
int Size() const
Returns the number of selected parts.
Definition: selection.h:104
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:100
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:77
void SaveCopyInUndoList()
Save a copy of the description (in a S expr string) for Undo/redo commands.
void Activate()
Run the tool.
const wxPoint & GetEnd() const
Definition: ds_draw_item.h:221
void CopyPrmsFromItemToPanel(DS_DATA_ITEM *aItem)
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:318
Represent a single point that can be used for modifying items.
Definition: edit_points.h:47
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
void RollbackFromUndo()
Apply the last command in Undo List without stacking a Redo.
RECTANGLE_POINTS
static std::shared_ptr< EDIT_POINTS > Make(EDA_ITEM *aItem, SCH_BASE_FRAME *frame)
const VECTOR2D Position() const
Returns the point where dragging has started.
Definition: tool_event.h:263
const wxPoint & GetEnd() const
Definition: ds_draw_item.h:135
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
EDA_ITEM * Front() const
Definition: selection.h:145
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: view.cpp:1512
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
const wxPoint GetStartPosUi(int ii=0) const
void SetEnd(const wxPoint &aPos) override
Definition: ds_draw_item.h:136
virtual void SetPosition(const VECTOR2I &aPosition)
Set new coordinates for an EDIT_POINT.
Definition: edit_points.h:106