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 (C) 2017-2023 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>
33#include <pad.h>
34#include <pcb_group.h>
35#include <pcb_generator.h>
36#include <pcb_edit_frame.h>
37#include <spread_footprints.h>
38#include <tools/pcb_actions.h>
40#include <tools/edit_tool.h>
42#include <tools/drc_tool.h>
44#include <router/router_tool.h>
46#include <zone_filler.h>
47#include <drc/drc_engine.h>
48#include <drc/drc_item.h>
49#include <drc/drc_rule.h>
51
52
53int EDIT_TOOL::Swap( const TOOL_EVENT& aEvent )
54{
55 if( isRouterActive() )
56 {
57 wxBell();
58 return 0;
59 }
60
62 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
63 {
64 sTool->FilterCollectorForMarkers( aCollector );
65 sTool->FilterCollectorForHierarchy( aCollector, true );
66 sTool->FilterCollectorForFreePads( aCollector );
67
68 // Iterate from the back so we don't have to worry about removals.
69 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
70 {
71 BOARD_ITEM* item = aCollector[i];
72
73 if( item->Type() == PCB_TRACE_T )
74 aCollector.Remove( item );
75 }
76 },
77 true /* prompt user regarding locked items */ );
78
79 if( selection.Size() < 2 )
80 return 0;
81
82 BOARD_COMMIT localCommit( this );
83 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
84
85 if( !commit )
86 commit = &localCommit;
87
88 std::vector<EDA_ITEM*> sorted = selection.GetItemsSortedBySelectionOrder();
89
90 // Save items, so changes can be undone
91 for( EDA_ITEM* item : selection )
92 {
93 if( !item->IsNew() && !item->IsMoving() )
94 commit->Modify( item );
95 }
96
97 for( size_t i = 0; i < sorted.size() - 1; i++ )
98 {
99 BOARD_ITEM* a = dynamic_cast<BOARD_ITEM*>( sorted[i] );
100 BOARD_ITEM* b = dynamic_cast<BOARD_ITEM*>( sorted[( i + 1 ) % sorted.size()] );
101
102 wxCHECK2( a && b, continue );
103
104 // Swap X,Y position
105 VECTOR2I aPos = a->GetPosition(), bPos = b->GetPosition();
106 std::swap( aPos, bPos );
107 a->SetPosition( aPos );
108 b->SetPosition( bPos );
109
110 // Handle footprints specially. They can be flipped to the back of the board which
111 // requires a special transformation.
112 if( a->Type() == PCB_FOOTPRINT_T && b->Type() == PCB_FOOTPRINT_T )
113 {
114 FOOTPRINT* aFP = static_cast<FOOTPRINT*>( a );
115 FOOTPRINT* bFP = static_cast<FOOTPRINT*>( b );
116
117 // Store initial orientation of footprints, before flipping them.
118 EDA_ANGLE aAngle = aFP->GetOrientation();
119 EDA_ANGLE bAngle = bFP->GetOrientation();
120
121 // Flip both if needed
122 if( aFP->IsFlipped() != bFP->IsFlipped() )
123 {
124 aFP->Flip( aPos, false );
125 bFP->Flip( bPos, false );
126 }
127
128 // Set orientation
129 std::swap( aAngle, bAngle );
130 aFP->SetOrientation( aAngle );
131 bFP->SetOrientation( bAngle );
132 }
133 // We can also do a layer swap safely for two objects of the same type,
134 // except groups which don't support layer swaps.
135 else if( a->Type() == b->Type() && a->Type() != PCB_GROUP_T )
136 {
137 // Swap layers
138 PCB_LAYER_ID aLayer = a->GetLayer(), bLayer = b->GetLayer();
139 std::swap( aLayer, bLayer );
140 a->SetLayer( aLayer );
141 b->SetLayer( bLayer );
142 }
143 }
144
145 if( !localCommit.Empty() )
146 localCommit.Push( _( "Swap" ) );
147
149
150 return 0;
151}
152
153
155{
156 if( isRouterActive() || m_dragging )
157 {
158 wxBell();
159 return 0;
160 }
161
162 BOARD_COMMIT commit( this );
164 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
165 {
166 sTool->FilterCollectorForMarkers( aCollector );
167 sTool->FilterCollectorForHierarchy( aCollector, true );
168 sTool->FilterCollectorForFreePads( aCollector, true );
169
170 // Iterate from the back so we don't have to worry about removals.
171 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
172 {
173 BOARD_ITEM* item = aCollector[i];
174
175 if( !dynamic_cast<FOOTPRINT*>( item ) )
176 aCollector.Remove( item );
177 }
178 },
179 true /* prompt user regarding locked items */ );
180
181 std::vector<FOOTPRINT*> footprintsToPack;
182
183 for( EDA_ITEM* item : selection )
184 footprintsToPack.push_back( static_cast<FOOTPRINT*>( item ) );
185
186 if( footprintsToPack.empty() )
187 return 0;
188
189 BOX2I footprintsBbox;
190
191 for( FOOTPRINT* item : footprintsToPack )
192 {
193 commit.Modify( item );
194 item->SetFlags( IS_MOVING );
195 footprintsBbox.Merge( item->GetBoundingBox( false, false ) );
196 }
197
198 SpreadFootprints( &footprintsToPack, footprintsBbox.Normalize().GetOrigin(), false );
199
200 if( doMoveSelection( aEvent, &commit ) )
201 commit.Push( _( "Pack footprints" ) );
202 else
203 commit.Revert();
204
205 return 0;
206}
207
208
209int EDIT_TOOL::Move( const TOOL_EVENT& aEvent )
210{
211 if( isRouterActive() || m_dragging )
212 {
213 wxBell();
214 return 0;
215 }
216
217 if( BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() ) )
218 {
219 wxCHECK( aEvent.SynchronousState(), 0 );
220 aEvent.SynchronousState()->store( STS_RUNNING );
221
222 if( doMoveSelection( aEvent, commit ) )
223 aEvent.SynchronousState()->store( STS_FINISHED );
224 else
225 aEvent.SynchronousState()->store( STS_CANCELLED );
226 }
227 else
228 {
229 BOARD_COMMIT localCommit( this );
230
231 if( doMoveSelection( aEvent, &localCommit ) )
232 {
233 localCommit.Push( _( "Move" ) );
234 }
235 else
236 {
237 localCommit.Revert();
238 }
239 }
240
241 return 0;
242}
243
244
245VECTOR2I EDIT_TOOL::getSafeMovement( const VECTOR2I& aMovement, const BOX2I& aSourceBBox,
246 const VECTOR2D& aBBoxOffset )
247{
248 typedef std::numeric_limits<int> coord_limits;
249
250 int max = coord_limits::max();
251 int min = -max;
252
253 double left = aBBoxOffset.x + aSourceBBox.GetPosition().x;
254 double top = aBBoxOffset.y + aSourceBBox.GetPosition().y;
255
256 double right = left + aSourceBBox.GetSize().x;
257 double bottom = top + aSourceBBox.GetSize().y;
258
259 // Do not restrict movement if bounding box is already out of bounds
260 if( left < min || top < min || right > max || bottom > max )
261 return aMovement;
262
263 // Constrain moving bounding box to coordinates limits
264 VECTOR2D tryMovement( aMovement );
265 VECTOR2D bBoxOrigin( aSourceBBox.GetPosition() + aBBoxOffset );
266 VECTOR2D clampedBBoxOrigin = GetClampedCoords( bBoxOrigin + tryMovement, COORDS_PADDING );
267
268 tryMovement = clampedBBoxOrigin - bBoxOrigin;
269
270 VECTOR2D bBoxEnd( aSourceBBox.GetEnd() + aBBoxOffset );
271 VECTOR2D clampedBBoxEnd = GetClampedCoords( bBoxEnd + tryMovement, COORDS_PADDING );
272
273 tryMovement = clampedBBoxEnd - bBoxEnd;
274
275 return GetClampedCoords<double, int>( tryMovement );
276}
277
278
280{
281 bool moveWithReference = aEvent.IsAction( &PCB_ACTIONS::moveWithReference );
282 bool moveIndividually = aEvent.IsAction( &PCB_ACTIONS::moveIndividually );
283
284 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
285 PCBNEW_SETTINGS* cfg = editFrame->GetPcbNewSettings();
286 BOARD* board = editFrame->GetBoard();
288 VECTOR2I originalCursorPos = controls->GetCursorPosition();
289 STATUS_TEXT_POPUP statusPopup( frame() );
290 wxString status;
291 size_t itemIdx = 0;
292
293 // Be sure that there is at least one item that we can modify. If nothing was selected before,
294 // try looking for the stuff under mouse cursor (i.e. KiCad old-style hover selection)
296 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
297 {
298 sTool->FilterCollectorForMarkers( aCollector );
299 sTool->FilterCollectorForHierarchy( aCollector, true );
300 },
301 // Prompt user regarding locked items if in board editor and in free-pad-mode (if
302 // we're not in free-pad mode we delay this until the second RequestSelection()).
304
305 if( m_dragging || selection.Empty() )
306 return false;
307
308 LSET item_layers = selection.GetSelectionLayers();
309 bool is_hover = selection.IsHover(); // N.B. This must be saved before the second call
310 // to RequestSelection() below
311 VECTOR2I pickedReferencePoint;
312
313 // Now filter out pads if not in free pads mode. We cannot do this in the first
314 // RequestSelection() as we need the item_layers when a pad is the selection front.
316 {
318 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
319 {
320 sTool->FilterCollectorForMarkers( aCollector );
321 sTool->FilterCollectorForHierarchy( aCollector, true );
322 sTool->FilterCollectorForFreePads( aCollector );
323 },
324 true /* prompt user regarding locked items */ );
325 }
326
327 if( selection.Empty() )
328 return false;
329
330 editFrame->PushTool( aEvent );
331 Activate();
332
333 // Must be done after Activate() so that it gets set into the correct context
334 controls->ShowCursor( true );
335 controls->SetAutoPan( true );
336 controls->ForceCursorPosition( false );
337
338 auto displayConstraintsMessage =
339 [editFrame]( bool constrained )
340 {
341 editFrame->DisplayConstraintsMsg( constrained ? _( "Constrain to H, V, 45" )
342 : wxString( wxT( "" ) ) );
343 };
344
345 auto updateStatusPopup =
346 [&]( EDA_ITEM* item, size_t ii, size_t count )
347 {
348 wxString popuptext = _( "Click to place %s (item %zu of %zu)\n"
349 "Press <esc> to cancel all; double-click to finish" );
350 wxString msg;
351
352 if( item->Type() == PCB_FOOTPRINT_T )
353 {
354 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
355 msg = fp->GetReference();
356 }
357 else if( item->Type() == PCB_PAD_T )
358 {
359 PAD* pad = static_cast<PAD*>( item );
360 FOOTPRINT* fp = pad->GetParentFootprint();
361 msg = wxString::Format( _( "%s pad %s" ), fp->GetReference(), pad->GetNumber() );
362 }
363 else
364 {
365 msg = item->GetTypeDesc().Lower();
366 }
367
368 statusPopup.SetText( wxString::Format( popuptext, msg, ii, count ) );
369 };
370
371 std::vector<BOARD_ITEM*> sel_items; // All the items operated on by the move below
372 std::vector<BOARD_ITEM*> orig_items; // All the original items in the selection
373
374 for( EDA_ITEM* item : selection )
375 {
376 if( BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( item ) )
377 {
378 if( !is_hover )
379 orig_items.push_back( boardItem );
380
381 sel_items.push_back( boardItem );
382 }
383
384 if( FOOTPRINT* footprint = dynamic_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
392 }
393 }
394
395 if( moveWithReference && !pickReferencePoint( _( "Select reference point for move..." ), "", "",
396 pickedReferencePoint ) )
397 {
398 if( is_hover )
400
401 editFrame->PopTool( aEvent );
402 return false;
403 }
404
405 if( moveIndividually )
406 {
407 orig_items.clear();
408
409 for( EDA_ITEM* item : selection.GetItemsSortedBySelectionOrder() )
410 {
411 if( BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( item ) )
412 orig_items.push_back( boardItem );
413 }
414
415 updateStatusPopup( orig_items[ itemIdx ], itemIdx + 1, orig_items.size() );
416 statusPopup.Popup();
417 statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
418 canvas()->SetStatusPopup( statusPopup.GetPanel() );
419
421 m_selectionTool->AddItemToSel( orig_items[ itemIdx ] );
422
423 sel_items.clear();
424 sel_items.push_back( orig_items[ itemIdx ] );
425 }
426
427 bool restore_state = false;
428 VECTOR2I originalPos;
429 VECTOR2I totalMovement;
430 VECTOR2D bboxMovement;
431 BOX2I originalBBox;
432 bool updateBBox = true;
434 TOOL_EVENT copy = aEvent;
435 TOOL_EVENT* evt = &copy;
436 VECTOR2I prevPos;
437
438 bool hv45Mode = false;
439 bool eatFirstMouseUp = true;
440 bool allowRedraw3D = cfg->m_Display.m_Live3DRefresh;
441 bool showCourtyardConflicts = !m_isFootprintEditor && cfg->m_ShowCourtyardCollisions;
442
443 // Used to test courtyard overlaps
444 std::unique_ptr<DRC_INTERACTIVE_COURTYARD_CLEARANCE> drc_on_move = nullptr;
445
446 if( showCourtyardConflicts )
447 {
448 std::shared_ptr<DRC_ENGINE> drcEngine = m_toolMgr->GetTool<DRC_TOOL>()->GetDRCEngine();
449 drc_on_move.reset( new DRC_INTERACTIVE_COURTYARD_CLEARANCE( drcEngine ) );
450 drc_on_move->Init( board );
451 }
452
453 displayConstraintsMessage( hv45Mode );
454
455 // Prime the pump
457
458 // Main loop: keep receiving events
459 do
460 {
461 VECTOR2I movement;
462 editFrame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
463 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
464 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
465
466 bool isSkip = evt->IsAction( &PCB_ACTIONS::skip ) && moveIndividually;
467
468 if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
469 eatFirstMouseUp = false;
470
471 if( evt->IsAction( &PCB_ACTIONS::move ) || evt->IsMotion() || evt->IsDrag( BUT_LEFT )
475 {
476 if( m_dragging && evt->Category() == TC_MOUSE )
477 {
478 bool redraw3D = false;
479
480 VECTOR2I mousePos( controls->GetMousePosition() );
481
482 m_cursor = grid.BestSnapAnchor( mousePos, item_layers,
483 grid.GetSelectionGrid( selection ), sel_items );
484
486 {
487 long action = controls->GetSettings().m_lastKeyboardCursorCommand;
488
489 // The arrow keys are by definition SINGLE AXIS. Do not allow the other
490 // axis to be snapped to the grid.
491 if( action == ACTIONS::CURSOR_LEFT || action == ACTIONS::CURSOR_RIGHT )
492 m_cursor.y = prevPos.y;
493 else if( action == ACTIONS::CURSOR_UP || action == ACTIONS::CURSOR_DOWN )
494 m_cursor.x = prevPos.x;
495 }
496
497 if( !selection.HasReferencePoint() )
498 originalPos = m_cursor;
499
500 if( hv45Mode )
501 {
502 VECTOR2I moveVector = m_cursor - originalPos;
503 m_cursor = originalPos + GetVectorSnapped45( moveVector );
504 }
505
506 if( updateBBox )
507 {
508 originalBBox = BOX2I();
509 bboxMovement = VECTOR2D();
510
511 for( EDA_ITEM* item : sel_items )
512 {
513 BOX2I viewBBOX = item->ViewBBox();
514
515 if( originalBBox.GetWidth() == 0 && originalBBox.GetHeight() == 0 )
516 originalBBox = viewBBOX;
517 else
518 originalBBox.Merge( viewBBOX );
519 }
520
521 updateBBox = false;
522 }
523
524 // Constrain selection bounding box to coordinates limits
525 movement = getSafeMovement( m_cursor - prevPos, originalBBox, bboxMovement );
526
527 // Apply constrained movement
528 m_cursor = prevPos + movement;
529
530 controls->ForceCursorPosition( true, m_cursor );
531 selection.SetReferencePoint( m_cursor );
532
533 prevPos = m_cursor;
534 totalMovement += movement;
535 bboxMovement += movement;
536
537 // Drag items to the current cursor position
538 for( EDA_ITEM* item : sel_items )
539 {
540 // Don't double move child items.
541 if( !item->GetParent() || !item->GetParent()->IsSelected() )
542 static_cast<BOARD_ITEM*>( item )->Move( movement );
543
544 if( item->Type() == PCB_GENERATOR_T && sel_items.size() == 1 )
545 {
547 static_cast<PCB_GENERATOR*>( item ) );
548 }
549
550 if( item->Type() == PCB_FOOTPRINT_T )
551 redraw3D = true;
552 }
553
554 if( redraw3D && allowRedraw3D )
555 editFrame->Update3DView( false, true );
556
557 if( showCourtyardConflicts && drc_on_move->m_FpInMove.size() )
558 {
559 drc_on_move->Run();
560 drc_on_move->UpdateConflicts( m_toolMgr->GetView(), true );
561 }
562
564 }
565 else if( !m_dragging && !evt->IsAction( &ACTIONS::refreshPreview ) )
566 {
567 // Prepare to start dragging
568 editFrame->HideSolderMask();
569
570 m_dragging = true;
571
572 for( EDA_ITEM* item : selection )
573 {
574 if( item->GetParent() && item->GetParent()->IsSelected() )
575 continue;
576
577 if( !item->IsNew() && !item->IsMoving() )
578 {
579 if( item->Type() == PCB_GENERATOR_T && sel_items.size() == 1 )
580 {
582 static_cast<PCB_GENERATOR*>( item ) );
583 }
584 else
585 {
586 aCommit->Modify( item );
587 }
588
589 item->SetFlags( IS_MOVING );
590
591 static_cast<BOARD_ITEM*>( item )->RunOnDescendants(
592 [&]( BOARD_ITEM* bItem )
593 {
594 item->SetFlags( IS_MOVING );
595 } );
596 }
597 }
598
599 m_cursor = controls->GetCursorPosition();
600
601 if( selection.HasReferencePoint() )
602 {
603 // start moving with the reference point attached to the cursor
604 grid.SetAuxAxes( false );
605
606 if( hv45Mode )
607 {
608 VECTOR2I moveVector = m_cursor - originalPos;
609 m_cursor = originalPos + GetVectorSnapped45( moveVector );
610 }
611
612 movement = m_cursor - selection.GetReferencePoint();
613
614 // Drag items to the current cursor position
615 for( EDA_ITEM* item : selection )
616 {
617 // Don't double move footprint pads, fields, etc.
618 if( item->GetParent() && item->GetParent()->IsSelected() )
619 continue;
620
621 static_cast<BOARD_ITEM*>( item )->Move( movement );
622 }
623
624 selection.SetReferencePoint( m_cursor );
625 }
626 else
627 {
628 for( BOARD_ITEM* item : sel_items )
629 {
630 if( showCourtyardConflicts && item->Type() == PCB_FOOTPRINT_T )
631 drc_on_move->m_FpInMove.push_back( static_cast<FOOTPRINT*>( item ) );
632 }
633
634 m_cursor = grid.BestDragOrigin( originalCursorPos, sel_items,
635 grid.GetSelectionGrid( selection ),
637
638 // Set the current cursor position to the first dragged item origin, so the
639 // movement vector could be computed later
640 if( moveWithReference )
641 {
642 selection.SetReferencePoint( pickedReferencePoint );
643 controls->ForceCursorPosition( true, pickedReferencePoint );
644 m_cursor = pickedReferencePoint;
645 }
646 else
647 {
648 // Check if user wants to warp the mouse to origin of moved object
649 if( !editFrame->GetMoveWarpsCursor() )
650 m_cursor = originalCursorPos; // No, so use original mouse pos instead
651
652 selection.SetReferencePoint( m_cursor );
653 grid.SetAuxAxes( true, m_cursor );
654 }
655
656 originalPos = m_cursor;
657 }
658
659 // Update variables for bounding box collision calculations
660 updateBBox = true;
661
662 controls->SetCursorPosition( m_cursor, false );
663
664 prevPos = m_cursor;
665 controls->SetAutoPan( true );
667 }
668
669 statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
670
672 }
673 else if( evt->IsCancelInteractive() || evt->IsActivate() )
674 {
675 if( m_dragging && evt->IsCancelInteractive() )
676 evt->SetPassEvent( false );
677
678 restore_state = true; // Canceling the tool means that items have to be restored
679 break; // Finish
680 }
681 else if( evt->IsAction( &ACTIONS::undo ) || evt->IsAction( &ACTIONS::doDelete ) )
682 {
683 restore_state = true; // Perform undo locally
684 break; // Finish
685 }
686 else if( evt->IsAction( &ACTIONS::duplicate ) || evt->IsAction( &ACTIONS::cut ) )
687 {
688 }
689 else if( evt->IsAction( &PCB_ACTIONS::rotateCw )
691 || evt->IsAction( &PCB_ACTIONS::flip )
693 || evt->IsAction( &PCB_ACTIONS::mirrorV ) )
694 {
695 updateBBox = true;
696 eatFirstMouseUp = false;
697 evt->SetPassEvent();
698 }
699 else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) || isSkip )
700 {
701 // Eat mouse-up/-click events that leaked through from the lock dialog
702 if( eatFirstMouseUp && !evt->IsAction( &ACTIONS::cursorClick ) )
703 {
704 eatFirstMouseUp = false;
705 continue;
706 }
707 else if( moveIndividually && m_dragging )
708 {
709 // Put skipped items back where they started
710 if( isSkip )
711 orig_items[itemIdx]->SetPosition( originalPos );
712
714
715 if( ++itemIdx < orig_items.size() )
716 {
717 BOARD_ITEM* nextItem = orig_items[itemIdx];
718
720
721 originalPos = nextItem->GetPosition();
722 m_selectionTool->AddItemToSel( nextItem );
723 selection.SetReferencePoint( originalPos );
724
725 sel_items.clear();
726 sel_items.push_back( nextItem );
727 updateStatusPopup( nextItem, itemIdx + 1, orig_items.size() );
728
729 // Pick up new item
730 aCommit->Modify( nextItem );
731 nextItem->Move( controls->GetCursorPosition( true ) - nextItem->GetPosition() );
732
733 continue;
734 }
735 }
736
737 break; // finish
738 }
739 else if( evt->IsDblClick( BUT_LEFT ) )
740 {
741 // The first click will move the new item, so put it back
742 if( moveIndividually )
743 orig_items[itemIdx]->SetPosition( originalPos );
744
745 break; // finish
746 }
747 else if( evt->IsAction( &PCB_ACTIONS::toggleHV45Mode ) )
748 {
749 hv45Mode = !hv45Mode;
750 displayConstraintsMessage( hv45Mode );
751 evt->SetPassEvent( false );
752 }
758 || evt->IsAction( &ACTIONS::redo ) )
759 {
760 wxBell();
761 }
762 else
763 {
764 evt->SetPassEvent();
765 }
766
767 } while( ( evt = Wait() ) ); // Assignment (instead of equality test) is intentional
768
769 // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared
770 if( showCourtyardConflicts )
771 drc_on_move->ClearConflicts( m_toolMgr->GetView() );
772
773 controls->ForceCursorPosition( false );
774 controls->ShowCursor( false );
775 controls->SetAutoPan( false );
776
777 m_dragging = false;
778
779 // Discard reference point when selection is "dropped" onto the board
780 selection.ClearReferencePoint();
781
782 // Unselect all items to clear selection flags and then re-select the originally selected
783 // items.
785
786 if( restore_state )
787 {
788 if( sel_items.size() == 1 && sel_items.back()->Type() == PCB_GENERATOR_T )
789 {
791 static_cast<PCB_GENERATOR*>( sel_items.back() ) );
792 }
793 }
794 else
795 {
796 if( sel_items.size() == 1 && sel_items.back()->Type() == PCB_GENERATOR_T )
797 {
799 static_cast<PCB_GENERATOR*>( sel_items.back() ) );
800 }
801
802 EDA_ITEMS oItems( orig_items.begin(), orig_items.end() );
804 }
805
806 // Remove the dynamic ratsnest from the screen
808
809 editFrame->PopTool( aEvent );
810 editFrame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
811
812 return !restore_state;
813}
814
BOX2< VECTOR2I > BOX2I
Definition: box2.h:853
@ CURSOR_RIGHT
Definition: actions.h:209
@ CURSOR_LEFT
Definition: actions.h:207
@ CURSOR_UP
Definition: actions.h:203
@ CURSOR_DOWN
Definition: actions.h:205
static TOOL_ACTION undo
Definition: actions.h:65
static TOOL_ACTION duplicate
Definition: actions.h:73
static TOOL_ACTION doDelete
Definition: actions.h:74
static TOOL_ACTION cursorClick
Definition: actions.h:130
static TOOL_ACTION redo
Definition: actions.h:66
static TOOL_ACTION cut
Definition: actions.h:67
static TOOL_ACTION refreshPreview
Definition: actions.h:113
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
virtual void Revert() override
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:77
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:225
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition: board_item.h:313
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:259
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:276
BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition: box2.h:120
const Vec & GetPosition() const
Definition: box2.h:185
const Vec & GetOrigin() const
Definition: box2.h:184
coord_type GetHeight() const
Definition: box2.h:189
coord_type GetWidth() const
Definition: box2.h:188
const Vec GetEnd() const
Definition: box2.h:186
const Vec & GetSize() const
Definition: box2.h:180
BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:589
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:81
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
bool Empty() const
Returns status of an item.
Definition: commit.h:142
void DisplayConstraintsMsg(const wxString &msg)
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
void SetStatusPopup(wxWindow *aPopup)
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:85
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:239
virtual void SetPosition(const VECTOR2I &aPos)
Definition: eda_item.h:240
wxString GetTypeDesc() const
Return a translated description of the type for this EDA_ITEM for display in user facing messages.
Definition: eda_item.cpp:320
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:123
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
bool IsSelected() const
Definition: eda_item.h:106
EDA_ITEM * GetParent() const
Definition: eda_item.h:99
virtual const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
Definition: eda_item.cpp:273
bool IsMoving() const
Definition: eda_item.h:104
bool IsNew() const
Definition: eda_item.h:103
bool isRouterActive() const
Definition: edit_tool.cpp:415
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)
Definition: edit_tool.cpp:2627
bool m_dragging
Definition: edit_tool.h:210
int Move(const TOOL_EVENT &aEvent)
Main loop in which events are handled.
static const unsigned int COORDS_PADDING
Definition: edit_tool.h:215
VECTOR2I getSafeMovement(const VECTOR2I &aMovement, const BOX2I &aSourceBBox, const VECTOR2D &aBBoxOffset)
VECTOR2I m_cursor
Definition: edit_tool.h:211
bool doMoveSelection(const TOOL_EVENT &aEvent, BOARD_COMMIT *aCommit)
Rebuilds the ratsnest for operations that require it outside the commit rebuild.
void rebuildConnectivity()
Definition: edit_tool.cpp:2801
PCB_SELECTION_TOOL * m_selectionTool
Definition: edit_tool.h:209
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:240
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:243
EDA_ANGLE GetOrientation() const
Definition: footprint.h:209
void SetOrientation(const EDA_ANGLE &aNewAngle)
Definition: footprint.cpp:2069
void SetAttributes(int aAttributes)
Definition: footprint.h:278
int GetAttributes() const
Definition: footprint.h:277
bool IsFlipped() const
Definition: footprint.h:351
PADS & Pads()
Definition: footprint.h:188
void Flip(const VECTOR2I &aCentre, bool aFlipLeftRight) override
Flip this object, i.e.
Definition: footprint.cpp:1937
const wxString & GetReference() const
Definition: footprint.h:556
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:204
An interface for classes handling user events controlling the view behavior such as zooming,...
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 VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const =0
Return the current mouse pointer position.
virtual void SetCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true, bool aTriggeredByArrows=false, long aArrowCommand=0)=0
Move cursor to the requested position expressed in world coordinates.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
const VC_SETTINGS & GetSettings() const
Apply VIEW_CONTROLS settings from an object.
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:556
Definition: pad.h:58
DISPLAY_OPTIONS m_Display
bool m_ShowCourtyardCollisions
static TOOL_ACTION toggleHV45Mode
Definition: pcb_actions.h:513
static TOOL_ACTION mirrorH
Mirroring of selected items.
Definition: pcb_actions.h:139
static TOOL_ACTION genPushEdit
Definition: pcb_actions.h:278
static TOOL_ACTION hideLocalRatsnest
Definition: pcb_actions.h:557
static TOOL_ACTION genStartEdit
Definition: pcb_actions.h:276
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:68
static TOOL_ACTION moveWithReference
move with a reference point
Definition: pcb_actions.h:126
static TOOL_ACTION moveExact
Activation of the exact move tool.
Definition: pcb_actions.h:177
static TOOL_ACTION copyWithReference
copy command with manual reference point selection
Definition: pcb_actions.h:129
static TOOL_ACTION genUpdateEdit
Definition: pcb_actions.h:277
static TOOL_ACTION updateLocalRatsnest
Definition: pcb_actions.h:558
static TOOL_ACTION moveIndividually
move items one-by-one
Definition: pcb_actions.h:123
static TOOL_ACTION positionRelative
Activation of the position relative tool.
Definition: pcb_actions.h:312
static TOOL_ACTION skip
Definition: pcb_actions.h:149
static TOOL_ACTION move
move or drag an item
Definition: pcb_actions.h:120
static TOOL_ACTION mirrorV
Definition: pcb_actions.h:140
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition: pcb_actions.h:76
static TOOL_ACTION flip
Flipping of selected objects.
Definition: pcb_actions.h:136
static TOOL_ACTION rotateCw
Rotation of selected objects.
Definition: pcb_actions.h:132
static TOOL_ACTION rotateCcw
Definition: pcb_actions.h:133
static TOOL_ACTION genRevertEdit
Definition: pcb_actions.h:279
Common, abstract interface for edit frames.
PCBNEW_SETTINGS * GetPcbNewSettings() 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.
PCB_SELECTION & RequestSelection(CLIENT_SELECTION_FILTER aClientFilter, bool aConfirmLockedItems=false)
Return the current selection, filtered according to aClientFilter.
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...
SELECTION_FILTER_OPTIONS & GetFilter()
Set up handlers for various events.
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.
int ClearSelection(const TOOL_EVENT &aEvent)
const LSET GetSelectionLayers()
PCB_BASE_EDIT_FRAME * frame() const
KIGFX::VIEW_CONTROLS * controls() const
BOARD * board() const
PCB_DRAW_PANEL_GAL * canvas() const
bool m_isFootprintEditor
const PCB_SELECTION & selection() const
FOOTPRINT * footprint() const
int AddItemToSel(const TOOL_EVENT &aEvent)
const std::vector< EDA_ITEM * > GetItemsSortedBySelectionOrder() const
Definition: selection.cpp:201
bool IsHover() const
Definition: selection.h:83
int Size() const
Returns the number of selected parts.
Definition: selection.h:115
void ClearReferencePoint()
Definition: selection.h:265
void SetReferencePoint(const VECTOR2I &aP)
Definition: selection.h:260
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:109
bool HasReferencePoint() const
Definition: selection.h:247
wxWindow * GetPanel()
Definition: status_popup.h:63
virtual void Popup(wxWindow *aFocus=nullptr)
virtual void Move(const wxPoint &aWhere)
Extension of STATUS_POPUP for displaying a single line text.
Definition: status_popup.h:84
void SetText(const wxString &aText)
Display a text.
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.
Definition: tools_holder.h:148
virtual void PushTool(const TOOL_EVENT &aEvent)
NB: the definition of "tool" is different at the user level.
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:216
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
Generic, UI-independent tool event.
Definition: tool_event.h:167
bool DisableGridSnapping() const
Definition: tool_event.h:363
bool IsCancelInteractive() const
Indicate the event should restart/end an ongoing interactive tool's event loop (eg esc key,...
Definition: tool_event.cpp:210
bool IsActivate() const
Definition: tool_event.h:337
COMMIT * Commit() const
Returns information about difference between current mouse cursor position and the place where draggi...
Definition: tool_event.h:275
bool IsClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:198
TOOL_EVENT_CATEGORY Category() const
Returns more specific information about the type of an event.
Definition: tool_event.h:243
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:307
int Modifier(int aMask=MD_MODIFIER_MASK) const
Definition: tool_event.h:358
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
Definition: tool_event.cpp:82
bool IsDblClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:204
std::atomic< SYNCRONOUS_TOOL_STATE > * SynchronousState() const
Definition: tool_event.h:272
void SetPassEvent(bool aPass=true)
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition: tool_event.h:252
bool IsMouseUp(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:317
bool IsMotion() const
Definition: tool_event.h:322
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.
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
void PostEvent(const TOOL_EVENT &aEvent)
Put an event to the event queue to be processed at the end of event processing cycle.
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:145
bool PostAction(const std::string &aActionName, T aParam)
Run the specified action after the current action (coroutine) ends.
Definition: tool_manager.h:230
bool RunSynchronousAction(const TOOL_ACTION &aAction, COMMIT *aCommit, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:192
KIGFX::VIEW * GetView() const
Definition: tool_manager.h:378
static bool IsZoneFillAction(const TOOL_EVENT *aEvent)
#define _(s)
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
Definition: eda_item.h:529
#define IS_MOVING
Item being moved.
@ FP_JUST_ADDED
Definition: footprint.h:77
VECTOR2< T > GetVectorSnapped45(const VECTOR2< T > &aVec, bool only45=false)
Snap a vector onto the nearest 0, 45 or 90 degree line.
VECTOR2< ret_type > GetClampedCoords(const VECTOR2< in_type > &aCoords, pad_type aPadding=1u)
Clamps a vector to values that can be negated, respecting numeric limits of coordinates data type wit...
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition: gtk/ui.cpp:588
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...
bool m_lastKeyboardCursorPositionValid
ACTIONS::CURSOR_UP, ACTIONS::CURSOR_DOWN, etc.
long m_lastKeyboardCursorCommand
Position of the above event.
@ TC_MOUSE
Definition: tool_event.h:54
@ MD_SHIFT
Definition: tool_event.h:142
@ STS_CANCELLED
Definition: tool_event.h:160
@ STS_FINISHED
Definition: tool_event.h:159
@ STS_RUNNING
Definition: tool_event.h:158
@ BUT_LEFT
Definition: tool_event.h:131
@ 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:108
@ 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
VECTOR2< double > VECTOR2D
Definition: vector2d.h:587