KiCad PCB EDA Suite
Loading...
Searching...
No Matches
edit_tool_move_fct.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) 2013-2017 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
7 * @author Tomasz Wlostowski <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
27#include <functional>
28#include <limits>
29#include <kiplatform/ui.h>
30#include <board.h>
31#include <board_commit.h>
34#include <pad.h>
35#include <pcb_group.h>
36#include <pcb_generator.h>
37#include <pcb_edit_frame.h>
38#include <spread_footprints.h>
39#include <tool/tool_manager.h>
40#include <tools/pcb_actions.h>
42#include <tools/edit_tool.h>
44#include <tools/drc_tool.h>
46#include <router/router_tool.h>
48#include <zone_filler.h>
49#include <drc/drc_engine.h>
51#include <view/view_controls.h>
52
53
54int EDIT_TOOL::Swap( const TOOL_EVENT& aEvent )
55{
56 if( isRouterActive() )
57 {
58 wxBell();
59 return 0;
60 }
61
62 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
63 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
64 {
65 sTool->FilterCollectorForMarkers( aCollector );
66 sTool->FilterCollectorForHierarchy( aCollector, true );
67 sTool->FilterCollectorForFreePads( aCollector );
68
69 // Iterate from the back so we don't have to worry about removals.
70 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
71 {
72 BOARD_ITEM* item = aCollector[i];
73
74 if( item->Type() == PCB_TRACE_T )
75 aCollector.Remove( item );
76 }
77
78 sTool->FilterCollectorForLockedItems( aCollector );
79 } );
80
81 if( selection.Size() < 2 )
82 return 0;
83
84 BOARD_COMMIT localCommit( this );
85 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
86
87 if( !commit )
88 commit = &localCommit;
89
90 std::vector<EDA_ITEM*> sorted = selection.GetItemsSortedBySelectionOrder();
91
92 // Save items, so changes can be undone
93 for( EDA_ITEM* item : selection )
94 commit->Modify( item, nullptr, RECURSE_MODE::RECURSE );
95
96 for( size_t i = 0; i < sorted.size() - 1; i++ )
97 {
98 EDA_ITEM* edaItemA = sorted[i];
99 EDA_ITEM* edaItemB = sorted[( i + 1 ) % sorted.size()];
100
101 if( !edaItemA->IsBOARD_ITEM() || !edaItemB->IsBOARD_ITEM() )
102 continue;
103
104 BOARD_ITEM* a = static_cast<BOARD_ITEM*>( edaItemA );
105 BOARD_ITEM* b = static_cast<BOARD_ITEM*>( edaItemB );
106
107 // Swap X,Y position
108 VECTOR2I aPos = a->GetPosition(), bPos = b->GetPosition();
109 std::swap( aPos, bPos );
110 a->SetPosition( aPos );
111 b->SetPosition( bPos );
112
113 // Handle footprints specially. They can be flipped to the back of the board which
114 // requires a special transformation.
115 if( a->Type() == PCB_FOOTPRINT_T && b->Type() == PCB_FOOTPRINT_T )
116 {
117 FOOTPRINT* aFP = static_cast<FOOTPRINT*>( a );
118 FOOTPRINT* bFP = static_cast<FOOTPRINT*>( b );
119
120 // Store initial orientation of footprints, before flipping them.
121 EDA_ANGLE aAngle = aFP->GetOrientation();
122 EDA_ANGLE bAngle = bFP->GetOrientation();
123
124 // Flip both if needed
125 if( aFP->IsFlipped() != bFP->IsFlipped() )
126 {
127 aFP->Flip( aPos, FLIP_DIRECTION::TOP_BOTTOM );
128 bFP->Flip( bPos, FLIP_DIRECTION::TOP_BOTTOM );
129 }
130
131 // Set orientation
132 std::swap( aAngle, bAngle );
133 aFP->SetOrientation( aAngle );
134 bFP->SetOrientation( bAngle );
135 }
136 // We can also do a layer swap safely for two objects of the same type,
137 // except groups which don't support layer swaps.
138 else if( a->Type() == b->Type() && a->Type() != PCB_GROUP_T )
139 {
140 // Swap layers
141 PCB_LAYER_ID aLayer = a->GetLayer(), bLayer = b->GetLayer();
142 std::swap( aLayer, bLayer );
143 a->SetLayer( aLayer );
144 b->SetLayer( bLayer );
145 }
146 }
147
148 if( !localCommit.Empty() )
149 localCommit.Push( _( "Swap" ) );
150
152
153 return 0;
154}
155
156
158{
159 if( isRouterActive() || m_dragging )
160 {
161 wxBell();
162 return 0;
163 }
164
165 BOARD_COMMIT commit( this );
166 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
167 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
168 {
169 sTool->FilterCollectorForMarkers( aCollector );
170 sTool->FilterCollectorForHierarchy( aCollector, true );
171 sTool->FilterCollectorForFreePads( aCollector, true );
172
173 // Iterate from the back so we don't have to worry about removals.
174 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
175 {
176 BOARD_ITEM* item = aCollector[i];
177
178 if( !dynamic_cast<FOOTPRINT*>( item ) )
179 aCollector.Remove( item );
180 }
181
182 sTool->FilterCollectorForLockedItems( aCollector );
183 } );
184
185 std::vector<FOOTPRINT*> footprintsToPack;
186
187 for( EDA_ITEM* item : selection )
188 footprintsToPack.push_back( static_cast<FOOTPRINT*>( item ) );
189
190 if( footprintsToPack.empty() )
191 return 0;
192
193 BOX2I footprintsBbox;
194
195 for( FOOTPRINT* fp : footprintsToPack )
196 {
197 commit.Modify( fp );
198 fp->SetFlags( IS_MOVING );
199 footprintsBbox.Merge( fp->GetBoundingBox( false ) );
200 }
201
202 SpreadFootprints( &footprintsToPack, footprintsBbox.Normalize().GetOrigin(), false );
203
204 if( doMoveSelection( aEvent, &commit, true ) )
205 commit.Push( _( "Pack Footprints" ) );
206 else
207 commit.Revert();
208
209 return 0;
210}
211
212
213int EDIT_TOOL::Move( const TOOL_EVENT& aEvent )
214{
215 if( isRouterActive() || m_dragging )
216 {
217 wxBell();
218 return 0;
219 }
220
221 if( BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() ) )
222 {
223 // Most moves will be synchronous unless they are coming from the API
224 if( aEvent.SynchronousState() )
225 aEvent.SynchronousState()->store( STS_RUNNING );
226
227 if( doMoveSelection( aEvent, commit, true ) )
228 {
229 if( aEvent.SynchronousState() )
230 aEvent.SynchronousState()->store( STS_FINISHED );
231 }
232 else if( aEvent.SynchronousState() )
233 {
234 aEvent.SynchronousState()->store( STS_CANCELLED );
235 }
236 }
237 else
238 {
239 BOARD_COMMIT localCommit( this );
240
241 if( doMoveSelection( aEvent, &localCommit, false ) )
242 localCommit.Push( _( "Move" ) );
243 else
244 localCommit.Revert();
245 }
246
247 // Notify point editor. (While doMoveSelection() will re-select the items and post this
248 // event, it's done before the edit flags are cleared in BOARD_COMMIT::Push() so the point
249 // editor doesn't fire up.)
250 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
251
252 return 0;
253}
254
255
256VECTOR2I EDIT_TOOL::getSafeMovement( const VECTOR2I& aMovement, const BOX2I& aSourceBBox,
257 const VECTOR2D& aBBoxOffset )
258{
259 typedef std::numeric_limits<int> coord_limits;
260
261 static const double max = coord_limits::max() - (int) COORDS_PADDING;
262 static const double min = -max;
263
264 BOX2D testBox( aSourceBBox.GetPosition(), aSourceBBox.GetSize() );
265 testBox.Offset( aBBoxOffset );
266
267 // Do not restrict movement if bounding box is already out of bounds
268 if( testBox.GetLeft() < min || testBox.GetTop() < min || testBox.GetRight() > max
269 || testBox.GetBottom() > max )
270 {
271 return aMovement;
272 }
273
274 testBox.Offset( aMovement );
275
276 if( testBox.GetLeft() < min )
277 testBox.Offset( min - testBox.GetLeft(), 0 );
278
279 if( max < testBox.GetRight() )
280 testBox.Offset( -( testBox.GetRight() - max ), 0 );
281
282 if( testBox.GetTop() < min )
283 testBox.Offset( 0, min - testBox.GetTop() );
284
285 if( max < testBox.GetBottom() )
286 testBox.Offset( 0, -( testBox.GetBottom() - max ) );
287
288 return KiROUND( testBox.GetPosition() - aBBoxOffset - aSourceBBox.GetPosition() );
289}
290
291
292bool EDIT_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, BOARD_COMMIT* aCommit, bool aAutoStart )
293{
294 const bool moveWithReference = aEvent.IsAction( &PCB_ACTIONS::moveWithReference );
295 const bool moveIndividually = aEvent.IsAction( &PCB_ACTIONS::moveIndividually );
296
298 PCBNEW_SETTINGS* cfg = editFrame->GetPcbNewSettings();
299 BOARD* board = editFrame->GetBoard();
301 VECTOR2I originalCursorPos = controls->GetCursorPosition();
302 VECTOR2I originalMousePos = controls->GetMousePosition();
303 std::unique_ptr<STATUS_TEXT_POPUP> statusPopup;
304 wxString status;
305 size_t itemIdx = 0;
306
307 // Be sure that there is at least one item that we can modify. If nothing was selected before,
308 // try looking for the stuff under mouse cursor (i.e. KiCad old-style hover selection)
309 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
310 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
311 {
312 sTool->FilterCollectorForMarkers( aCollector );
313 sTool->FilterCollectorForHierarchy( aCollector, true );
314 sTool->FilterCollectorForFreePads( aCollector );
315 sTool->FilterCollectorForTableCells( aCollector );
316 sTool->FilterCollectorForLockedItems( aCollector );
317 } );
318
319 if( m_dragging || selection.Empty() )
320 return false;
321
322 TOOL_EVENT pushedEvent = aEvent;
323 editFrame->PushTool( aEvent );
324 Activate();
325
326 // Must be done after Activate() so that it gets set into the correct context
327 controls->ShowCursor( true );
328 controls->SetAutoPan( true );
329 controls->ForceCursorPosition( false );
330
331 auto displayConstraintsMessage =
332 [editFrame]( bool constrained )
333 {
334 editFrame->DisplayConstraintsMsg( constrained ? _( "Constrain to H, V, 45" )
335 : wxString( wxT( "" ) ) );
336 };
337
338 auto updateStatusPopup =
339 [&]( EDA_ITEM* item, size_t ii, size_t count )
340 {
341 wxString popuptext = _( "Click to place %s (item %zu of %zu)\n"
342 "Press <esc> to cancel all; double-click to finish" );
343 wxString msg;
344
345 if( item->Type() == PCB_FOOTPRINT_T )
346 {
347 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
348 msg = fp->GetReference();
349 }
350 else if( item->Type() == PCB_PAD_T )
351 {
352 PAD* pad = static_cast<PAD*>( item );
353 FOOTPRINT* fp = pad->GetParentFootprint();
354 msg = wxString::Format( _( "%s pad %s" ), fp->GetReference(), pad->GetNumber() );
355 }
356 else
357 {
358 msg = item->GetTypeDesc().Lower();
359 }
360
361 if( !statusPopup )
362 statusPopup = std::make_unique<STATUS_TEXT_POPUP>( frame() );
363
364 statusPopup->SetText( wxString::Format( popuptext, msg, ii, count ) );
365 };
366
367 std::vector<BOARD_ITEM*> sel_items; // All the items operated on by the move below
368 std::vector<BOARD_ITEM*> orig_items; // All the original items in the selection
369
370 for( EDA_ITEM* item : selection )
371 {
372 if( item->IsBOARD_ITEM() )
373 {
374 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
375
376 if( !selection.IsHover() )
377 orig_items.push_back( boardItem );
378
379 sel_items.push_back( boardItem );
380 }
381
382 if( item->Type() == PCB_FOOTPRINT_T )
383 {
384 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
385
386 for( PAD* pad : footprint->Pads() )
387 sel_items.push_back( pad );
388
389 // Clear this flag here; it will be set by the netlist updater if the footprint is new
390 // so that it was skipped in the initial connectivity update in OnNetlistChanged
391 footprint->SetAttributes( footprint->GetAttributes() & ~FP_JUST_ADDED );
392 }
393 }
394
395 VECTOR2I pickedReferencePoint;
396
397 if( moveWithReference && !pickReferencePoint( _( "Select reference point for move..." ), "", "",
398 pickedReferencePoint ) )
399 {
400 if( selection.IsHover() )
402
403 editFrame->PopTool( pushedEvent );
404 return false;
405 }
406
407 if( moveIndividually )
408 {
409 orig_items.clear();
410
411 for( EDA_ITEM* item : selection.GetItemsSortedBySelectionOrder() )
412 {
413 if( item->IsBOARD_ITEM() )
414 orig_items.push_back( static_cast<BOARD_ITEM*>( item ) );
415 }
416
417 updateStatusPopup( orig_items[ itemIdx ], itemIdx + 1, orig_items.size() );
418 statusPopup->Popup();
419 statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
420 canvas()->SetStatusPopup( statusPopup->GetPanel() );
421
422 m_selectionTool->ClearSelection();
423 m_selectionTool->AddItemToSel( orig_items[ itemIdx ] );
424
425 sel_items.clear();
426 sel_items.push_back( orig_items[ itemIdx ] );
427 }
428
429 bool restore_state = false;
430 VECTOR2I originalPos;
431 VECTOR2D bboxMovement;
432 BOX2I originalBBox;
433 bool updateBBox = true;
434 LSET layers( { editFrame->GetActiveLayer() } );
436 TOOL_EVENT copy = aEvent;
437 TOOL_EVENT* evt = &copy;
438 VECTOR2I prevPos;
439 bool enableLocalRatsnest = true;
440
441 bool hv45Mode = GetAngleSnapMode() != LEADER_MODE::DIRECT;
442 bool eatFirstMouseUp = true;
443 bool allowRedraw3D = cfg->m_Display.m_Live3DRefresh;
444 bool showCourtyardConflicts = !m_isFootprintEditor && cfg->m_ShowCourtyardCollisions;
445
446 // Used to test courtyard overlaps
447 std::unique_ptr<DRC_INTERACTIVE_COURTYARD_CLEARANCE> drc_on_move = nullptr;
448
449 if( showCourtyardConflicts )
450 {
451 std::shared_ptr<DRC_ENGINE> drcEngine = m_toolMgr->GetTool<DRC_TOOL>()->GetDRCEngine();
452 drc_on_move.reset( new DRC_INTERACTIVE_COURTYARD_CLEARANCE( drcEngine ) );
453 drc_on_move->Init( board );
454 }
455
456 displayConstraintsMessage( hv45Mode );
457
458 // Prime the pump
459 m_toolMgr->PostAction( ACTIONS::refreshPreview );
460
461 // Main loop: keep receiving events
462 do
463 {
464 VECTOR2I movement;
466 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
467 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
468
469 bool isSkip = evt->IsAction( &PCB_ACTIONS::skip ) && moveIndividually;
470
471 if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
472 eatFirstMouseUp = false;
473
474 if( evt->IsAction( &PCB_ACTIONS::move )
475 || evt->IsMotion()
476 || evt->IsDrag( BUT_LEFT )
480 {
481 if( m_dragging && ( evt->IsMotion()
482 || evt->IsDrag( BUT_LEFT )
483 || evt->IsAction( &ACTIONS::refreshPreview ) ) )
484 {
485 bool redraw3D = false;
486
487 VECTOR2I mousePos( controls->GetMousePosition() );
488
489 m_cursor = grid.BestSnapAnchor( mousePos, layers, grid.GetSelectionGrid( selection ), sel_items );
490
491 if( controls->GetSettings().m_lastKeyboardCursorPositionValid )
492 {
493 grid.SetSnap( false );
494 grid.SetUseGrid( false );
495 }
496
497 m_cursor = grid.BestSnapAnchor( mousePos, layers, grid.GetSelectionGrid( selection ), sel_items );
498
499 if( !selection.HasReferencePoint() )
500 originalPos = m_cursor;
501
502 if( hv45Mode )
503 {
504 VECTOR2I moveVector = m_cursor - originalPos;
505 m_cursor = originalPos + GetVectorSnapped45( moveVector );
506 }
507
508 if( updateBBox )
509 {
510 originalBBox = BOX2I();
511 bboxMovement = VECTOR2D();
512
513 for( EDA_ITEM* item : sel_items )
514 originalBBox.Merge( item->ViewBBox() );
515
516 updateBBox = false;
517 }
518
519 // Constrain selection bounding box to coordinates limits
520 movement = getSafeMovement( m_cursor - prevPos, originalBBox, bboxMovement );
521
522 // Apply constrained movement
523 m_cursor = prevPos + movement;
524
525 controls->ForceCursorPosition( true, m_cursor );
526 selection.SetReferencePoint( m_cursor );
527
528 prevPos = m_cursor;
529 bboxMovement += movement;
530
531 // Drag items to the current cursor position
532 for( BOARD_ITEM* item : sel_items )
533 {
534 // Don't double move child items.
535 if( !item->GetParent() || !item->GetParent()->IsSelected() )
536 item->Move( movement );
537
538 if( item->Type() == PCB_GENERATOR_T && sel_items.size() == 1 )
539 {
540 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genUpdateEdit, aCommit,
541 static_cast<PCB_GENERATOR*>( item ) );
542 }
543
544 if( item->Type() == PCB_FOOTPRINT_T )
545 redraw3D = true;
546 }
547
548 if( redraw3D && allowRedraw3D )
549 editFrame->Update3DView( false, true );
550
551 if( showCourtyardConflicts && drc_on_move->m_FpInMove.size() )
552 {
553 drc_on_move->Run();
554 drc_on_move->UpdateConflicts( m_toolMgr->GetView(), true );
555 }
556
558 }
559 else if( !m_dragging && ( aAutoStart || !evt->IsAction( &ACTIONS::refreshPreview ) ) )
560 {
561 // Prepare to start dragging
562 editFrame->HideSolderMask();
563
564 m_dragging = true;
565
566 for( BOARD_ITEM* item : sel_items )
567 {
568 if( item->GetParent() && item->GetParent()->IsSelected() )
569 continue;
570
571 if( !item->IsNew() && !item->IsMoving() )
572 {
573 if( item->Type() == PCB_GENERATOR_T && sel_items.size() == 1 )
574 {
575 enableLocalRatsnest = false;
576
577 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genStartEdit, aCommit,
578 static_cast<PCB_GENERATOR*>( item ) );
579 }
580 else
581 {
582 aCommit->Modify( item, nullptr, RECURSE_MODE::RECURSE );
583 }
584
585 item->SetFlags( IS_MOVING );
586
587 if( item->Type() == PCB_SHAPE_T )
588 static_cast<PCB_SHAPE*>( item )->UpdateHatching();
589
590 item->RunOnChildren(
591 [&]( BOARD_ITEM* child )
592 {
593 child->SetFlags( IS_MOVING );
594
595 if( child->Type() == PCB_SHAPE_T )
596 static_cast<PCB_SHAPE*>( child )->UpdateHatching();
597 },
599 }
600 }
601
602 m_cursor = controls->GetCursorPosition();
603
604 if( selection.HasReferencePoint() )
605 {
606 // start moving with the reference point attached to the cursor
607 grid.SetAuxAxes( false );
608
609 if( hv45Mode )
610 {
611 VECTOR2I moveVector = m_cursor - originalPos;
612 m_cursor = originalPos + GetVectorSnapped45( moveVector );
613 }
614
615 movement = m_cursor - selection.GetReferencePoint();
616
617 // Drag items to the current cursor position
618 for( EDA_ITEM* item : selection )
619 {
620 if( !item->IsBOARD_ITEM() )
621 continue;
622
623 // Don't double move footprint pads, fields, etc.
624 if( item->GetParent() && item->GetParent()->IsSelected() )
625 continue;
626
627 static_cast<BOARD_ITEM*>( item )->Move( movement );
628 }
629
630 selection.SetReferencePoint( m_cursor );
631 }
632 else
633 {
634 if( showCourtyardConflicts )
635 {
636 std::vector<FOOTPRINT*>& FPs = drc_on_move->m_FpInMove;
637
638 for( BOARD_ITEM* item : sel_items )
639 {
640 if( item->Type() == PCB_FOOTPRINT_T )
641 FPs.push_back( static_cast<FOOTPRINT*>( item ) );
642
643 item->RunOnChildren(
644 [&]( BOARD_ITEM* child )
645 {
646 if( child->Type() == PCB_FOOTPRINT_T )
647 FPs.push_back( static_cast<FOOTPRINT*>( child ) );
648 },
650 }
651 }
652
653 // Use the mouse position over cursor, as otherwise large grids will allow only
654 // snapping to items that are closest to grid points
655 m_cursor = grid.BestDragOrigin( originalMousePos, sel_items, grid.GetSelectionGrid( selection ),
656 &m_selectionTool->GetFilter() );
657
658 // Set the current cursor position to the first dragged item origin, so the
659 // movement vector could be computed later
660 if( moveWithReference )
661 {
662 selection.SetReferencePoint( pickedReferencePoint );
663 controls->ForceCursorPosition( true, pickedReferencePoint );
664 m_cursor = pickedReferencePoint;
665 }
666 else
667 {
668 VECTOR2I snapped = grid.Align( m_cursor, grid.GetSelectionGrid( selection ) );
669 VECTOR2I delta = snapped - m_cursor;
670
671 if( delta.x || delta.y )
672 {
673 for( BOARD_ITEM* item : sel_items )
674 {
675 if( item->GetParent() && item->GetParent()->IsSelected() )
676 continue;
677
678 item->Move( delta );
679 }
680 }
681
682 selection.SetReferencePoint( snapped );
683 grid.SetAuxAxes( true, snapped );
684
685 if( !editFrame->GetMoveWarpsCursor() )
686 m_cursor = originalCursorPos;
687 else
688 m_cursor = snapped;
689 }
690
691 originalPos = selection.GetReferencePoint();
692 }
693
694 // Update variables for bounding box collision calculations
695 updateBBox = true;
696
697 controls->SetCursorPosition( m_cursor, false );
698
699 prevPos = m_cursor;
700 controls->SetAutoPan( true );
702 }
703
704 if( statusPopup )
705 statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
706
707 if( enableLocalRatsnest )
708 m_toolMgr->PostAction( PCB_ACTIONS::updateLocalRatsnest, movement );
709 }
710 else if( evt->IsCancelInteractive() || evt->IsActivate() )
711 {
712 if( m_dragging && evt->IsCancelInteractive() )
713 evt->SetPassEvent( false );
714
715 restore_state = true; // Canceling the tool means that items have to be restored
716 break; // Finish
717 }
718 else if( evt->IsClick( BUT_RIGHT ) )
719 {
720 m_selectionTool->GetToolMenu().ShowContextMenu( selection );
721 }
722 else if( evt->IsAction( &ACTIONS::undo ) )
723 {
724 restore_state = true; // Perform undo locally
725 break; // Finish
726 }
727 else if( evt->IsAction( &ACTIONS::doDelete ) )
728 {
729 evt->SetPassEvent();
730 // Exit on a delete; there will no longer be anything to drag.
731 break;
732 }
733 else if( evt->IsAction( &ACTIONS::duplicate ) || evt->IsAction( &ACTIONS::cut ) )
734 {
735 wxBell();
736 }
737 else if( evt->IsAction( &PCB_ACTIONS::rotateCw )
739 || evt->IsAction( &PCB_ACTIONS::flip )
741 || evt->IsAction( &PCB_ACTIONS::mirrorV ) )
742 {
743 updateBBox = true;
744 eatFirstMouseUp = false;
745 evt->SetPassEvent();
746 }
747 else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) || isSkip )
748 {
749 // Eat mouse-up/-click events that leaked through from the lock dialog
750 if( eatFirstMouseUp && !evt->IsAction( &ACTIONS::cursorClick ) )
751 {
752 eatFirstMouseUp = false;
753 continue;
754 }
755 else if( moveIndividually && m_dragging )
756 {
757 // Put skipped items back where they started
758 if( isSkip )
759 orig_items[itemIdx]->SetPosition( originalPos );
760
761 view()->Update( orig_items[itemIdx] );
763
764 if( ++itemIdx < orig_items.size() )
765 {
766 BOARD_ITEM* nextItem = orig_items[itemIdx];
767
768 m_selectionTool->ClearSelection();
769
770 originalPos = nextItem->GetPosition();
771 m_selectionTool->AddItemToSel( nextItem );
772 selection.SetReferencePoint( originalPos );
773
774 sel_items.clear();
775 sel_items.push_back( nextItem );
776 updateStatusPopup( nextItem, itemIdx + 1, orig_items.size() );
777
778 // Pick up new item
779 aCommit->Modify( nextItem, nullptr, RECURSE_MODE::RECURSE );
780 nextItem->Move( controls->GetCursorPosition( true ) - nextItem->GetPosition() );
781
782 continue;
783 }
784 }
785
786 break; // finish
787 }
788 else if( evt->IsDblClick( BUT_LEFT ) )
789 {
790 // The first click will move the new item, so put it back
791 if( moveIndividually )
792 orig_items[itemIdx]->SetPosition( originalPos );
793
794 break; // finish
795 }
796 else if( evt->IsAction( &PCB_ACTIONS::toggleHV45Mode ) )
797 {
799 displayConstraintsMessage( hv45Mode );
800 evt->SetPassEvent( false );
801 }
802 else if( evt->IsAction( &ACTIONS::increment ) )
803 {
804 if( evt->HasParameter() )
805 m_toolMgr->RunSynchronousAction( ACTIONS::increment, aCommit, evt->Parameter<ACTIONS::INCREMENT>() );
806 else
807 m_toolMgr->RunSynchronousAction( ACTIONS::increment, aCommit, ACTIONS::INCREMENT { 1, 0 } );
808 }
815 || evt->IsAction( &ACTIONS::redo ) )
816 {
817 wxBell();
818 }
819 else
820 {
821 evt->SetPassEvent();
822 }
823
824 } while( ( evt = Wait() ) ); // Assignment (instead of equality test) is intentional
825
826 // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared
827 if( showCourtyardConflicts )
828 drc_on_move->ClearConflicts( m_toolMgr->GetView() );
829
830 controls->ForceCursorPosition( false );
831 controls->ShowCursor( false );
832 controls->SetAutoPan( false );
833
834 m_dragging = false;
835
836 // Discard reference point when selection is "dropped" onto the board
837 selection.ClearReferencePoint();
838
839 // Unselect all items to clear selection flags and then re-select the originally selected
840 // items.
842
843 if( restore_state )
844 {
845 if( sel_items.size() == 1 && sel_items.back()->Type() == PCB_GENERATOR_T )
846 {
847 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genCancelEdit, aCommit,
848 static_cast<PCB_GENERATOR*>( sel_items.back() ) );
849 }
850 }
851 else
852 {
853 if( sel_items.size() == 1 && sel_items.back()->Type() == PCB_GENERATOR_T )
854 {
855 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genFinishEdit, aCommit,
856 static_cast<PCB_GENERATOR*>( sel_items.back() ) );
857 }
858
859 EDA_ITEMS oItems( orig_items.begin(), orig_items.end() );
860 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &oItems );
861 }
862
863 // Remove the dynamic ratsnest from the screen
865
866 editFrame->PopTool( pushedEvent );
868
869 return !restore_state;
870}
871
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
BOX2< VECTOR2D > BOX2D
Definition box2.h:923
static TOOL_ACTION undo
Definition actions.h:75
static TOOL_ACTION duplicate
Definition actions.h:84
static TOOL_ACTION doDelete
Definition actions.h:85
static TOOL_ACTION cursorClick
Definition actions.h:179
static TOOL_ACTION redo
Definition actions.h:76
static TOOL_ACTION increment
Definition actions.h:94
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:223
static TOOL_ACTION cut
Definition actions.h:77
static TOOL_ACTION refreshPreview
Definition actions.h:158
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition actions.h:231
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
virtual void Revert() override
Revert the commit by restoring the modified items state.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:79
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:232
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition board_item.h:339
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:280
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:317
constexpr const Vec & GetPosition() const
Definition box2.h:211
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:146
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:658
constexpr coord_type GetLeft() const
Definition box2.h:228
constexpr const Vec & GetOrigin() const
Definition box2.h:210
constexpr coord_type GetRight() const
Definition box2.h:217
constexpr const SizeVec & GetSize() const
Definition box2.h:206
constexpr coord_type GetTop() const
Definition box2.h:229
constexpr void Offset(coord_type dx, coord_type dy)
Definition box2.h:259
constexpr coord_type GetBottom() const
Definition box2.h:222
int GetCount() const
Return the number of objects in the list.
Definition collector.h:83
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition collector.h:111
bool Empty() const
Definition commit.h:137
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:106
void DisplayConstraintsMsg(const wxString &msg)
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
virtual VECTOR2I GetPosition() const
Definition eda_item.h:272
virtual void SetPosition(const VECTOR2I &aPos)
Definition eda_item.h:273
wxString GetTypeDesc() const
Return a translated description of the type for this EDA_ITEM for display in user facing messages.
Definition eda_item.cpp:392
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:142
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
bool IsSelected() const
Definition eda_item.h:127
EDA_ITEM * GetParent() const
Definition eda_item.h:112
virtual const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
Definition eda_item.cpp:345
bool IsMoving() const
Definition eda_item.h:125
bool IsNew() const
Definition eda_item.h:124
bool isRouterActive() const
bool doMoveSelection(const TOOL_EVENT &aEvent, BOARD_COMMIT *aCommit, bool aAutoStart)
Rebuilds the ratsnest for operations that require it outside the commit rebuild.
int Swap(const TOOL_EVENT &aEvent)
Swap currently selected items' positions.
int PackAndMoveFootprints(const TOOL_EVENT &aEvent)
Try to fit selected footprints inside a minimal area and start movement.
bool pickReferencePoint(const wxString &aTooltip, const wxString &aSuccessMessage, const wxString &aCanceledMessage, VECTOR2I &aReferencePoint)
bool m_dragging
Definition edit_tool.h:232
int Move(const TOOL_EVENT &aEvent)
Main loop in which events are handled.
static const unsigned int COORDS_PADDING
Definition edit_tool.h:237
VECTOR2I getSafeMovement(const VECTOR2I &aMovement, const BOX2I &aSourceBBox, const VECTOR2D &aBBoxOffset)
VECTOR2I m_cursor
Definition edit_tool.h:233
void rebuildConnectivity()
PCB_SELECTION_TOOL * m_selectionTool
Definition edit_tool.h:231
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
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition actions.h:356
EDA_ANGLE GetOrientation() const
Definition footprint.h:248
void SetOrientation(const EDA_ANGLE &aNewAngle)
bool IsFlipped() const
Definition footprint.h:434
void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
const wxString & GetReference() const
Definition footprint.h:661
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:207
An interface for classes handling user events controlling the view behavior such as zooming,...
bool IsBOARD_ITEM() const
Definition view_item.h:102
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
Definition pad.h:54
DISPLAY_OPTIONS m_Display
static TOOL_ACTION toggleHV45Mode
static TOOL_ACTION mirrorH
Mirroring of selected items.
static TOOL_ACTION genFinishEdit
static TOOL_ACTION hideLocalRatsnest
static TOOL_ACTION genStartEdit
static TOOL_ACTION moveWithReference
move with a reference point
static TOOL_ACTION moveExact
Activation of the exact move tool.
static TOOL_ACTION copyWithReference
copy command with manual reference point selection
static TOOL_ACTION genCancelEdit
static TOOL_ACTION positionRelativeInteractively
static TOOL_ACTION genUpdateEdit
static TOOL_ACTION updateLocalRatsnest
static TOOL_ACTION moveIndividually
move items one-by-one
static TOOL_ACTION positionRelative
static TOOL_ACTION skip
static TOOL_ACTION move
move or drag an item
static TOOL_ACTION mirrorV
static TOOL_ACTION flip
Flipping of selected objects.
static TOOL_ACTION rotateCw
Rotation of selected objects.
static TOOL_ACTION rotateCcw
Common, abstract interface for edit frames.
PCBNEW_SETTINGS * GetPcbNewSettings() const
virtual PCB_LAYER_ID GetActiveLayer() const
virtual MAGNETIC_SETTINGS * GetMagneticItemsSettings()
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
BOARD * GetBoard() const
virtual void Update3DView(bool aMarkDirty, bool aRefresh, const wxString *aTitle=nullptr)
Update the 3D view, if the viewer is opened by this frame.
The selection tool: currently supports:
void FilterCollectorForMarkers(GENERAL_COLLECTOR &aCollector) const
Drop any PCB_MARKERs from the collector.
void FilterCollectorForFreePads(GENERAL_COLLECTOR &aCollector, bool aForcePromotion=false) const
Check the "allow free pads" setting and if disabled, replace any pads in the collector with their par...
void FilterCollectorForHierarchy(GENERAL_COLLECTOR &aCollector, bool aMultiselect) const
In general we don't want to select both a parent and any of it's children.
void FilterCollectorForLockedItems(GENERAL_COLLECTOR &aCollector)
In the PCB editor strip out any locked items unless the OverrideLocks checkbox is set.
void FilterCollectorForTableCells(GENERAL_COLLECTOR &aCollector) const
Promote any table cell selections to the whole table.
T * frame() const
KIGFX::PCB_VIEW * view() const
LEADER_MODE GetAngleSnapMode() const
Get the current angle snapping mode.
KIGFX::VIEW_CONTROLS * controls() const
BOARD * board() const
PCB_DRAW_PANEL_GAL * canvas() const
const PCB_SELECTION & selection() const
FOOTPRINT * footprint() const
virtual void PopTool(const TOOL_EVENT &aEvent)
Pops a tool from the stack.
bool GetMoveWarpsCursor() const
Indicate that a move operation should warp the mouse pointer to the origin of the move object.
virtual void PushTool(const TOOL_EVENT &aEvent)
NB: the definition of "tool" is different at the user level.
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
Generic, UI-independent tool event.
Definition tool_event.h:171
bool DisableGridSnapping() const
Definition tool_event.h:371
bool HasParameter() const
Definition tool_event.h:464
bool IsCancelInteractive() const
Indicate the event should restart/end an ongoing interactive tool's event loop (eg esc key,...
bool IsActivate() const
Definition tool_event.h:345
COMMIT * Commit() const
Definition tool_event.h:283
bool IsClick(int aButtonMask=BUT_ANY) const
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition tool_event.h:315
int Modifier(int aMask=MD_MODIFIER_MASK) const
Return information about key modifiers state (Ctrl, Alt, etc.).
Definition tool_event.h:366
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:473
bool IsDblClick(int aButtonMask=BUT_ANY) const
std::atomic< SYNCRONOUS_TOOL_STATE > * SynchronousState() const
Definition tool_event.h:280
void SetPassEvent(bool aPass=true)
Definition tool_event.h:256
bool IsMouseUp(int aButtonMask=BUT_ANY) const
Definition tool_event.h:325
bool IsMotion() const
Definition tool_event.h:330
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.
static bool IsZoneFillAction(const TOOL_EVENT *aEvent)
@ MOVING
Definition cursors.h:48
@ ARROW
Definition cursors.h:46
#define _(s)
@ RECURSE
Definition eda_item.h:51
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
Definition eda_item.h:566
#define IS_MOVING
Item being moved.
@ FP_JUST_ADDED
Definition footprint.h:86
a few functions useful in geometry calculations.
VECTOR2< T > GetVectorSnapped45(const VECTOR2< T > &aVec, bool only45=false)
Snap a vector onto the nearest 0, 45 or 90 degree line.
@ DIRECT
Unconstrained point-to-point.
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:29
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition wxgtk/ui.cpp:689
Class to handle a set of BOARD_ITEMs.
void SpreadFootprints(std::vector< FOOTPRINT * > *aFootprints, VECTOR2I aTargetBoxPosition, bool aGroupBySheet, int aComponentGap, int aGroupGap)
Footprints (after loaded by reading a netlist for instance) are moved to be in a small free area (out...
int delta
@ MD_SHIFT
Definition tool_event.h:143
@ STS_CANCELLED
Definition tool_event.h:164
@ STS_FINISHED
Definition tool_event.h:163
@ STS_RUNNING
Definition tool_event.h:162
@ BUT_LEFT
Definition tool_event.h:132
@ BUT_RIGHT
Definition tool_event.h:133
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:88
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:91
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:110
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:86
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:96
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694