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 case WSG_RECT_T:
80 {
81 DS_DRAW_ITEM_RECT* rect = static_cast<DS_DRAW_ITEM_RECT*>( aItem );
82 VECTOR2I topLeft = rect->GetStart();
83 VECTOR2I botRight = rect->GetEnd();
84
85 if( topLeft.y > botRight.y )
86 std::swap( topLeft.y, botRight.y );
87
88 if( topLeft.x > botRight.x )
89 std::swap( topLeft.x, botRight.x );
90
91 points->AddPoint( topLeft );
92 points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
93 points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
94 points->AddPoint( botRight );
95 break;
96 }
97 default:
98 points.reset();
99 break;
100 }
101
102 return points;
103 }
104
105private:
107};
108
109
111 TOOL_INTERACTIVE( "plEditor.PointEditor" ),
112 m_frame( nullptr ),
113 m_selectionTool( nullptr ),
114 m_editedPoint( nullptr )
115{
116}
117
118
120{
121 if( aReason == MODEL_RELOAD )
122 {
123 // Init variables used by every drawing tool
125 }
126
127 m_editPoints.reset();
128}
129
130
132{
135 return true;
136}
137
138
140{
141 EDIT_POINT* point = m_editedPoint;
142
143 if( aEvent.IsMotion() )
144 point = m_editPoints->FindPoint( aEvent.Position(), getView() );
145 else if( aEvent.IsDrag( BUT_LEFT ) )
146 point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
147 else
148 point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
149
150 if( m_editedPoint != point )
151 setEditedPoint( point );
152}
153
154
156{
158 const PL_SELECTION& selection = m_selectionTool->GetSelection();
159
160 if( selection.Size() != 1 || !selection.Front()->IsType( { WSG_LINE_T, WSG_RECT_T } ) )
161 return 0;
162
163 EDA_ITEM* item = (EDA_ITEM*) selection.Front();
164
165 // Wait till drawing tool is done
166 if( item->IsNew() )
167 return 0;
168
169 Activate();
170 // Must be done after Activate() so that it gets set into the correct context
171 controls->ShowCursor( true );
172
174
175 if( !m_editPoints )
176 return 0;
177
178 m_angleItem = std::make_unique<KIGFX::PREVIEW::ANGLE_ITEM>( m_editPoints.get() );
179
180 getView()->Add( m_editPoints.get() );
181 getView()->Add( m_angleItem.get() );
182 setEditedPoint( nullptr );
183 updateEditedPoint( aEvent );
184 bool inDrag = false;
185 bool modified = false;
186
187 // Main loop: keep receiving events
188 while( TOOL_EVENT* evt = Wait() )
189 {
190 if( !m_editPoints || evt->IsSelectionEvent() )
191 break;
192
193 if ( !inDrag )
194 updateEditedPoint( *evt );
195
196 if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
197 {
198 if( !inDrag )
199 {
200 try
201 {
202 m_frame->SaveCopyInUndoList();
203 }
204 catch( const fmt::format_error& exc )
205 {
206 wxLogWarning( wxS( "Exception \"%s\" serializing string ocurred." ),
207 exc.what() );
208 return 1;
209 }
210
211 controls->ForceCursorPosition( false );
212 inDrag = true;
213 modified = true;
214 }
215
216 m_editedPoint->SetPosition( controls->GetCursorPosition( !evt->DisableGridSnapping() ) );
217
218 updateItem();
219 updatePoints();
220 }
221
222 else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
223 {
224 controls->SetAutoPan( false );
225 inDrag = false;
226 }
227
228 else if( evt->IsCancelInteractive() || evt->IsActivate() )
229 {
230 if( inDrag ) // Restore the last change
231 {
232 m_frame->RollbackFromUndo();
233 inDrag = false;
234 modified = false;
235 }
236 else if( evt->IsCancelInteractive() )
237 {
238 break;
239 }
240
241 if( evt->IsActivate() && !evt->IsMoveTool() )
242 break;
243 }
244 else
245 {
246 evt->SetPassEvent();
247 }
248
249 controls->SetAutoPan( inDrag );
250 controls->CaptureCursor( inDrag );
251 }
252
253 controls->SetAutoPan( false );
254 controls->CaptureCursor( false );
255
256 if( m_editPoints )
257 {
258 getView()->Remove( m_editPoints.get() );
259 getView()->Remove( m_angleItem.get() );
260
261 if( modified )
262 m_frame->OnModify();
263
264 m_editPoints.reset();
265 m_angleItem.reset();
266 m_frame->GetCanvas()->Refresh();
267 }
268
269 return 0;
270}
271
272
273void pinEditedCorner( int editedPointIndex, int minWidth, int minHeight, VECTOR2I& topLeft,
274 VECTOR2I& topRight, VECTOR2I& botLeft, VECTOR2I& botRight )
275{
276 switch( editedPointIndex )
277 {
278 case RECT_TOPLEFT:
279 // pin edited point within opposite corner
280 topLeft.x = std::min( topLeft.x, botRight.x - minWidth );
281 topLeft.y = std::min( topLeft.y, botRight.y - minHeight );
282
283 // push edited point edges to adjacent corners
284 topRight.y = topLeft.y;
285 botLeft.x = topLeft.x;
286
287 break;
288
289 case RECT_TOPRIGHT:
290 // pin edited point within opposite corner
291 topRight.x = std::max( topRight.x, botLeft.x + minWidth );
292 topRight.y = std::min( topRight.y, botLeft.y - minHeight );
293
294 // push edited point edges to adjacent corners
295 topLeft.y = topRight.y;
296 botRight.x = topRight.x;
297
298 break;
299
300 case RECT_BOTLEFT:
301 // pin edited point within opposite corner
302 botLeft.x = std::min( botLeft.x, topRight.x - minWidth );
303 botLeft.y = std::max( botLeft.y, topRight.y + minHeight );
304
305 // push edited point edges to adjacent corners
306 botRight.y = botLeft.y;
307 topLeft.x = botLeft.x;
308
309 break;
310
311 case RECT_BOTRIGHT:
312 // pin edited point within opposite corner
313 botRight.x = std::max( botRight.x, topLeft.x + minWidth );
314 botRight.y = std::max( botRight.y, topLeft.y + minHeight );
315
316 // push edited point edges to adjacent corners
317 botLeft.y = botRight.y;
318 topRight.x = botRight.x;
319
320 break;
321 }
322}
323
324
326{
327 EDA_ITEM* item = m_editPoints->GetParent();
328
329 if( !item )
330 return;
331
332 DS_DATA_ITEM* dataItem = static_cast<DS_DRAW_ITEM_BASE*>( item )->GetPeer();
333
334 // The current item is perhaps not the main item if we have a set of repeated items.
335 // So we change the coordinate references in dataItem using move vectors of the start and
336 // end points that are the same for each repeated item.
337
338 switch( item->Type() )
339 {
340 case WSG_LINE_T:
341 {
342 DS_DRAW_ITEM_LINE* line = static_cast<DS_DRAW_ITEM_LINE*>( item );
343
344 VECTOR2I move_startpoint = m_editPoints->Point( LINE_START ).GetPosition() - line->GetStart();
345 VECTOR2I move_endpoint = m_editPoints->Point( LINE_END ).GetPosition() - line->GetEnd();
346
347 dataItem->MoveStartPointToIU( dataItem->GetStartPosIU() + move_startpoint );
348 dataItem->MoveEndPointToIU( dataItem->GetEndPosIU() + move_endpoint );
349
350 for( DS_DRAW_ITEM_BASE* draw_item : dataItem->GetDrawItems() )
351 {
352 DS_DRAW_ITEM_LINE* draw_line = static_cast<DS_DRAW_ITEM_LINE*>( draw_item );
353
354 draw_line->SetStart( draw_line->GetStart() + move_startpoint );
355 draw_line->SetEnd( draw_line->GetEnd() + move_endpoint );
356 getView()->Update( draw_item );
357 }
358
359 break;
360 }
361
362 case WSG_RECT_T:
363 {
364 DS_DRAW_ITEM_RECT* rect = static_cast<DS_DRAW_ITEM_RECT*>( item );
365 VECTOR2I topLeft = m_editPoints->Point( RECT_TOPLEFT ).GetPosition();
366 VECTOR2I topRight = m_editPoints->Point( RECT_TOPRIGHT ).GetPosition();
367 VECTOR2I botLeft = m_editPoints->Point( RECT_BOTLEFT ).GetPosition();
368 VECTOR2I botRight = m_editPoints->Point( RECT_BOTRIGHT ).GetPosition();
369
371 drawSheetIUScale.MilsToIU( 1 ),
372 topLeft, topRight, botLeft, botRight );
373
374 VECTOR2I start_delta, end_delta;
375
376 if( rect->GetStart().y > rect->GetEnd().y )
377 {
378 start_delta.y = botRight.y - rect->GetStart().y;
379 end_delta.y = topLeft.y - rect->GetEnd().y;
380 }
381 else
382 {
383 start_delta.y = topLeft.y - rect->GetStart().y;
384 end_delta.y = botRight.y - rect->GetEnd().y;
385 }
386
387 if( rect->GetStart().x > rect->GetEnd().x )
388 {
389 start_delta.x = botRight.x - rect->GetStart().x;
390 end_delta.x = topLeft.x - rect->GetEnd().x;
391 }
392 else
393 {
394 start_delta.x = topLeft.x - rect->GetStart().x;
395 end_delta.x = botRight.x - rect->GetEnd().x;
396 }
397
398 dataItem->MoveStartPointToIU( dataItem->GetStartPosIU() + start_delta );
399 dataItem->MoveEndPointToIU( dataItem->GetEndPosIU() + end_delta );
400
401 for( DS_DRAW_ITEM_BASE* draw_item : dataItem->GetDrawItems() )
402 {
403 DS_DRAW_ITEM_RECT* draw_rect = static_cast<DS_DRAW_ITEM_RECT*>( draw_item );
404
405 draw_rect->SetStart( draw_rect->GetStart() + start_delta );
406 draw_rect->SetEnd( draw_rect->GetEnd() + end_delta );
407 getView()->Update( draw_item );
408 }
409
410 break;
411 }
412
413 default:
414 break;
415 }
416
417 m_frame->SetMsgPanel( item );
418
419 // The Properties frame will be updated. Avoid flicker during update:
420 m_frame->GetPropertiesFrame()->Freeze();
421 m_frame->GetPropertiesFrame()->CopyPrmsFromItemToPanel( dataItem );
422 m_frame->GetPropertiesFrame()->Thaw();
423}
424
425
427{
428 if( !m_editPoints )
429 return;
430
431 EDA_ITEM* item = m_editPoints->GetParent();
432
433 if( !item )
434 return;
435
436 switch( item->Type() )
437 {
438 case WSG_LINE_T:
439 {
440 DS_DRAW_ITEM_LINE* line = static_cast<DS_DRAW_ITEM_LINE*>( item );
441
442 m_editPoints->Point( LINE_START ).SetPosition( line->GetStart() );
443 m_editPoints->Point( LINE_END ).SetPosition( line->GetEnd() );
444 break;
445 }
446
447 case WSG_RECT_T:
448 {
449 DS_DRAW_ITEM_RECT* rect = static_cast<DS_DRAW_ITEM_RECT*>( item );
450 VECTOR2I topLeft = rect->GetPosition();
451 VECTOR2I botRight = rect->GetEnd();
452
453 if( topLeft.y > botRight.y )
454 std::swap( topLeft.y, botRight.y );
455
456 if( topLeft.x > botRight.x )
457 std::swap( topLeft.x, botRight.x );
458
459 m_editPoints->Point( RECT_TOPLEFT ).SetPosition( (VECTOR2I) topLeft );
460 m_editPoints->Point( RECT_TOPRIGHT ).SetPosition( VECTOR2I( botRight.x, topLeft.y ) );
461 m_editPoints->Point( RECT_BOTLEFT ).SetPosition( VECTOR2I( topLeft.x, botRight.y ) );
462 m_editPoints->Point( RECT_BOTRIGHT ).SetPosition( (VECTOR2I) botRight );
463 break;
464 }
465
466 default:
467 break;
468 }
469
470 getView()->Update( m_editPoints.get() );
471 getView()->Update( m_angleItem.get() );
472}
473
474
476{
478
479 if( aPoint )
480 {
481 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
482 controls->ForceCursorPosition( true, aPoint->GetPosition() );
483 controls->ShowCursor( true );
484 }
485 else
486 {
487 if( m_frame->ToolStackIsEmpty() )
488 controls->ShowCursor( false );
489
490 controls->ForceCursorPosition( false );
491 }
492
493 m_editedPoint = aPoint;
494}
495
496
498{
499 updatePoints();
500 return 0;
501}
502
503
510
511
constexpr EDA_IU_SCALE drawSheetIUScale
Definition base_units.h:113
static TOOL_ACTION activatePointEditor
Definition actions.h:272
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:192
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:346
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:353
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.
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:218
@ WSG_RECT_T
Definition typeinfo.h:219
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695