KiCad PCB EDA Suite
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-2022 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 <advanced_config.h>
28#include <limits>
29#include <board.h>
31#include <footprint.h>
32#include <fp_shape.h>
33#include <fp_textbox.h>
34#include <collectors.h>
35#include <pcb_edit_frame.h>
37#include <kiway.h>
38#include <array_creator.h>
39#include <pcbnew_settings.h>
40#include <spread_footprints.h>
41#include <status_popup.h>
43#include <tool/tool_manager.h>
44#include <tools/pcb_actions.h>
46#include <tools/edit_tool.h>
50#include <tools/pad_tool.h>
51#include <tools/drc_tool.h>
52#include <tools/drawing_tool.h>
53#include <view/view_controls.h>
56#include <bitmaps.h>
57#include <cassert>
58#include <functional>
59using namespace std::placeholders;
60#include "kicad_clipboard.h"
61#include <wx/hyperlink.h>
62#include <router/router_tool.h>
66#include <board_commit.h>
67#include <pcb_group.h>
68#include <pcb_target.h>
69#include <zone_filler.h>
70
72#include <drc/drc_engine.h>
73#include <drc/drc_item.h>
74#include <drc/drc_rule.h>
75#include <pad.h>
78
79
81{
82public:
86 {
87 m_isRuleDriven = false;
88 }
89
91 {
92 }
93
94 void Init( BOARD* aBoard );
95
96 virtual bool Run() override;
97
98 virtual const wxString GetName() const override
99 {
100 return wxT( "courtyard_clearance" );
101 }
102
103 virtual const wxString GetDescription() const override
104 {
105 return wxT( "Tests footprints' courtyard clearance" );
106 }
107
108 // The list of footprints having issues
109 std::set<FOOTPRINT*> m_FpInConflict;
110
111 // The list of moved footprints
112 std::vector<FOOTPRINT*> m_FpInMove;
113
114private:
116
117private:
119};
120
121
122
124{
125 for( FOOTPRINT* fpA: m_board->Footprints() )
126 {
127 if( fpA->IsSelected() )
128 continue;
129
130 const SHAPE_POLY_SET& frontA = fpA->GetCourtyard( F_CrtYd );
131 const SHAPE_POLY_SET& backA = fpA->GetCourtyard( B_CrtYd );
132
133 if( frontA.OutlineCount() == 0 && backA.OutlineCount() == 0 )
134 // No courtyards defined and no hole testing against other footprint's courtyards
135 continue;
136
137 BOX2I frontBBox = frontA.BBoxFromCaches();
138 BOX2I backBBox = backA.BBoxFromCaches();
139
142
143 BOX2I fpABBox = fpA->GetBoundingBox();
144
145 for( FOOTPRINT* fpB : m_FpInMove )
146 {
147 fpB->BuildCourtyardCaches();
148 BOX2I fpBBBox = fpB->GetBoundingBox();
149 const SHAPE_POLY_SET& frontB = fpB->GetCourtyard( F_CrtYd );
150 const SHAPE_POLY_SET& backB = fpB->GetCourtyard( B_CrtYd );
151 DRC_CONSTRAINT constraint;
152 int clearance;
153 int actual;
154 VECTOR2I pos;
155
156 if( frontA.OutlineCount() > 0 && frontB.OutlineCount() > 0
157 && frontBBox.Intersects( frontB.BBoxFromCaches() ) )
158 {
159 // Currently, do not use DRC engine for calculation time reasons
160 // constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu );
161 // constraint.GetValue().Min();
162 clearance = 0;
163
164 if( frontA.Collide( &frontB, clearance, &actual, &pos ) )
165 {
166 m_FpInConflict.insert( fpA );
167 m_FpInConflict.insert( fpB );
168 }
169 }
170
171 if( backA.OutlineCount() > 0 && backB.OutlineCount() > 0
172 && backBBox.Intersects( backB.BBoxFromCaches() ) )
173 {
174 // Currently, do not use DRC engine for calculation time reasons
175 // constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu );
176 // constraint.GetValue().Min();
177 clearance = 0;
178
179
180 if( backA.Collide( &backB, clearance, &actual, &pos ) )
181 {
182 m_FpInConflict.insert( fpA );
183 m_FpInConflict.insert( fpB );
184 }
185 }
186
187 // Now test if a pad hole of some other footprint is inside the courtyard area
188 // of the moved footprint
189 auto testPadAgainstCourtyards =
190 [&]( const PAD* pad, FOOTPRINT* footprint ) -> bool
191 {
192 if( pad->HasHole() )
193 {
194 std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
195 const SHAPE_POLY_SET& front = footprint->GetCourtyard( F_CrtYd );
196 const SHAPE_POLY_SET& back = footprint->GetCourtyard( B_CrtYd );
197
198 if( front.OutlineCount() > 0 && front.Collide( hole.get(), 0 ) )
199 return true;
200 else if( back.OutlineCount() > 0 && back.Collide( hole.get(), 0 ) )
201 return true;
202 }
203
204 return false;
205 };
206
207 bool skipNextCmp = false;
208
209 if( ( frontA.OutlineCount() > 0 && frontA.BBoxFromCaches().Intersects( fpBBBox ) )
210 || ( backA.OutlineCount() > 0 && backA.BBoxFromCaches().Intersects( fpBBBox ) ) )
211 {
212 for( const PAD* padB : fpB->Pads() )
213 {
214 if( testPadAgainstCourtyards( padB, fpA ) )
215 {
216 m_FpInConflict.insert( fpA );
217 m_FpInConflict.insert( fpB );
218 skipNextCmp = true;
219 break;
220 }
221 }
222 }
223
224 if( skipNextCmp )
225 continue; // fpA and fpB are already in list
226
227 if( ( frontB.OutlineCount() > 0 && frontB.BBoxFromCaches().Intersects( fpABBox ) )
228 || ( backB.OutlineCount() > 0 && backB.BBoxFromCaches().Intersects( fpABBox ) ) )
229 {
230 for( const PAD* padA : fpA->Pads() )
231 {
232 if( testPadAgainstCourtyards( padA, fpB ) )
233 {
234 m_FpInConflict.insert( fpA );
235 m_FpInConflict.insert( fpB );
236 break;
237 }
238 }
239 }
240 }
241 }
242}
243
244
246{
247 m_board = aBoard;
248
249 // Update courtyard data and clear the COURTYARD_CONFLICT flag
250 for( FOOTPRINT* fp: m_board->Footprints() )
251 {
253 fp->BuildCourtyardCaches();
254 }
255}
256
257
259{
260 m_FpInConflict.clear();
262
263 DRC_CONSTRAINT constraint;
264
267
269
270 return true;
271}
272
273
274int EDIT_TOOL::Swap( const TOOL_EVENT& aEvent )
275{
276 if( isRouterActive() || m_dragging )
277 {
278 wxBell();
279 return 0;
280 }
281
283 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
284 {
285 sTool->FilterCollectorForMarkers( aCollector );
286 sTool->FilterCollectorForHierarchy( aCollector, true );
287 sTool->FilterCollectorForFreePads( aCollector );
288
289 // Iterate from the back so we don't have to worry about removals.
290 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
291 {
292 BOARD_ITEM* item = aCollector[i];
293
294 switch( item->Type() )
295 {
296 case PCB_TRACE_T: aCollector.Remove( item ); break;
297
298 default: break;
299 }
300 }
301 },
302 true /* prompt user regarding locked items */ );
303
304 if( selection.Size() < 2 )
305 return 0;
306
307 std::vector<EDA_ITEM*> sorted = selection.GetItemsSortedBySelectionOrder();
308
309 // When editing footprints, all items have the same parent
310 if( IsFootprintEditor() )
311 {
312 m_commit->Modify( selection.Front() );
313 }
314 else
315 {
316 // Save items, so changes can be undone
317 for( EDA_ITEM* item : selection )
318 {
319 // Don't double move footprint pads, fields, etc.
320 //
321 // For PCB_GROUP_T, the parent is the board.
322 if( item->GetParent() && item->GetParent()->IsSelected() )
323 continue;
324
325 m_commit->Modify( item );
326
327 // If moving a group, record position of all the descendants for undo
328 if( item->Type() == PCB_GROUP_T )
329 {
330 PCB_GROUP* group = static_cast<PCB_GROUP*>( item );
331 group->RunOnDescendants( [&]( BOARD_ITEM* bItem )
332 {
333 m_commit->Modify( bItem );
334 });
335 }
336 }
337 }
338
339 for( size_t i = 0; i < sorted.size() - 1; i++ )
340 {
341 BOARD_ITEM* a = static_cast<BOARD_ITEM*>( sorted[i] );
342 BOARD_ITEM* b = static_cast<BOARD_ITEM*>( sorted[( i + 1 ) % sorted.size()] );
343
344 // Swap X,Y position
345 VECTOR2I aPos = a->GetPosition(), bPos = b->GetPosition();
346 std::swap( aPos, bPos );
347 a->SetPosition( aPos );
348 b->SetPosition( bPos );
349
350 // Pads need special handling to keep their offset from their parent
351 if( a->Type() == PCB_PAD_T )
352 static_cast<PAD*>( a )->SetLocalCoord();
353
354 if( b->Type() == PCB_PAD_T )
355 static_cast<PAD*>( b )->SetLocalCoord();
356
357 // Handle footprints specially. They can be flipped to the back of the board which
358 // requires a special transformation.
359 if( a->Type() == PCB_FOOTPRINT_T && b->Type() == PCB_FOOTPRINT_T )
360 {
361 FOOTPRINT* aFP = static_cast<FOOTPRINT*>( a );
362 FOOTPRINT* bFP = static_cast<FOOTPRINT*>( b );
363
364 // Flip both if needed
365 if( aFP->IsFlipped() != bFP->IsFlipped() )
366 {
367 aFP->Flip( aPos, false );
368 bFP->Flip( bPos, false );
369 }
370
371 // Set orientation
372 EDA_ANGLE aAngle = aFP->GetOrientation(), bAngle = bFP->GetOrientation();
373 std::swap( aAngle, bAngle );
374 aFP->SetOrientation( aAngle );
375 bFP->SetOrientation( bAngle );
376 }
377 // We can also do a layer swap safely for two objects of the same type,
378 // except groups which don't support layer swaps.
379 else if( a->Type() == b->Type() && a->Type() != PCB_GROUP_T )
380 {
381 // Swap layers
382 PCB_LAYER_ID aLayer = a->GetLayer(), bLayer = b->GetLayer();
383 std::swap( aLayer, bLayer );
384 a->SetLayer( aLayer );
385 b->SetLayer( bLayer );
386 }
387 }
388
389 m_commit->Push( _( "Swap" ) );
390
391 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
392
393 return 0;
394}
395
396
398{
399 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
400
402 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
403 {
404 // Iterate from the back so we don't have to worry about removals.
405 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
406 {
407 BOARD_ITEM* item = aCollector[i];
408
409 if( !dynamic_cast<FOOTPRINT*>( item ) )
410 aCollector.Remove( item );
411 }
412 },
413 true /* prompt user regarding locked items */ );
414
415 std::vector<FOOTPRINT*> footprintsToPack;
416
417 for( EDA_ITEM* item : selection )
418 footprintsToPack.push_back( static_cast<FOOTPRINT*>( item ) );
419
420 if( footprintsToPack.empty() )
421 return 0;
422
423 BOARD_COMMIT commit( editFrame );
424 BOX2I footprintsBbox;
425
426 for( FOOTPRINT* item : footprintsToPack )
427 {
428 commit.Modify( item );
429 footprintsBbox.Merge( item->GetBoundingBox( false, false ) );
430 }
431
432 SpreadFootprints( &footprintsToPack, footprintsBbox.Normalize().GetOrigin(), false );
433
434 commit.Push( _( "Pack footprints" ) );
435
436 return doMoveSelection( aEvent );
437}
438
439
440int EDIT_TOOL::Move( const TOOL_EVENT& aEvent )
441{
442 if( isRouterActive() )
443 {
444 wxBell();
445 return 0;
446 }
447
448 return doMoveSelection( aEvent );
449}
450
451
453{
455
456 if( isRouterActive() )
457 {
458 wxBell();
459 return 0;
460 }
461
462 EDA_ITEMS sortedItems = selTool->GetSelection().GetItemsSortedBySelectionOrder();
463
464 if( sortedItems.size() == 0 )
465 return 0;
466
467 for( EDA_ITEM* item : sortedItems )
468 {
469 selTool->ClearSelection();
470 selTool->AddItemToSel( item );
471 doMoveSelection( aEvent );
472 }
473
474 selTool->AddItemsToSel( &sortedItems );
475
476 return 0;
477}
478
479
481{
482 if( isRouterActive() )
483 {
484 wxBell();
485 return 0;
486 }
487
488 return doMoveSelection( aEvent, true );
489}
490
491
492VECTOR2I EDIT_TOOL::getSafeMovement( const VECTOR2I& aMovement, const BOX2I& aSourceBBox,
493 const VECTOR2D& aBBoxOffset )
494{
495 typedef std::numeric_limits<int> coord_limits;
496
497 int max = coord_limits::max();
498 int min = -max;
499
500 double left = aBBoxOffset.x + aSourceBBox.GetPosition().x;
501 double top = aBBoxOffset.y + aSourceBBox.GetPosition().y;
502
503 double right = left + aSourceBBox.GetSize().x;
504 double bottom = top + aSourceBBox.GetSize().y;
505
506 // Do not restrict movement if bounding box is already out of bounds
507 if( left < min || top < min || right > max || bottom > max )
508 return aMovement;
509
510 // Constrain moving bounding box to coordinates limits
511 VECTOR2D tryMovement( aMovement );
512
513 VECTOR2D clampedBBoxOrigin = GetClampedCoords(
514 VECTOR2D( aSourceBBox.GetPosition() ) + aBBoxOffset + tryMovement, COORDS_PADDING );
515
516 tryMovement = clampedBBoxOrigin - aBBoxOffset - aSourceBBox.GetPosition();
517
518 VECTOR2D clampedBBoxEnd = GetClampedCoords(
519 VECTOR2D( aSourceBBox.GetEnd() ) + aBBoxOffset + tryMovement, COORDS_PADDING );
520
521 tryMovement = clampedBBoxEnd - aBBoxOffset - aSourceBBox.GetEnd();
522
523 return GetClampedCoords<double, int>( tryMovement );
524}
525
526
527int EDIT_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, bool aPickReference )
528{
529 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
530 BOARD* board = editFrame->GetBoard();
532 VECTOR2I originalCursorPos = controls->GetCursorPosition();
533
534 // Be sure that there is at least one item that we can modify. If nothing was selected before,
535 // try looking for the stuff under mouse cursor (i.e. KiCad old-style hover selection)
537 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
538 {
539 sTool->FilterCollectorForMarkers( aCollector );
540 sTool->FilterCollectorForHierarchy( aCollector, true );
541 },
542 // Prompt user regarding locked items if in board editor and in free-pad-mode (if
543 // we're not in free-pad mode we delay this until the second RequestSelection()).
544 editFrame->GetPcbNewSettings()->m_AllowFreePads && !m_isFootprintEditor );
545
546 if( m_dragging || selection.Empty() )
547 return 0;
548
549 LSET item_layers = selection.GetSelectionLayers();
550 bool is_hover = selection.IsHover(); // N.B. This must be saved before the second call
551 // to RequestSelection() below
552 VECTOR2I pickedReferencePoint;
553
554 // Now filter out pads if not in free pads mode. We cannot do this in the first
555 // RequestSelection() as we need the item_layers when a pad is the selection front.
557 {
559 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
560 {
561 sTool->FilterCollectorForMarkers( aCollector );
562 sTool->FilterCollectorForHierarchy( aCollector, true );
563 sTool->FilterCollectorForFreePads( aCollector );
564 },
565 true /* prompt user regarding locked items */ );
566 }
567
568 if( selection.Empty() )
569 return 0;
570
571 editFrame->PushTool( aEvent );
572
573 Activate();
574 // Must be done after Activate() so that it gets set into the correct context
575 controls->ShowCursor( true );
576 controls->SetAutoPan( true );
578
579 if( aPickReference && !pickReferencePoint( _( "Select reference point for move..." ), "", "",
580 pickedReferencePoint ) )
581 {
582 if( is_hover )
584
585 editFrame->PopTool( aEvent );
586 return 0;
587 }
588
589 auto displayConstraintsMessage =
590 [editFrame]( bool constrained )
591 {
592 editFrame->DisplayConstraintsMsg( constrained ? _( "Constrain to H, V, 45" )
593 : wxT( "" ) );
594 };
595
596 std::vector<BOARD_ITEM*> sel_items; // All the items operated on by the move below
597 std::vector<BOARD_ITEM*> orig_items; // All the original items in the selection
598 std::vector<FOOTPRINT*> lastFpInConflict; // last footprints with courtyard overlapping
599
600 for( EDA_ITEM* item : selection )
601 {
602 BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( item );
603 FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( item );
604
605 if( boardItem )
606 {
607 if( !is_hover )
608 orig_items.push_back( boardItem );
609
610 sel_items.push_back( boardItem );
611 }
612
613 if( footprint )
614 {
615 for( PAD* pad : footprint->Pads() )
616 sel_items.push_back( pad );
617
618 // Clear this flag here; it will be set by the netlist updater if the footprint is new
619 // so that it was skipped in the initial connectivity update in OnNetlistChanged
620 footprint->SetAttributes( footprint->GetAttributes() & ~FP_JUST_ADDED );
621 }
622 }
623
624 bool restore_state = false;
625 VECTOR2I originalPos;
626 VECTOR2I totalMovement;
627 VECTOR2D bboxMovement;
628 BOX2I originalBBox;
629 bool updateBBox = true;
631 TOOL_EVENT copy = aEvent;
632 TOOL_EVENT* evt = &copy;
633 VECTOR2I prevPos;
634
635 bool hv45Mode = false;
636 bool eatFirstMouseUp = true;
637 bool hasRedrawn3D = false;
638 bool allowRedraw3D = editFrame->GetPcbNewSettings()->m_Display.m_Live3DRefresh;
639 // Courtyard conflicts will be tested only if the LAYER_CONFLICTS_SHADOW gal layer is visible
640 bool showCourtyardConflicts = !m_isFootprintEditor
642
643 displayConstraintsMessage( hv45Mode );
644
645 // Used to test courtyard overlaps
647
648 if( showCourtyardConflicts )
649 {
650 drc_on_move.Init( board );
651 drc_on_move.SetDRCEngine( m_toolMgr->GetTool<DRC_TOOL>()->GetDRCEngine().get() );
652 }
653
654 // Prime the pump
656
657 // Main loop: keep receiving events
658 do
659 {
660 VECTOR2I movement;
662 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
663 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
664
665 if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
666 eatFirstMouseUp = false;
667
668 if( evt->IsAction( &PCB_ACTIONS::move ) || evt->IsMotion() || evt->IsDrag( BUT_LEFT )
672 {
673 if( m_dragging && evt->Category() == TC_MOUSE )
674 {
675 bool redraw3D = false;
676
677 VECTOR2I mousePos( controls->GetMousePosition() );
678
679 m_cursor = grid.BestSnapAnchor( mousePos, item_layers, sel_items );
680
682 {
684
685 // The arrow keys are by definition SINGLE AXIS. Do not allow the other
686 // axis to be snapped to the grid.
687 if( action == ACTIONS::CURSOR_LEFT || action == ACTIONS::CURSOR_RIGHT )
688 m_cursor.y = prevPos.y;
689 else if( action == ACTIONS::CURSOR_UP || action == ACTIONS::CURSOR_DOWN )
690 m_cursor.x = prevPos.x;
691 }
692
694 originalPos = m_cursor;
695
696 if( hv45Mode )
697 {
698 VECTOR2I moveVector = m_cursor - originalPos;
699 m_cursor = originalPos + GetVectorSnapped45( moveVector );
700 }
701
702 if( updateBBox )
703 {
704 originalBBox = BOX2I();
705 bboxMovement = VECTOR2D();
706
707 for( EDA_ITEM* item : sel_items )
708 {
709 BOX2I viewBBOX = item->ViewBBox();
710
711 if( originalBBox.GetWidth() == 0 && originalBBox.GetHeight() == 0 )
712 originalBBox = viewBBOX;
713 else
714 originalBBox.Merge( viewBBOX );
715 }
716
717 updateBBox = false;
718 }
719
720 // Constrain selection bounding box to coordinates limits
721 movement = getSafeMovement( m_cursor - prevPos, originalBBox, bboxMovement );
722
723 // Apply constrained movement
724 m_cursor = prevPos + movement;
725
728
729 prevPos = m_cursor;
730 totalMovement += movement;
731 bboxMovement += movement;
732
733 // Drag items to the current cursor position
734 for( EDA_ITEM* item : sel_items )
735 {
736 // Don't double move footprint pads, fields, etc.
737 //
738 // For PCB_GROUP_T, we make sure the selection includes only the top level
739 // group and not its descendants.
740 if( !item->GetParent() || !item->GetParent()->IsSelected() )
741 static_cast<BOARD_ITEM*>( item )->Move( movement );
742
743 if( !redraw3D && item->Type() == PCB_FOOTPRINT_T )
744 redraw3D = true;
745 }
746
747 if( redraw3D && allowRedraw3D )
748 {
749 editFrame->Update3DView( false, true );
750 hasRedrawn3D = true;
751 }
752
753 if( showCourtyardConflicts && drc_on_move.m_FpInMove.size() )
754 {
755 drc_on_move.Run();
756
757 bool need_redraw = false; // will be set to true if a COURTYARD_CONFLICT
758 // has changed
759
760 // Ensure the "old" conflicts are cleared
761 for( FOOTPRINT* fp: lastFpInConflict )
762 {
763 fp->ClearFlags( COURTYARD_CONFLICT );
764 m_toolMgr->GetView()->Update( fp );
765 need_redraw = true;
766 }
767
768 lastFpInConflict.clear();
769
770 for( FOOTPRINT* fp: drc_on_move.m_FpInConflict )
771 {
772 if( !fp->HasFlag( COURTYARD_CONFLICT ) )
773 {
775 m_toolMgr->GetView()->Update( fp );
776 need_redraw = true;
777 }
778
779 lastFpInConflict.push_back( fp );
780 }
781
782 if( need_redraw )
784 }
785
787 }
788 else if( !m_dragging && !evt->IsAction( &ACTIONS::refreshPreview ) )
789 {
790 // Prepare to start dragging
791 if( !( evt->IsAction( &PCB_ACTIONS::move )
795 {
797 break;
798 }
799
800 editFrame->HideSolderMask();
801
802 m_dragging = true;
803
804 // When editing footprints, all items have the same parent
805 if( IsFootprintEditor() )
806 {
807 m_commit->Modify( selection.Front() );
808 }
809 else
810 {
811 // Save items, so changes can be undone
812 for( EDA_ITEM* item : selection )
813 {
814 // Don't double move footprint pads, fields, etc.
815 //
816 // For PCB_GROUP_T, the parent is the board.
817 if( item->GetParent() && item->GetParent()->IsSelected() )
818 continue;
819
820 m_commit->Modify( item );
821
822 // If moving a group, record position of all the descendants for undo
823 if( item->Type() == PCB_GROUP_T )
824 {
825 PCB_GROUP* group = static_cast<PCB_GROUP*>( item );
826 group->RunOnDescendants( [&]( BOARD_ITEM* bItem )
827 {
828 m_commit->Modify( bItem );
829 });
830 }
831 }
832 }
833
834 editFrame->UndoRedoBlock( true );
836
838 {
839 // start moving with the reference point attached to the cursor
840 grid.SetAuxAxes( false );
841
842 if( hv45Mode )
843 {
844 VECTOR2I moveVector = m_cursor - originalPos;
845 m_cursor = originalPos + GetVectorSnapped45( moveVector );
846 }
847
848 movement = m_cursor - selection.GetReferencePoint();
849
850 // Drag items to the current cursor position
851 for( EDA_ITEM* item : selection )
852 {
853 // Don't double move footprint pads, fields, etc.
854 if( item->GetParent() && item->GetParent()->IsSelected() )
855 continue;
856
857 static_cast<BOARD_ITEM*>( item )->Move( movement );
858 }
859
861 }
862 else
863 {
864 std::vector<BOARD_ITEM*> items;
865
866 for( EDA_ITEM* item : selection )
867 {
868 items.push_back( static_cast<BOARD_ITEM*>( item ) );
869
870 if( item->Type() == PCB_FOOTPRINT_T )
871 drc_on_move.m_FpInMove.push_back( static_cast<FOOTPRINT*>( item ) );
872 }
873
874 m_cursor = grid.BestDragOrigin( originalCursorPos, items );
875
876 // Set the current cursor position to the first dragged item origin, so the
877 // movement vector could be computed later
878 if( aPickReference )
879 {
880 selection.SetReferencePoint( pickedReferencePoint );
881 controls->ForceCursorPosition( true, pickedReferencePoint );
882 m_cursor = pickedReferencePoint;
883 }
884 else
885 {
886 // Check if user wants to warp the mouse to origin of moved object
887 if( !editFrame->GetMoveWarpsCursor() )
888 m_cursor = originalCursorPos; // No, so use original mouse pos instead
889
891 grid.SetAuxAxes( true, m_cursor );
892 }
893
894 originalPos = m_cursor;
895 }
896
897 // Update variables for bounding box collision calculations
898 updateBBox = true;
899
901
902 prevPos = m_cursor;
903 controls->SetAutoPan( true );
905 }
906
908 }
909 else if( evt->IsCancelInteractive() || evt->IsActivate() )
910 {
911 if( m_dragging && evt->IsCancelInteractive() )
912 evt->SetPassEvent( false );
913
914 restore_state = true; // Canceling the tool means that items have to be restored
915 break; // Finish
916 }
917 else if( evt->IsAction( &ACTIONS::undo ) )
918 {
919 restore_state = true; // Perform undo locally
920 break; // Finish
921 }
922 else if( evt->IsAction( &ACTIONS::doDelete ) || evt->IsAction( &ACTIONS::cut ) )
923 {
924 // Dispatch TOOL_ACTIONs
925 evt->SetPassEvent();
926 break; // finish -- there is no further processing for removed items
927 }
928 else if( evt->IsAction( &ACTIONS::duplicate ) )
929 {
930 evt->SetPassEvent();
931 break; // finish -- Duplicate tool will start a new Move with the dup'ed items
932 }
933 else if( evt->IsAction( &PCB_ACTIONS::rotateCw )
935 || evt->IsAction( &PCB_ACTIONS::flip )
937 || evt->IsAction( &PCB_ACTIONS::mirrorV ) )
938 {
939 updateBBox = true;
940 eatFirstMouseUp = false;
941 evt->SetPassEvent();
942 }
943 else if( evt->IsAction( &PCB_ACTIONS::moveExact ) )
944 {
945 // Reset positions so the Move Exactly is from the start.
946 for( EDA_ITEM* item : selection )
947 {
948 BOARD_ITEM* i = static_cast<BOARD_ITEM*>( item );
949 i->Move( -totalMovement );
950 }
951
952 break; // finish -- we moved exactly, so we are finished
953 }
954 else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) )
955 {
956 // Eat mouse-up/-click events that leaked through from the lock dialog
957 if( eatFirstMouseUp && evt->Parameter<intptr_t>() != ACTIONS::CURSOR_CLICK )
958 {
959 eatFirstMouseUp = false;
960 continue;
961 }
962
963 break; // finish
964 }
965 else if( evt->IsAction( &PCB_ACTIONS::toggleHV45Mode ) )
966 {
967 hv45Mode = !hv45Mode;
968 displayConstraintsMessage( hv45Mode );
969 evt->SetPassEvent( false );
970 }
971 else
972 {
973 evt->SetPassEvent();
974 }
975
976 } while( ( evt = Wait() ) ); // Assignment (instead of equality test) is intentional
977
978 // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared
979 for( FOOTPRINT* fp: lastFpInConflict )
980 {
981 m_toolMgr->GetView()->Update( fp );
982 fp->ClearFlags( COURTYARD_CONFLICT );
983 }
984
986 controls->ShowCursor( false );
987 controls->SetAutoPan( false );
988
989 m_dragging = false;
990 editFrame->UndoRedoBlock( false );
991
992 m_toolMgr->GetTool<DRAWING_TOOL>()->UpdateStatusBar();
993
994 if( hasRedrawn3D && restore_state )
995 editFrame->Update3DView( false, true );
996
997 // Discard reference point when selection is "dropped" onto the board
999
1000 // Unselect all items to clear selection flags and then re-select the originally selected
1001 // items.
1003 m_toolMgr->RunAction( PCB_ACTIONS::selectItems, true, &orig_items );
1004
1005 // TODO: there's an encapsulation leak here: this commit often has more than just the move
1006 // in it; for instance it might have a paste, append board, etc. as well.
1007 if( restore_state )
1008 m_commit->Revert();
1009 else
1010 m_commit->Push( _( "Drag" ) );
1011
1012 // Remove the dynamic ratsnest from the screen
1014
1015 editFrame->PopTool( aEvent );
1017
1018 return restore_state ? -1 : 0;
1019}
1020
BOX2< VECTOR2I > BOX2I
Definition: box2.h:847
@ CURSOR_RIGHT
Definition: actions.h:189
@ CURSOR_LEFT
Definition: actions.h:189
@ CURSOR_CLICK
Definition: actions.h:190
@ CURSOR_UP
Definition: actions.h:189
@ CURSOR_DOWN
Definition: actions.h:189
static TOOL_ACTION undo
Definition: actions.h:65
static TOOL_ACTION duplicate
Definition: actions.h:72
static TOOL_ACTION doDelete
Definition: actions.h:73
static TOOL_ACTION cut
Definition: actions.h:67
static TOOL_ACTION refreshPreview
Definition: actions.h:109
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) 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:50
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:167
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition: board_item.h:253
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:201
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:265
bool IsElementVisible(GAL_LAYER_ID aLayer) const
Test whether a given element category is visible.
Definition: board.cpp:567
FOOTPRINTS & Footprints()
Definition: board.h:307
BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition: box2.h:119
const Vec & GetPosition() const
Definition: box2.h:184
const Vec & GetOrigin() const
Definition: box2.h:183
bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:269
coord_type GetHeight() const
Definition: box2.h:188
coord_type GetWidth() const
Definition: box2.h:187
const Vec GetEnd() const
Definition: box2.h:185
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:506
const Vec & GetSize() const
Definition: box2.h:179
BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:588
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:81
COMMIT & Modify(EDA_ITEM *aItem)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
Tool responsible for drawing graphical elements like lines, arcs, circles, etc.
Definition: drawing_tool.h:51
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:139
bool QueryWorstConstraint(DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT &aConstraint)
virtual const wxString GetName() const override
virtual const wxString GetDescription() const override
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
DRC_ENGINE * m_drcEngine
void SetDRCEngine(DRC_ENGINE *engine)
std::shared_ptr< DRC_ENGINE > GetDRCEngine()
Definition: drc_tool.h:78
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:85
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:249
virtual void SetPosition(const VECTOR2I &aPos)
Definition: eda_item.h:250
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:142
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:143
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition: eda_item.h:145
bool isRouterActive() const
Definition: edit_tool.cpp:296
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.
int MoveWithReference(const TOOL_EVENT &aEvent)
Move an item but with a reference point selected first.
bool pickReferencePoint(const wxString &aTooltip, const wxString &aSuccessMessage, const wxString &aCanceledMessage, VECTOR2I &aReferencePoint)
Definition: edit_tool.cpp:2076
int doMoveSelection(const TOOL_EVENT &aEvent, bool aPickReference=false)
bool m_dragging
Definition: edit_tool.h:209
int Move(const TOOL_EVENT &aEvent)
Main loop in which events are handled.
static const unsigned int COORDS_PADDING
Definition: edit_tool.h:214
std::unique_ptr< BOARD_COMMIT > m_commit
Definition: edit_tool.h:208
VECTOR2I getSafeMovement(const VECTOR2I &aMovement, const BOX2I &aSourceBBox, const VECTOR2D &aBBoxOffset)
VECTOR2I m_cursor
Definition: edit_tool.h:210
bool invokeInlineRouter(int aDragMode)
Definition: edit_tool.cpp:266
PCB_SELECTION_TOOL * m_selectionTool
Definition: edit_tool.h:207
int MoveIndividually(const TOOL_EVENT &aEvent)
Move a selection of items one-at-a-time.
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:210
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:213
EDA_ANGLE GetOrientation() const
Definition: footprint.h:195
void SetOrientation(const EDA_ANGLE &aNewAngle)
Definition: footprint.cpp:1766
void SetAttributes(int aAttributes)
Definition: footprint.h:246
int GetAttributes() const
Definition: footprint.h:245
bool IsFlipped() const
Definition: footprint.h:319
PADS & Pads()
Definition: footprint.h:174
void Flip(const VECTOR2I &aCentre, bool aFlipLeftRight) override
Flip this object, i.e.
Definition: footprint.cpp:1553
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.
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:1574
void MarkTargetDirty(int aTarget)
Set or clear target 'dirty' flag.
Definition: view.h:617
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:530
T Min() const
Definition: minoptmax.h:33
Definition: pad.h:58
void SetLocalCoord()
< Set relative coordinates.
Definition: pad.cpp:638
DISPLAY_OPTIONS m_Display
TRACK_DRAG_ACTION m_TrackDragAction
static TOOL_ACTION toggleHV45Mode
Definition: pcb_actions.h:458
static TOOL_ACTION mirrorH
Mirroring of selected items.
Definition: pcb_actions.h:129
static TOOL_ACTION hideLocalRatsnest
Definition: pcb_actions.h:499
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:59
static TOOL_ACTION moveWithReference
move with a reference point
Definition: pcb_actions.h:116
static TOOL_ACTION moveExact
Activation of the exact move tool.
Definition: pcb_actions.h:148
static TOOL_ACTION updateLocalRatsnest
Definition: pcb_actions.h:500
static TOOL_ACTION moveIndividually
move items one-by-one
Definition: pcb_actions.h:113
static TOOL_ACTION move
move or drag an item
Definition: pcb_actions.h:110
static TOOL_ACTION mirrorV
Definition: pcb_actions.h:130
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition: pcb_actions.h:66
static TOOL_ACTION flip
Flipping of selected objects.
Definition: pcb_actions.h:126
static TOOL_ACTION rotateCw
Rotation of selected objects.
Definition: pcb_actions.h:122
static TOOL_ACTION rotateCcw
Definition: pcb_actions.h:123
Common, abstract interface for edit frames.
void UndoRedoBlock(bool aBlock=true)
Enable/disable undo and redo operations.
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.
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:51
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) 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.
int ClearSelection(const TOOL_EVENT &aEvent)
PCB_SELECTION & GetSelection()
const LSET GetSelectionLayers()
bool IsFootprintEditor() const
KIGFX::VIEW_CONTROLS * controls() const
BOARD * board() const
bool m_isFootprintEditor
const PCB_SELECTION & selection() const
FOOTPRINT * footprint() const
int AddItemsToSel(const TOOL_EVENT &aEvent)
int AddItemToSel(const TOOL_EVENT &aEvent)
const std::vector< EDA_ITEM * > GetItemsSortedBySelectionOrder() const
Definition: selection.cpp:202
VECTOR2I GetReferencePoint() const
Definition: selection.h:244
bool IsHover() const
Definition: selection.h:81
EDA_ITEM * Front() const
Definition: selection.h:200
void ClearReferencePoint()
Definition: selection.h:257
void SetReferencePoint(const VECTOR2I &aP)
Definition: selection.h:252
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:107
bool HasReferencePoint() const
Definition: selection.h:239
Represent a set of closed polygons.
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
int OutlineCount() const
Return the number of vertices in a given outline/hole.
const BOX2I BBoxFromCaches() 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.
Definition: tools_holder.h:153
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:214
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:156
bool DisableGridSnapping() const
Definition: tool_event.h:344
bool IsCancelInteractive() const
Indicate the event should restart/end an ongoing interactive tool's event loop (eg esc key,...
Definition: tool_event.cpp:212
T Parameter() const
Return a non-standard parameter assigned to the event.
Definition: tool_event.h:442
bool IsActivate() const
Definition: tool_event.h:318
bool IsClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:200
TOOL_EVENT_CATEGORY Category() const
Returns more specific information about the type of an event.
Definition: tool_event.h:230
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:288
int Modifier(int aMask=MD_MODIFIER_MASK) const
Definition: tool_event.h:339
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:88
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:239
bool IsMouseUp(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:298
bool IsMotion() const
Definition: tool_event.h:303
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 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, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:142
KIGFX::VIEW * GetView() const
Definition: tool_manager.h:285
@ COURTYARD_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:51
#define _(s)
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
Definition: eda_item.h:524
#define COURTYARD_CONFLICT
temporary set when moving footprints having courtyard overlapping
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=0u)
Clamps a vector to values that can be negated, respecting numeric limits of coordinates data type wit...
@ LAYER_CONFLICTS_SHADOW
shadow layer for items flagged conficting
Definition: layer_ids.h:241
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ F_CrtYd
Definition: layer_ids.h:117
@ B_CrtYd
Definition: layer_ids.h:116
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition: definitions.h:50
@ DM_ANY
Definition: pns_router.h:77
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:50
@ MD_SHIFT
Definition: tool_event.h:138
@ BUT_LEFT
Definition: tool_event.h:127
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:115
@ 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:617
VECTOR2< int > VECTOR2I
Definition: vector2d.h:618