KiCad PCB EDA Suite
Loading...
Searching...
No Matches
edit_tool.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2013-2017 CERN
5 * Copyright (C) 2017-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
7 * @author Tomasz Wlostowski <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
27#include <advanced_config.h>
28#include <limits>
29#include <board.h>
31#include <footprint.h>
32#include <pcb_shape.h>
33#include <pcb_group.h>
34#include <pcb_target.h>
35#include <pcb_text.h>
36#include <pcb_textbox.h>
37#include <collectors.h>
38#include <pcb_edit_frame.h>
40#include <kiway.h>
41#include <array_creator.h>
42#include <status_popup.h>
44#include <tool/tool_manager.h>
45#include <tools/pcb_actions.h>
47#include <tools/edit_tool.h>
51#include <tools/pad_tool.h>
52#include <view/view_controls.h>
55#include <core/kicad_algo.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 <zone_filler.h>
68#include <pcb_bitmap.h>
69
70const unsigned int EDIT_TOOL::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
71
73 PCB_TOOL_BASE( "pcbnew.InteractiveEdit" ),
74 m_selectionTool( nullptr ),
75 m_dragging( false )
76{
77}
78
79
81{
82 m_dragging = false;
83
84 m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( getEditFrame<PCB_BASE_EDIT_FRAME>() );
85
86 if( aReason != RUN )
87 m_commit.reset( new BOARD_COMMIT( this ) );
88}
89
90
92 CONDITIONAL_MENU( aTool )
93{
94 SetIcon( BITMAPS::special_tools );
95 SetTitle( _( "Positioning Tools" ) );
96
101}
102
103
105{
106 // Find the selection tool, so they can cooperate
108
109 auto positioningToolsSubMenu = std::make_shared<POSITIONING_TOOLS_MENU>( this );
110 m_selectionTool->GetToolMenu().RegisterSubMenu( positioningToolsSubMenu );
111
112 auto propertiesCondition =
113 [&]( const SELECTION& aSel )
114 {
115 if( aSel.GetSize() == 0 )
116 {
118 {
121
122 if( ds && ds->HitTestDrawingSheetItems( getView(), cursor ) )
123 return true;
124 }
125
126 return false;
127 }
128
129 if( aSel.GetSize() == 1 )
130 return true;
131
132 for( EDA_ITEM* item : aSel )
133 {
134 if( !dynamic_cast<PCB_TRACK*>( item ) )
135 return false;
136 }
137
138 return true;
139 };
140
141 auto inFootprintEditor =
142 [ this ]( const SELECTION& aSelection )
143 {
144 return m_isFootprintEditor;
145 };
146
147 auto canMirror =
148 [ this ]( const SELECTION& aSelection )
149 {
151 && SELECTION_CONDITIONS::OnlyTypes( { PCB_PAD_T } )( aSelection ) )
152 {
153 return false;
154 }
155
157 };
158
159 auto singleFootprintCondition = SELECTION_CONDITIONS::OnlyTypes( { PCB_FOOTPRINT_T } )
161
162 auto noActiveToolCondition =
163 [ this ]( const SELECTION& aSelection )
164 {
165 return frame()->ToolStackIsEmpty();
166 };
167
168 auto notMovingCondition =
169 [ this ]( const SELECTION& aSelection )
170 {
174 };
175
176 auto noItemsCondition =
177 [ this ]( const SELECTION& aSelections ) -> bool
178 {
179 return frame()->GetBoard() && !frame()->GetBoard()->IsEmpty();
180 };
181
182 auto isSkippable =
183 [ this ]( const SELECTION& aSelection )
184 {
186 };
187
188 static std::vector<KICAD_T> connectedTypes = { PCB_TRACE_T,
189 PCB_ARC_T,
190 PCB_VIA_T,
191 PCB_PAD_T,
192 PCB_ZONE_T };
193
194 static std::vector<KICAD_T> unroutableTypes = { PCB_TRACE_T,
195 PCB_ARC_T,
196 PCB_VIA_T,
197 PCB_PAD_T,
199
200 static std::vector<KICAD_T> trackTypes = { PCB_TRACE_T,
201 PCB_ARC_T,
202 PCB_VIA_T };
203
204 static std::vector<KICAD_T> filletTypes = { PCB_SHAPE_LOCATE_POLY_T,
207
208
209 // Add context menu entries that are displayed when selection tool is active
211
213 && notMovingCondition );
215 && SELECTION_CONDITIONS::OnlyTypes( unroutableTypes )
216 && notMovingCondition
217 && !inFootprintEditor );
219 && notMovingCondition );
220 menu.AddItem( PCB_ACTIONS::skip, isSkippable );
222 && SELECTION_CONDITIONS::OnlyTypes( trackTypes ) );
233 menu.AddItem( PCB_ACTIONS::mirrorH, canMirror );
234 menu.AddItem( PCB_ACTIONS::mirrorV, canMirror );
238
239 menu.AddItem( PCB_ACTIONS::properties, propertiesCondition );
240
242 && !inFootprintEditor );
244
245 // Footprint actions
246 menu.AddSeparator();
247 menu.AddItem( PCB_ACTIONS::editFpInFpEditor, singleFootprintCondition );
248 menu.AddItem( PCB_ACTIONS::updateFootprint, singleFootprintCondition );
249 menu.AddItem( PCB_ACTIONS::changeFootprint, singleFootprintCondition );
250
251 // Add the submenu for the special positioning tools
252 menu.AddSeparator( 100 );
253 menu.AddMenu( positioningToolsSubMenu.get(), SELECTION_CONDITIONS::NotEmpty, 100 );
254
255 menu.AddSeparator( 150 );
258
259 // Selection tool handles the context menu for some other tools, such as the Picker.
260 // Don't add things like Paste when another tool is active.
261 menu.AddItem( ACTIONS::paste, noActiveToolCondition, 150 );
262 menu.AddItem( ACTIONS::pasteSpecial, noActiveToolCondition && !inFootprintEditor, 150 );
265
266 menu.AddSeparator( 150 );
267 menu.AddItem( ACTIONS::selectAll, noItemsCondition, 150 );
268
269 return true;
270}
271
272
274{
275 // GetAndPlace makes sense only in board editor, although it is also called
276 // in fpeditor, that shares the same EDIT_TOOL list
277 if( !getEditFrame<PCB_BASE_FRAME>()->IsType( FRAME_PCB_EDITOR ) )
278 return 0;
279
281 FOOTPRINT* fp = getEditFrame<PCB_BASE_FRAME>()->GetFootprintFromBoardByReference();
282
283 if( fp )
284 {
286 m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, (void*) fp );
287
288 selectionTool->GetSelection().SetReferencePoint( fp->GetPosition() );
290 }
291
292 return 0;
293}
294
295
296bool EDIT_TOOL::invokeInlineRouter( int aDragMode )
297{
298 ROUTER_TOOL* theRouter = m_toolMgr->GetTool<ROUTER_TOOL>();
299
300 if( !theRouter )
301 return false;
302
303 // don't allow switch from moving to dragging
304 if( m_dragging )
305 {
306 wxBell();
307 return false;
308 }
309
310 // make sure we don't accidentally invoke inline routing mode while the router is already
311 // active!
312 if( theRouter->IsToolActive() )
313 return false;
314
315 if( theRouter->CanInlineDrag( aDragMode ) )
316 {
318 static_cast<intptr_t>( aDragMode ) );
319 return true;
320 }
321
322 return false;
323}
324
325
327{
329
330 return router && router->RoutingInProgress();
331}
332
333
334int EDIT_TOOL::Drag( const TOOL_EVENT& aEvent )
335{
336 if( !m_toolMgr->GetTool<ROUTER_TOOL>() )
337 return false; // don't drag when no router tool (i.e. fp editor)
338
340 return false; // don't drag when router is already active
341
342 int mode = PNS::DM_ANY;
343
344 if( aEvent.IsAction( &PCB_ACTIONS::dragFreeAngle ) )
345 mode |= PNS::DM_FREE_ANGLE;
346
348 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
349 {
350 sTool->FilterCollectorForFreePads( aCollector );
351 sTool->FilterCollectorForHierarchy( aCollector, true );
352
353 if( aCollector.GetCount() > 1 )
354 sTool->GuessSelectionCandidates( aCollector, aPt );
355
356 /*
357 * If we have a knee between two segments, or a via attached to two segments,
358 * then drop the selection to a single item.
359 */
360
361 std::vector<PCB_TRACK*> tracks;
362 std::vector<PCB_TRACK*> vias;
363
364 for( EDA_ITEM* item : aCollector )
365 {
366 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
367 {
368 if( track->Type() == PCB_VIA_T )
369 vias.push_back( track );
370 else
371 tracks.push_back( track );
372 }
373 }
374
375 auto connected = []( PCB_TRACK* track, const VECTOR2I& pt )
376 {
377 return track->GetStart() == pt || track->GetEnd() == pt;
378 };
379
380 if( tracks.size() == 2 && vias.size() == 0 )
381 {
382 if( connected( tracks[0], tracks[1]->GetStart() )
383 || connected( tracks[0], tracks[1]->GetEnd() ) )
384 {
385 aCollector.Remove( tracks[1] );
386 }
387 }
388 else if( tracks.size() == 2 && vias.size() == 1 )
389 {
390 if( connected( tracks[0], vias[0]->GetPosition() )
391 && connected( tracks[1], vias[0]->GetPosition() ) )
392 {
393 aCollector.Remove( tracks[0] );
394 aCollector.Remove( tracks[1] );
395 }
396 }
397 },
398 true /* prompt user regarding locked items */ );
399
400 if( selection.Empty() )
401 return 0;
402
403 if( selection.Size() == 1 && selection.Front()->Type() == PCB_ARC_T )
404 {
405 // TODO: This really should be done in PNS to ensure DRC is maintained, but for now
406 // it allows interactive editing of an arc track
407 return DragArcTrack( aEvent );
408 }
409 else
410 {
411 invokeInlineRouter( mode );
412 }
413
414 return 0;
415}
416
417
419{
421
422 if( selection.Size() != 1 || selection.Front()->Type() != PCB_ARC_T )
423 return 0;
424
425 PCB_ARC* theArc = static_cast<PCB_ARC*>( selection.Front() );
426 EDA_ANGLE maxTangentDeviation( ADVANCED_CFG::GetCfg().m_MaxTangentAngleDeviation, DEGREES_T );
427
428 if( theArc->GetAngle() + maxTangentDeviation >= ANGLE_180 )
429 {
430 wxString msg = wxString::Format( _( "Unable to resize arc tracks of %s or greater." ),
431 EDA_UNIT_UTILS::UI::MessageTextFromValue( ANGLE_180 - maxTangentDeviation ) );
432 frame()->ShowInfoBarError( msg );
433
434 return 0; // don't bother with > 180 degree arcs
435 }
436
438
439 Activate();
440 // Must be done after Activate() so that it gets set into the correct context
441 controls->ShowCursor( true );
442 controls->SetAutoPan( true );
443
444 bool restore_state = false;
445 VECTOR2I arcCenter = theArc->GetCenter();
446 SEG tanStart = SEG( arcCenter, theArc->GetStart() ).PerpendicularSeg( theArc->GetStart() );
447 SEG tanEnd = SEG( arcCenter, theArc->GetEnd() ).PerpendicularSeg( theArc->GetEnd() );
448
449 //Ensure the tangent segments are in the correct orientation
450 OPT_VECTOR2I tanIntersect = tanStart.IntersectLines( tanEnd );
451
452 if( !tanIntersect )
453 return 0;
454
455 tanStart.A = *tanIntersect;
456 tanStart.B = theArc->GetStart();
457 tanEnd.A = *tanIntersect;
458 tanEnd.B = theArc->GetEnd();
459
460 auto getUniqueTrackAtAnchorCollinear =
461 [&]( const VECTOR2I& aAnchor, const SEG& aCollinearSeg ) -> PCB_TRACK*
462 {
463 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
464
465 // Allow items at a distance within the width of the arc track
466 int allowedDeviation = theArc->GetWidth();
467
468 std::vector<BOARD_CONNECTED_ITEM*> itemsOnAnchor;
469
470 for( int i = 0; i < 3; i++ )
471 {
472 itemsOnAnchor = conn->GetConnectedItemsAtAnchor( theArc, aAnchor,
475 allowedDeviation );
476 allowedDeviation /= 2;
477
478 if( itemsOnAnchor.size() == 1 )
479 break;
480 }
481
482 PCB_TRACK* retval = nullptr;
483
484 if( itemsOnAnchor.size() == 1 && itemsOnAnchor.front()->Type() == PCB_TRACE_T )
485 {
486 retval = static_cast<PCB_TRACK*>( itemsOnAnchor.front() );
487 SEG trackSeg( retval->GetStart(), retval->GetEnd() );
488
489 // Allow deviations in colinearity as defined in ADVANCED_CFG
490 if( trackSeg.Angle( aCollinearSeg ) > maxTangentDeviation )
491 retval = nullptr;
492 }
493
494 if( !retval )
495 {
496 retval = new PCB_TRACK( theArc->GetParent() );
497 retval->SetStart( aAnchor );
498 retval->SetEnd( aAnchor );
499 retval->SetNet( theArc->GetNet() );
500 retval->SetLayer( theArc->GetLayer() );
501 retval->SetWidth( theArc->GetWidth() );
502 retval->SetLocked( theArc->IsLocked() );
503 retval->SetFlags( IS_NEW );
504 getView()->Add( retval );
505 }
506
507 return retval;
508 };
509
510 PCB_TRACK* trackOnStart = getUniqueTrackAtAnchorCollinear( theArc->GetStart(), tanStart);
511 PCB_TRACK* trackOnEnd = getUniqueTrackAtAnchorCollinear( theArc->GetEnd(), tanEnd );
512
513 // Make copies of items to be edited
514 PCB_ARC* theArcCopy = new PCB_ARC( *theArc );
515 PCB_TRACK* trackOnStartCopy = new PCB_TRACK( *trackOnStart );
516 PCB_TRACK* trackOnEndCopy = new PCB_TRACK( *trackOnEnd );
517
518 if( trackOnStart->GetLength() != 0 )
519 {
520 tanStart.A = trackOnStart->GetStart();
521 tanStart.B = trackOnStart->GetEnd();
522 }
523
524 if( trackOnEnd->GetLength() != 0 )
525 {
526 tanEnd.A = trackOnEnd->GetStart();
527 tanEnd.B = trackOnEnd->GetEnd();
528 }
529
530 // Recalculate intersection point
531 if( tanIntersect = tanStart.IntersectLines( tanEnd ); !tanIntersect )
532 return 0;
533
534 auto isTrackStartClosestToArcStart =
535 [&]( PCB_TRACK* aTrack ) -> bool
536 {
537 double trackStartToArcStart = GetLineLength( aTrack->GetStart(), theArc->GetStart() );
538 double trackEndToArcStart = GetLineLength( aTrack->GetEnd(), theArc->GetStart() );
539
540 return trackStartToArcStart < trackEndToArcStart;
541 };
542
543 bool isStartTrackOnStartPt = isTrackStartClosestToArcStart( trackOnStart );
544 bool isEndTrackOnStartPt = isTrackStartClosestToArcStart( trackOnEnd );
545
546 // Calculate constraints
547 //======================
548 // maxTanCircle is the circle with maximum radius that is tangent to the two adjacent straight
549 // tracks and whose tangent points are constrained within the original tracks and their
550 // projected intersection points.
551 //
552 // The cursor will be constrained first within the isosceles triangle formed by the segments
553 // cSegTanStart, cSegTanEnd and cSegChord. After that it will be constrained to be outside
554 // maxTanCircle.
555 //
556 //
557 // ____________ <-cSegTanStart
558 // / * . ' *
559 // cSegTanEnd-> / * . ' *
560 // /* . ' <-cSegChord *
561 // /. '
562 // /* *
563 //
564 // * c * <-maxTanCircle
565 //
566 // * *
567 //
568 // * *
569 // * *
570 // * *
571 //
572
573 auto getFurthestPointToTanInterstect =
574 [&]( VECTOR2I& aPointA, VECTOR2I& aPointB ) -> VECTOR2I
575 {
576 if( ( aPointA - *tanIntersect ).EuclideanNorm()
577 > ( aPointB - *tanIntersect ).EuclideanNorm() )
578 {
579 return aPointA;
580 }
581 else
582 {
583 return aPointB;
584 }
585 };
586
587 CIRCLE maxTanCircle;
588 VECTOR2I tanStartPoint = getFurthestPointToTanInterstect( tanStart.A, tanStart.B );
589 VECTOR2I tanEndPoint = getFurthestPointToTanInterstect( tanEnd.A, tanEnd.B );
590 VECTOR2I tempTangentPoint = tanEndPoint;
591
592 if( getFurthestPointToTanInterstect( tanStartPoint, tanEndPoint ) == tanEndPoint )
593 tempTangentPoint = tanStartPoint;
594
595 maxTanCircle.ConstructFromTanTanPt( tanStart, tanEnd, tempTangentPoint );
596 VECTOR2I maxTanPtStart = tanStart.LineProject( maxTanCircle.Center );
597 VECTOR2I maxTanPtEnd = tanEnd.LineProject( maxTanCircle.Center );
598
599 SEG cSegTanStart( maxTanPtStart, *tanIntersect );
600 SEG cSegTanEnd( maxTanPtEnd, *tanIntersect );
601 SEG cSegChord( maxTanPtStart, maxTanPtEnd );
602
603 int cSegTanStartSide = cSegTanStart.Side( theArc->GetMid() );
604 int cSegTanEndSide = cSegTanEnd.Side( theArc->GetMid() );
605 int cSegChordSide = cSegChord.Side( theArc->GetMid() );
606
607 bool eatFirstMouseUp = true;
608
609 // Start the tool loop
610 while( TOOL_EVENT* evt = Wait() )
611 {
612 m_cursor = controls->GetMousePosition();
613
614 // Constrain cursor within the isosceles triangle
615 if( cSegTanStartSide != cSegTanStart.Side( m_cursor )
616 || cSegTanEndSide != cSegTanEnd.Side( m_cursor )
617 || cSegChordSide != cSegChord.Side( m_cursor ) )
618 {
619 std::vector<VECTOR2I> possiblePoints;
620
621 possiblePoints.push_back( cSegTanEnd.NearestPoint( m_cursor ) );
622 possiblePoints.push_back( cSegChord.NearestPoint( m_cursor ) );
623
624 VECTOR2I closest = cSegTanStart.NearestPoint( m_cursor );
625
626 for( VECTOR2I candidate : possiblePoints )
627 {
628 if( ( candidate - m_cursor ).SquaredEuclideanNorm()
629 < ( closest - m_cursor ).SquaredEuclideanNorm() )
630 {
631 closest = candidate;
632 }
633 }
634
635 m_cursor = closest;
636 }
637
638 // Constrain cursor to be outside maxTanCircle
639 if( ( m_cursor - maxTanCircle.Center ).EuclideanNorm() < maxTanCircle.Radius )
640 m_cursor = maxTanCircle.NearestPoint( m_cursor );
641
642 controls->ForceCursorPosition( true, m_cursor );
643
644 // Calculate resulting object coordinates
645 CIRCLE circlehelper;
646 circlehelper.ConstructFromTanTanPt( cSegTanStart, cSegTanEnd, m_cursor );
647
648 VECTOR2I newCenter = circlehelper.Center;
649 VECTOR2I newStart = cSegTanStart.LineProject( newCenter );
650 VECTOR2I newEnd = cSegTanEnd.LineProject( newCenter );
651 VECTOR2I newMid = CalcArcMid( newStart, newEnd, newCenter );
652
653 // Update objects
654 theArc->SetStart( newStart );
655 theArc->SetEnd( newEnd );
656 theArc->SetMid( newMid );
657
658 if( isStartTrackOnStartPt )
659 trackOnStart->SetStart( newStart );
660 else
661 trackOnStart->SetEnd( newStart );
662
663 if( isEndTrackOnStartPt )
664 trackOnEnd->SetStart( newEnd );
665 else
666 trackOnEnd->SetEnd( newEnd );
667
668 // Update view
669 getView()->Update( trackOnStart );
670 getView()->Update( trackOnEnd );
671 getView()->Update( theArc );
672
673 // Handle events
674 if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
675 {
676 eatFirstMouseUp = false;
677 }
678 else if( evt->IsCancelInteractive() || evt->IsActivate() )
679 {
680 restore_state = true; // Canceling the tool means that items have to be restored
681 break; // Finish
682 }
683 else if( evt->IsAction( &ACTIONS::undo ) )
684 {
685 restore_state = true; // Perform undo locally
686 break; // Finish
687 }
688 else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT )
689 || evt->IsDblClick( BUT_LEFT ) )
690 {
691 // Eat mouse-up/-click events that leaked through from the lock dialog
692 if( eatFirstMouseUp && evt->Parameter<intptr_t>() != ACTIONS::CURSOR_CLICK )
693 {
694 eatFirstMouseUp = false;
695 continue;
696 }
697
698 break; // Finish
699 }
700 }
701
702 // Ensure we only do one commit operation on each object
703 auto processTrack =
704 [&]( PCB_TRACK* aTrack, PCB_TRACK* aTrackCopy, int aMaxLengthIU ) -> bool
705 {
706 if( aTrack->IsNew() )
707 {
708 getView()->Remove( aTrack );
709
710 if( aTrack->GetLength() <= aMaxLengthIU )
711 {
712 aTrack->SetParentGroup( nullptr );
713 delete aTrack;
714 aTrack = nullptr;
715
716 aTrackCopy->SetParentGroup( nullptr );
717 delete aTrackCopy;
718 aTrackCopy = nullptr;
719
720 return false;
721 }
722 else
723 {
724 m_commit->Add( aTrack );
725
726 aTrackCopy->SetParentGroup( nullptr );
727 delete aTrackCopy;
728 aTrackCopy = nullptr;
729
730 return true;
731 }
732 }
733 else if( aTrack->GetLength() <= aMaxLengthIU )
734 {
735 aTrack->SwapItemData( aTrackCopy ); //restore the original before notifying COMMIT
736 m_commit->Remove( aTrack );
737
738 aTrackCopy->SetParentGroup( nullptr );
739 delete aTrackCopy;
740 aTrackCopy = nullptr;
741
742 return false;
743 }
744 else
745 {
746 m_commit->Modified( aTrack, aTrackCopy );
747 }
748
749 return true;
750 };
751
752 // Amend the end points of the arc if we delete the joining tracks
753 VECTOR2I newStart = trackOnStart->GetStart();
754 VECTOR2I newEnd = trackOnEnd->GetStart();
755
756 if( isStartTrackOnStartPt )
757 newStart = trackOnStart->GetEnd();
758
759 if( isEndTrackOnStartPt )
760 newEnd = trackOnEnd->GetEnd();
761
762 int maxLengthIU =
763 KiROUND( ADVANCED_CFG::GetCfg().m_MaxTrackLengthToKeep * pcbIUScale.IU_PER_MM );
764
765 if( !processTrack( trackOnStart, trackOnStartCopy, maxLengthIU ) )
766 theArc->SetStart( newStart );
767
768 if( !processTrack( trackOnEnd, trackOnEndCopy, maxLengthIU ) )
769 theArc->SetEnd( newEnd );
770
771 processTrack( theArc, theArcCopy, 0 ); // only delete the arc if start and end points coincide
772
773 // Should we commit?
774 if( restore_state )
775 m_commit->Revert();
776 else
777 m_commit->Push( _( "Drag Arc Track" ) );
778
779 return 0;
780}
781
782
784{
786 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
787 {
788 // Iterate from the back so we don't have to worry about removals.
789 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
790 {
791 BOARD_ITEM* item = aCollector[ i ];
792
793 if( !dynamic_cast<PCB_TRACK*>( item ) )
794 aCollector.Remove( item );
795 }
796 },
797 true /* prompt user regarding locked items */ );
798
799 for( EDA_ITEM* item : selection )
800 {
801 if( item->Type() == PCB_VIA_T )
802 {
803 PCB_VIA* via = static_cast<PCB_VIA*>( item );
804
805 m_commit->Modify( via );
806
807 int new_width;
808 int new_drill;
809
810 if( via->GetViaType() == VIATYPE::MICROVIA )
811 {
812 NETCLASS* netClass = via->GetEffectiveNetClass();
813
814 new_width = netClass->GetuViaDiameter();
815 new_drill = netClass->GetuViaDrill();
816 }
817 else
818 {
819 new_width = board()->GetDesignSettings().GetCurrentViaSize();
820 new_drill = board()->GetDesignSettings().GetCurrentViaDrill();
821 }
822
823 via->SetDrill( new_drill );
824 via->SetWidth( new_width );
825 }
826 else if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
827 {
828 PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item );
829
830 wxCHECK( track, 0 );
831
832 m_commit->Modify( track );
833
834 int new_width = board()->GetDesignSettings().GetCurrentTrackWidth();
835 track->SetWidth( new_width );
836 }
837 }
838
839 m_commit->Push( _( "Edit track width/via size" ) );
840
841 if( selection.IsHover() )
842 {
844
845 // Notify other tools of the changes -- This updates the visual ratsnest
847 }
848
849 return 0;
850}
851
852
854{
855 // Store last used fillet radius to allow pressing "enter" if repeat fillet is required
856 static long long filletRadiusIU = 0;
857
859 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
860 {
861 // Iterate from the back so we don't have to worry about removals.
862 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
863 {
864 BOARD_ITEM* item = aCollector[i];
865
866 if( !dynamic_cast<PCB_TRACK*>( item ) )
867 aCollector.Remove( item );
868 }
869 },
870 true /* prompt user regarding locked items */ );
871
872 if( selection.Size() < 2 )
873 {
874 frame()->ShowInfoBarMsg( _( "At least two straight track segments must be selected." ) );
875 return 0;
876 }
877
878 WX_UNIT_ENTRY_DIALOG dia( frame(), _( "Enter fillet radius:" ), _( "Fillet Tracks" ),
879 filletRadiusIU );
880
881 if( dia.ShowModal() == wxID_CANCEL )
882 return 0;
883
884 filletRadiusIU = dia.GetValue();
885
886 if( filletRadiusIU == 0 )
887 {
888 frame()->ShowInfoBarMsg( _( "A radius of zero was entered.\n"
889 "The fillet operation was not performed." ) );
890 return 0;
891 }
892
893 struct FILLET_OP
894 {
895 PCB_TRACK* t1;
896 PCB_TRACK* t2;
897 // Start point of track is modified after PCB_ARC is added, otherwise the end point:
898 bool t1Start = true;
899 bool t2Start = true;
900 };
901
902 std::vector<FILLET_OP> filletOperations;
903 bool operationPerformedOnAtLeastOne = false;
904 bool didOneAttemptFail = false;
905 std::set<PCB_TRACK*> processedTracks;
906
907 for( EDA_ITEM* item : selection )
908 {
909 PCB_TRACK* track = dyn_cast<PCB_TRACK*>( item );
910
911 if( !track || track->Type() != PCB_TRACE_T || track->GetLength() == 0 )
912 {
913 continue;
914 }
915
916 auto processFilletOp =
917 [&]( bool aStartPoint )
918 {
919 std::shared_ptr<CONNECTIVITY_DATA> c = board()->GetConnectivity();
920 VECTOR2I anchor = aStartPoint ? track->GetStart()
921 : track->GetEnd();
922 std::vector<BOARD_CONNECTED_ITEM*> itemsOnAnchor;
923
924 itemsOnAnchor = c->GetConnectedItemsAtAnchor( track, anchor,
927
928 if( itemsOnAnchor.size() > 0
929 && selection.Contains( itemsOnAnchor.at( 0 ) )
930 && itemsOnAnchor.at( 0 )->Type() == PCB_TRACE_T )
931 {
932 PCB_TRACK* trackOther = dyn_cast<PCB_TRACK*>( itemsOnAnchor.at( 0 ) );
933
934 // Make sure we don't fillet the same pair of tracks twice
935 if( processedTracks.find( trackOther ) == processedTracks.end() )
936 {
937 if( itemsOnAnchor.size() == 1 )
938 {
939 FILLET_OP filletOp;
940 filletOp.t1 = track;
941 filletOp.t2 = trackOther;
942 filletOp.t1Start = aStartPoint;
943 filletOp.t2Start = track->IsPointOnEnds( filletOp.t2->GetStart() );
944 filletOperations.push_back( filletOp );
945 }
946 else
947 {
948 // User requested to fillet these two tracks but not possible as
949 // there are other elements connected at that point
950 didOneAttemptFail = true;
951 }
952 }
953 }
954 };
955
956 processFilletOp( true ); // on the start point of track
957 processFilletOp( false ); // on the end point of track
958
959 processedTracks.insert( track );
960 }
961
962 std::vector<BOARD_ITEM*> itemsToAddToSelection;
963
964 for( FILLET_OP filletOp : filletOperations )
965 {
966 PCB_TRACK* track1 = filletOp.t1;
967 PCB_TRACK* track2 = filletOp.t2;
968
969 bool trackOnStart = track1->IsPointOnEnds( track2->GetStart() );
970 bool trackOnEnd = track1->IsPointOnEnds( track2->GetEnd() );
971
972 if( trackOnStart && trackOnEnd )
973 continue; // Ignore duplicate tracks
974
975 if( ( trackOnStart || trackOnEnd ) && track1->GetLayer() == track2->GetLayer() )
976 {
977 SEG t1Seg( track1->GetStart(), track1->GetEnd() );
978 SEG t2Seg( track2->GetStart(), track2->GetEnd() );
979
980 if( t1Seg.ApproxCollinear( t2Seg ) )
981 continue;
982
983 SHAPE_ARC sArc( t1Seg, t2Seg, filletRadiusIU );
984 VECTOR2I t1newPoint, t2newPoint;
985
986 auto setIfPointOnSeg =
987 []( VECTOR2I& aPointToSet, SEG aSegment, VECTOR2I aVecToTest )
988 {
989 VECTOR2I segToVec = aSegment.NearestPoint( aVecToTest ) - aVecToTest;
990
991 // Find out if we are on the segment (minimum precision)
993 {
994 aPointToSet.x = aVecToTest.x;
995 aPointToSet.y = aVecToTest.y;
996 return true;
997 }
998
999 return false;
1000 };
1001
1002 //Do not draw a fillet if the end points of the arc are not within the track segments
1003 if( !setIfPointOnSeg( t1newPoint, t1Seg, sArc.GetP0() )
1004 && !setIfPointOnSeg( t2newPoint, t2Seg, sArc.GetP0() ) )
1005 {
1006 didOneAttemptFail = true;
1007 continue;
1008 }
1009
1010 if( !setIfPointOnSeg( t1newPoint, t1Seg, sArc.GetP1() )
1011 && !setIfPointOnSeg( t2newPoint, t2Seg, sArc.GetP1() ) )
1012 {
1013 didOneAttemptFail = true;
1014 continue;
1015 }
1016
1017 PCB_ARC* tArc = new PCB_ARC( frame()->GetBoard(), &sArc );
1018 tArc->SetLayer( track1->GetLayer() );
1019 tArc->SetWidth( track1->GetWidth() );
1020 tArc->SetNet( track1->GetNet() );
1021 tArc->SetLocked( track1->IsLocked() );
1022 m_commit->Add( tArc );
1023 itemsToAddToSelection.push_back( tArc );
1024
1025 m_commit->Modify( track1 );
1026 m_commit->Modify( track2 );
1027
1028 if( filletOp.t1Start )
1029 track1->SetStart( t1newPoint );
1030 else
1031 track1->SetEnd( t1newPoint );
1032
1033 if( filletOp.t2Start )
1034 track2->SetStart( t2newPoint );
1035 else
1036 track2->SetEnd( t2newPoint );
1037
1038 operationPerformedOnAtLeastOne = true;
1039 }
1040 }
1041
1042 m_commit->Push( _( "Fillet Tracks" ) );
1043
1044 //select the newly created arcs
1045 for( BOARD_ITEM* item : itemsToAddToSelection )
1047
1048 if( !operationPerformedOnAtLeastOne )
1049 frame()->ShowInfoBarMsg( _( "Unable to fillet the selected track segments." ) );
1050 else if( didOneAttemptFail )
1051 frame()->ShowInfoBarMsg( _( "Some of the track segments could not be filleted." ) );
1052
1053 return 0;
1054}
1055
1056
1058{
1059 // Store last used fillet radius to allow pressing "enter" if repeat fillet is required
1060 static long long filletRadiusIU = 0;
1061
1063 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1064 {
1065
1066 std::vector<VECTOR2I> pts;
1067
1068 // Iterate from the back so we don't have to worry about removals.
1069 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1070 {
1071 BOARD_ITEM* item = aCollector[i];
1072
1073 // We've converted the polygon and rectangle to segments, so drop everything
1074 // that isn't a segment at this point
1075 if( !item->IsType( { PCB_SHAPE_LOCATE_SEGMENT_T,
1076 PCB_SHAPE_LOCATE_POLY_T,
1077 PCB_SHAPE_LOCATE_RECT_T } ) )
1078 {
1079 aCollector.Remove( item );
1080 }
1081 }
1082 },
1083 true /* prompt user regarding locked items */ );
1084
1085 std::set<PCB_SHAPE*> lines_to_add;
1086 std::vector<PCB_SHAPE*> items_to_remove;
1087
1088 for( EDA_ITEM* item : selection )
1089 {
1090 std::vector<VECTOR2I> pts;
1091 PCB_SHAPE *graphic = static_cast<PCB_SHAPE*>( item );
1092 PCB_LAYER_ID layer = graphic->GetLayer();
1093 int width = graphic->GetWidth();
1094
1095 if( graphic->GetShape() == SHAPE_T::RECT )
1096 {
1097 items_to_remove.push_back( graphic );
1098 VECTOR2I start( graphic->GetStart() );
1099 VECTOR2I end( graphic->GetEnd() );
1100 pts.emplace_back( start );
1101 pts.emplace_back( VECTOR2I( end.x, start.y ) );
1102 pts.emplace_back( end );
1103 pts.emplace_back( VECTOR2I( start.x, end.y ) );
1104 }
1105
1106 if( graphic->GetShape() == SHAPE_T::POLY )
1107 {
1108 items_to_remove.push_back( graphic );
1109
1110 for( int jj = 0; jj < graphic->GetPolyShape().VertexCount(); ++jj )
1111 pts.emplace_back( graphic->GetPolyShape().CVertex( jj ) );
1112 }
1113
1114 for( size_t jj = 1; jj < pts.size(); ++jj )
1115 {
1116 PCB_SHAPE *line = new PCB_SHAPE( frame()->GetModel(), SHAPE_T::SEGMENT );
1117
1118 line->SetStart( pts[jj - 1] );
1119 line->SetEnd( pts[jj] );
1120 line->SetWidth( width );
1121 line->SetLayer( layer );
1122 lines_to_add.insert( line );
1123 }
1124
1125 if( pts.size() > 1 )
1126 {
1127 PCB_SHAPE *line = new PCB_SHAPE( frame()->GetModel(), SHAPE_T::SEGMENT );
1128
1129 line->SetStart( pts.back() );
1130 line->SetEnd( pts.front() );
1131 line->SetWidth( width );
1132 line->SetLayer( layer );
1133 lines_to_add.insert( line );
1134 }
1135 }
1136
1137 for( PCB_SHAPE* item : lines_to_add )
1138 selection.Add( item );
1139
1140 for( PCB_SHAPE* item : items_to_remove )
1141 selection.Remove( item );
1142
1143 if( selection.CountType( PCB_SHAPE_LOCATE_SEGMENT_T ) < 2 )
1144 {
1145 frame()->ShowInfoBarMsg( _( "A shape with least two lines must be selected." ) );
1146 return 0;
1147 }
1148
1149 WX_UNIT_ENTRY_DIALOG dia( frame(), _( "Enter fillet radius:" ), _( "Fillet Lines" ),
1150 filletRadiusIU );
1151
1152 if( dia.ShowModal() == wxID_CANCEL )
1153 return 0;
1154
1155 filletRadiusIU = dia.GetValue();
1156
1157 if( filletRadiusIU == 0 )
1158 {
1159 frame()->ShowInfoBarMsg( _( "A radius of zero was entered.\n"
1160 "The fillet operation was not performed." ) );
1161 return 0;
1162 }
1163
1164 bool operationPerformedOnAtLeastOne = false;
1165 bool didOneAttemptFail = false;
1166 std::vector<BOARD_ITEM*> itemsToAddToSelection;
1167
1168 // Only modify one parent in FP editor
1169 if( m_isFootprintEditor )
1170 m_commit->Modify( selection.Front() );
1171
1172 alg::for_all_pairs( selection.begin(), selection.end(), [&]( EDA_ITEM* a, EDA_ITEM* b )
1173 {
1174 PCB_SHAPE* line_a = static_cast<PCB_SHAPE*>( a );
1175 PCB_SHAPE* line_b = static_cast<PCB_SHAPE*>( b );
1176
1177 if( line_a->GetLength() == 0.0 || line_b->GetLength() == 0 )
1178 return;
1179
1180 SEG seg_a( line_a->GetStart(), line_a->GetEnd() );
1181 SEG seg_b( line_b->GetStart(), line_b->GetEnd() );
1182 VECTOR2I* a_pt;
1183 VECTOR2I* b_pt;
1184
1185 if (seg_a.A == seg_b.A)
1186 {
1187 a_pt = &seg_a.A;
1188 b_pt = &seg_b.A;
1189 }
1190 else if (seg_a.A == seg_b.B)
1191 {
1192 a_pt = &seg_a.A;
1193 b_pt = &seg_b.B;
1194 }
1195 else if (seg_a.B == seg_b.A)
1196 {
1197 a_pt = &seg_a.B;
1198 b_pt = &seg_b.A;
1199 }
1200 else if (seg_a.B == seg_b.B)
1201 {
1202 a_pt = &seg_a.B;
1203 b_pt = &seg_b.B;
1204 }
1205 else
1206 return;
1207
1208
1209 SHAPE_ARC sArc( seg_a, seg_b, filletRadiusIU );
1210 VECTOR2I t1newPoint, t2newPoint;
1211
1212 auto setIfPointOnSeg =
1213 []( VECTOR2I& aPointToSet, SEG aSegment, VECTOR2I aVecToTest )
1214 {
1215 VECTOR2I segToVec = aSegment.NearestPoint( aVecToTest ) - aVecToTest;
1216
1217 // Find out if we are on the segment (minimum precision)
1219 {
1220 aPointToSet.x = aVecToTest.x;
1221 aPointToSet.y = aVecToTest.y;
1222 return true;
1223 }
1224
1225 return false;
1226 };
1227
1228 //Do not draw a fillet if the end points of the arc are not within the track segments
1229 if( !setIfPointOnSeg( t1newPoint, seg_a, sArc.GetP0() )
1230 && !setIfPointOnSeg( t2newPoint, seg_b, sArc.GetP0() ) )
1231 {
1232 didOneAttemptFail = true;
1233 return;
1234 }
1235
1236 if( !setIfPointOnSeg( t1newPoint, seg_a, sArc.GetP1() )
1237 && !setIfPointOnSeg( t2newPoint, seg_b, sArc.GetP1() ) )
1238 {
1239 didOneAttemptFail = true;
1240 return;
1241 }
1242
1243 PCB_SHAPE* tArc = new PCB_SHAPE( frame()->GetBoard(), SHAPE_T::ARC );
1244
1245 tArc->SetArcGeometry( sArc.GetP0(), sArc.GetArcMid(), sArc.GetP1() );
1246 tArc->SetWidth( line_a->GetWidth() );
1247 tArc->SetLayer( line_a->GetLayer() );
1248 tArc->SetLocked( line_a->IsLocked() );
1249
1250 if( lines_to_add.count( line_a ) )
1251 {
1252 lines_to_add.erase( line_a );
1253 itemsToAddToSelection.push_back( line_a );
1254 }
1255 else if( !m_isFootprintEditor )
1256 {
1257 m_commit->Modify( line_a );
1258 }
1259
1260 if( lines_to_add.count( line_b ) )
1261 {
1262 lines_to_add.erase( line_b );
1263 itemsToAddToSelection.push_back( line_b );
1264 }
1265 else if( !m_isFootprintEditor )
1266 {
1267 m_commit->Modify( line_b );
1268 }
1269
1270 itemsToAddToSelection.push_back( tArc );
1271 *a_pt = t1newPoint;
1272 *b_pt = t2newPoint;
1273 line_a->SetStart( seg_a.A );
1274 line_a->SetEnd( seg_a.B );
1275 line_b->SetStart( seg_b.A );
1276 line_b->SetEnd( seg_b.B );
1277
1278 operationPerformedOnAtLeastOne = true;
1279
1280 } );
1281
1282 for( auto item : items_to_remove )
1283 {
1284 m_commit->Remove( item );
1285 m_selectionTool->RemoveItemFromSel( item, true );
1286 }
1287
1288 //select the newly created arcs
1289 for( BOARD_ITEM* item : itemsToAddToSelection )
1290 {
1291 m_commit->Add( item );
1292 m_selectionTool->AddItemToSel( item, true );
1293 }
1294
1295 if( !items_to_remove.empty() )
1296 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1297
1298 if( !itemsToAddToSelection.empty() )
1299 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1300
1301 // Notify other tools of the changes
1302 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1303
1304 m_commit->Push( _( "Fillet Lines" ) );
1305
1306 if( !operationPerformedOnAtLeastOne )
1307 frame()->ShowInfoBarMsg( _( "Unable to fillet the selected lines." ) );
1308 else if( didOneAttemptFail )
1309 frame()->ShowInfoBarMsg( _( "Some of the lines could not be filleted." ) );
1310
1311 return 0;
1312}
1313
1314
1316{
1317 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1319 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1320 {
1321 } );
1322
1323 // Tracks & vias are treated in a special way:
1324 if( ( SELECTION_CONDITIONS::OnlyTypes( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } ) )( selection ) )
1325 {
1326 DIALOG_TRACK_VIA_PROPERTIES dlg( editFrame, selection, *m_commit );
1327 dlg.ShowQuasiModal(); // QuasiModal required for NET_SELECTOR
1328 }
1329 else if( selection.Size() == 1 )
1330 {
1331 // Display properties dialog
1332 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
1333
1334 // Do not handle undo buffer, it is done by the properties dialogs
1335 editFrame->OnEditItemRequest( item );
1336
1337 // Notify other tools of the changes
1339 }
1340 else if( selection.Size() == 0 && getView()->IsLayerVisible( LAYER_DRAWINGSHEET ) )
1341 {
1342 DS_PROXY_VIEW_ITEM* ds = editFrame->GetCanvas()->GetDrawingSheet();
1343 VECTOR2D cursorPos = getViewControls()->GetCursorPosition( false );
1344
1345 if( ds && ds->HitTestDrawingSheetItems( getView(), cursorPos ) )
1347 else
1349 }
1350
1351 if( selection.IsHover() )
1352 {
1354 }
1355 else
1356 {
1357 // Check for items becoming invisible and drop them from the selection.
1358
1359 PCB_SELECTION selCopy = selection;
1360 LSET visible = editFrame->GetBoard()->GetVisibleLayers();
1361
1362 for( EDA_ITEM* eda_item : selCopy )
1363 {
1364 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( eda_item );
1365
1366 if( !( item->GetLayerSet() & visible ).any() )
1368 }
1369 }
1370
1371 return 0;
1372}
1373
1374
1375int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
1376{
1377 if( isRouterActive() )
1378 {
1379 wxBell();
1380 return 0;
1381 }
1382
1383 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1384
1385 // Be sure that there is at least one item that we can modify. If nothing was selected before,
1386 // try looking for the stuff under mouse cursor (i.e. KiCad old-style hover selection)
1388 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1389 {
1390 sTool->FilterCollectorForHierarchy( aCollector, true );
1391 sTool->FilterCollectorForMarkers( aCollector );
1392 },
1393 // Prompt user regarding locked items if in board editor and in free-pad-mode (if
1394 // we're not in free-pad mode we delay this until the second RequestSelection()).
1396
1397 if( selection.Empty() )
1398 return 0;
1399
1400 std::optional<VECTOR2I> oldRefPt;
1401 bool is_hover = selection.IsHover(); // N.B. This must be saved before the second
1402 // call to RequestSelection() below
1403
1404 if( selection.HasReferencePoint() )
1405 oldRefPt = selection.GetReferencePoint();
1406
1407 // Now filter out pads if not in free pads mode. We cannot do this in the first
1408 // RequestSelection() as we need the reference point when a pad is the selection front.
1410 {
1411 selection = m_selectionTool->RequestSelection(
1412 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1413 {
1414 sTool->FilterCollectorForMarkers( aCollector );
1415 sTool->FilterCollectorForHierarchy( aCollector, true );
1416 sTool->FilterCollectorForFreePads( aCollector );
1417 },
1418 true /* prompt user regarding locked items */ );
1419 }
1420
1421 // Did we filter everything out? If so, don't try to operate further
1422 if( selection.Empty() )
1423 return 0;
1424
1425 updateModificationPoint( selection );
1426
1427 VECTOR2I refPt = selection.GetReferencePoint();
1428 EDA_ANGLE rotateAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *editFrame, aEvent );
1429
1430 // Calculate view bounding box
1431 BOX2I viewBBox = selection.Front()->ViewBBox();
1432
1433 for( EDA_ITEM* item : selection )
1434 viewBBox.Merge( item->ViewBBox() );
1435
1436 // Check if the view bounding box will go out of bounds
1437 VECTOR2D rotPos = viewBBox.GetPosition();
1438 VECTOR2D rotEnd = viewBBox.GetEnd();
1439
1440 RotatePoint( &rotPos.x, &rotPos.y, refPt.x, refPt.y, rotateAngle );
1441 RotatePoint( &rotEnd.x, &rotEnd.y, refPt.x, refPt.y, rotateAngle );
1442
1443 typedef std::numeric_limits<int> coord_limits;
1444
1445 int max = coord_limits::max() - COORDS_PADDING;
1446 int min = -max;
1447
1448 bool outOfBounds = rotPos.x < min || rotPos.x > max || rotPos.y < min || rotPos.y > max
1449 || rotEnd.x < min || rotEnd.x > max || rotEnd.y < min || rotEnd.y > max;
1450
1451 if( !outOfBounds )
1452 {
1453 // When editing footprints, all items have the same parent
1454 if( IsFootprintEditor() )
1455 m_commit->Modify( selection.Front() );
1456
1457 for( EDA_ITEM* item : selection )
1458 {
1459 if( !item->IsNew() && !IsFootprintEditor() )
1460 {
1461 m_commit->Modify( item );
1462
1463 // If rotating a group, record position of all the descendants for undo
1464 if( item->Type() == PCB_GROUP_T )
1465 {
1466 static_cast<PCB_GROUP*>( item )->RunOnDescendants( [&]( BOARD_ITEM* bItem )
1467 {
1468 m_commit->Modify( bItem );
1469 });
1470 }
1471 }
1472
1473 static_cast<BOARD_ITEM*>( item )->Rotate( refPt, rotateAngle );
1474 }
1475
1476 if( !m_dragging )
1477 m_commit->Push( _( "Rotate" ) );
1478
1479 if( is_hover && !m_dragging )
1481
1483
1484 if( m_dragging )
1486 }
1487
1488 // Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
1489 // to this now invalid reference
1490 if( oldRefPt )
1491 selection.SetReferencePoint( *oldRefPt );
1492 else
1493 selection.ClearReferencePoint();
1494
1495 return 0;
1496}
1497
1498
1502static VECTOR2I mirrorPointX( const VECTOR2I& aPoint, const VECTOR2I& aMirrorPoint )
1503{
1504 VECTOR2I mirrored = aPoint;
1505
1506 mirrored.x -= aMirrorPoint.x;
1507 mirrored.x = -mirrored.x;
1508 mirrored.x += aMirrorPoint.x;
1509
1510 return mirrored;
1511}
1512
1513
1517static VECTOR2I mirrorPointY( const VECTOR2I& aPoint, const VECTOR2I& aMirrorPoint )
1518{
1519 VECTOR2I mirrored = aPoint;
1520
1521 mirrored.y -= aMirrorPoint.y;
1522 mirrored.y = -mirrored.y;
1523 mirrored.y += aMirrorPoint.y;
1524
1525 return mirrored;
1526}
1527
1528
1532static void mirrorPadX( PAD& aPad, const VECTOR2I& aMirrorPoint )
1533{
1534 if( aPad.GetShape() == PAD_SHAPE::CUSTOM )
1535 aPad.FlipPrimitives( true ); // mirror primitives left to right
1536
1537 VECTOR2I tmpPt = mirrorPointX( aPad.GetPosition(), aMirrorPoint );
1538 aPad.SetPosition( tmpPt );
1539
1540 tmpPt = aPad.GetOffset();
1541 tmpPt.x = -tmpPt.x;
1542 aPad.SetOffset( tmpPt );
1543
1544 auto tmpz = aPad.GetDelta();
1545 tmpz.x = -tmpz.x;
1546 aPad.SetDelta( tmpz );
1547
1548 aPad.SetOrientation( -aPad.GetOrientation() );
1549}
1550
1551
1555static void mirrorPadY( PAD& aPad, const VECTOR2I& aMirrorPoint )
1556{
1557 if( aPad.GetShape() == PAD_SHAPE::CUSTOM )
1558 aPad.FlipPrimitives( false ); // mirror primitives top to bottom
1559
1560 VECTOR2I tmpPt = mirrorPointY( aPad.GetPosition(), aMirrorPoint );
1561 aPad.SetPosition( tmpPt );
1562
1563 tmpPt = aPad.GetOffset();
1564 tmpPt.y = -tmpPt.y;
1565 aPad.SetOffset( tmpPt );
1566
1567 auto tmpz = aPad.GetDelta();
1568 tmpz.y = -tmpz.y;
1569 aPad.SetDelta( tmpz );
1570
1571 aPad.SetOrientation( -aPad.GetOrientation() );
1572}
1573
1574
1575const std::vector<KICAD_T> EDIT_TOOL::MirrorableItems = {
1577 PCB_TEXT_T,
1579 PCB_ZONE_T,
1580 PCB_PAD_T,
1582 PCB_ARC_T,
1583 PCB_VIA_T,
1584};
1585
1586int EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
1587{
1588 if( isRouterActive() )
1589 {
1590 wxBell();
1591 return 0;
1592 }
1593
1595 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1596 {
1597 sTool->FilterCollectorForMarkers( aCollector );
1598 sTool->FilterCollectorForHierarchy( aCollector, true );
1599 sTool->FilterCollectorForFreePads( aCollector );
1600 },
1601 !m_dragging /* prompt user regarding locked items */ );
1602
1603 if( selection.Empty() )
1604 return 0;
1605
1606 updateModificationPoint( selection );
1607 VECTOR2I mirrorPoint = selection.GetReferencePoint();
1608
1609 // When editing footprints, all items have the same parent
1610 if( IsFootprintEditor() )
1611 m_commit->Modify( selection.Front() );
1612
1613 // Set the mirroring options.
1614 // Unfortunately, the mirror function do not have the same parameter for all items
1615 // So we need these 2 parameters to avoid mistakes
1616 bool mirrorLeftRight = true;
1617 bool mirrorAroundXaxis = false;
1618
1619 if( aEvent.IsAction( &PCB_ACTIONS::mirrorV ) )
1620 {
1621 mirrorLeftRight = false;
1622 mirrorAroundXaxis = true;
1623 }
1624
1625 for( EDA_ITEM* item : selection )
1626 {
1627 if( !item->IsType( MirrorableItems ) )
1628 continue;
1629
1630 if( !item->IsNew() && !IsFootprintEditor() )
1631 m_commit->Modify( item );
1632
1633 // modify each object as necessary
1634 switch( item->Type() )
1635 {
1636 case PCB_SHAPE_T:
1637 static_cast<PCB_SHAPE*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
1638 break;
1639
1640 case PCB_ZONE_T:
1641 static_cast<ZONE*>( item )->Mirror( mirrorPoint, mirrorLeftRight );
1642 break;
1643
1644 case PCB_TEXT_T:
1645 static_cast<PCB_TEXT*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
1646 break;
1647
1648 case PCB_TEXTBOX_T:
1649 static_cast<PCB_TEXTBOX*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
1650 break;
1651
1652 case PCB_PAD_T:
1653 if( mirrorLeftRight )
1654 mirrorPadX( *static_cast<PAD*>( item ), mirrorPoint );
1655 else
1656 mirrorPadY( *static_cast<PAD*>( item ), mirrorPoint );
1657
1658 break;
1659
1660 case PCB_TRACE_T:
1661 case PCB_ARC_T:
1662 case PCB_VIA_T:
1663 static_cast<PCB_TRACK*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
1664 break;
1665
1666 default:
1667 // it's likely the commit object is wrong if you get here
1668 // Unsure if PCB_GROUP_T needs special attention here.
1669 assert( false );
1670 break;
1671 }
1672 }
1673
1674 if( !m_dragging )
1675 m_commit->Push( _( "Mirror" ) );
1676
1677 if( selection.IsHover() && !m_dragging )
1679
1681
1682 if( m_dragging )
1684
1685 return 0;
1686}
1687
1688
1689int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent )
1690{
1691 if( isRouterActive() )
1692 {
1693 wxBell();
1694 return 0;
1695 }
1696
1698 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1699 {
1700 sTool->FilterCollectorForMarkers( aCollector );
1701 sTool->FilterCollectorForHierarchy( aCollector, true );
1702 sTool->FilterCollectorForFreePads( aCollector );
1703 },
1704 !m_dragging /* prompt user regarding locked items */ );
1705
1706 if( selection.Empty() )
1707 return 0;
1708
1709 std::optional<VECTOR2I> oldRefPt;
1710
1711 if( selection.HasReferencePoint() )
1712 oldRefPt = selection.GetReferencePoint();
1713
1714 updateModificationPoint( selection );
1715
1716 // Flip around the anchor for footprints, and the bounding box center for board items
1717 VECTOR2I refPt = IsFootprintEditor() ? VECTOR2I( 0, 0 ) : selection.GetCenter();
1718
1719 // If only one item selected, flip around the selection or item anchor point (instead
1720 // of the bounding box center) to avoid moving the item anchor
1721 if( selection.GetSize() == 1 )
1722 {
1723 if( m_dragging && selection.HasReferencePoint() )
1724 refPt = selection.GetReferencePoint();
1725 else
1726 refPt = static_cast<BOARD_ITEM*>( selection.GetItem( 0 ) )->GetPosition();
1727 }
1728
1729 bool leftRight = frame()->GetPcbNewSettings()->m_FlipLeftRight;
1730
1731 // When editing footprints, all items have the same parent
1732 if( IsFootprintEditor() )
1733 m_commit->Modify( selection.Front() );
1734
1735 for( EDA_ITEM* item : selection )
1736 {
1737 if( !item->IsNew() && !IsFootprintEditor() )
1738 m_commit->Modify( item );
1739
1740 if( item->Type() == PCB_GROUP_T )
1741 {
1742 static_cast<PCB_GROUP*>( item )->RunOnDescendants( [&]( BOARD_ITEM* bItem )
1743 {
1744 m_commit->Modify( bItem );
1745 });
1746 }
1747
1748 static_cast<BOARD_ITEM*>( item )->Flip( refPt, leftRight );
1749 }
1750
1751 if( !m_dragging )
1752 m_commit->Push( _( "Change Side / Flip" ) );
1753
1754 if( selection.IsHover() && !m_dragging )
1756
1758
1759 if( m_dragging )
1761
1762 // Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
1763 // to this now invalid reference
1764 if( oldRefPt )
1765 selection.SetReferencePoint( *oldRefPt );
1766 else
1767 selection.ClearReferencePoint();
1768
1769 return 0;
1770}
1771
1772
1773void EDIT_TOOL::DeleteItems( const PCB_SELECTION& aItems, bool aIsCut )
1774{
1775 // As we are about to remove items, they have to be removed from the selection first
1777
1778 for( EDA_ITEM* item : aItems )
1779 {
1780 BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
1781 FOOTPRINT* parentFP = board_item->GetParentFootprint();
1782 PCB_GROUP* parentGroup = board_item->GetParentGroup();
1783
1784 if( parentGroup )
1785 {
1786 m_commit->Modify( parentGroup );
1787 parentGroup->RemoveItem( board_item );
1788 }
1789
1790 switch( item->Type() )
1791 {
1792 case PCB_TEXT_T:
1793 switch( static_cast<PCB_TEXT*>( board_item )->GetType() )
1794 {
1797 wxASSERT( parentFP );
1798 m_commit->Modify( parentFP );
1799 static_cast<PCB_TEXT*>( board_item )->SetVisible( false );
1800 getView()->Update( board_item );
1801 break;
1802
1804 if( parentFP )
1805 {
1806 m_commit->Modify( parentFP );
1807 getView()->Remove( board_item );
1808 parentFP->Remove( board_item );
1809 }
1810 else
1811 {
1812 m_commit->Remove( board_item );
1813 }
1814 break;
1815
1816 default:
1817 wxFAIL; // Shouldn't get here
1818 break;
1819 }
1820
1821 break;
1822
1823 case PCB_SHAPE_T:
1824 case PCB_TEXTBOX_T:
1825 case PCB_BITMAP_T:
1826 if( parentFP )
1827 {
1828 m_commit->Modify( parentFP );
1829 getView()->Remove( board_item );
1830 parentFP->Remove( board_item );
1831 }
1832 else
1833 {
1834 m_commit->Remove( board_item );
1835 }
1836
1837 break;
1838
1839 case PCB_PAD_T:
1840 if( IsFootprintEditor() || frame()->GetPcbNewSettings()->m_AllowFreePads )
1841 {
1842 m_commit->Modify( parentFP );
1843 getView()->Remove( board_item );
1844 parentFP->Remove( board_item );
1845 }
1846
1847 break;
1848
1849 case PCB_ZONE_T:
1850 if( parentFP )
1851 {
1852 m_commit->Modify( parentFP );
1853 getView()->Remove( board_item );
1854 parentFP->Remove( board_item );
1855 }
1856 else
1857 {
1858 // We process the zones special so that cutouts can be deleted when the delete
1859 // tool is called from inside a cutout when the zone is selected.
1860 // Only interact with cutouts when deleting and a single item is selected
1861 if( !aIsCut && aItems.GetSize() == 1 )
1862 {
1864 ZONE* zone = static_cast<ZONE*>( board_item );
1865
1866 int outlineIdx, holeIdx;
1867
1868 if( zone->HitTestCutout( curPos, &outlineIdx, &holeIdx ) )
1869 {
1870 // Remove the cutout
1871 m_commit->Modify( zone );
1872 zone->RemoveCutout( outlineIdx, holeIdx );
1873 zone->UnFill();
1874
1875 // TODO Refill zone when KiCad supports auto re-fill
1876
1877 // Update the display
1878 zone->HatchBorder();
1879 canvas()->Refresh();
1880
1881 // Restore the selection on the original zone
1883
1884 break;
1885 }
1886 }
1887
1888 // Remove the entire zone otherwise
1889 m_commit->Remove( board_item );
1890 }
1891
1892 break;
1893
1894 case PCB_GROUP_T:
1895 {
1896 PCB_GROUP* group = static_cast<PCB_GROUP*>( board_item );
1897
1898 auto removeItem =
1899 [&]( BOARD_ITEM* bItem )
1900 {
1901 if( bItem->GetParent() && bItem->GetParent()->Type() == PCB_FOOTPRINT_T )
1902 {
1903 // Silently ignore delete of Reference or Value if they happen to be
1904 // in group.
1905 if( bItem->Type() == PCB_TEXT_T )
1906 {
1907 PCB_TEXT* textItem = static_cast<PCB_TEXT*>( bItem );
1908
1909 if( textItem->GetType() != PCB_TEXT::TEXT_is_DIVERS )
1910 return;
1911 }
1912 else if( bItem->Type() == PCB_PAD_T )
1913 {
1914 if( !IsFootprintEditor()
1916 {
1917 return;
1918 }
1919 }
1920
1921 m_commit->Modify( bItem->GetParent() );
1922 getView()->Remove( bItem );
1923 bItem->GetParent()->Remove( bItem );
1924 }
1925 else
1926 {
1927 m_commit->Remove( bItem );
1928 }
1929 };
1930
1931 removeItem( group );
1932
1933 group->RunOnDescendants( [&]( BOARD_ITEM* aDescendant )
1934 {
1935 removeItem( aDescendant );
1936 });
1937 break;
1938 }
1939
1940 default:
1941 m_commit->Remove( board_item );
1942 break;
1943 }
1944 }
1945
1946 // If the entered group has been emptied then leave it.
1947 PCB_GROUP* enteredGroup = m_selectionTool->GetEnteredGroup();
1948
1949 if( enteredGroup && enteredGroup->GetItems().empty() )
1951
1952 if( aIsCut )
1953 m_commit->Push( _( "Cut" ) );
1954 else
1955 m_commit->Push( _( "Delete" ) );
1956}
1957
1958
1959int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent )
1960{
1961 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1962
1963 if( isRouterActive() )
1964 {
1966 return 0;
1967 }
1968
1969 editFrame->PushTool( aEvent );
1970
1971 std::vector<BOARD_ITEM*> lockedItems;
1972 Activate();
1973
1974 // get a copy instead of reference (as we're going to clear the selection before removing items)
1975 PCB_SELECTION selectionCopy;
1978
1979 // If we are in a "Cut" operation, then the copied selection exists already and we want to
1980 // delete exactly that; no more, no fewer. Any filtering for locked items must be done in
1981 // the copyToClipboard() routine.
1982 if( isCut )
1983 {
1984 selectionCopy = m_selectionTool->GetSelection();
1985 }
1986 else
1987 {
1988 // When not in free-pad mode we normally auto-promote selected pads to their parent
1989 // footprints. But this is probably a little too dangerous for a destructive operation,
1990 // so we just do the promotion but not the deletion (allowing for a second delete to do
1991 // it if that's what the user wanted).
1992 selectionCopy = m_selectionTool->RequestSelection(
1993 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1994 {
1995 } );
1996
1997 size_t beforeFPCount = selectionCopy.CountType( PCB_FOOTPRINT_T );
1998
2000 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2001 {
2002 sTool->FilterCollectorForFreePads( aCollector );
2003 } );
2004
2005 if( !selectionCopy.IsHover()
2006 && m_selectionTool->GetSelection().CountType( PCB_FOOTPRINT_T ) > beforeFPCount )
2007 {
2008 wxBell();
2009 canvas()->Refresh();
2010 editFrame->PopTool( aEvent );
2011 return 0;
2012 }
2013
2014 // In "alternative" mode, we expand selected track items to their full connection.
2015 if( isAlt && ( selectionCopy.HasType( PCB_TRACE_T ) || selectionCopy.HasType( PCB_VIA_T ) ) )
2016 {
2018 }
2019
2020 // Finally run RequestSelection() one more time to find out what user wants to do about
2021 // locked objects.
2022 selectionCopy = m_selectionTool->RequestSelection(
2023 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2024 {
2025 sTool->FilterCollectorForFreePads( aCollector );
2026 },
2027 true /* prompt user regarding locked items */ );
2028 }
2029
2030 DeleteItems( selectionCopy, isCut );
2031
2032 editFrame->PopTool( aEvent );
2033 return 0;
2034}
2035
2036
2038{
2039 if( isRouterActive() )
2040 {
2041 wxBell();
2042 return 0;
2043 }
2044
2046 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2047 {
2048 sTool->FilterCollectorForMarkers( aCollector );
2049 sTool->FilterCollectorForHierarchy( aCollector, true );
2050 },
2051 true /* prompt user regarding locked items */ );
2052
2053 if( selection.Empty() )
2054 return 0;
2055
2056 VECTOR2I translation;
2057 EDA_ANGLE rotation;
2058 ROTATION_ANCHOR rotationAnchor = selection.Size() > 1 ? ROTATE_AROUND_SEL_CENTER
2060
2061 // TODO: Implement a visible bounding border at the edge
2062 BOX2I sel_box = selection.GetBoundingBox();
2063
2064 DIALOG_MOVE_EXACT dialog( frame(), translation, rotation, rotationAnchor, sel_box );
2065 int ret = dialog.ShowModal();
2066
2067 if( ret == wxID_OK )
2068 {
2069 EDA_ANGLE angle = rotation;
2070 VECTOR2I rp = selection.GetCenter();
2071 VECTOR2I selCenter( rp.x, rp.y );
2072
2073 // Make sure the rotation is from the right reference point
2074 selCenter += translation;
2075
2076 if( !frame()->GetPcbNewSettings()->m_Display.m_DisplayInvertYAxis )
2077 rotation = -rotation;
2078
2079 // When editing footprints, all items have the same parent
2080 if( IsFootprintEditor() )
2081 m_commit->Modify( selection.Front() );
2082
2083 for( EDA_ITEM* selItem : selection )
2084 {
2085 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selItem );
2086
2087 if( !item->IsNew() && !IsFootprintEditor() )
2088 {
2089 m_commit->Modify( item );
2090
2091 if( item->Type() == PCB_GROUP_T )
2092 {
2093 PCB_GROUP* group = static_cast<PCB_GROUP*>( item );
2094
2095 group->RunOnDescendants( [&]( BOARD_ITEM* bItem )
2096 {
2097 m_commit->Modify( bItem );
2098 });
2099 }
2100 }
2101
2102 if( !item->GetParent() || !item->GetParent()->IsSelected() )
2103 item->Move( translation );
2104
2105 switch( rotationAnchor )
2106 {
2108 item->Rotate( item->GetPosition(), angle );
2109 break;
2111 item->Rotate( selCenter, angle );
2112 break;
2114 item->Rotate( frame()->GetScreen()->m_LocalOrigin, angle );
2115 break;
2117 item->Rotate( board()->GetDesignSettings().GetAuxOrigin(), angle );
2118 break;
2119 }
2120
2121 if( !m_dragging )
2122 getView()->Update( item );
2123 }
2124
2125 m_commit->Push( _( "Move exact" ) );
2126
2127 if( selection.IsHover() )
2129
2131
2132 if( m_dragging )
2134 }
2135
2136 return 0;
2137}
2138
2139
2141{
2142 if( isRouterActive() )
2143 {
2144 wxBell();
2145 return 0;
2146 }
2147
2148 bool increment = aEvent.IsAction( &PCB_ACTIONS::duplicateIncrement );
2149
2150 // Be sure that there is at least one item that we can modify
2152 []( const VECTOR2I&, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2153 {
2154 sTool->FilterCollectorForMarkers( aCollector );
2155 sTool->FilterCollectorForHierarchy( aCollector, true );
2156 } );
2157
2158 if( selection.Empty() )
2159 return 0;
2160
2161 // we have a selection to work on now, so start the tool process
2162 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2163
2164 // If the selection was given a hover, we do not keep the selection after completion
2165 bool is_hover = selection.IsHover();
2166
2167 std::vector<BOARD_ITEM*> new_items;
2168 new_items.reserve( selection.Size() );
2169
2170 // Each selected item is duplicated and pushed to new_items list
2171 // Old selection is cleared, and new items are then selected.
2172 for( EDA_ITEM* item : selection )
2173 {
2174 BOARD_ITEM* dupe_item = nullptr;
2175 BOARD_ITEM* orig_item = static_cast<BOARD_ITEM*>( item );
2176
2178 {
2179 FOOTPRINT* parentFootprint = editFrame->GetBoard()->GetFirstFootprint();
2180 dupe_item = parentFootprint->DuplicateItem( orig_item );
2181
2182 if( increment && dupe_item->Type() == PCB_PAD_T
2183 && static_cast<PAD*>( dupe_item )->CanHaveNumber() )
2184 {
2185 PAD_TOOL* padTool = m_toolMgr->GetTool<PAD_TOOL>();
2186 wxString padNumber = padTool->GetLastPadNumber();
2187 padNumber = parentFootprint->GetNextPadNumber( padNumber );
2188 padTool->SetLastPadNumber( padNumber );
2189 static_cast<PAD*>( dupe_item )->SetNumber( padNumber );
2190 }
2191 }
2192 else if( FOOTPRINT* parentFootprint = orig_item->GetParentFootprint() )
2193 {
2194 m_commit->Modify( parentFootprint );
2195 dupe_item = parentFootprint->DuplicateItem( orig_item, true /* add to parent */ );
2196 }
2197 else
2198 {
2199 switch( orig_item->Type() )
2200 {
2201 case PCB_FOOTPRINT_T:
2202 case PCB_TEXT_T:
2203 case PCB_TEXTBOX_T:
2204 case PCB_BITMAP_T:
2205 case PCB_SHAPE_T:
2206 case PCB_TRACE_T:
2207 case PCB_ARC_T:
2208 case PCB_VIA_T:
2209 case PCB_ZONE_T:
2210 case PCB_TARGET_T:
2211 case PCB_DIM_ALIGNED_T:
2212 case PCB_DIM_CENTER_T:
2213 case PCB_DIM_RADIAL_T:
2215 case PCB_DIM_LEADER_T:
2216 dupe_item = orig_item->Duplicate();
2217 break;
2218
2219 case PCB_GROUP_T:
2220 dupe_item = static_cast<PCB_GROUP*>( orig_item )->DeepDuplicate();
2221 break;
2222
2223 default:
2224 wxASSERT_MSG( false, wxString::Format( wxT( "Unhandled item type %d" ),
2225 orig_item->Type() ) );
2226 break;
2227 }
2228 }
2229
2230 if( dupe_item )
2231 {
2232 if( dupe_item->Type() == PCB_GROUP_T )
2233 {
2234 static_cast<PCB_GROUP*>( dupe_item )->RunOnDescendants(
2235 [&]( BOARD_ITEM* bItem )
2236 {
2237 m_commit->Add( bItem );
2238 });
2239 }
2240
2241 // Clear the selection flag here, otherwise the PCB_SELECTION_TOOL
2242 // will not properly select it later on
2243 dupe_item->ClearSelected();
2244
2245 new_items.push_back( dupe_item );
2246 m_commit->Add( dupe_item );
2247 }
2248 }
2249
2250 // Clear the old selection first
2252
2253 // Select the new items
2254 m_toolMgr->RunAction( PCB_ACTIONS::selectItems, true, &new_items );
2255
2256 // record the new items as added
2257 if( !selection.Empty() )
2258 {
2259 editFrame->DisplayToolMsg( wxString::Format( _( "Duplicated %d item(s)" ),
2260 (int) new_items.size() ) );
2261
2262 // TODO(ISM): This line can't be used to activate the tool until we allow multiple
2263 // activations.
2264 // m_toolMgr->RunAction( PCB_ACTIONS::move, true );
2265 // Instead we have to create the event and call the tool's function
2266 // directly
2267
2268 // If items were duplicated, pick them up
2269 // this works well for "dropping" copies around and pushes the commit
2271 Move( evt );
2272
2273 // Deslect the duplicated item if we originally started as a hover selection
2274 if( is_hover )
2276 }
2277
2278 return 0;
2279}
2280
2281
2283{
2284 if( isRouterActive() )
2285 {
2286 wxBell();
2287 return 0;
2288 }
2289
2290 // Be sure that there is at least one item that we can modify
2292 []( const VECTOR2I&, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2293 {
2294 sTool->FilterCollectorForMarkers( aCollector );
2295 sTool->FilterCollectorForHierarchy( aCollector, true );
2296 } );
2297
2298 if( selection.Empty() )
2299 return 0;
2300
2301 // we have a selection to work on now, so start the tool process
2302 PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
2303 ARRAY_CREATOR array_creator( *editFrame, m_isFootprintEditor, selection, m_toolMgr );
2304 array_creator.Invoke();
2305
2306 return 0;
2307}
2308
2309
2311 PCB_SELECTION_TOOL* sTool )
2312{
2313 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
2314 {
2315 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( aCollector[i] );
2316
2317 if( item->Type() != PCB_PAD_T )
2318 aCollector.Remove( i );
2319 }
2320}
2321
2322
2324 PCB_SELECTION_TOOL* sTool )
2325{
2326 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
2327 {
2328 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( aCollector[i] );
2329
2330 if( item->Type() != PCB_FOOTPRINT_T )
2331 aCollector.Remove( i );
2332 }
2333}
2334
2335
2337{
2338 if( m_dragging && aSelection.HasReferencePoint() )
2339 return false;
2340
2341 // Can't modify an empty group
2342 if( aSelection.Empty() )
2343 return false;
2344
2345 // When there is only one item selected, the reference point is its position...
2346 if( aSelection.Size() == 1 )
2347 {
2348 auto item = static_cast<BOARD_ITEM*>( aSelection.Front() );
2349 auto pos = item->GetPosition();
2350 aSelection.SetReferencePoint( VECTOR2I( pos.x, pos.y ) );
2351 }
2352 // ...otherwise modify items with regard to the grid-snapped center position
2353 else
2354 {
2355 PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
2356 aSelection.SetReferencePoint( grid.BestSnapAnchor( aSelection.GetCenter(), nullptr ) );
2357 }
2358
2359 return true;
2360}
2361
2362
2363bool EDIT_TOOL::pickReferencePoint( const wxString& aTooltip, const wxString& aSuccessMessage,
2364 const wxString& aCanceledMessage, VECTOR2I& aReferencePoint )
2365{
2367 std::optional<VECTOR2I> pickedPoint;
2368 bool done = false;
2369
2370 m_statusPopup->SetText( aTooltip );
2371
2373 picker->SetSnapping( true );
2374
2375 picker->SetClickHandler(
2376 [&]( const VECTOR2D& aPoint ) -> bool
2377 {
2378 pickedPoint = aPoint;
2379
2380 if( !aSuccessMessage.empty() )
2381 {
2382 m_statusPopup->SetText( aSuccessMessage );
2383 m_statusPopup->Expire( 800 );
2384 }
2385 else
2386 {
2387 m_statusPopup->Hide();
2388 }
2389
2390 return false; // we don't need any more points
2391 } );
2392
2393 picker->SetMotionHandler(
2394 [&]( const VECTOR2D& aPos )
2395 {
2396 m_statusPopup->Move( wxGetMousePosition() + wxPoint( 20, -50 ) );
2397 } );
2398
2399 picker->SetCancelHandler(
2400 [&]()
2401 {
2402 if( !aCanceledMessage.empty() )
2403 {
2404 m_statusPopup->SetText( aCanceledMessage );
2405 m_statusPopup->Expire( 800 );
2406 }
2407 else
2408 {
2409 m_statusPopup->Hide();
2410 }
2411 } );
2412
2413 picker->SetFinalizeHandler(
2414 [&]( const int& aFinalState )
2415 {
2416 done = true;
2417 } );
2418
2419 m_statusPopup->Move( wxGetMousePosition() + wxPoint( 20, -50 ) );
2420 m_statusPopup->Popup();
2421 canvas()->SetStatusPopup( m_statusPopup->GetPanel() );
2422
2424 m_toolMgr->RunAction( ACTIONS::pickerSubTool, true, (void*) &subtoolEvent );
2425
2426 while( !done )
2427 {
2428 // Pass events unless we receive a null event, then we must shut down
2429 if( TOOL_EVENT* evt = Wait() )
2430 evt->SetPassEvent();
2431 else
2432 break;
2433 }
2434
2435 // Ensure statusPopup is hidden after use and before deleting it:
2436 canvas()->SetStatusPopup( nullptr );
2437 m_statusPopup->Hide();
2438
2439 if( pickedPoint )
2440 aReferencePoint = *pickedPoint;
2441
2442 return pickedPoint.has_value();
2443}
2444
2445
2447{
2448 CLIPBOARD_IO io;
2450 getEditFrame<PCB_BASE_EDIT_FRAME>()->GetMagneticItemsSettings() );
2451 TOOL_EVENT selectReferencePoint( aEvent.Category(), aEvent.Action(),
2452 "pcbnew.InteractiveEdit.selectReferencePoint",
2453 TOOL_ACTION_SCOPE::AS_GLOBAL );
2454
2455 frame()->PushTool( selectReferencePoint );
2456 Activate();
2457
2459 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2460 {
2461 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
2462 {
2463 BOARD_ITEM* item = aCollector[i];
2464
2465 // We can't copy both a footprint and its text in the same operation, so if
2466 // both are selected, remove the text
2467 if( item->Type() == PCB_TEXT_T && aCollector.HasItem( item->GetParentFootprint() ) )
2468 aCollector.Remove( item );
2469 }
2470 },
2471
2472 // Prompt user regarding locked items.
2473 aEvent.IsAction( &ACTIONS::cut ) && !m_isFootprintEditor );
2474
2475 if( !selection.Empty() )
2476 {
2477 std::vector<BOARD_ITEM*> items;
2478
2479 for( EDA_ITEM* item : selection )
2480 items.push_back( static_cast<BOARD_ITEM*>( item ) );
2481
2482 VECTOR2I refPoint;
2483
2485 {
2486 if( !pickReferencePoint( _( "Select reference point for the copy..." ),
2487 _( "Selection copied" ),
2488 _( "Copy canceled" ),
2489 refPoint ) )
2490 {
2491 frame()->PopTool( selectReferencePoint );
2492 return 0;
2493 }
2494 }
2495 else
2496 {
2497 refPoint = grid.BestDragOrigin( getViewControls()->GetCursorPosition(), items );
2498 }
2499
2500 selection.SetReferencePoint( refPoint );
2501
2502 io.SetBoard( board() );
2503 io.SaveSelection( selection, m_isFootprintEditor );
2504 frame()->SetStatusText( _( "Selection copied" ) );
2505 }
2506
2507 frame()->PopTool( selectReferencePoint );
2508
2509 if( selection.IsHover() )
2511
2512 return 0;
2513}
2514
2515
2517{
2518 if( !copyToClipboard( aEvent ) )
2519 {
2520 // N.B. Setting the CUT flag prevents lock filtering as we only want to delete the items
2521 // that were copied to the clipboard, no more, no fewer. Filtering for locked item, if
2522 // any will be done in the copyToClipboard() routine
2523 TOOL_EVENT evt = aEvent;
2525 Remove( evt );
2526 }
2527
2528 return 0;
2529}
2530
2531
2533{
2537}
2538
2539
2541{
2543 Go( &EDIT_TOOL::Move, PCB_ACTIONS::move.MakeEvent() );
2549 Go( &EDIT_TOOL::Flip, PCB_ACTIONS::flip.MakeEvent() );
2550 Go( &EDIT_TOOL::Remove, ACTIONS::doDelete.MakeEvent() );
2558 Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorH.MakeEvent() );
2559 Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorV.MakeEvent() );
2560 Go( &EDIT_TOOL::Swap, PCB_ACTIONS::swap.MakeEvent() );
2565
2568 Go( &EDIT_TOOL::cutToClipboard, ACTIONS::cut.MakeEvent() );
2569}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
static TOOL_ACTION paste
Definition: actions.h:69
static TOOL_ACTION pickerSubTool
Definition: actions.h:160
static TOOL_ACTION copy
Definition: actions.h:68
static TOOL_ACTION pasteSpecial
Definition: actions.h:70
static TOOL_ACTION pageSettings
Definition: actions.h:56
@ CURSOR_CLICK
Definition: actions.h:192
static TOOL_ACTION undo
Definition: actions.h:65
static TOOL_ACTION duplicate
Definition: actions.h:72
static TOOL_ACTION doDelete
Definition: actions.h:73
REMOVE_FLAGS
Definition: actions.h:196
static TOOL_ACTION cut
Definition: actions.h:67
static TOOL_ACTION selectAll
Definition: actions.h:71
void SetTitle(const wxString &aTitle) override
Set title for the menu.
Definition: action_menu.cpp:87
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
Definition: action_menu.cpp:73
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
VECTOR2D m_LocalOrigin
Relative Screen cursor coordinate (on grid) in user units.
Definition: base_screen.h:90
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
const VECTOR2I & GetAuxOrigin()
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:71
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:196
void SetParentGroup(PCB_GROUP *aGroup)
Definition: board_item.h:84
void SwapItemData(BOARD_ITEM *aImage)
Swap data between aItem and aImage.
Definition: board_item.cpp:167
virtual void SetLocked(bool aLocked)
Definition: board_item.h:270
PCB_GROUP * GetParentGroup() const
Definition: board_item.h:85
virtual void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle)
Rotate this object.
Definition: board_item.cpp:282
virtual BOARD_ITEM * Duplicate() const
Create a copy of this BOARD_ITEM.
Definition: board_item.cpp:185
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition: board_item.h:282
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:230
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:240
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:201
virtual bool IsLocked() const
Definition: board_item.cpp:72
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:176
LSET GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:625
bool BuildConnectivity(PROGRESS_REPORTER *aReporter=nullptr)
Build or rebuild the board connectivity database for the board, especially the list of connected item...
Definition: board.cpp:168
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:405
bool IsEmpty() const
Definition: board.h:363
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:728
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:432
const Vec & GetPosition() const
Definition: box2.h:185
const Vec GetEnd() const
Definition: box2.h:186
BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:589
Represent basic circle geometry with utility geometry functions.
Definition: circle.h:33
VECTOR2I Center
Public to make access simpler.
Definition: circle.h:116
int Radius
Public to make access simpler.
Definition: circle.h:115
CIRCLE & ConstructFromTanTanPt(const SEG &aLineA, const SEG &aLineB, const VECTOR2I &aP)
Construct this circle such that it is tangent to the given segments and passes through the given poin...
Definition: circle.cpp:51
VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute the point on the circumference of the circle that is the closest to aP.
Definition: circle.cpp:197
void SaveSelection(const PCB_SELECTION &selected, bool isFootprintEditor)
void SetBoard(BOARD *aBoard)
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:81
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition: collector.h:109
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
void AddSeparator(int aOrder=ANY_ORDER)
Add a separator to the menu.
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
int ShowQuasiModal()
bool HitTestDrawingSheetItems(KIGFX::VIEW *aView, const VECTOR2I &aPosition)
void ShowInfoBarMsg(const wxString &aMsg, bool aShowCloseButton=false)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an info icon on the left of...
void ShowInfoBarError(const wxString &aErrorMsg, bool aShowCloseButton=false, WX_INFOBAR::MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an error icon on the left o...
void DisplayToolMsg(const wxString &msg) override
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
void SetStatusPopup(wxWindow *aPopup)
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:85
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:232
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:123
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
void ClearSelected()
Definition: eda_item.h:118
bool IsSelected() const
Definition: eda_item.h:106
virtual const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
Definition: eda_item.cpp:254
bool IsNew() const
Definition: eda_item.h:103
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:247
SHAPE_T GetShape() const
Definition: eda_shape.h:113
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:145
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:124
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:120
int GetWidth() const
Definition: eda_shape.h:109
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:149
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:555
void SetWidth(int aWidth)
Definition: eda_shape.h:108
bool isRouterActive() const
Definition: edit_tool.cpp:326
int Drag(const TOOL_EVENT &aEvent)
Invoke the PNS router to drag tracks or do an offline resizing of an arc track if a single arc track ...
Definition: edit_tool.cpp:334
int Flip(const TOOL_EVENT &aEvent)
Rotate currently selected items.
Definition: edit_tool.cpp:1689
int Duplicate(const TOOL_EVENT &aEvent)
Duplicate the current selection and starts a move action.
Definition: edit_tool.cpp:2140
int FilletLines(const TOOL_EVENT &aEvent)
Fillet (i.e.
Definition: edit_tool.cpp:1057
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 Mirror(const TOOL_EVENT &aEvent)
Mirror the current selection.
Definition: edit_tool.cpp:1586
int CreateArray(const TOOL_EVENT &aEvent)
Create an array of the selected items, invoking the array editor dialog to set the options.
Definition: edit_tool.cpp:2282
bool pickReferencePoint(const wxString &aTooltip, const wxString &aSuccessMessage, const wxString &aCanceledMessage, VECTOR2I &aReferencePoint)
Rebuilds the ratsnest for operations that require it outside the commit rebuild.
Definition: edit_tool.cpp:2363
bool Init() override
Init() is called once upon a registration of the tool.
Definition: edit_tool.cpp:104
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
Definition: edit_tool.cpp:80
void DeleteItems(const PCB_SELECTION &aItems, bool aIsCut)
Definition: edit_tool.cpp:1773
bool m_dragging
Definition: edit_tool.h:211
int MoveExact(const TOOL_EVENT &aEvent)
Invoke a dialog box to allow moving of the item by an exact amount.
Definition: edit_tool.cpp:2037
int Move(const TOOL_EVENT &aEvent)
Main loop in which events are handled.
static const unsigned int COORDS_PADDING
Definition: edit_tool.h:216
std::unique_ptr< BOARD_COMMIT > m_commit
Definition: edit_tool.h:210
bool updateModificationPoint(PCB_SELECTION &aSelection)
Definition: edit_tool.cpp:2336
std::unique_ptr< STATUS_TEXT_POPUP > m_statusPopup
Definition: edit_tool.h:214
int DragArcTrack(const TOOL_EVENT &aTrack)
Drag-resize an arc (and change end points of connected straight segments).
Definition: edit_tool.cpp:418
int copyToClipboard(const TOOL_EVENT &aEvent)
Send the current selection to the clipboard by formatting it as a fake pcb see #AppendBoardFromClipbo...
Definition: edit_tool.cpp:2446
int Remove(const TOOL_EVENT &aEvent)
Delete currently selected items.
Definition: edit_tool.cpp:1959
int cutToClipboard(const TOOL_EVENT &aEvent)
Cut the current selection to the clipboard by formatting it as a fake pcb see #AppendBoardFromClipboa...
Definition: edit_tool.cpp:2516
static const std::vector< KICAD_T > MirrorableItems
Definition: edit_tool.h:114
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.
Definition: edit_tool.cpp:2310
VECTOR2I m_cursor
Definition: edit_tool.h:212
bool invokeInlineRouter(int aDragMode)
Definition: edit_tool.cpp:296
void rebuildConnectivity()
Definition: edit_tool.cpp:2532
void setTransitions() override
< Set up handlers for various events.
Definition: edit_tool.cpp:2540
int ChangeTrackWidth(const TOOL_EVENT &aEvent)
Definition: edit_tool.cpp:783
int GetAndPlace(const TOOL_EVENT &aEvent)
Definition: edit_tool.cpp:273
int FilletTracks(const TOOL_EVENT &aEvent)
Fillet (i.e.
Definition: edit_tool.cpp:853
PCB_SELECTION_TOOL * m_selectionTool
Definition: edit_tool.h:209
int Properties(const TOOL_EVENT &aEvent)
Display properties window for the selected object.
Definition: edit_tool.cpp:1315
static void FootprintFilter(const VECTOR2I &, GENERAL_COLLECTOR &aCollector, PCB_SELECTION_TOOL *sTool)
A selection filter which prunes the selection to contain only items of type #PCB_MODULE_T.
Definition: edit_tool.cpp:2323
int Rotate(const TOOL_EVENT &aEvent)
Rotate currently selected items.
Definition: edit_tool.cpp:1375
static const TOOL_EVENT SelectedEvent
Definition: actions.h:207
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:214
static const TOOL_EVENT ConnectivityChangedEvent
Selected item had a property changed (except movement)
Definition: actions.h:211
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:208
void Remove(BOARD_ITEM *aItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: footprint.cpp:628
BOARD_ITEM * DuplicateItem(const BOARD_ITEM *aItem, bool aAddToFootprint=false)
Duplicate a given item within the footprint, optionally adding it to the board.
Definition: footprint.cpp:1770
wxString GetNextPadNumber(const wxString &aLastPadName) const
Return the next available pad number in the footprint.
Definition: footprint.cpp:1880
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:204
static const std::vector< KICAD_T > DraggableItems
A scan list for items that can be dragged.
Definition: collectors.h:270
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 SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:314
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:351
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:1608
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition: view.h:410
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:536
A collection of nets and the parameters used to route or test these nets.
Definition: netclass.h:47
int GetuViaDrill() const
Definition: netclass.h:92
int GetuViaDiameter() const
Definition: netclass.h:88
Tool relating to pads and pad settings.
Definition: pad_tool.h:37
void SetLastPadNumber(const wxString &aPadNumber)
Definition: pad_tool.h:66
wxString GetLastPadNumber() const
Definition: pad_tool.h:65
Definition: pad.h:59
void FlipPrimitives(bool aFlipLeftRight)
Flip (mirror) the primitives left to right or top to bottom, around the anchor position in custom pad...
Definition: pad.cpp:745
VECTOR2I GetPosition() const override
Definition: pad.h:202
void SetOffset(const VECTOR2I &aOffset)
Definition: pad.h:264
const VECTOR2I & GetOffset() const
Definition: pad.h:265
void SetDelta(const VECTOR2I &aSize)
Definition: pad.h:254
void SetPosition(const VECTOR2I &aPos) override
Definition: pad.h:196
const VECTOR2I & GetDelta() const
Definition: pad.h:255
PAD_SHAPE GetShape() const
Definition: pad.h:194
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition: pad.h:362
void SetOrientation(const EDA_ANGLE &aAngle)
Set the rotation angle of the pad.
Definition: pad.cpp:662
static TOOL_ACTION drag45Degree
Definition: pcb_actions.h:166
static TOOL_ACTION duplicateIncrement
Activation of the duplication tool with incrementing (e.g. pad number)
Definition: pcb_actions.h:155
static TOOL_ACTION routerUndoLastSegment
Definition: pcb_actions.h:222
static TOOL_ACTION changeTrackWidth
Update selected tracks & vias to the current track & via dimensions.
Definition: pcb_actions.h:142
static TOOL_ACTION unrouteSelected
Removes all tracks from the selected items to the first pad.
Definition: pcb_actions.h:83
static TOOL_ACTION mirrorH
Mirroring of selected items.
Definition: pcb_actions.h:129
static TOOL_ACTION updateFootprint
Definition: pcb_actions.h:365
static TOOL_ACTION breakTrack
Break a single track into two segments at the cursor.
Definition: pcb_actions.h:164
static TOOL_ACTION getAndPlace
Find an item and start moving.
Definition: pcb_actions.h:516
static TOOL_ACTION properties
Activation of the edit tool.
Definition: pcb_actions.h:149
static TOOL_ACTION editFpInFpEditor
Definition: pcb_actions.h:387
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 swap
Swapping of selected items.
Definition: pcb_actions.h:133
static TOOL_ACTION moveExact
Activation of the exact move tool.
Definition: pcb_actions.h:152
static TOOL_ACTION selectConnection
Select tracks between junctions or expands an existing selection to pads or the entire connection.
Definition: pcb_actions.h:80
static TOOL_ACTION assignNetClass
Definition: pcb_actions.h:340
static TOOL_ACTION packAndMoveFootprints
Pack and start moving selected footprints.
Definition: pcb_actions.h:136
static TOOL_ACTION copyWithReference
copy command with manual reference point selection
Definition: pcb_actions.h:119
static TOOL_ACTION dragFreeAngle
Definition: pcb_actions.h:167
static TOOL_ACTION inspectClearance
Definition: pcb_actions.h:493
static TOOL_ACTION updateLocalRatsnest
Definition: pcb_actions.h:510
static TOOL_ACTION deleteFull
Definition: pcb_actions.h:159
static TOOL_ACTION moveIndividually
move items one-by-one
Definition: pcb_actions.h:113
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition: pcb_actions.h:62
static TOOL_ACTION filletTracks
Fillet (i.e. adds an arc tangent to) all selected straight tracks by a user defined radius.
Definition: pcb_actions.h:145
static TOOL_ACTION footprintProperties
Definition: pcb_actions.h:421
static TOOL_ACTION filletLines
Definition: pcb_actions.h:146
static TOOL_ACTION changeFootprint
Definition: pcb_actions.h:367
static TOOL_ACTION routerInlineDrag
Activation of the Push and Shove router (inline dragging mode)
Definition: pcb_actions.h:242
static TOOL_ACTION positionRelative
Activation of the position relative tool.
Definition: pcb_actions.h:269
static TOOL_ACTION skip
Definition: pcb_actions.h:139
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 createArray
Tool for creating an array of objects.
Definition: pcb_actions.h:436
static TOOL_ACTION rotateCw
Rotation of selected objects.
Definition: pcb_actions.h:122
static TOOL_ACTION rotateCcw
Definition: pcb_actions.h:123
void SetMid(const VECTOR2I &aMid)
Definition: pcb_track.h:314
EDA_ANGLE GetAngle() const
Definition: pcb_track.cpp:1140
const VECTOR2I & GetMid() const
Definition: pcb_track.h:315
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_track.h:321
Common, abstract interface for edit frames.
virtual void OnEditItemRequest(BOARD_ITEM *aItem)=0
Install the corresponding dialog editor for the given item.
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
PCBNEW_SETTINGS * GetPcbNewSettings() const
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
PCB_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
BOARD * GetBoard() const
DS_PROXY_VIEW_ITEM * GetDrawingSheet() const
void RedrawRatsnest()
Return the bounding box of the view that should be used if model is not valid.
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:51
std::unordered_set< BOARD_ITEM * > & GetItems()
Definition: pcb_group.h:68
bool RemoveItem(BOARD_ITEM *aItem)
Remove item from group.
Definition: pcb_group.cpp:86
Generic tool for picking an item.
The selection tool: currently supports:
void GuessSelectionCandidates(GENERAL_COLLECTOR &aCollector, const VECTOR2I &aWhere) const
Try to guess best selection candidates in case multiple items are clicked, by doing some brain-dead h...
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...
PCB_GROUP * GetEnteredGroup()
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()
void ExitGroup(bool aSelectGroup=false)
Leave the currently-entered group.
@ TEXT_is_DIVERS
Definition: pcb_text.h:52
@ TEXT_is_REFERENCE
Definition: pcb_text.h:50
@ TEXT_is_VALUE
Definition: pcb_text.h:51
TEXT_TYPE GetType() const
Definition: pcb_text.h:89
bool IsFootprintEditor() const
PCB_BASE_EDIT_FRAME * frame() const
KIGFX::VIEW_CONTROLS * controls() const
BOARD * board() const
PCB_DRAW_PANEL_GAL * canvas() const
bool m_isFootprintEditor
const PCB_SELECTION & selection() const
void SetWidth(int aWidth)
Definition: pcb_track.h:106
virtual double GetLength() const
Get the length of the track using the hypotenuse calculation.
Definition: pcb_track.cpp:313
int GetWidth() const
Definition: pcb_track.h:107
void SetEnd(const VECTOR2I &aEnd)
Definition: pcb_track.h:109
void SetStart(const VECTOR2I &aStart)
Definition: pcb_track.h:112
const VECTOR2I & GetStart() const
Definition: pcb_track.h:113
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:110
EDA_ITEM_FLAGS IsPointOnEnds(const VECTOR2I &point, int min_dist=0) const
Return STARTPOINT if point if near (dist = min_dist) start point, ENDPOINT if point if near (dist = m...
Definition: pcb_track.cpp:235
void SetMotionHandler(MOTION_HANDLER aHandler)
Set a handler for mouse motion.
Definition: picker_tool.h:82
void SetClickHandler(CLICK_HANDLER aHandler)
Set a handler for mouse click event.
Definition: picker_tool.h:71
void SetSnapping(bool aSnap)
Definition: picker_tool.h:64
void SetCancelHandler(CANCEL_HANDLER aHandler)
Set a handler for cancel events (ESC or context-menu Cancel).
Definition: picker_tool.h:91
void SetFinalizeHandler(FINALIZE_HANDLER aHandler)
Set a handler for the finalize event.
Definition: picker_tool.h:102
POSITIONING_TOOLS_MENU(TOOL_INTERACTIVE *aTool)
Definition: edit_tool.cpp:91
bool RoutingInProgress()
Returns whether routing is currently active.
bool CanInlineDrag(int aDragMode)
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition: seg.cpp:269
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition: seg.h:210
bool ApproxCollinear(const SEG &aSeg, int aDistanceThreshold=1) const
Definition: seg.cpp:392
VECTOR2I LineProject(const VECTOR2I &aP) const
Compute the perpendicular projection point of aP on a line passing through ends of the segment.
Definition: seg.cpp:312
SEG PerpendicularSeg(const VECTOR2I &aP) const
Compute a segment perpendicular to this one, passing through point aP.
Definition: seg.cpp:207
int Side(const VECTOR2I &aP) const
Determine on which side of directed line passing via segment ends point aP lies.
Definition: seg.h:143
EDA_ANGLE Angle(const SEG &aOther) const
Determine the smallest angle between two segments.
Definition: seg.cpp:97
static SELECTION_CONDITION HasTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if among the selected items there is at least one of a given types.
static SELECTION_CONDITION HasType(KICAD_T aType)
Create a functor that tests if among the selected items there is at least one of a given type.
static bool NotEmpty(const SELECTION &aSelection)
Test if there are any items selected.
static SELECTION_CONDITION MoreThan(int aNumber)
Create a functor that tests if the number of selected items is greater than the value given as parame...
static SELECTION_CONDITION Count(int aNumber)
Create a functor that tests if the number of selected items is equal to the value given as parameter.
static bool ShowAlways(const SELECTION &aSelection)
The default condition function (always returns true).
static SELECTION_CONDITION OnlyTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if the selected items are only of given types.
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
int AddItemToSel(const TOOL_EVENT &aEvent)
virtual KIGFX::VIEW_ITEM * GetItem(unsigned int aIdx) const override
Definition: selection.cpp:75
VECTOR2I GetReferencePoint() const
Definition: selection.h:252
virtual VECTOR2I GetCenter() const
Returns the center point of the selection area bounding box.
Definition: selection.cpp:93
bool IsHover() const
Definition: selection.h:83
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:99
EDA_ITEM * Front() const
Definition: selection.h:208
bool HasType(KICAD_T aType) const
Checks if there is at least one item of requested kind.
Definition: selection.cpp:144
int Size() const
Returns the number of selected parts.
Definition: selection.h:115
void ClearReferencePoint()
Definition: selection.h:265
void SetReferencePoint(const VECTOR2I &aP)
Definition: selection.h:260
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:109
bool HasReferencePoint() const
Definition: selection.h:247
size_t CountType(KICAD_T aType) const
Definition: selection.cpp:156
bool Contains(EDA_ITEM *aItem) const
Definition: selection.cpp:84
virtual BOX2I GetBoundingBox() const
Definition: selection.cpp:133
const VECTOR2I & GetP1() const
Definition: shape_arc.h:113
const VECTOR2I & GetP0() const
Definition: shape_arc.h:112
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the index-th vertex in a given hole outline within a given outline.
static const int MIN_PRECISION_IU
This is the minimum precision for all the points in a shape.
Definition: shape.h:129
virtual void PopTool(const TOOL_EVENT &aEvent)
Pops a tool from the stack.
virtual void PushTool(const TOOL_EVENT &aEvent)
NB: the definition of "tool" is different at the user level.
bool ToolStackIsEmpty()
Definition: tools_holder.h:128
bool IsCurrentTool(const TOOL_ACTION &aAction) const
TOOL_EVENT MakeEvent() const
Return the event associated with the action (i.e.
Definition: tool_action.cpp:72
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:216
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
bool IsToolActive() const
Definition: tool_base.cpp:31
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
@ RUN
Tool is invoked after being inactive.
Definition: tool_base.h:79
Generic, UI-independent tool event.
Definition: tool_event.h:156
T Parameter() const
Return a non-standard parameter assigned to the event.
Definition: tool_event.h:442
TOOL_ACTIONS Action() const
These give a tool a method of informing the TOOL_MANAGER that a particular event should be passed on ...
Definition: tool_event.h:233
void SetParameter(T aParam)
Set a non-standard parameter assigned to the event.
Definition: tool_event.h:460
TOOL_EVENT_CATEGORY Category() const
Returns more specific information about the type of an event.
Definition: tool_event.h:230
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:81
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
TOOL_MENU & GetToolMenu()
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
void Activate()
Run the tool.
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
void PostEvent(const TOOL_EVENT &aEvent)
Put an event to the event queue to be processed at the end of event processing cycle.
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:142
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:44
void RegisterSubMenu(std::shared_ptr< ACTION_MENU > aSubMenu)
Store a submenu of this menu model.
Definition: tool_menu.cpp:50
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:265
An extension of WX_TEXT_ENTRY_DIALOG that uses UNIT_BINDER to request a dimension (e....
long long GetValue()
Returns the value in internal units.
Handle a list of polygons defining a copper zone.
Definition: zone.h:72
bool UnFill()
Removes the zone filling.
Definition: zone.cpp:218
bool HitTestCutout(const VECTOR2I &aRefPos, int *aOutlineIdx=nullptr, int *aHoleIdx=nullptr) const
Test if the given point is contained within a cutout of the zone.
Definition: zone.cpp:524
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:883
void RemoveCutout(int aOutlineIdx, int aHoleIdx)
Remove a cutout from the zone.
Definition: zone.cpp:742
ROTATION_ANCHOR
@ ROTATE_AROUND_USER_ORIGIN
@ ROTATE_AROUND_SEL_CENTER
@ ROTATE_AROUND_AUX_ORIGIN
@ ROTATE_AROUND_ITEM_ANCHOR
#define _(s)
static constexpr EDA_ANGLE & ANGLE_180
Definition: eda_angle.h:433
@ DEGREES_T
Definition: eda_angle.h:31
#define IS_NEW
New item, just created.
static VECTOR2I mirrorPointY(const VECTOR2I &aPoint, const VECTOR2I &aMirrorPoint)
Mirror a point about the vertical axis passing through another point.
Definition: edit_tool.cpp:1517
static void mirrorPadX(PAD &aPad, const VECTOR2I &aMirrorPoint)
Mirror a pad in the vertical axis passing through a point (mirror left to right).
Definition: edit_tool.cpp:1532
static VECTOR2I mirrorPointX(const VECTOR2I &aPoint, const VECTOR2I &aMirrorPoint)
Mirror a point about the vertical axis passing through another point.
Definition: edit_tool.cpp:1502
static void mirrorPadY(PAD &aPad, const VECTOR2I &aMirrorPoint)
Mirror a pad in the vertical axis passing through a point (mirror left to right).
Definition: edit_tool.cpp:1555
static std::vector< KICAD_T > connectedTypes
@ FRAME_PCB_EDITOR
Definition: frame_type.h:40
@ LAYER_DRAWINGSHEET
drawingsheet frame and titleblock
Definition: layer_ids.h:217
@ LAYER_SCHEMATIC_DRAWINGSHEET
Definition: layer_ids.h:386
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
wxString MessageTextFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A helper to convert the double length aValue to a string in inches, millimeters, or unscaled units.
Definition: eda_units.cpp:315
@ DM_ANY
Definition: pns_router.h:77
@ DM_FREE_ANGLE
Definition: pns_router.h:75
SGLIB_API S3DMODEL * GetModel(SCENEGRAPH *aNode)
Function GetModel creates an S3DMODEL representation of aNode (raw data, no transforms)
Definition: ifsg_api.cpp:338
EDA_ANGLE GetEventRotationAngle(const PCB_BASE_EDIT_FRAME &aFrame, const TOOL_EVENT &aEvent)
Function getEventRotationAngle()
void for_all_pairs(_InputIterator __first, _InputIterator __last, _Function __f)
Apply a function to every possible pair of elements of a sequence.
Definition: kicad_algo.h:83
void Flip(T &aValue)
Class to handle a set of BOARD_ITEMs.
BOARD * GetBoard()
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
const double IU_PER_MM
Definition: base_units.h:77
constexpr int mmToIU(double mm) const
Definition: base_units.h:89
@ BUT_LEFT
Definition: tool_event.h:127
const VECTOR2I CalcArcMid(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, bool aMinArcAngle=true)
Return the middle point of an arc, half-way between aStart and aEnd.
Definition: trigo.cpp:163
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Definition: trigo.cpp:183
double GetLineLength(const VECTOR2I &aPointA, const VECTOR2I &aPointB)
Return the length of a line segment defined by aPointA and aPointB.
Definition: trigo.h:188
double EuclideanNorm(const VECTOR2I &vector)
Definition: trigo.h:129
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:101
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:98
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:93
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:99
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:106
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition: typeinfo.h:91
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:103
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:90
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition: typeinfo.h:102
@ PCB_SHAPE_LOCATE_SEGMENT_T
Definition: typeinfo.h:121
@ PCB_SHAPE_LOCATE_RECT_T
Definition: typeinfo.h:122
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:86
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:97
@ PCB_BITMAP_T
class PCB_BITMAP, bitmap on a layer
Definition: typeinfo.h:89
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_SHAPE_LOCATE_POLY_T
Definition: typeinfo.h:125
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:94
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:92
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:100
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588