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