KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pl_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 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, see <https://www.gnu.org/licenses/>.
19 */
20
22#include <confirm.h>
23#include <view/view_controls.h>
24#include <tool/tool_manager.h>
25#include <bitmaps.h>
29
32
33#include "pl_editor_frame.h"
34#include "tools/pl_actions.h"
37#include "pgm_base.h"
38
40 TOOL_INTERACTIVE( "plEditor.InteractiveDrawing" ),
41 m_frame( nullptr ),
42 m_selectionTool( nullptr )
43{
44}
45
46
50
51
53{
56
57 auto& ctxMenu = m_menu->GetMenu();
58
59 // cancel current tool goes in main context menu at the top if present
61 ctxMenu.AddSeparator( 1 );
62
63 // Finally, add the standard zoom/grid items
64 m_frame->AddStandardSubMenus( *m_menu.get() );
65
66 return true;
67}
68
69
71{
72 if( aReason != REDRAW )
73 m_pendingItem.reset();
74
75 if( aReason == MODEL_RELOAD )
77}
78
79
81{
83 VECTOR2I cursorPos;
84 bool isText = aEvent.IsAction( &PL_ACTIONS::placeText );
85 DS_DATA_ITEM* item = nullptr;
86
88
89 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
90 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
91
92 auto setCursor =
93 [&]()
94 {
95 if( item )
96 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PLACE );
97 else if( isText )
98 {
99 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
100 }
101 else if( aEvent.IsAction( &PL_ACTIONS::placeImage ) )
102 {
103 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
104 }
105 else
106 {
107 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
108 }
109 };
110
111 auto cleanup =
112 [&]()
113 {
114 getView()->Remove( item->GetDrawItems()[0] );
115 item = nullptr;
116 m_pendingItem.reset();
117 };
118
119 auto createPending =
120 [&]() -> bool
121 {
122 m_pendingItem.reset( m_frame->CreateDrawingSheetItem( type ) );
123 item = m_pendingItem.get();
124
125 if( !item )
126 return false;
127
128 item->MoveToIU( getViewControls()->GetCursorPosition() );
129
130 DS_DRAW_ITEM_BASE* drawItem = item->GetDrawItems()[0];
131 drawItem->SetFlags( IS_NEW | IS_MOVING );
132 getView()->Update( drawItem );
133
134 setCursor();
135 return true;
136 };
137
138 Activate();
139 // Must be done after Activate() so that it gets set into the correct context
140 getViewControls()->ShowCursor( true );
141 // Set initial cursor
142 setCursor();
143
144 if( isText )
145 createPending();
146
147 if( aEvent.HasPosition() )
148 m_toolMgr->PrimeTool( aEvent.Position() );
149
150 // Main loop: keep receiving events
151 while( TOOL_EVENT* evt = Wait() )
152 {
153 setCursor();
154 cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
155
156 if( evt->IsCancelInteractive() || ( !isText && item && evt->IsAction( &ACTIONS::undo ) ) )
157 {
158 bool wasPending = item != nullptr;
159
160 if( wasPending )
161 cleanup();
162
163 if( isText || !wasPending )
164 break;
165 }
166 else if( evt->IsActivate() )
167 {
168 if( item )
169 cleanup();
170
171 if( evt->IsMoveTool() )
172 {
173 // Make sure we come back after the move tool runs
174 m_frame->PushTool( originalEvent );
175 }
176
177 break;
178 }
179 else if( evt->IsClick( BUT_LEFT ) )
180 {
181 bool placeItem = item != nullptr;
182
183 if( !item )
184 {
186
187 if( createPending() )
188 {
189 // Text is a single-click-place. All others are first-click-creates,
190 // second-click-places.
191 placeItem = isText;
192 }
193 }
194
195 if( item && placeItem )
196 {
197 m_frame->SaveCopyInUndoList();
199
200 item->MoveStartPointToIU( cursorPos );
201
202 DS_DRAW_ITEM_BASE* drawItem = item->GetDrawItems()[0];
203 drawItem->SetPosition( item->GetStartPosIU( 0 ) );
204 drawItem->ClearEditFlags();
205
206 // A full canvas rebuild drops the pending item from the view, so a plain
207 // view update is not enough to bring it back
208 getView()->Remove( drawItem );
209 getView()->Add( drawItem );
210
211 // Now we select and inform other tools, so that the Properties panel
212 // is updated.
214 m_selectionTool->AddItemToSel( drawItem, false );
215
216 item = nullptr;
217
218 m_frame->OnModify();
219
220 if( isText )
221 createPending();
222 }
223 }
224 else if( evt->IsClick( BUT_RIGHT ) )
225 {
226 // Warp after context menu only if dragging...
227 if( !item )
228 m_toolMgr->VetoContextMenuMouseWarp();
229
230 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
231 }
232 else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
233 {
234 item->MoveStartPointToIU( cursorPos );
235
236 DS_DRAW_ITEM_BASE* drawItem = item->GetDrawItems()[0];
237 drawItem->SetPosition( item->GetStartPosIU( 0 ) );
238
239 getView()->Remove( drawItem );
240 getView()->Add( drawItem );
241 }
242 else
243 {
244 evt->SetPassEvent();
245 }
246
247 // Enable autopanning and cursor capture only for two click placements
248 getViewControls()->SetAutoPan( !isText && item != nullptr );
249 getViewControls()->CaptureCursor( !isText && item != nullptr );
250 }
251
252 getViewControls()->SetAutoPan( false );
253 getViewControls()->CaptureCursor( false );
254 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
255 return 0;
256}
257
258
260{
262 DS_DRAW_ITEM_BASE* item = nullptr;
263
264 // We might be running as the same shape in another co-routine. Make sure that one
265 // gets whacked.
266 m_toolMgr->DeactivateTool();
267
269
270 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
271 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
272
273 auto setCursor =
274 [&]()
275 {
276 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
277 };
278
279 Activate();
280 // Must be done after Activate() so that it gets set into the correct context
281 getViewControls()->ShowCursor( true );
282 // Set initial cursor
283 setCursor();
284
285 if( aEvent.HasPosition() )
286 m_toolMgr->PrimeTool( aEvent.Position() );
287
288 // Main loop: keep receiving events
289 while( TOOL_EVENT* evt = Wait() )
290 {
291 setCursor();
292
293 VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
294
295 if( evt->IsCancelInteractive() || ( item && evt->IsAction( &ACTIONS::undo ) ) )
296 {
298
299 if( item )
300 {
301 item = nullptr;
302
303 // Pop the undo stack and delete the item being placed
304 m_frame->RollbackFromUndo();
305 }
306 else
307 {
308 break;
309 }
310 }
311 else if( evt->IsActivate() )
312 {
313 if( item )
314 {
315 item = nullptr;
316
317 // Pop the undo stack and delete the item being placed
318 m_frame->RollbackFromUndo();
319 continue;
320 }
321
322 if( evt->IsPointEditor() || evt->IsMoveTool() )
323 {
324 // Make sure we come back after the move tool is done
325 m_frame->PushTool( originalEvent );
326 break;
327 }
328 }
329 else if( evt->IsClick( BUT_LEFT ) )
330 {
331 if( !item ) // start drawing
332 {
333 m_frame->SaveCopyInUndoList();
335
336 DS_DATA_ITEM* dataItem = m_frame->CreateDrawingSheetItem( type );
338 dataItem->MoveToIU( cursorPos );
339
340 item = dataItem->GetDrawItems()[0];
341 item->SetFlags( IS_NEW );
342
343 // Select the item but don't inform other tools (to prevent the Properties
344 // panel from updating the item before it has been placed)
345 m_selectionTool->AddItemToSel( item, true );
346 }
347 else // finish drawing
348 {
349 // Now we re-select and inform other tools, so that the Properties panel
350 // is updated.
352 m_selectionTool->AddItemToSel( item, false );
353
354 item->ClearEditFlags();
355 item = nullptr;
356
357 // Activate point editor immediately to allow resizing of the item just created
359
360 m_frame->OnModify();
361 }
362 }
363 else if( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() )
364 {
365 if( item )
366 {
367 item->GetPeer()->MoveEndPointToIU( cursorPos );
368 item->SetEnd( item->GetPeer()->GetEndPosIU( 0 ) );
369 getView()->Update( item );
370 }
371 }
372 else if( evt->IsClick( BUT_RIGHT ) )
373 {
374 // Warp after context menu only if dragging...
375 if( !item )
376 m_toolMgr->VetoContextMenuMouseWarp();
377
378 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
379 }
380 else
381 {
382 evt->SetPassEvent();
383 }
384
385 // Enable autopanning and cursor capture only when there is a shape being drawn
386 getViewControls()->SetAutoPan( item != nullptr );
387 getViewControls()->CaptureCursor( item != nullptr );
388 }
389
390 getViewControls()->SetAutoPan( false );
391 getViewControls()->CaptureCursor( false );
392 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
393 return 0;
394}
395
396
398{
399 auto importer = std::make_unique<GRAPHICS_IMPORTER_PL_EDITOR>();
400
401 // The dialog takes the importer, but the items have to be fetched back out of it.
402 GRAPHICS_IMPORTER_PL_EDITOR* sink = importer.get();
403
404 // A drawing sheet has nothing to group items into.
405 DIALOG_IMPORT_GFX dlg( m_frame, std::move( importer ), false );
406
407 if( aEvent.HasParameter() )
408 dlg.SetFilenameOverride( *aEvent.Parameter<wxString*>() );
409
410 if( dlg.ShowModal() != wxID_OK )
411 return 0;
412
413 std::vector<std::unique_ptr<DS_DATA_ITEM>>& items = sink->GetDataItems();
414
415 if( items.empty() )
416 {
417 DisplayInfoMessage( m_frame, _( "No graphic items found in file." ) );
418 return 0;
419 }
420
421 m_frame->SaveCopyInUndoList();
422
424
425 for( std::unique_ptr<DS_DATA_ITEM>& item : items )
426 {
427 DS_DATA_ITEM* dataItem = item.release();
428
430
431 // Builds the item's drawable shapes and puts them in the view.
432 m_frame->SyncDataItem( dataItem );
433
434 for( DS_DRAW_ITEM_BASE* drawItem : dataItem->GetDrawItems() )
435 m_selectionTool->AddItemToSel( drawItem, true );
436 }
437
438 items.clear();
439
440 m_frame->OnModify();
441 m_frame->GetCanvas()->Refresh();
442
443 PL_SELECTION& selection = m_selectionTool->GetSelection();
444
445 // Hand the items to the move tool the way Paste does, so they jump to the cursor.
446 if( dlg.IsPlacementInteractive() && !selection.Empty() )
447 {
448 selection.SetReferencePoint( selection.GetTopLeftItem()->GetPosition() );
449 m_toolMgr->PostAction( PL_ACTIONS::move );
450 }
451
452 return 0;
453}
454
455
static TOOL_ACTION cancelInteractive
Definition actions.h:68
static TOOL_ACTION undo
Definition actions.h:71
static TOOL_ACTION activatePointEditor
Definition actions.h:268
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
static TOOL_ACTION refreshPreview
Definition actions.h:155
Import a graphics file into whichever editor opened the dialog.
void SetFilenameOverride(const wxString &aFilenameOverride)
Set the filename override to be applied in TransferDataToWindow.
int ShowModal() override
Drawing sheet structure type definitions.
void MoveStartPointToIU(const VECTOR2I &aPosition)
Move the starting point of the item to a new position.
void MoveToIU(const VECTOR2I &aPosition)
Move item to a new position.
const VECTOR2I GetStartPosIU(int ii=0) const
const std::vector< DS_DRAW_ITEM_BASE * > & GetDrawItems() const
static DS_DATA_MODEL & GetTheInstance()
Return the instance of DS_DATA_MODEL used in the application.
void Append(DS_DATA_ITEM *aItem)
Base class to handle basic graphic items.
virtual VECTOR2I GetPosition() const
Definition eda_item.h:348
virtual void ClearEditFlags()
Definition eda_item.h:178
virtual void SetPosition(const VECTOR2I &aPos)
Definition eda_item.h:349
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
Turn imported geometry into drawing sheet items.
std::vector< std::unique_ptr< DS_DATA_ITEM > > & GetDataItems()
The imported items, which the caller takes over.
virtual void CaptureCursor(bool aEnabled)
Force the cursor to stay within the drawing panel area.
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:365
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition view.cpp:488
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:2631
static TOOL_ACTION placeImage
Definition pl_actions.h:37
static TOOL_ACTION drawRectangle
Definition pl_actions.h:39
static TOOL_ACTION placeImportedGraphics
Definition pl_actions.h:38
static TOOL_ACTION placeText
Definition pl_actions.h:36
static TOOL_ACTION move
Definition pl_actions.h:44
static TOOL_ACTION drawLine
Definition pl_actions.h:40
PL_SELECTION_TOOL * m_selectionTool
int PlaceImportedGraphics(const TOOL_EVENT &aEvent)
Import a graphics file as drawing sheet items.
int PlaceItem(const TOOL_EVENT &aEvent)
int DrawShape(const TOOL_EVENT &aEvent)
PL_EDITOR_FRAME * m_frame
void setTransitions() override
Set up handlers for various events.
std::unique_ptr< DS_DATA_ITEM > m_pendingItem
bool Init() override
Init() is called once upon a registration of the tool.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
EDA_ITEM * GetTopLeftItem(bool onlyModules=false) const override
static bool ShowAlways(const SELECTION &aSelection)
The default condition function (always returns true).
void SetReferencePoint(const VECTOR2I &aP)
bool Empty() const
Checks if there is anything selected.
Definition selection.h:114
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:182
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:40
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
KIGFX::VIEW * getView() const
Return the instance of KIGFX::VIEW object used in the application.
Definition tool_base.cpp:34
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:74
@ REDRAW
Full drawing refresh.
Definition tool_base.h:79
@ MODEL_RELOAD
Model changes (the sheet for a schematic)
Definition tool_base.h:76
Generic, UI-independent tool event.
Definition tool_event.h:167
bool HasPosition() const
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition tool_event.h:256
bool HasParameter() const
Definition tool_event.h:460
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:289
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:484
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).
std::unique_ptr< TOOL_MENU > m_menu
The functions below are not yet implemented - their interface may change.
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.
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition confirm.cpp:245
This file is part of the common library.
@ PLACE
Definition cursors.h:95
@ ARROW
Definition cursors.h:43
@ PENCIL
Definition cursors.h:49
#define _(s)
#define IS_NEW
New item, just created.
#define IS_MOVING
Item being moved.
see class PGM_BASE
@ BUT_LEFT
Definition tool_event.h:128
@ BUT_RIGHT
Definition tool_event.h:129
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708