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