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 <algorithm>
29#include <limits>
30#include <kiplatform/ui.h>
31#include <board.h>
32#include <board_commit.h>
35#include <pad.h>
36#include <pcb_group.h>
37#include <pcb_generator.h>
38#include <pcb_edit_frame.h>
39#include <spread_footprints.h>
40#include <tool/tool_manager.h>
41#include <tools/pcb_actions.h>
43#include <tools/edit_tool.h>
45#include <tools/drc_tool.h>
47#include <router/router_tool.h>
49#include <zone_filler.h>
50#include <drc/drc_engine.h>
52#include <view/view_controls.h>
53
55#include <wx/richmsgdlg.h>
56#include <wx/choicdlg.h>
57#include <unordered_set>
58#include <unordered_map>
59
60
62 const std::vector<PAD*>& aPads,
63 const wxString& aDialogTitle,
64 bool& aIncludeConnectedPads )
65{
66 if( aPads.empty() )
67 {
68 aIncludeConnectedPads = true;
69 return true;
70 }
71
72 std::unordered_set<PAD*> uniquePads( aPads.begin(), aPads.end() );
73
74 wxString msg;
75 msg.Printf( _( "%zu unselected pad(s) are connected to these nets. How do you want to proceed?" ),
76 uniquePads.size() );
77
78 wxString details;
79 details << _( "Connected tracks, vias, and other non-zone copper items will still swap nets"
80 " even if you ignore the unselected pads." )
81 << "\n \n" // Add space so GTK doesn't eat the newlines
82 << _( "Unselected pads:" ) << '\n';
83
84 for( PAD* pad : uniquePads )
85 {
86 const FOOTPRINT* fp = pad->GetParentFootprint();
87 details << wxS( " • " ) << ( fp ? fp->GetReference() : _( "<no reference designator>" ) ) << wxS( ":" )
88 << pad->GetNumber() << '\n';
89 }
90
91
92 wxRichMessageDialog dlg( aFrame, msg, aDialogTitle, wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_WARNING );
93 dlg.SetYesNoLabels( _( "Ignore Unselected Pads" ), _( "Swap All Connected Pads" ) );
94 dlg.SetExtendedMessage( details );
95
96 int ret = dlg.ShowModal();
97
98 if( ret == wxID_CANCEL )
99 return false;
100
101 aIncludeConnectedPads = ( ret == wxID_NO );
102 return true;
103}
104
105
106int EDIT_TOOL::Swap( const TOOL_EVENT& aEvent )
107{
108 if( isRouterActive() )
109 {
110 wxBell();
111 return 0;
112 }
113
114 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
115 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
116 {
117 sTool->FilterCollectorForMarkers( aCollector );
118 sTool->FilterCollectorForHierarchy( aCollector, true );
119 sTool->FilterCollectorForFreePads( aCollector );
120
121 // Iterate from the back so we don't have to worry about removals.
122 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
123 {
124 BOARD_ITEM* item = aCollector[i];
125
126 if( item->Type() == PCB_TRACE_T )
127 aCollector.Remove( item );
128 }
129
130 sTool->FilterCollectorForLockedItems( aCollector );
131 } );
132
133 if( selection.Size() < 2 )
134 return 0;
135
136 BOARD_COMMIT localCommit( this );
137 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
138
139 if( !commit )
140 commit = &localCommit;
141
142 std::vector<EDA_ITEM*> sorted = selection.GetItemsSortedBySelectionOrder();
143
144 // Save items, so changes can be undone
145 for( EDA_ITEM* item : selection )
146 commit->Modify( item, nullptr, RECURSE_MODE::RECURSE );
147
148 for( size_t i = 0; i < sorted.size() - 1; i++ )
149 {
150 EDA_ITEM* edaItemA = sorted[i];
151 EDA_ITEM* edaItemB = sorted[( i + 1 ) % sorted.size()];
152
153 if( !edaItemA->IsBOARD_ITEM() || !edaItemB->IsBOARD_ITEM() )
154 continue;
155
156 BOARD_ITEM* a = static_cast<BOARD_ITEM*>( edaItemA );
157 BOARD_ITEM* b = static_cast<BOARD_ITEM*>( edaItemB );
158
159 // Swap X,Y position
160 VECTOR2I aPos = a->GetPosition(), bPos = b->GetPosition();
161 std::swap( aPos, bPos );
162 a->SetPosition( aPos );
163 b->SetPosition( bPos );
164
165 // Handle footprints specially. They can be flipped to the back of the board which
166 // requires a special transformation.
167 if( a->Type() == PCB_FOOTPRINT_T && b->Type() == PCB_FOOTPRINT_T )
168 {
169 FOOTPRINT* aFP = static_cast<FOOTPRINT*>( a );
170 FOOTPRINT* bFP = static_cast<FOOTPRINT*>( b );
171
172 // Store initial orientation of footprints, before flipping them.
173 EDA_ANGLE aAngle = aFP->GetOrientation();
174 EDA_ANGLE bAngle = bFP->GetOrientation();
175
176 // Flip both if needed
177 if( aFP->IsFlipped() != bFP->IsFlipped() )
178 {
179 aFP->Flip( aPos, FLIP_DIRECTION::TOP_BOTTOM );
180 bFP->Flip( bPos, FLIP_DIRECTION::TOP_BOTTOM );
181 }
182
183 // Set orientation
184 std::swap( aAngle, bAngle );
185 aFP->SetOrientation( aAngle );
186 bFP->SetOrientation( bAngle );
187 }
188 // We can also do a layer swap safely for two objects of the same type,
189 // except groups which don't support layer swaps.
190 else if( a->Type() == b->Type() && a->Type() != PCB_GROUP_T )
191 {
192 // Swap layers
193 PCB_LAYER_ID aLayer = a->GetLayer(), bLayer = b->GetLayer();
194 std::swap( aLayer, bLayer );
195 a->SetLayer( aLayer );
196 b->SetLayer( bLayer );
197 }
198 }
199
200 if( !localCommit.Empty() )
201 localCommit.Push( _( "Swap" ) );
202
204
205 return 0;
206}
207
208
210{
211 if( isRouterActive() )
212 {
213 wxBell();
214 return 0;
215 }
216
218
219 if( selection.Size() < 2 || !selection.OnlyContains( { PCB_PAD_T } ) )
220 return 0;
221
222 // Get selected pads in selection order, because swapping is cyclic and we let the user pick
223 // the rotation order
224 std::vector<EDA_ITEM*> orderedPads = selection.GetItemsSortedBySelectionOrder();
225 std::vector<PAD*> pads;
226 const size_t padsCount = orderedPads.size();
227
228 for( EDA_ITEM* it : orderedPads )
229 pads.push_back( static_cast<PAD*>( static_cast<BOARD_ITEM*>( it ) ) );
230
231
232 // Record original nets and build selected set for quick membership tests
233 std::vector<int> originalNets( padsCount );
234 std::unordered_set<PAD*> selectedPads;
235
236 for( size_t i = 0; i < padsCount; ++i )
237 {
238 originalNets[i] = pads[i]->GetNetCode();
239 selectedPads.insert( pads[i] );
240 }
241
242 // If all nets are the same, nothing to do
243 bool allSame = true;
244 for( size_t i = 1; i < padsCount; ++i )
245 {
246 if( originalNets[i] != originalNets[0] )
247 {
248 allSame = false;
249 break;
250 }
251 }
252
253 if( allSame )
254 return 0;
255
256 // Desired new nets are a cyclic rotation of original nets (like Swap positions)
257 auto newNetForIndex = [&]( size_t i )
258 {
259 return originalNets[( i + 1 ) % padsCount];
260 };
261
262 // Take an event commit since we will eventually support this while actively routing the board
263 BOARD_COMMIT localCommit( this );
264 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
265
266 if( !commit )
267 commit = &localCommit;
268
269 // Connectivity to find items connected to each pad
270 std::shared_ptr<CONNECTIVITY_DATA> connectivity = board()->GetConnectivity();
271
272 // Accumulate changes: for each item, assign the resulting new net
273 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
274 std::vector<PAD*> nonSelectedPadsToChange;
275
276 for( size_t i = 0; i < padsCount; ++i )
277 {
278 PAD* pad = pads[i];
279 int fromNet = originalNets[i];
280 int toNet = newNetForIndex( i );
281
282 // For each connected item, if it matches fromNet, schedule it for toNet
283 for( BOARD_CONNECTED_ITEM* ci : connectivity->GetConnectedItems( pad, 0 ) )
284 {
285 switch( ci->Type() )
286 {
287 case PCB_TRACE_T:
288 case PCB_ARC_T:
289 case PCB_VIA_T:
290 case PCB_PAD_T:
291 break;
292 // Exclude zones, user probably doesn't want to change zone nets
293 default:
294 continue;
295 }
296
297 if( ci->GetNetCode() != fromNet )
298 continue;
299
300 // Track conflicts: if already assigned a different new net, just overwrite (last wins)
301 itemNewNets[ci] = toNet;
302
303 if( ci->Type() == PCB_PAD_T )
304 {
305 PAD* otherPad = static_cast<PAD*>( ci );
306
307 if( !selectedPads.count( otherPad ) )
308 nonSelectedPadsToChange.push_back( otherPad );
309 }
310 }
311 }
312
313 bool includeConnectedPads = true;
314
315 if( !PromptConnectedPadDecision( frame(), nonSelectedPadsToChange, _( "Swap Pad Nets" ), includeConnectedPads ) )
316 {
317 return 0;
318 }
319
320 // Apply changes
321 // 1) Selected pads get their new nets directly
322 for( size_t i = 0; i < padsCount; ++i )
323 {
324 commit->Modify( pads[i] );
325 pads[i]->SetNetCode( newNetForIndex( i ) );
326 }
327
328 // 2) Connected items propagate, depending on user choice
329 for( const auto& itemNewNet : itemNewNets )
330 {
331 BOARD_CONNECTED_ITEM* item = itemNewNet.first;
332 int newNet = itemNewNet.second;
333
334 if( item->Type() == PCB_PAD_T )
335 {
336 PAD* p = static_cast<PAD*>( item );
337
338 if( selectedPads.count( p ) )
339 continue; // already changed above
340
341 if( !includeConnectedPads )
342 continue; // skip non-selected pads if requested
343 }
344
345 commit->Modify( item );
346 item->SetNetCode( newNet );
347 }
348
349 if( !localCommit.Empty() )
350 localCommit.Push( _( "Swap Pad Nets" ) );
351
352 // Ensure connectivity visuals update
355
356 return 0;
357}
358
359
361{
362 if( isRouterActive() )
363 {
364 wxBell();
365 return 0;
366 }
367
368 auto showError = [this]()
369 {
370 frame()->ShowInfoBarError( _( "Gate swapping must be performed on pads within one multi-gate footprint." ) );
371 };
372
374
375 // Get our sanity checks out of the way to clean up later loops
376 FOOTPRINT* targetFp = nullptr;
377 bool fail = false;
378
379 for( EDA_ITEM* it : selection )
380 {
381 // This shouldn't happen due to the filter, but just in case
382 if( it->Type() != PCB_PAD_T )
383 {
384 fail = true;
385 break;
386 }
387
388 FOOTPRINT* fp = static_cast<PAD*>( static_cast<BOARD_ITEM*>( it ) )->GetParentFootprint();
389
390 if( !targetFp )
391 targetFp = fp;
392 else if( fp && targetFp != fp )
393 {
394 fail = true;
395 break;
396 }
397 }
398
399 if( fail || !targetFp || targetFp->GetUnitInfo().size() < 2 )
400 {
401 showError();
402 return 0;
403 }
404
405
406 const auto& units = targetFp->GetUnitInfo();
407
408 // Collect unit hits and ordered unit list based on selection order
409 std::vector<bool> unitHit( units.size(), false );
410 std::vector<int> unitOrder;
411
412 std::vector<EDA_ITEM*> orderedPads = selection.GetItemsSortedBySelectionOrder();
413
414 for( EDA_ITEM* it : orderedPads )
415 {
416 PAD* pad = static_cast<PAD*>( static_cast<BOARD_ITEM*>( it ) );
417
418 const wxString& padNum = pad->GetNumber();
419 int unitIdx = -1;
420
421 for( size_t i = 0; i < units.size(); ++i )
422 {
423 for( const auto& p : units[i].m_pins )
424 {
425 if( p == padNum )
426 {
427 unitIdx = static_cast<int>( i );
428
429 if( !unitHit[i] )
430 unitOrder.push_back( unitIdx );
431
432 unitHit[i] = true;
433 break;
434 }
435 }
436
437 if( unitIdx >= 0 )
438 break;
439 }
440 }
441
442 // Determine active units from selection order: 0 -> bail, 1 -> single-unit flow, 2+ -> cycle
443 std::vector<int> activeUnitIdx;
444 int sourceIdx = -1;
445
446 if( unitOrder.size() >= 2 )
447 {
448 activeUnitIdx = unitOrder;
449 sourceIdx = unitOrder.front();
450 }
451 // If we only have one gate selected, we must have a target unit name parameter to proceed
452 else if( unitOrder.size() == 1 && aEvent.HasParameter() )
453 {
454 sourceIdx = unitOrder.front();
455 wxString targetUnitByName = aEvent.Parameter<wxString>();
456
457 int targetIdx = -1;
458
459 for( size_t i = 0; i < units.size(); ++i )
460 {
461 if( static_cast<int>( i ) == sourceIdx )
462 continue;
463
464 if( units[i].m_pins.size() == units[sourceIdx].m_pins.size() && units[i].m_unitName == targetUnitByName )
465 {
466 targetIdx = static_cast<int>( i );
467 }
468 }
469
470 if( targetIdx < 0 )
471 {
472 showError();
473 return 0;
474 }
475
476 activeUnitIdx.push_back( sourceIdx );
477 activeUnitIdx.push_back( targetIdx );
478 }
479 else
480 {
481 showError();
482 return 0;
483 }
484
485 // Verify equal pin counts across all active units
486 const size_t pinCount = units[activeUnitIdx.front()].m_pins.size();
487
488 for( int idx : activeUnitIdx )
489 {
490 if( units[idx].m_pins.size() != pinCount )
491 {
492 frame()->ShowInfoBarError( _( "Gate swapping must be performed on gates with equal pin counts." ) );
493 return 0;
494 }
495 }
496
497 // Build per-unit pad arrays and net vectors
498 const size_t unitCount = activeUnitIdx.size();
499 std::vector<std::vector<PAD*>> unitPads( unitCount );
500 std::vector<std::vector<int>> unitNets( unitCount );
501
502 for( size_t ui = 0; ui < unitCount; ++ui )
503 {
504 int uidx = activeUnitIdx[ui];
505 const auto& pins = units[uidx].m_pins;
506
507 for( size_t pi = 0; pi < pinCount; ++pi )
508 {
509 PAD* p = targetFp->FindPadByNumber( pins[pi] );
510
511 if( !p )
512 {
513 frame()->ShowInfoBarError( _( "Gate swapping failed: pad in unit missing from footprint." ) );
514 return 0;
515 }
516
517 unitPads[ui].push_back( p );
518 unitNets[ui].push_back( p->GetNetCode() );
519 }
520 }
521
522 // If all unit nets match across positions, nothing to do
523 bool allSame = true;
524
525 for( size_t pi = 0; pi < pinCount && allSame; ++pi )
526 {
527 int refNet = unitNets[0][pi];
528
529 for( size_t ui = 1; ui < unitCount; ++ui )
530 {
531 if( unitNets[ui][pi] != refNet )
532 {
533 allSame = false;
534 break;
535 }
536 }
537 }
538
539 if( allSame )
540 {
541 frame()->ShowInfoBarError( _( "Gate swapping has no effect: all selected gates have identical nets." ) );
542 return 0;
543 }
544
545 // TODO: someday support swapping while routing and take that commit
546 BOARD_COMMIT localCommit( this );
547 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
548
549 if( !commit )
550 commit = &localCommit;
551
552 std::shared_ptr<CONNECTIVITY_DATA> connectivity = board()->GetConnectivity();
553
554 // Accumulate changes: item -> new net
555 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
556 std::vector<PAD*> nonSelectedPadsToChange;
557
558 // Selected pads in the swap (for suppressing re-adding in connected pad handling)
559 std::unordered_set<PAD*> swapPads;
560
561 for( const auto& v : unitPads )
562 swapPads.insert( v.begin(), v.end() );
563
564 // Schedule net swaps for connectivity-attached items
565 auto scheduleForPad = [&]( PAD* pad, int fromNet, int toNet )
566 {
567 for( BOARD_CONNECTED_ITEM* ci : connectivity->GetConnectedItems( pad, 0 ) )
568 {
569 switch( ci->Type() )
570 {
571 case PCB_TRACE_T:
572 case PCB_ARC_T:
573 case PCB_VIA_T:
574 case PCB_PAD_T:
575 break;
576
577 default:
578 continue;
579 }
580
581 if( ci->GetNetCode() != fromNet )
582 continue;
583
584 itemNewNets[ ci ] = toNet;
585
586 if( ci->Type() == PCB_PAD_T )
587 {
588 PAD* other = static_cast<PAD*>( ci );
589
590 if( !swapPads.count( other ) )
591 nonSelectedPadsToChange.push_back( other );
592 }
593 }
594 };
595
596 // For each position, rotate nets among units forward
597 for( size_t pi = 0; pi < pinCount; ++pi )
598 {
599 for( size_t ui = 0; ui < unitCount; ++ui )
600 {
601 size_t fromIdx = ui;
602 size_t toIdx = ( ui + 1 ) % unitCount;
603
604 PAD* padFrom = unitPads[fromIdx][pi];
605 int fromNet = unitNets[fromIdx][pi];
606 int toNet = unitNets[toIdx][pi];
607
608 scheduleForPad( padFrom, fromNet, toNet );
609 }
610 }
611
612 bool includeConnectedPads = true;
613
614 if( !PromptConnectedPadDecision( frame(), nonSelectedPadsToChange, _( "Swap Gate Nets" ), includeConnectedPads ) )
615 {
616 return 0;
617 }
618
619 // Apply pad net swaps: rotate per position
620 for( size_t pi = 0; pi < pinCount; ++pi )
621 {
622 // First write back nets for each unit's pad at this position
623 for( size_t ui = 0; ui < unitCount; ++ui )
624 {
625 size_t toIdx = ( ui + 1 ) % unitCount;
626 PAD* pad = unitPads[ui][pi];
627 int newNet = unitNets[toIdx][pi];
628
629 commit->Modify( pad );
630 pad->SetNetCode( newNet );
631 }
632 }
633
634 // Apply connected items
635 for( const auto& kv : itemNewNets )
636 {
637 BOARD_CONNECTED_ITEM* item = kv.first;
638 int newNet = kv.second;
639
640 if( item->Type() == PCB_PAD_T )
641 {
642 PAD* p = static_cast<PAD*>( item );
643
644 if( swapPads.count( p ) )
645 continue;
646
647 if( !includeConnectedPads )
648 continue;
649 }
650
651 commit->Modify( item );
652 item->SetNetCode( newNet );
653 }
654
655 if( !localCommit.Empty() )
656 localCommit.Push( _( "Swap Gate Nets" ) );
657
660
661 return 0;
662}
663
664
666{
667 if( isRouterActive() || m_dragging )
668 {
669 wxBell();
670 return 0;
671 }
672
673 BOARD_COMMIT commit( this );
674 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
675 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
676 {
677 sTool->FilterCollectorForMarkers( aCollector );
678 sTool->FilterCollectorForHierarchy( aCollector, true );
679 sTool->FilterCollectorForFreePads( aCollector, true );
680
681 // Iterate from the back so we don't have to worry about removals.
682 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
683 {
684 BOARD_ITEM* item = aCollector[i];
685
686 if( !dynamic_cast<FOOTPRINT*>( item ) )
687 aCollector.Remove( item );
688 }
689
690 sTool->FilterCollectorForLockedItems( aCollector );
691 } );
692
693 std::vector<FOOTPRINT*> footprintsToPack;
694
695 for( EDA_ITEM* item : selection )
696 footprintsToPack.push_back( static_cast<FOOTPRINT*>( item ) );
697
698 if( footprintsToPack.empty() )
699 return 0;
700
701 BOX2I footprintsBbox;
702
703 for( FOOTPRINT* fp : footprintsToPack )
704 {
705 commit.Modify( fp );
706 fp->SetFlags( IS_MOVING );
707 footprintsBbox.Merge( fp->GetBoundingBox( false ) );
708 }
709
710 SpreadFootprints( &footprintsToPack, footprintsBbox.Normalize().GetOrigin(), false );
711
712 if( doMoveSelection( aEvent, &commit, true ) )
713 commit.Push( _( "Pack Footprints" ) );
714 else
715 commit.Revert();
716
717 return 0;
718}
719
720
721int EDIT_TOOL::Move( const TOOL_EVENT& aEvent )
722{
723 if( isRouterActive() || m_dragging )
724 {
725 wxBell();
726 return 0;
727 }
728
729 if( BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() ) )
730 {
731 // Most moves will be synchronous unless they are coming from the API
732 if( aEvent.SynchronousState() )
733 aEvent.SynchronousState()->store( STS_RUNNING );
734
735 if( doMoveSelection( aEvent, commit, true ) )
736 {
737 if( aEvent.SynchronousState() )
738 aEvent.SynchronousState()->store( STS_FINISHED );
739 }
740 else if( aEvent.SynchronousState() )
741 {
742 aEvent.SynchronousState()->store( STS_CANCELLED );
743 }
744 }
745 else
746 {
747 BOARD_COMMIT localCommit( this );
748
749 if( doMoveSelection( aEvent, &localCommit, false ) )
750 localCommit.Push( _( "Move" ) );
751 else
752 localCommit.Revert();
753 }
754
755 // Notify point editor. (While doMoveSelection() will re-select the items and post this
756 // event, it's done before the edit flags are cleared in BOARD_COMMIT::Push() so the point
757 // editor doesn't fire up.)
758 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
759
760 return 0;
761}
762
763
764VECTOR2I EDIT_TOOL::getSafeMovement( const VECTOR2I& aMovement, const BOX2I& aSourceBBox,
765 const VECTOR2D& aBBoxOffset )
766{
767 typedef std::numeric_limits<int> coord_limits;
768
769 static const double max = coord_limits::max() - (int) COORDS_PADDING;
770 static const double min = -max;
771
772 BOX2D testBox( aSourceBBox.GetPosition(), aSourceBBox.GetSize() );
773 testBox.Offset( aBBoxOffset );
774
775 // Do not restrict movement if bounding box is already out of bounds
776 if( testBox.GetLeft() < min || testBox.GetTop() < min || testBox.GetRight() > max
777 || testBox.GetBottom() > max )
778 {
779 return aMovement;
780 }
781
782 testBox.Offset( aMovement );
783
784 if( testBox.GetLeft() < min )
785 testBox.Offset( min - testBox.GetLeft(), 0 );
786
787 if( max < testBox.GetRight() )
788 testBox.Offset( -( testBox.GetRight() - max ), 0 );
789
790 if( testBox.GetTop() < min )
791 testBox.Offset( 0, min - testBox.GetTop() );
792
793 if( max < testBox.GetBottom() )
794 testBox.Offset( 0, -( testBox.GetBottom() - max ) );
795
796 return KiROUND( testBox.GetPosition() - aBBoxOffset - aSourceBBox.GetPosition() );
797}
798
799
800bool EDIT_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, BOARD_COMMIT* aCommit, bool aAutoStart )
801{
802 const bool moveWithReference = aEvent.IsAction( &PCB_ACTIONS::moveWithReference );
803 const bool moveIndividually = aEvent.IsAction( &PCB_ACTIONS::moveIndividually );
804
806 PCBNEW_SETTINGS* cfg = editFrame->GetPcbNewSettings();
807 BOARD* board = editFrame->GetBoard();
809 VECTOR2I originalCursorPos = controls->GetCursorPosition();
810 VECTOR2I originalMousePos = controls->GetMousePosition();
811 std::unique_ptr<STATUS_TEXT_POPUP> statusPopup;
812 wxString status;
813 size_t itemIdx = 0;
814
815 // Be sure that there is at least one item that we can modify. If nothing was selected before,
816 // try looking for the stuff under mouse cursor (i.e. KiCad old-style hover selection)
817 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
818 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
819 {
820 sTool->FilterCollectorForMarkers( aCollector );
821 sTool->FilterCollectorForHierarchy( aCollector, true );
822 sTool->FilterCollectorForFreePads( aCollector );
823 sTool->FilterCollectorForTableCells( aCollector );
824 sTool->FilterCollectorForLockedItems( aCollector );
825 } );
826
827 if( m_dragging || selection.Empty() )
828 return false;
829
830 TOOL_EVENT pushedEvent = aEvent;
831 editFrame->PushTool( aEvent );
832 Activate();
833
834 // Must be done after Activate() so that it gets set into the correct context
835 controls->ShowCursor( true );
836 controls->SetAutoPan( true );
837 controls->ForceCursorPosition( false );
838
839 auto displayConstraintsMessage =
840 [editFrame]( LEADER_MODE aMode )
841 {
842 wxString msg;
843
844 switch( aMode )
845 {
847 msg = _( "Angle snap lines: 45°" );
848 break;
849
851 msg = _( "Angle snap lines: 90°" );
852 break;
853
854 default:
855 msg.clear();
856 break;
857 }
858
859 editFrame->DisplayConstraintsMsg( msg );
860 };
861
862 auto updateStatusPopup =
863 [&]( EDA_ITEM* item, size_t ii, size_t count )
864 {
865 wxString popuptext = _( "Click to place %s (item %zu of %zu)\n"
866 "Press <esc> to cancel all; double-click to finish" );
867 wxString msg;
868
869 if( item->Type() == PCB_FOOTPRINT_T )
870 {
871 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
872 msg = fp->GetReference();
873 }
874 else if( item->Type() == PCB_PAD_T )
875 {
876 PAD* pad = static_cast<PAD*>( item );
877 FOOTPRINT* fp = pad->GetParentFootprint();
878 msg = wxString::Format( _( "%s pad %s" ), fp->GetReference(), pad->GetNumber() );
879 }
880 else
881 {
882 msg = item->GetTypeDesc().Lower();
883 }
884
885 if( !statusPopup )
886 statusPopup = std::make_unique<STATUS_TEXT_POPUP>( frame() );
887
888 statusPopup->SetText( wxString::Format( popuptext, msg, ii, count ) );
889 };
890
891 std::vector<BOARD_ITEM*> sel_items; // All the items operated on by the move below
892 std::vector<BOARD_ITEM*> orig_items; // All the original items in the selection
893
894 for( EDA_ITEM* item : selection )
895 {
896 if( item->IsBOARD_ITEM() )
897 {
898 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
899
900 if( !selection.IsHover() )
901 orig_items.push_back( boardItem );
902
903 sel_items.push_back( boardItem );
904 }
905
906 if( item->Type() == PCB_FOOTPRINT_T )
907 {
908 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
909
910 for( PAD* pad : footprint->Pads() )
911 sel_items.push_back( pad );
912
913 // Clear this flag here; it will be set by the netlist updater if the footprint is new
914 // so that it was skipped in the initial connectivity update in OnNetlistChanged
915 footprint->SetAttributes( footprint->GetAttributes() & ~FP_JUST_ADDED );
916 }
917 }
918
919 VECTOR2I pickedReferencePoint;
920
921 if( moveWithReference && !pickReferencePoint( _( "Select reference point for move..." ), "", "",
922 pickedReferencePoint ) )
923 {
924 if( selection.IsHover() )
926
927 editFrame->PopTool( pushedEvent );
928 return false;
929 }
930
931 if( moveIndividually )
932 {
933 orig_items.clear();
934
935 for( EDA_ITEM* item : selection.GetItemsSortedBySelectionOrder() )
936 {
937 if( item->IsBOARD_ITEM() )
938 orig_items.push_back( static_cast<BOARD_ITEM*>( item ) );
939 }
940
941 updateStatusPopup( orig_items[ itemIdx ], itemIdx + 1, orig_items.size() );
942 statusPopup->Popup();
943 statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
944 canvas()->SetStatusPopup( statusPopup->GetPanel() );
945
946 m_selectionTool->ClearSelection();
947 m_selectionTool->AddItemToSel( orig_items[ itemIdx ] );
948
949 sel_items.clear();
950 sel_items.push_back( orig_items[ itemIdx ] );
951 }
952
953 bool restore_state = false;
954 VECTOR2I originalPos;
955 VECTOR2D bboxMovement;
956 BOX2I originalBBox;
957 bool updateBBox = true;
958 LSET layers( { editFrame->GetActiveLayer() } );
960 TOOL_EVENT copy = aEvent;
961 TOOL_EVENT* evt = &copy;
962 VECTOR2I prevPos;
963 bool enableLocalRatsnest = true;
964
965 LEADER_MODE angleSnapMode = GetAngleSnapMode();
966 bool eatFirstMouseUp = true;
967 bool allowRedraw3D = cfg->m_Display.m_Live3DRefresh;
968 bool showCourtyardConflicts = !m_isFootprintEditor && cfg->m_ShowCourtyardCollisions;
969
970 // Used to test courtyard overlaps
971 std::unique_ptr<DRC_INTERACTIVE_COURTYARD_CLEARANCE> drc_on_move = nullptr;
972
973 if( showCourtyardConflicts )
974 {
975 std::shared_ptr<DRC_ENGINE> drcEngine = m_toolMgr->GetTool<DRC_TOOL>()->GetDRCEngine();
976 drc_on_move.reset( new DRC_INTERACTIVE_COURTYARD_CLEARANCE( drcEngine ) );
977 drc_on_move->Init( board );
978 }
979
980 auto configureAngleSnap =
981 [&]( LEADER_MODE aMode )
982 {
983 std::vector<VECTOR2I> directions;
984
985 switch( aMode )
986 {
988 directions = { VECTOR2I( 1, 0 ), VECTOR2I( 0, 1 ), VECTOR2I( 1, 1 ), VECTOR2I( 1, -1 ) };
989 break;
990
992 directions = { VECTOR2I( 1, 0 ), VECTOR2I( 0, 1 ) };
993 break;
994
995 default:
996 break;
997 }
998
999 grid.SetSnapLineDirections( directions );
1000
1001 if( directions.empty() )
1002 {
1003 grid.ClearSnapLine();
1004 }
1005 else
1006 {
1007 grid.SetSnapLineOrigin( originalPos );
1008 }
1009 };
1010
1011 configureAngleSnap( angleSnapMode );
1012 displayConstraintsMessage( angleSnapMode );
1013
1014 // Prime the pump
1015 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1016
1017 // Main loop: keep receiving events
1018 do
1019 {
1020 VECTOR2I movement;
1022 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1023 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1024
1025 bool isSkip = evt->IsAction( &PCB_ACTIONS::skip ) && moveIndividually;
1026
1027 if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
1028 eatFirstMouseUp = false;
1029
1030 if( evt->IsAction( &PCB_ACTIONS::move )
1031 || evt->IsMotion()
1032 || evt->IsDrag( BUT_LEFT )
1036 {
1037 if( m_dragging && ( evt->IsMotion()
1038 || evt->IsDrag( BUT_LEFT )
1039 || evt->IsAction( &ACTIONS::refreshPreview ) ) )
1040 {
1041 bool redraw3D = false;
1042
1043 GRID_HELPER_GRIDS selectionGrid = grid.GetSelectionGrid( selection );
1044
1045 if( controls->GetSettings().m_lastKeyboardCursorPositionValid )
1046 {
1047 VECTOR2I keyboardPos( controls->GetSettings().m_lastKeyboardCursorPosition );
1048
1049 grid.SetSnap( false );
1050 m_cursor = grid.Align( keyboardPos, selectionGrid );
1051 }
1052 else
1053 {
1054 VECTOR2I mousePos( controls->GetMousePosition() );
1055
1056 m_cursor = grid.BestSnapAnchor( mousePos, layers, selectionGrid, sel_items );
1057 }
1058
1059 if( !selection.HasReferencePoint() )
1060 originalPos = m_cursor;
1061
1062 if( updateBBox )
1063 {
1064 originalBBox = BOX2I();
1065 bboxMovement = VECTOR2D();
1066
1067 for( EDA_ITEM* item : sel_items )
1068 originalBBox.Merge( item->ViewBBox() );
1069
1070 updateBBox = false;
1071 }
1072
1073 // Constrain selection bounding box to coordinates limits
1074 movement = getSafeMovement( m_cursor - prevPos, originalBBox, bboxMovement );
1075
1076 // Apply constrained movement
1077 m_cursor = prevPos + movement;
1078
1079 controls->ForceCursorPosition( true, m_cursor );
1080 selection.SetReferencePoint( m_cursor );
1081
1082 prevPos = m_cursor;
1083 bboxMovement += movement;
1084
1085 // Drag items to the current cursor position
1086 for( BOARD_ITEM* item : sel_items )
1087 {
1088 // Don't double move child items.
1089 if( !item->GetParent() || !item->GetParent()->IsSelected() )
1090 item->Move( movement );
1091
1092 if( item->Type() == PCB_GENERATOR_T && sel_items.size() == 1 )
1093 {
1094 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genUpdateEdit, aCommit,
1095 static_cast<PCB_GENERATOR*>( item ) );
1096 }
1097
1098 if( item->Type() == PCB_FOOTPRINT_T )
1099 redraw3D = true;
1100 }
1101
1102 if( redraw3D && allowRedraw3D )
1103 editFrame->Update3DView( false, true );
1104
1105 if( showCourtyardConflicts && drc_on_move->m_FpInMove.size() )
1106 {
1107 drc_on_move->Run();
1108 drc_on_move->UpdateConflicts( m_toolMgr->GetView(), true );
1109 }
1110
1112 }
1113 else if( !m_dragging && ( aAutoStart || !evt->IsAction( &ACTIONS::refreshPreview ) ) )
1114 {
1115 // Prepare to start dragging
1116 editFrame->HideSolderMask();
1117
1118 m_dragging = true;
1119
1120 for( BOARD_ITEM* item : sel_items )
1121 {
1122 if( item->GetParent() && item->GetParent()->IsSelected() )
1123 continue;
1124
1125 if( !item->IsNew() && !item->IsMoving() )
1126 {
1127 if( item->Type() == PCB_GENERATOR_T && sel_items.size() == 1 )
1128 {
1129 enableLocalRatsnest = false;
1130
1131 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genStartEdit, aCommit,
1132 static_cast<PCB_GENERATOR*>( item ) );
1133 }
1134 else
1135 {
1136 aCommit->Modify( item, nullptr, RECURSE_MODE::RECURSE );
1137 }
1138
1139 item->SetFlags( IS_MOVING );
1140
1141 if( item->Type() == PCB_SHAPE_T )
1142 static_cast<PCB_SHAPE*>( item )->UpdateHatching();
1143
1144 item->RunOnChildren(
1145 [&]( BOARD_ITEM* child )
1146 {
1147 child->SetFlags( IS_MOVING );
1148
1149 if( child->Type() == PCB_SHAPE_T )
1150 static_cast<PCB_SHAPE*>( child )->UpdateHatching();
1151 },
1153 }
1154 }
1155
1156 m_cursor = controls->GetCursorPosition();
1157
1158 if( selection.HasReferencePoint() )
1159 {
1160 // start moving with the reference point attached to the cursor
1161 grid.SetAuxAxes( false );
1162
1163 movement = m_cursor - selection.GetReferencePoint();
1164
1165 // Drag items to the current cursor position
1166 for( EDA_ITEM* item : selection )
1167 {
1168 if( !item->IsBOARD_ITEM() )
1169 continue;
1170
1171 // Don't double move footprint pads, fields, etc.
1172 if( item->GetParent() && item->GetParent()->IsSelected() )
1173 continue;
1174
1175 static_cast<BOARD_ITEM*>( item )->Move( movement );
1176 }
1177
1178 selection.SetReferencePoint( m_cursor );
1179 }
1180 else
1181 {
1182 if( showCourtyardConflicts )
1183 {
1184 std::vector<FOOTPRINT*>& FPs = drc_on_move->m_FpInMove;
1185
1186 for( BOARD_ITEM* item : sel_items )
1187 {
1188 if( item->Type() == PCB_FOOTPRINT_T )
1189 FPs.push_back( static_cast<FOOTPRINT*>( item ) );
1190
1191 item->RunOnChildren(
1192 [&]( BOARD_ITEM* child )
1193 {
1194 if( child->Type() == PCB_FOOTPRINT_T )
1195 FPs.push_back( static_cast<FOOTPRINT*>( child ) );
1196 },
1198 }
1199 }
1200
1201 // Use the mouse position over cursor, as otherwise large grids will allow only
1202 // snapping to items that are closest to grid points
1203 m_cursor = grid.BestDragOrigin( originalMousePos, sel_items, grid.GetSelectionGrid( selection ),
1204 &m_selectionTool->GetFilter() );
1205
1206 // Set the current cursor position to the first dragged item origin, so the
1207 // movement vector could be computed later
1208 if( moveWithReference )
1209 {
1210 selection.SetReferencePoint( pickedReferencePoint );
1211
1212 if( angleSnapMode != LEADER_MODE::DIRECT )
1213 grid.SetSnapLineOrigin( selection.GetReferencePoint() );
1214
1215 controls->ForceCursorPosition( true, pickedReferencePoint );
1216 m_cursor = pickedReferencePoint;
1217 }
1218 else
1219 {
1220 VECTOR2I snapped = grid.Align( m_cursor, grid.GetSelectionGrid( selection ) );
1221 VECTOR2I delta = snapped - m_cursor;
1222
1223 if( delta.x || delta.y )
1224 {
1225 for( BOARD_ITEM* item : sel_items )
1226 {
1227 if( item->GetParent() && item->GetParent()->IsSelected() )
1228 continue;
1229
1230 item->Move( delta );
1231 }
1232 }
1233
1234 selection.SetReferencePoint( snapped );
1235
1236 if( angleSnapMode != LEADER_MODE::DIRECT )
1237 grid.SetSnapLineOrigin( selection.GetReferencePoint() );
1238
1239 grid.SetAuxAxes( true, snapped );
1240
1241 if( !editFrame->GetMoveWarpsCursor() )
1242 m_cursor = originalCursorPos;
1243 else
1244 m_cursor = snapped;
1245 }
1246
1247 originalPos = selection.GetReferencePoint();
1248 }
1249
1250 // Update variables for bounding box collision calculations
1251 updateBBox = true;
1252
1253 controls->SetCursorPosition( m_cursor, false );
1254
1255 prevPos = m_cursor;
1256 controls->SetAutoPan( true );
1258 }
1259
1260 if( statusPopup )
1261 statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
1262
1263 if( enableLocalRatsnest )
1264 m_toolMgr->PostAction( PCB_ACTIONS::updateLocalRatsnest, movement );
1265 }
1266 else if( evt->IsCancelInteractive() || evt->IsActivate() )
1267 {
1268 if( m_dragging && evt->IsCancelInteractive() )
1269 evt->SetPassEvent( false );
1270
1271 restore_state = true; // Canceling the tool means that items have to be restored
1272 break; // Finish
1273 }
1274 else if( evt->IsClick( BUT_RIGHT ) )
1275 {
1276 m_selectionTool->GetToolMenu().ShowContextMenu( selection );
1277 }
1278 else if( evt->IsAction( &ACTIONS::undo ) )
1279 {
1280 restore_state = true; // Perform undo locally
1281 break; // Finish
1282 }
1283 else if( evt->IsAction( &ACTIONS::doDelete ) )
1284 {
1285 evt->SetPassEvent();
1286 // Exit on a delete; there will no longer be anything to drag.
1287 break;
1288 }
1289 else if( evt->IsAction( &ACTIONS::duplicate ) || evt->IsAction( &ACTIONS::cut ) )
1290 {
1291 wxBell();
1292 }
1293 else if( evt->IsAction( &PCB_ACTIONS::rotateCw )
1295 || evt->IsAction( &PCB_ACTIONS::flip )
1296 || evt->IsAction( &PCB_ACTIONS::mirrorH )
1297 || evt->IsAction( &PCB_ACTIONS::mirrorV ) )
1298 {
1299 updateBBox = true;
1300 eatFirstMouseUp = false;
1301 evt->SetPassEvent();
1302 }
1303 else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) || isSkip )
1304 {
1305 // Eat mouse-up/-click events that leaked through from the lock dialog
1306 if( eatFirstMouseUp && !evt->IsAction( &ACTIONS::cursorClick ) )
1307 {
1308 eatFirstMouseUp = false;
1309 continue;
1310 }
1311 else if( moveIndividually && m_dragging )
1312 {
1313 // Put skipped items back where they started
1314 if( isSkip )
1315 orig_items[itemIdx]->SetPosition( originalPos );
1316
1317 view()->Update( orig_items[itemIdx] );
1319
1320 if( ++itemIdx < orig_items.size() )
1321 {
1322 BOARD_ITEM* nextItem = orig_items[itemIdx];
1323
1324 m_selectionTool->ClearSelection();
1325
1326 originalPos = nextItem->GetPosition();
1327 m_selectionTool->AddItemToSel( nextItem );
1328 selection.SetReferencePoint( originalPos );
1329 if( angleSnapMode != LEADER_MODE::DIRECT )
1330 grid.SetSnapLineOrigin( selection.GetReferencePoint() );
1331
1332 sel_items.clear();
1333 sel_items.push_back( nextItem );
1334 updateStatusPopup( nextItem, itemIdx + 1, orig_items.size() );
1335
1336 // Pick up new item
1337 aCommit->Modify( nextItem, nullptr, RECURSE_MODE::RECURSE );
1338 nextItem->Move( controls->GetCursorPosition( true ) - nextItem->GetPosition() );
1339
1340 continue;
1341 }
1342 }
1343
1344 break; // finish
1345 }
1346 else if( evt->IsDblClick( BUT_LEFT ) )
1347 {
1348 // The first click will move the new item, so put it back
1349 if( moveIndividually )
1350 orig_items[itemIdx]->SetPosition( originalPos );
1351
1352 break; // finish
1353 }
1355 {
1356 angleSnapMode = GetAngleSnapMode();
1357 configureAngleSnap( angleSnapMode );
1358 displayConstraintsMessage( angleSnapMode );
1359 evt->SetPassEvent( true );
1360 }
1361 else if( evt->IsAction( &ACTIONS::increment ) )
1362 {
1363 if( evt->HasParameter() )
1364 m_toolMgr->RunSynchronousAction( ACTIONS::increment, aCommit, evt->Parameter<ACTIONS::INCREMENT>() );
1365 else
1366 m_toolMgr->RunSynchronousAction( ACTIONS::increment, aCommit, ACTIONS::INCREMENT { 1, 0 } );
1367 }
1374 || evt->IsAction( &ACTIONS::redo ) )
1375 {
1376 wxBell();
1377 }
1378 else
1379 {
1380 evt->SetPassEvent();
1381 }
1382
1383 } while( ( evt = Wait() ) ); // Assignment (instead of equality test) is intentional
1384
1385 // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared
1386 if( showCourtyardConflicts )
1387 drc_on_move->ClearConflicts( m_toolMgr->GetView() );
1388
1389 controls->ForceCursorPosition( false );
1390 controls->ShowCursor( false );
1391 controls->SetAutoPan( false );
1392
1393 m_dragging = false;
1394
1395 // Discard reference point when selection is "dropped" onto the board
1396 selection.ClearReferencePoint();
1397
1398 // Unselect all items to clear selection flags and then re-select the originally selected
1399 // items.
1400 m_toolMgr->RunAction( ACTIONS::selectionClear );
1401
1402 if( restore_state )
1403 {
1404 if( sel_items.size() == 1 && sel_items.back()->Type() == PCB_GENERATOR_T )
1405 {
1406 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genCancelEdit, aCommit,
1407 static_cast<PCB_GENERATOR*>( sel_items.back() ) );
1408 }
1409 }
1410 else
1411 {
1412 if( sel_items.size() == 1 && sel_items.back()->Type() == PCB_GENERATOR_T )
1413 {
1414 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genFinishEdit, aCommit,
1415 static_cast<PCB_GENERATOR*>( sel_items.back() ) );
1416 }
1417
1418 EDA_ITEMS oItems( orig_items.begin(), orig_items.end() );
1419 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &oItems );
1420 }
1421
1422 // Remove the dynamic ratsnest from the screen
1424
1425 editFrame->PopTool( pushedEvent );
1427
1428 return !restore_state;
1429}
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 derived from BOARD_ITEM for items that can be connected and have a net,...
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
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:322
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
int SwapGateNets(const TOOL_EVENT &aEvent)
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:242
int Move(const TOOL_EVENT &aEvent)
Main loop in which events are handled.
static const unsigned int COORDS_PADDING
Definition edit_tool.h:247
VECTOR2I getSafeMovement(const VECTOR2I &aMovement, const BOX2I &aSourceBBox, const VECTOR2D &aBBoxOffset)
int SwapPadNets(const TOOL_EVENT &aEvent)
Swap nets between selected pads and propagate to connected copper items (tracks, arcs,...
static void PadFilter(const VECTOR2I &, GENERAL_COLLECTOR &aCollector, PCB_SELECTION_TOOL *sTool)
A selection filter which prunes the selection to contain only items of type PCB_PAD_T.
VECTOR2I m_cursor
Definition edit_tool.h:243
void rebuildConnectivity()
PCB_SELECTION_TOOL * m_selectionTool
Definition edit_tool.h:241
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)
const std::vector< FP_UNIT_INFO > & GetUnitInfo() const
Definition footprint.h:760
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
PAD * FindPadByNumber(const wxString &aPadNumber, PAD *aSearchAfterMe=nullptr) const
Return a PAD with a matching number.
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 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 angleSnapModeChanged
Notification event when angle mode changes.
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.
static bool PromptConnectedPadDecision(PCB_BASE_EDIT_FRAME *aFrame, const std::vector< PAD * > &aPads, const wxString &aDialogTitle, bool &aIncludeConnectedPads)
@ FP_JUST_ADDED
Definition footprint.h:86
a few functions useful in geometry calculations.
LEADER_MODE
The kind of the leader line.
@ DEG45
45 Degree only
@ DIRECT
Unconstrained point-to-point.
@ DEG90
90 Degree only
GRID_HELPER_GRIDS
Definition grid_helper.h:44
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
#define kv
@ 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_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:111
@ 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_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:98
@ 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