KiCad PCB EDA Suite
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  * @author Maciej Suminski <maciej.suminski@cern.ch>
6  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
7  * Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.txt for contributors.
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>
30 #include <footprint.h>
31 #include <fp_shape.h>
32 #include <collectors.h>
33 #include <pcb_edit_frame.h>
35 #include <kiway.h>
36 #include <array_creator.h>
37 #include <pcbnew_settings.h>
38 #include <status_popup.h>
40 #include <tool/tool_manager.h>
41 #include <tools/pcb_actions.h>
43 #include <tools/edit_tool.h>
44 #include <tools/pcb_picker_tool.h>
45 #include <tools/tool_event_utils.h>
46 #include <tools/pcb_grid_helper.h>
47 #include <tools/pad_tool.h>
48 #include <pad_naming.h>
49 #include <view/view_controls.h>
52 #include <confirm.h>
53 #include <bitmaps.h>
54 #include <cassert>
55 #include <functional>
56 using namespace std::placeholders;
57 #include "kicad_clipboard.h"
58 #include <wx/hyperlink.h>
59 #include <widgets/infobar.h>
60 #include <router/router_tool.h>
64 #include <board_commit.h>
65 #include <pcb_target.h>
66 #include <zone_filler.h>
67 
68 
70  PCB_TOOL_BASE( "pcbnew.InteractiveEdit" ),
71  m_selectionTool( NULL ),
72  m_dragging( false )
73 {
74 }
75 
76 
78 {
79  m_dragging = false;
80 
81  m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( getEditFrame<PCB_BASE_EDIT_FRAME>() );
82 
83  if( aReason != RUN )
84  m_commit.reset( new BOARD_COMMIT( this ) );
85 }
86 
87 
89  CONDITIONAL_MENU( aTool )
90 {
92  SetTitle( _( "Special Tools" ) );
93 
98 }
99 
100 
102 {
103  // Find the selection tool, so they can cooperate
105 
106  auto propertiesCondition =
107  [&]( const SELECTION& aSel )
108  {
109  if( aSel.GetSize() == 0 )
110  {
112  {
115 
116  if( ds && ds->HitTestDrawingSheetItems( getView(), (wxPoint) cursor ) )
117  return true;
118  }
119 
120  return false;
121  }
122 
123  if( aSel.GetSize() == 1 )
124  return true;
125 
126  for( EDA_ITEM* item : aSel )
127  {
128  if( !dynamic_cast<TRACK*>( item ) )
129  return false;
130  }
131 
132  return true;
133  };
134 
135  auto inFootprintEditor =
136  [ this ]( const SELECTION& aSelection )
137  {
138  return m_isFootprintEditor;
139  };
140 
141  auto singleFootprintCondition = SELECTION_CONDITIONS::OnlyType( PCB_FOOTPRINT_T )
143 
144  auto noActiveToolCondition =
145  [ this ]( const SELECTION& aSelection )
146  {
147  return frame()->ToolStackIsEmpty();
148  };
149 
150  auto notMovingCondition =
151  [ this ]( const SELECTION& aSelection )
152  {
155  };
156 
157  auto noItemsCondition =
158  [ this ]( const SELECTION& aSelections ) -> bool
159  {
160  return frame()->GetBoard() && !frame()->GetBoard()->IsEmpty();
161  };
162 
163  // Add context menu entries that are displayed when selection tool is active
165 
167  && notMovingCondition );
179  menu.AddItem( PCB_ACTIONS::mirror, inFootprintEditor && SELECTION_CONDITIONS::NotEmpty );
180 
181  menu.AddItem( PCB_ACTIONS::properties, propertiesCondition );
182 
183  // Footprint actions
184  menu.AddSeparator();
185  menu.AddItem( PCB_ACTIONS::editFpInFpEditor, singleFootprintCondition );
186  menu.AddItem( PCB_ACTIONS::updateFootprint, singleFootprintCondition );
187  menu.AddItem( PCB_ACTIONS::changeFootprint, singleFootprintCondition );
188 
189  // Add the submenu for create array and special move
190  auto specialToolsSubMenu = std::make_shared<SPECIAL_TOOLS_CONTEXT_MENU>( this );
191  menu.AddSeparator();
192  m_selectionTool->GetToolMenu().AddSubMenu( specialToolsSubMenu );
193  menu.AddMenu( specialToolsSubMenu.get(), SELECTION_CONDITIONS::NotEmpty, 100 );
194 
195  menu.AddSeparator( 150 );
198  // Selection tool handles the context menu for some other tools, such as the Picker.
199  // Don't add things like Paste when another tool is active.
200  menu.AddItem( ACTIONS::paste, noActiveToolCondition, 150 );
201  menu.AddItem( ACTIONS::pasteSpecial, noActiveToolCondition, 150 );
204 
205  menu.AddSeparator( 150 );
206  menu.AddItem( ACTIONS::selectAll, noItemsCondition, 150 );
207 
208  return true;
209 }
210 
211 
212 int EDIT_TOOL::GetAndPlace( const TOOL_EVENT& aEvent )
213 {
214  // GetAndPlace makes sense only in board editor, although it is also called
215  // in fpeditor, that shares the same EDIT_TOOL list
216  if( !getEditFrame<PCB_BASE_FRAME>()->IsType( FRAME_PCB_EDITOR ) )
217  return 0;
218 
220  FOOTPRINT* fp = getEditFrame<PCB_BASE_FRAME>()->GetFootprintFromBoardByReference();
221 
222  if( fp )
223  {
225  m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, (void*) fp );
226 
227  selectionTool->GetSelection().SetReferencePoint( fp->GetPosition() );
229  }
230 
231  return 0;
232 }
233 
234 
235 bool EDIT_TOOL::invokeInlineRouter( int aDragMode )
236 {
238 
239  if( !theRouter )
240  return false;
241 
242  // don't allow switch from moving to dragging
243  if( m_dragging )
244  {
245  wxBell();
246  return false;
247  }
248 
249  // make sure we don't accidentally invoke inline routing mode while the router is already active!
250  if( theRouter->IsToolActive() )
251  return false;
252 
253  if( theRouter->CanInlineDrag( aDragMode ) )
254  {
256  return true;
257  }
258 
259  return false;
260 }
261 
262 
264 {
266 
267  return router && router->Router()->Settings().InlineDragEnabled();
268 }
269 
270 
272 {
274 
275  return router && router->IsToolActive();
276 }
277 
278 
279 int EDIT_TOOL::Drag( const TOOL_EVENT& aEvent )
280 {
281  int mode = PNS::DM_ANY;
282 
283  if( aEvent.IsAction( &PCB_ACTIONS::dragFreeAngle ) )
284  mode |= PNS::DM_FREE_ANGLE;
285 
287  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
288  {
289  // Iterate from the back so we don't have to worry about removals.
290  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
291  {
292  BOARD_ITEM* item = aCollector[ i ];
293 
294  // We don't operate on pads; convert them to footprint selections
295  if( !sTool->IsFootprintEditor() && item->Type() == PCB_PAD_T )
296  {
297  aCollector.Remove( item );
298 
299  if( item->GetParent() && !aCollector.HasItem( item->GetParent() ) )
300  aCollector.Append( item->GetParent() );
301  }
302  }
303 
304  sTool->FilterCollectorForHierarchy( aCollector, true );
305  },
306  true /* prompt user regarding locked items */ );
307 
308  if( selection.Empty() )
309  return 0;
310 
311  if( selection.Size() == 1 && selection.Front()->Type() == PCB_ARC_T )
312  {
313  // TODO: This really should be done in PNS to ensure DRC is maintained, but for now
314  // it allows interactive editing of an arc track
315  return DragArcTrack( aEvent );
316  }
317  else
318  {
319  invokeInlineRouter( mode );
320  }
321 
322  return 0;
323 }
324 
325 
327 {
329 
330  if( selection.Size() != 1 || selection.Front()->Type() != PCB_ARC_T )
331  return 0;
332 
333  Activate();
334 
335  ARC* theArc = static_cast<ARC*>( selection.Front() );
336  double arcAngleDegrees = std::abs( theArc->GetAngle() ) / 10.0;
337 
338  if( arcAngleDegrees + ADVANCED_CFG::GetCfg().m_MaxTangentAngleDeviation >= 180.0 )
339  {
341  wxString::Format( _( "Unable to resize arc tracks %.1f degrees or greater." ),
342  180.0 - ADVANCED_CFG::GetCfg().m_MaxTangentAngleDeviation ) );
343  return 0; // don't bother with > 180 degree arcs
344  }
345 
347 
348  controls->ShowCursor( true );
349  controls->SetAutoPan( true );
350  bool restore_state = false;
351 
352  VECTOR2I arcCenter = theArc->GetCenter();
353  SEG tanStart = SEG( arcCenter, theArc->GetStart() ).PerpendicularSeg( theArc->GetStart() );
354  SEG tanEnd = SEG( arcCenter, theArc->GetEnd() ).PerpendicularSeg( theArc->GetEnd() );
355 
356  //Ensure the tangent segments are in the correct orientation
357  VECTOR2I tanIntersect = tanStart.IntersectLines( tanEnd ).get();
358  tanStart.A = tanIntersect;
359  tanStart.B = theArc->GetStart();
360  tanEnd.A = tanIntersect;
361  tanEnd.B = theArc->GetEnd();
362 
363  KICAD_T track_types[] = { PCB_PAD_T, PCB_VIA_T, PCB_TRACE_T, PCB_ARC_T, EOT };
364 
365  auto getUniqueTrackAtAnchorCollinear =
366  [&]( const VECTOR2I& aAnchor, const SEG& aCollinearSeg ) -> TRACK*
367  {
368  auto conn = board()->GetConnectivity();
369 
370  // Allow items at a distance within the width of the arc track
371  int allowedDeviation = theArc->GetWidth();
372 
373  std::vector<BOARD_CONNECTED_ITEM*> itemsOnAnchor;
374 
375  for( int i = 0; i < 3; i++ )
376  {
377  itemsOnAnchor = conn->GetConnectedItemsAtAnchor( theArc, aAnchor, track_types,
378  allowedDeviation );
379  allowedDeviation /= 2;
380 
381  if( itemsOnAnchor.size() == 1 )
382  break;
383  }
384 
385  TRACK* retval = nullptr;
386 
387  if( itemsOnAnchor.size() == 1 && itemsOnAnchor.front()->Type() == PCB_TRACE_T )
388  {
389  retval = static_cast<TRACK*>( itemsOnAnchor.front() );
390  SEG trackSeg( retval->GetStart(), retval->GetEnd() );
391 
392  // Allow deviations in colinearity as defined in ADVANCED_CFG
393  if( trackSeg.AngleDegrees( aCollinearSeg )
395  {
396  retval = nullptr;
397  }
398  }
399 
400  if( !retval )
401  {
402  retval = new TRACK( theArc->GetParent() );
403  retval->SetStart( (wxPoint) aAnchor );
404  retval->SetEnd( (wxPoint) aAnchor );
405  retval->SetNet( theArc->GetNet() );
406  retval->SetLayer( theArc->GetLayer() );
407  retval->SetWidth( theArc->GetWidth() );
408  retval->SetFlags( IS_NEW );
409  getView()->Add( retval );
410  }
411 
412  return retval;
413  };
414 
415  TRACK* trackOnStart = getUniqueTrackAtAnchorCollinear( theArc->GetStart(), tanStart);
416  TRACK* trackOnEnd = getUniqueTrackAtAnchorCollinear( theArc->GetEnd(), tanEnd );
417 
418  // Make copies of items to be edited
419  ARC* theArcCopy = new ARC( *theArc );
420  TRACK* trackOnStartCopy = new TRACK( *trackOnStart );
421  TRACK* trackOnEndCopy = new TRACK( *trackOnEnd );
422 
423  if( trackOnStart->GetLength() != 0 )
424  {
425  tanStart.A = trackOnStart->GetStart();
426  tanStart.B = trackOnStart->GetEnd();
427  }
428 
429  if( trackOnEnd->GetLength() != 0 )
430  {
431  tanEnd.A = trackOnEnd->GetStart();
432  tanEnd.B = trackOnEnd->GetEnd();
433  }
434 
435  // Recalculate intersection point
436  tanIntersect = tanStart.IntersectLines( tanEnd ).get();
437 
438  auto isTrackStartClosestToArcStart =
439  [&]( TRACK* aPointA ) -> bool
440  {
441  return GetLineLength( aPointA->GetStart(), theArc->GetStart() )
442  < GetLineLength( aPointA->GetEnd(), theArc->GetStart() );
443  };
444 
445  bool isStartTrackOnStartPt = isTrackStartClosestToArcStart( trackOnStart );
446  bool isEndTrackOnStartPt = isTrackStartClosestToArcStart( trackOnEnd );
447 
448  // Calculate constraints
449  //======================
450  // maxTanCircle is the circle with maximum radius that is tangent to the two adjacent straight
451  // tracks and whose tangent points are constrained within the original tracks and their
452  // projected intersection points.
453  //
454  // The cursor will be constrained first within the isosceles triangle formed by the segments
455  // cSegTanStart, cSegTanEnd and cSegChord. After that it will be constratined to be outside
456  // maxTanCircle.
457  //
458  //
459  // ____________ <-cSegTanStart
460  // / * . ' *
461  // cSegTanEnd-> / * . ' *
462  // /* . ' <-cSegChord *
463  // /. '
464  // /* *
465  //
466  // * c * <-maxTanCircle
467  //
468  // * *
469  //
470  // * *
471  // * *
472  // * *
473  //
474 
475  auto getFurthestPointToTanInterstect =
476  [&]( VECTOR2I& aPointA, VECTOR2I& aPointB ) -> VECTOR2I
477  {
478  if( ( aPointA - tanIntersect ).EuclideanNorm()
479  > ( aPointB - tanIntersect ).EuclideanNorm() )
480  {
481  return aPointA;
482  }
483  else
484  {
485  return aPointB;
486  }
487  };
488 
489  CIRCLE maxTanCircle;
490 
491  VECTOR2I tanStartPoint = getFurthestPointToTanInterstect( tanStart.A, tanStart.B );
492  VECTOR2I tanEndPoint = getFurthestPointToTanInterstect( tanEnd.A, tanEnd.B );
493  VECTOR2I tempTangentPoint = tanEndPoint;
494 
495  if( getFurthestPointToTanInterstect( tanStartPoint, tanEndPoint ) == tanEndPoint )
496  tempTangentPoint = tanStartPoint;
497 
498  maxTanCircle.ConstructFromTanTanPt( tanStart, tanEnd, tempTangentPoint );
499  VECTOR2I maxTanPtStart = tanStart.LineProject( maxTanCircle.Center );
500  VECTOR2I maxTanPtEnd = tanEnd.LineProject( maxTanCircle.Center );
501 
502  SEG cSegTanStart( maxTanPtStart, tanIntersect );
503  SEG cSegTanEnd( maxTanPtEnd, tanIntersect );
504  SEG cSegChord( maxTanPtStart, maxTanPtEnd );
505 
506  int cSegTanStartSide = cSegTanStart.Side( theArc->GetMid() );
507  int cSegTanEndSide = cSegTanEnd.Side( theArc->GetMid() );
508  int cSegChordSide = cSegChord.Side( theArc->GetMid() );
509 
510  // Start the tool loop
511  //====================
512  while( TOOL_EVENT* evt = Wait() )
513  {
515 
516  // Constrain cursor within the isosceles triangle
517  if( cSegTanStartSide != cSegTanStart.Side( m_cursor )
518  || cSegTanEndSide != cSegTanEnd.Side( m_cursor )
519  || cSegChordSide != cSegChord.Side( m_cursor ) )
520  {
521  std::vector<VECTOR2I> possiblePoints;
522 
523  possiblePoints.push_back( cSegTanEnd.NearestPoint( m_cursor ) );
524  possiblePoints.push_back( cSegChord.NearestPoint( m_cursor ) );
525 
526  VECTOR2I closest = cSegTanStart.NearestPoint( m_cursor );
527 
528  for( VECTOR2I candidate : possiblePoints )
529  {
530  if( ( candidate - m_cursor ).EuclideanNorm()
531  < ( closest - m_cursor ).EuclideanNorm() )
532  {
533  closest = candidate;
534  }
535  }
536 
537  m_cursor = closest;
538  }
539 
540  // Constrain cursor to be outside maxTanCircle
541  if( ( m_cursor - maxTanCircle.Center ).EuclideanNorm() < maxTanCircle.Radius )
542  m_cursor = maxTanCircle.NearestPoint( m_cursor );
543 
545 
546  // Calculate resulting object coordinates
547  CIRCLE circlehelper;
548  circlehelper.ConstructFromTanTanPt( cSegTanStart, cSegTanEnd, m_cursor );
549 
550  VECTOR2I newCenter = circlehelper.Center;
551  VECTOR2I newStart = cSegTanStart.LineProject( newCenter );
552  VECTOR2I newEnd = cSegTanEnd.LineProject( newCenter );
553  VECTOR2I newMid = GetArcMid( newStart, newEnd, newCenter );
554 
555  //Update objects
556  theArc->SetStart( (wxPoint) newStart );
557  theArc->SetEnd( (wxPoint) newEnd );
558  theArc->SetMid( (wxPoint) newMid );
559 
560  if( isStartTrackOnStartPt )
561  trackOnStart->SetStart( (wxPoint) newStart );
562  else
563  trackOnStart->SetEnd( (wxPoint) newStart );
564 
565  if( isEndTrackOnStartPt )
566  trackOnEnd->SetStart( (wxPoint) newEnd );
567  else
568  trackOnEnd->SetEnd( (wxPoint) newEnd );
569 
570  //Update view
571  getView()->Update( trackOnStart );
572  getView()->Update( trackOnEnd );
573  getView()->Update( theArc );
574 
575  //Handle events
576  if( evt->IsCancelInteractive() || evt->IsActivate() )
577  {
578  restore_state = true; // Canceling the tool means that items have to be restored
579  break; // Finish
580  }
581  else if( evt->IsAction( &ACTIONS::undo ) )
582  {
583  restore_state = true; // Perform undo locally
584  break; // Finish
585  }
586  else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT )
587  || evt->IsDblClick( BUT_LEFT ) )
588  {
589  break; // Finish
590  }
591  }
592 
593  // Ensure we only do one commit operation on each object
594  auto processTrack =
595  [&]( TRACK* aTrack, TRACK* aTrackCopy, int aMaxLengthIU ) -> bool
596  {
597  if( aTrack->IsNew() )
598  {
599  getView()->Remove( aTrack );
600 
601  if( aTrack->GetLength() <= aMaxLengthIU )
602  {
603  delete aTrack;
604  delete aTrackCopy;
605  aTrack = nullptr;
606  aTrackCopy = nullptr;
607  return false;
608  }
609  else
610  {
611  m_commit->Add( aTrack );
612  delete aTrackCopy;
613  aTrackCopy = nullptr;
614  return true;
615  }
616  }
617  else if( aTrack->GetLength() <= aMaxLengthIU )
618  {
619  aTrack->SwapData( aTrackCopy ); //restore the original before notifying COMMIT
620  m_commit->Remove( aTrack );
621  delete aTrackCopy;
622  aTrackCopy = nullptr;
623  return false;
624  }
625  else
626  {
627  m_commit->Modified( aTrack, aTrackCopy );
628  }
629 
630  return true;
631  };
632 
633  // Ammend the end points of the arc if we delete the joining tracks
634  wxPoint newStart = trackOnStart->GetStart();
635  wxPoint newEnd = trackOnEnd->GetStart();
636 
637  if( isStartTrackOnStartPt )
638  newStart = trackOnStart->GetEnd();
639 
640  if( isEndTrackOnStartPt )
641  newEnd = trackOnEnd->GetEnd();
642 
643  int maxLengthIU = KiROUND( ADVANCED_CFG::GetCfg().m_MaxTrackLengthToKeep * IU_PER_MM );
644 
645  if( !processTrack( trackOnStart, trackOnStartCopy, maxLengthIU ) )
646  theArc->SetStart( newStart );
647 
648  if( !processTrack( trackOnEnd, trackOnEndCopy, maxLengthIU ) )
649  theArc->SetEnd( newEnd );
650 
651  processTrack( theArc, theArcCopy, 0 ); // only delete the arc if start and end points coincide
652 
653  // Should we commit?
654  if( restore_state )
655  m_commit->Revert();
656  else
657  m_commit->Push( _( "Drag Arc Track" ) );
658 
659  return 0;
660 }
661 
662 
663 int EDIT_TOOL::Move( const TOOL_EVENT& aEvent )
664 {
665  if( isRouterActive() )
666  {
667  wxBell();
668  return 0;
669  }
670 
671  return doMoveSelection( aEvent );
672 }
673 
674 
676 {
677  if( isRouterActive() )
678  {
679  wxBell();
680  return 0;
681  }
682 
683  return doMoveSelection( aEvent, true );
684 }
685 
686 
687 // Note: aEvent MUST NOT be const&; the source will get de-allocated if we go into the picker's
688 // event loop.
689 int EDIT_TOOL::doMoveSelection( TOOL_EVENT aEvent, bool aPickReference )
690 {
691  PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
693  VECTOR2I originalCursorPos = controls->GetCursorPosition();
694 
695  // Be sure that there is at least one item that we can modify. If nothing was selected before,
696  // try looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection)
698  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
699  {
700  // Iterate from the back so we don't have to worry about removals.
701  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
702  {
703  BOARD_ITEM* item = aCollector[ i ];
704 
705  if( item->Type() == PCB_MARKER_T )
706  aCollector.Remove( item );
707  }
708  } );
709 
710  if( m_dragging || selection.Empty() )
711  return 0;
712 
713  LSET item_layers = selection.GetSelectionLayers();
714  bool is_hover = selection.IsHover(); // N.B. This must be saved before the re-selection
715  // below
716  VECTOR2I pickedReferencePoint;
717 
718  // Now filter out locked and grouped items. We cannot do this in the first RequestSelection()
719  // as we need the item_layers when a pad is the selection front.
720  if( frame()->Settings().m_AllowFreePads )
721  {
723  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
724  {
725  std::set<BOARD_ITEM*> to_add;
726 
727  // Iterate from the back so we don't have to worry about removals.
728  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
729  {
730  BOARD_ITEM* item = aCollector[i];
731 
732  if( item->Type() == PCB_MARKER_T )
733  aCollector.Remove( item );
734 
735  // Locked pads do not get moved independently of the footprint
736  if( !sTool->IsFootprintEditor() && item->Type() == PCB_PAD_T
737  && item->IsLocked() )
738  {
739  if( !aCollector.HasItem( item->GetParent() ) )
740  to_add.insert( item->GetParent() );
741 
742  aCollector.Remove( item );
743  }
744  }
745 
746  for( BOARD_ITEM* item : to_add )
747  aCollector.Append( item );
748  },
749  !m_isFootprintEditor /* prompt user regarding locked items only in pcb editor*/ );
750  }
751  else
752  {
753  // Unlocked pads are treated as locked if the setting m_AllowFreePads is false
755  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector,
756  PCB_SELECTION_TOOL* sTool )
757  {
758  std::set<BOARD_ITEM*> to_add;
759 
760  // Iterate from the back so we don't have to worry about removals.
761  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
762  {
763  BOARD_ITEM* item = aCollector[i];
764 
765  if( item->Type() == PCB_MARKER_T )
766  aCollector.Remove( item );
767 
768  // Treat all pads as locked (i.e. cannot be moved indepenendtly of footprint)
769  if( !sTool->IsFootprintEditor() && item->Type() == PCB_PAD_T )
770  {
771  if( !aCollector.HasItem( item->GetParent() ) )
772  to_add.insert( item->GetParent() );
773 
774  aCollector.Remove( item );
775  }
776  }
777 
778  for( BOARD_ITEM* item : to_add )
779  aCollector.Append( item );
780  },
781  !m_isFootprintEditor /* prompt user regarding locked items only in pcb editor*/ );
782  }
783 
784  if( selection.Empty() )
785  return 0;
786 
787  std::string tool = aEvent.GetCommandStr().get();
788  editFrame->PushTool( tool );
789  Activate();
790  controls->ShowCursor( true );
791  controls->SetAutoPan( true );
792 
793  if( aPickReference && !pickReferencePoint( _( "Select reference point for move..." ), "", "",
794  pickedReferencePoint ) )
795  {
796  if( is_hover )
798 
799  editFrame->PopTool( tool );
800  return 0;
801  }
802 
803  std::vector<BOARD_ITEM*> sel_items; // All the items operated on by the move below
804  std::vector<BOARD_ITEM*> orig_items; // All the original items in the selection
805 
806  for( EDA_ITEM* item : selection )
807  {
808  BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( item );
809  FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( item );
810 
811  if( boardItem )
812  {
813  orig_items.push_back( boardItem );
814  sel_items.push_back( boardItem );
815  }
816 
817  if( footprint )
818  {
819  for( PAD* pad : footprint->Pads() )
820  sel_items.push_back( pad );
821  }
822  }
823 
824  bool restore_state = false;
825  VECTOR2I totalMovement;
827  TOOL_EVENT* evt = &aEvent;
828  VECTOR2I prevPos;
829 
830  bool hasMouseMoved = false;
831  bool hasRedrawn3D = false;
832  bool allowRedraw3D = editFrame->GetDisplayOptions().m_Live3DRefresh;
833 
834  // Prime the pump
836 
837  // Main loop: keep receiving events
838  do
839  {
840  VECTOR2I movement;
842  grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
843  grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
844 
845  if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
846  hasMouseMoved = true;
847 
848  if( evt->IsAction( &PCB_ACTIONS::move ) || evt->IsMotion() || evt->IsDrag( BUT_LEFT )
849  || evt->IsAction( &ACTIONS::refreshPreview )
850  || evt->IsAction( &PCB_ACTIONS::moveWithReference ) )
851  {
852  if( m_dragging && evt->Category() == TC_MOUSE )
853  {
854  bool redraw3D = false;
855 
856  VECTOR2I mousePos( controls->GetMousePosition() );
857 
858  m_cursor = grid.BestSnapAnchor( mousePos, item_layers, sel_items );
859 
861  {
863 
864  // The arrow keys are by definition SINGLE AXIS. Do not allow the other
865  // axis to be snapped to the grid.
866  if( action == ACTIONS::CURSOR_LEFT || action == ACTIONS::CURSOR_RIGHT )
867  m_cursor.y = prevPos.y;
868  else if( action == ACTIONS::CURSOR_UP || action == ACTIONS::CURSOR_DOWN )
869  m_cursor.x = prevPos.x;
870  }
871 
874 
875  movement = m_cursor - prevPos;
876  prevPos = m_cursor;
877  totalMovement += movement;
878 
879  // Drag items to the current cursor position
880  for( EDA_ITEM* item : sel_items )
881  {
882  // Don't double move footprint pads, fields, etc.
883  //
884  // For PCB_GROUP_T, we make sure the selection includes only the top level
885  // group and not its descendants.
886  if( !item->GetParent() || !item->GetParent()->IsSelected() )
887  static_cast<BOARD_ITEM*>( item )->Move( movement );
888 
889  if( !redraw3D && item->Type() == PCB_FOOTPRINT_T )
890  redraw3D = true;
891  }
892 
893  if( redraw3D && allowRedraw3D )
894  {
895  editFrame->Update3DView( false, true );
896  hasRedrawn3D = true;
897  }
898 
900  }
901  else if( !m_dragging && !evt->IsAction( &ACTIONS::refreshPreview ) )
902  {
903  // Prepare to start dragging
904  if( !( evt->IsAction( &PCB_ACTIONS::move )
905  || evt->IsAction( &PCB_ACTIONS::moveWithReference ) )
907  {
909  break;
910  }
911 
912  m_dragging = true;
913 
914  // When editing footprints, all items have the same parent
915  if( IsFootprintEditor() )
916  {
917  m_commit->Modify( selection.Front() );
918  }
919  else
920  {
921  // Save items, so changes can be undone
922  for( EDA_ITEM* item : selection )
923  {
924  // Don't double move footprint pads, fields, etc.
925  //
926  // For PCB_GROUP_T, the parent is the board.
927  if( item->GetParent() && item->GetParent()->IsSelected() )
928  continue;
929 
930  m_commit->Modify( item );
931 
932  // If moving a group, record position of all the descendants for undo
933  if( item->Type() == PCB_GROUP_T )
934  {
935  PCB_GROUP* group = static_cast<PCB_GROUP*>( item );
936  group->RunOnDescendants( [&]( BOARD_ITEM* bItem )
937  {
938  m_commit->Modify( bItem );
939  });
940  }
941  }
942  }
943 
944  editFrame->UndoRedoBlock( true );
946 
948  {
949  // start moving with the reference point attached to the cursor
950  grid.SetAuxAxes( false );
951 
952  movement = m_cursor - selection.GetReferencePoint();
953 
954  // Drag items to the current cursor position
955  for( EDA_ITEM* item : selection )
956  {
957  // Don't double move footprint pads, fields, etc.
958  if( item->GetParent() && item->GetParent()->IsSelected() )
959  continue;
960 
961  static_cast<BOARD_ITEM*>( item )->Move( movement );
962  }
963 
965  }
966  else
967  {
968  std::vector<BOARD_ITEM*> items;
969 
970  for( EDA_ITEM* item : selection )
971  items.push_back( static_cast<BOARD_ITEM*>( item ) );
972 
973  m_cursor = grid.BestDragOrigin( originalCursorPos, items );
974 
975  // Set the current cursor position to the first dragged item origin, so the
976  // movement vector could be computed later
977  if( aPickReference )
978  {
979  selection.SetReferencePoint( pickedReferencePoint );
980  controls->ForceCursorPosition( true, pickedReferencePoint );
981  m_cursor = pickedReferencePoint;
982  }
983  else
984  {
985  // Check if user wants to warp the mouse to origin of moved object
986  if( !editFrame->GetMoveWarpsCursor() )
987  m_cursor = originalCursorPos; // No, so use original mouse pos instead
988 
990  grid.SetAuxAxes( true, m_cursor );
991  }
992  }
993 
995 
996  prevPos = m_cursor;
997  controls->SetAutoPan( true );
999  }
1000 
1002  new VECTOR2I( movement ) );
1003  }
1004 
1005  else if( evt->IsCancelInteractive() || evt->IsActivate() )
1006  {
1007  if( m_dragging && evt->IsCancelInteractive() )
1008  evt->SetPassEvent( false );
1009 
1010  restore_state = true; // Canceling the tool means that items have to be restored
1011  break; // Finish
1012  }
1013 
1014  else if( evt->IsAction( &ACTIONS::undo ) )
1015  {
1016  restore_state = true; // Perform undo locally
1017  break; // Finish
1018  }
1019 
1020  // Dispatch TOOL_ACTIONs
1021  else if( evt->IsAction( &ACTIONS::doDelete ) )
1022  {
1023  break; // finish -- there is no further processing for removed items
1024  }
1025  else if( evt->IsAction( &ACTIONS::duplicate ) )
1026  {
1027  break; // finish -- Duplicate tool will start a new Move with the dup'ed items
1028  }
1029  else if( evt->IsAction( &PCB_ACTIONS::moveExact ) )
1030  {
1031  // Reset positions so the Move Exactly is from the start.
1032  for( EDA_ITEM* item : selection )
1033  {
1034  BOARD_ITEM* i = static_cast<BOARD_ITEM*>( item );
1035  i->Move( -totalMovement );
1036  }
1037 
1038  break; // finish -- we moved exactly, so we are finished
1039  }
1040  else if( hasMouseMoved && ( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
1041  {
1042  // Ignore mouse up and click events until we receive at least one mouse move or
1043  // mouse drag event
1044  break; // finish
1045  }
1046  else
1047  {
1048  evt->SetPassEvent();
1049  }
1050 
1051  } while( ( evt = Wait() ) ); // Assignment (instead of equality test) is intentional
1052 
1053  controls->ForceCursorPosition( false );
1054  controls->ShowCursor( false );
1055  controls->SetAutoPan( false );
1056 
1057  m_dragging = false;
1058  editFrame->UndoRedoBlock( false );
1059 
1060  if( hasRedrawn3D && restore_state )
1061  editFrame->Update3DView( false, true );
1062 
1063  // Discard reference point when selection is "dropped" onto the board
1065 
1066  // TODO: there's an ecapsulation leak here: this commit often has more than just the move
1067  // in it; for instance it might have a paste, append board, etc. as well.
1068  if( restore_state )
1069  m_commit->Revert();
1070  else
1071  m_commit->Push( _( "Drag" ) );
1072 
1073  // Remove the dynamic ratsnest from the screen
1075 
1076  // Unselect all items to update flags
1078 
1079  // Reselect items if they were already selected and we completed the move
1080  if( !is_hover && !restore_state )
1081  m_toolMgr->RunAction( PCB_ACTIONS::selectItems, true, &orig_items );
1082 
1083  editFrame->PopTool( tool );
1084 
1085  return restore_state ? -1 : 0;
1086 }
1087 
1088 
1090 {
1092  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1093  {
1094  // Iterate from the back so we don't have to worry about removals.
1095  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1096  {
1097  BOARD_ITEM* item = aCollector[ i ];
1098 
1099  if( !dynamic_cast<TRACK*>( item ) )
1100  aCollector.Remove( item );
1101  }
1102  },
1103  true /* prompt user regarding locked items */ );
1104 
1105  for( EDA_ITEM* item : selection )
1106  {
1107  if( item->Type() == PCB_VIA_T )
1108  {
1109  VIA* via = static_cast<VIA*>( item );
1110 
1111  m_commit->Modify( via );
1112 
1113  int new_width;
1114  int new_drill;
1115 
1116  if( via->GetViaType() == VIATYPE::MICROVIA )
1117  {
1118  NETCLASS* netClass = via->GetNetClass();
1119 
1120  new_width = netClass->GetuViaDiameter();
1121  new_drill = netClass->GetuViaDrill();
1122  }
1123  else
1124  {
1125  new_width = board()->GetDesignSettings().GetCurrentViaSize();
1126  new_drill = board()->GetDesignSettings().GetCurrentViaDrill();
1127  }
1128 
1129  via->SetDrill( new_drill );
1130  via->SetWidth( new_width );
1131  }
1132  else if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
1133  {
1134  TRACK* track = dynamic_cast<TRACK*>( item );
1135 
1136  wxCHECK( track, 0 );
1137 
1138  m_commit->Modify( track );
1139 
1140  int new_width = board()->GetDesignSettings().GetCurrentTrackWidth();
1141  track->SetWidth( new_width );
1142  }
1143  }
1144 
1145  m_commit->Push( _("Edit track width/via size") );
1146 
1147  if( selection.IsHover() )
1148  {
1150 
1151  // Notify other tools of the changes -- This updates the visual ratsnest
1153  }
1154 
1155  return 0;
1156 }
1157 
1158 
1160 {
1161  // Store last used fillet radius to allow pressing "enter" if repeat fillet is required
1162  static long long filletRadiusIU = 0;
1163 
1165  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1166  {
1167  // Iterate from the back so we don't have to worry about removals.
1168  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1169  {
1170  BOARD_ITEM* item = aCollector[i];
1171 
1172  if( !dynamic_cast<TRACK*>( item ) )
1173  aCollector.Remove( item );
1174  }
1175  },
1176  !m_dragging /* prompt user regarding locked items */ );
1177 
1178  if( selection.Size() < 2 )
1179  {
1180  frame()->ShowInfoBarMsg( _( "At least two straight track segments must be selected." ) );
1181  return 0;
1182  }
1183 
1184  WX_UNIT_ENTRY_DIALOG dia( frame(), _( "Enter fillet radius:" ), _( "Fillet Tracks" ),
1185  filletRadiusIU );
1186 
1187  if( dia.ShowModal() == wxID_CANCEL )
1188  return 0;
1189 
1190  filletRadiusIU = dia.GetValue();
1191 
1192  if( filletRadiusIU == 0 )
1193  {
1194  frame()->ShowInfoBarMsg( _( "A radius of zero was entered.\n"
1195  "The fillet operation was not performed." ) );
1196  return 0;
1197  }
1198 
1199 
1200  struct FILLET_OP
1201  {
1202  TRACK* t1;
1203  TRACK* t2;
1204  //If true, start point of track is modified after ARC is added, otherwise the end point:
1205  bool t1Start = true;
1206  bool t2Start = true;
1207  };
1208 
1209  std::vector<FILLET_OP> filletOperations;
1210  KICAD_T track_types[] = { PCB_PAD_T, PCB_VIA_T, PCB_TRACE_T, PCB_ARC_T, EOT };
1211  bool operationPerformedOnAtLeastOne = false;
1212  bool didOneAttemptFail = false;
1213  std::set<TRACK*> processedTracks;
1214 
1215  for( auto it = selection.begin(); it != selection.end(); it++ )
1216  {
1217  TRACK* track = dyn_cast<TRACK*>( *it );
1218 
1219  if( !track || track->Type() != PCB_TRACE_T || track->IsLocked()
1220  || track->GetLength() == 0 )
1221  {
1222  continue;
1223  }
1224 
1225  auto processFilletOp =
1226  [&]( bool aStartPoint )
1227  {
1228  wxPoint anchor = ( aStartPoint ) ? track->GetStart() : track->GetEnd();
1229  auto connectivity = board()->GetConnectivity();
1230  auto itemsOnAnchor = connectivity->GetConnectedItemsAtAnchor( track, anchor,
1231  track_types );
1232 
1233  if( itemsOnAnchor.size() > 0
1234  && selection.Contains( itemsOnAnchor.at( 0 ) )
1235  && itemsOnAnchor.at( 0 )->Type() == PCB_TRACE_T )
1236  {
1237  TRACK* trackOther = dyn_cast<TRACK*>( itemsOnAnchor.at( 0 ) );
1238 
1239  // Make sure we don't fillet the same pair of tracks twice
1240  if( processedTracks.find( trackOther ) == processedTracks.end() )
1241  {
1242  if( itemsOnAnchor.size() == 1 )
1243  {
1244  FILLET_OP filletOp;
1245  filletOp.t1 = track;
1246  filletOp.t2 = trackOther;
1247  filletOp.t1Start = aStartPoint;
1248  filletOp.t2Start = track->IsPointOnEnds( filletOp.t2->GetStart() );
1249  filletOperations.push_back( filletOp );
1250  }
1251  else
1252  {
1253  // User requested to fillet these two tracks but not possible as
1254  // there are other elements connected at that point
1255  didOneAttemptFail = true;
1256  }
1257  }
1258  }
1259  };
1260 
1261  processFilletOp( true ); // on the start point of track
1262  processFilletOp( false ); // on the end point of track
1263 
1264  processedTracks.insert( track );
1265  }
1266 
1267  std::vector<BOARD_ITEM*> itemsToAddToSelection;
1268 
1269  for( FILLET_OP filletOp : filletOperations )
1270  {
1271  TRACK* track1 = filletOp.t1;
1272  TRACK* track2 = filletOp.t2;
1273 
1274  bool trackOnStart = track1->IsPointOnEnds( track2->GetStart() );
1275  bool trackOnEnd = track1->IsPointOnEnds( track2->GetEnd() );
1276 
1277  if( trackOnStart && trackOnEnd )
1278  continue; // Ignore duplicate tracks
1279 
1280  if( ( trackOnStart || trackOnEnd ) && track1->GetLayer() == track2->GetLayer() )
1281  {
1282  SEG t1Seg( track1->GetStart(), track1->GetEnd() );
1283  SEG t2Seg( track2->GetStart(), track2->GetEnd() );
1284 
1285  if( t1Seg.ApproxCollinear( t2Seg ) )
1286  continue;
1287 
1288  SHAPE_ARC sArc( t1Seg, t2Seg, filletRadiusIU );
1289 
1290  wxPoint t1newPoint, t2newPoint;
1291 
1292  auto setIfPointOnSeg =
1293  []( wxPoint& aPointToSet, SEG aSegment, VECTOR2I aVecToTest )
1294  {
1295  VECTOR2I segToVec = aSegment.NearestPoint( aVecToTest ) - aVecToTest;
1296 
1297  // Find out if we are on the segment (minimum precision)
1298  if( segToVec.EuclideanNorm() < SHAPE_ARC::MIN_PRECISION_IU )
1299  {
1300  aPointToSet.x = aVecToTest.x;
1301  aPointToSet.y = aVecToTest.y;
1302  return true;
1303  }
1304 
1305  return false;
1306  };
1307 
1308  //Do not draw a fillet if the end points of the arc are not within the track segments
1309  if( !setIfPointOnSeg( t1newPoint, t1Seg, sArc.GetP0() )
1310  && !setIfPointOnSeg( t2newPoint, t2Seg, sArc.GetP0() ) )
1311  {
1312  didOneAttemptFail = true;
1313  continue;
1314  }
1315 
1316  if( !setIfPointOnSeg( t1newPoint, t1Seg, sArc.GetP1() )
1317  && !setIfPointOnSeg( t2newPoint, t2Seg, sArc.GetP1() ) )
1318  {
1319  didOneAttemptFail = true;
1320  continue;
1321  }
1322 
1323  ARC* tArc = new ARC( frame()->GetBoard(), &sArc );
1324  tArc->SetLayer( track1->GetLayer() );
1325  tArc->SetWidth( track1->GetWidth() );
1326  tArc->SetNet( track1->GetNet() );
1327  m_commit->Add( tArc );
1328  itemsToAddToSelection.push_back( tArc );
1329 
1330  m_commit->Modify( track1 );
1331  m_commit->Modify( track2 );
1332 
1333  if( filletOp.t1Start )
1334  track1->SetStart( t1newPoint );
1335  else
1336  track1->SetEnd( t1newPoint );
1337 
1338  if( filletOp.t2Start )
1339  track2->SetStart( t2newPoint );
1340  else
1341  track2->SetEnd( t2newPoint );
1342 
1343  operationPerformedOnAtLeastOne = true;
1344  }
1345  }
1346 
1347  m_commit->Push( _( "Fillet Tracks" ) );
1348 
1349  //select the newly created arcs
1350  for( BOARD_ITEM* item : itemsToAddToSelection )
1351  m_selectionTool->AddItemToSel( item );
1352 
1353  if( !operationPerformedOnAtLeastOne )
1354  frame()->ShowInfoBarMsg( _( "Unable to fillet the selected track segments." ) );
1355  else if( didOneAttemptFail )
1356  frame()->ShowInfoBarMsg( _( "Some of the track segments could not be filleted." ) );
1357 
1358  return 0;
1359 }
1360 
1361 
1362 int EDIT_TOOL::Properties( const TOOL_EVENT& aEvent )
1363 {
1364  PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1366  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1367  {
1368  } );
1369 
1370  // Tracks & vias are treated in a special way:
1372  {
1373  DIALOG_TRACK_VIA_PROPERTIES dlg( editFrame, selection, *m_commit );
1374  dlg.ShowQuasiModal(); // QuasiModal required for NET_SELECTOR
1375  }
1376  else if( selection.Size() == 1 )
1377  {
1378  // Display properties dialog
1379  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
1380 
1381  // Do not handle undo buffer, it is done by the properties dialogs
1382  editFrame->OnEditItemRequest( item );
1383 
1384  // Notify other tools of the changes
1386  }
1387  else if( selection.Size() == 0 && getView()->IsLayerVisible( LAYER_DRAWINGSHEET ) )
1388  {
1389  DS_PROXY_VIEW_ITEM* ds = editFrame->GetCanvas()->GetDrawingSheet();
1390  VECTOR2D cursorPos = getViewControls()->GetCursorPosition( false );
1391 
1392  if( ds && ds->HitTestDrawingSheetItems( getView(), (wxPoint) cursorPos ) )
1394  }
1395 
1396  if( selection.IsHover() )
1397  {
1399 
1400  // Notify other tools of the changes -- This updates the visual ratsnest
1402  }
1403 
1404  return 0;
1405 }
1406 
1407 
1408 int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
1409 {
1410  if( isRouterActive() )
1411  {
1412  wxBell();
1413  return 0;
1414  }
1415 
1416  PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1417 
1419  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1420  {
1421  sTool->FilterCollectorForHierarchy( aCollector, true );
1422  },
1423  !m_dragging /* prompt user regarding locked items */ );
1424 
1425  if( selection.Empty() )
1426  return 0;
1427 
1428  OPT<VECTOR2I> oldRefPt = boost::make_optional<VECTOR2I>( false, VECTOR2I( 0, 0 ) );
1429 
1431  oldRefPt = selection.GetReferencePoint();
1432 
1434 
1436  const int rotateAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *editFrame, aEvent );
1437 
1438  // When editing footprints, all items have the same parent
1439  if( IsFootprintEditor() )
1440  m_commit->Modify( selection.Front() );
1441 
1442  for( EDA_ITEM* item : selection )
1443  {
1444  if( !item->IsNew() && !IsFootprintEditor() )
1445  {
1446  m_commit->Modify( item );
1447 
1448  // If rotating a group, record position of all the descendants for undo
1449  if( item->Type() == PCB_GROUP_T )
1450  {
1451  static_cast<PCB_GROUP*>( item )->RunOnDescendants(
1452  [&]( BOARD_ITEM* bItem )
1453  {
1454  m_commit->Modify( bItem );
1455  });
1456  }
1457  }
1458 
1459  static_cast<BOARD_ITEM*>( item )->Rotate( refPt, rotateAngle );
1460  }
1461 
1462  if( !m_dragging )
1463  m_commit->Push( _( "Rotate" ) );
1464 
1465  if( selection.IsHover() && !m_dragging )
1467 
1469 
1470  if( m_dragging )
1472 
1473  // Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
1474  // to this now invalid reference
1475  if( oldRefPt )
1476  selection.SetReferencePoint( *oldRefPt );
1477  else
1479 
1480  return 0;
1481 }
1482 
1483 
1487 static wxPoint mirrorPointX( const wxPoint& aPoint, const wxPoint& aMirrorPoint )
1488 {
1489  wxPoint mirrored = aPoint;
1490 
1491  mirrored.x -= aMirrorPoint.x;
1492  mirrored.x = -mirrored.x;
1493  mirrored.x += aMirrorPoint.x;
1494 
1495  return mirrored;
1496 }
1497 
1498 
1502 static void mirrorPadX( PAD& aPad, const wxPoint& aMirrorPoint )
1503 {
1504  if( aPad.GetShape() == PAD_SHAPE::CUSTOM )
1505  aPad.FlipPrimitives( true ); // mirror primitives left to right
1506 
1507  wxPoint tmpPt = mirrorPointX( aPad.GetPosition(), aMirrorPoint );
1508  aPad.SetPosition( tmpPt );
1509 
1510  aPad.SetX0( aPad.GetPosition().x );
1511 
1512  tmpPt = aPad.GetOffset();
1513  tmpPt.x = -tmpPt.x;
1514  aPad.SetOffset( tmpPt );
1515 
1516  auto tmpz = aPad.GetDelta();
1517  tmpz.x = -tmpz.x;
1518  aPad.SetDelta( tmpz );
1519 
1520  aPad.SetOrientation( -aPad.GetOrientation() );
1521 }
1522 
1523 
1524 int EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
1525 {
1526  if( isRouterActive() )
1527  {
1528  wxBell();
1529  return 0;
1530  }
1531 
1533  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1534  {
1535  sTool->FilterCollectorForHierarchy( aCollector, true );
1536  },
1537  !m_dragging /* prompt user regarding locked items */ );
1538 
1539  if( selection.Empty() )
1540  return 0;
1541 
1543  auto refPoint = selection.GetReferencePoint();
1544  wxPoint mirrorPoint( refPoint.x, refPoint.y );
1545 
1546  // When editing footprints, all items have the same parent
1547  if( IsFootprintEditor() )
1548  m_commit->Modify( selection.Front() );
1549 
1550  for( EDA_ITEM* item : selection )
1551  {
1552  // only modify items we can mirror
1553  switch( item->Type() )
1554  {
1555  case PCB_FP_SHAPE_T:
1556  case PCB_FP_TEXT_T:
1557  case PCB_FP_ZONE_T:
1558  case PCB_PAD_T:
1559  // Only create undo entry for items on the board
1560  if( !item->IsNew() && !IsFootprintEditor() )
1561  m_commit->Modify( item );
1562 
1563  break;
1564  default:
1565  continue;
1566  }
1567 
1568  // modify each object as necessary
1569  switch( item->Type() )
1570  {
1571  case PCB_FP_SHAPE_T:
1572  {
1573  FP_SHAPE* shape = static_cast<FP_SHAPE*>( item );
1574  shape->Mirror( mirrorPoint, false );
1575  break;
1576  }
1577 
1578  case PCB_FP_ZONE_T:
1579  {
1580  FP_ZONE* zone = static_cast<FP_ZONE*>( item );
1581  zone->Mirror( mirrorPoint, false );
1582  break;
1583  }
1584 
1585  case PCB_FP_TEXT_T:
1586  {
1587  FP_TEXT* text = static_cast<FP_TEXT*>( item );
1588  text->Mirror( mirrorPoint, false );
1589  break;
1590  }
1591 
1592  case PCB_PAD_T:
1593  {
1594  PAD* pad = static_cast<PAD*>( item );
1595  mirrorPadX( *pad, mirrorPoint );
1596  break;
1597  }
1598 
1599  default:
1600  // it's likely the commit object is wrong if you get here
1601  // Unsure if PCB_GROUP_T needs special attention here.
1602  assert( false );
1603  break;
1604  }
1605  }
1606 
1607  if( !m_dragging )
1608  m_commit->Push( _( "Mirror" ) );
1609 
1610  if( selection.IsHover() && !m_dragging )
1612 
1614 
1615  if( m_dragging )
1617 
1618  return 0;
1619 }
1620 
1621 
1622 int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent )
1623 {
1624  if( isRouterActive() )
1625  {
1626  wxBell();
1627  return 0;
1628  }
1629 
1631  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1632  {
1633  sTool->FilterCollectorForHierarchy( aCollector, true );
1634  },
1635  !m_dragging /* prompt user regarding locked items */ );
1636 
1637  if( selection.Empty() )
1638  return 0;
1639 
1640  OPT<VECTOR2I> oldRefPt = boost::make_optional<VECTOR2I>( false, VECTOR2I( 0, 0 ) );
1641 
1643  oldRefPt = selection.GetReferencePoint();
1644 
1646 
1647  // Flip around the anchor for footprints, and the bounding box center for board items
1648  VECTOR2I refPt = IsFootprintEditor() ? VECTOR2I( 0, 0 ) : selection.GetCenter();
1649 
1650  // If only one item selected, flip around the selection or item anchor point (instead
1651  // of the bounding box center) to avoid moving the item anchor
1652  if( selection.GetSize() == 1 )
1653  {
1655  refPt = selection.GetReferencePoint();
1656  else
1657  refPt = static_cast<BOARD_ITEM*>( selection.GetItem( 0 ) )->GetPosition();
1658  }
1659 
1660  bool leftRight = frame()->Settings().m_FlipLeftRight;
1661 
1662  // When editing footprints, all items have the same parent
1663  if( IsFootprintEditor() )
1664  m_commit->Modify( selection.Front() );
1665 
1666  for( EDA_ITEM* item : selection )
1667  {
1668  if( !item->IsNew() && !IsFootprintEditor() )
1669  m_commit->Modify( item );
1670 
1671  if( item->Type() == PCB_GROUP_T )
1672  {
1673  static_cast<PCB_GROUP*>( item )->RunOnDescendants( [&]( BOARD_ITEM* bItem )
1674  {
1675  m_commit->Modify( bItem );
1676  });
1677  }
1678 
1679  static_cast<BOARD_ITEM*>( item )->Flip( refPt, leftRight );
1680  }
1681 
1682  if( !m_dragging )
1683  m_commit->Push( _( "Change Side / Flip" ) );
1684 
1685  if( selection.IsHover() && !m_dragging )
1687 
1689 
1690  if( m_dragging )
1692 
1693  // Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
1694  // to this now invalid reference
1695  if( oldRefPt )
1696  selection.SetReferencePoint( *oldRefPt );
1697  else
1699 
1700  return 0;
1701 }
1702 
1703 
1704 int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent )
1705 {
1706  if( isRouterActive() )
1707  {
1708  wxBell();
1709  return 0;
1710  }
1711 
1712  std::vector<BOARD_ITEM*> lockedItems;
1713  Activate();
1714 
1715  // get a copy instead of reference (as we're going to clear the selection before removing items)
1716  PCB_SELECTION selectionCopy;
1719 
1720  // If we are in a "Cut" operation, then the copied selection exists already and we want to
1721  // delete exactly that; no more, no fewer. Any filtering for locked items must be done in
1722  // the copyToClipboard() routine.
1723  if( isCut )
1724  {
1725  selectionCopy = m_selectionTool->GetSelection();
1726  }
1727  else
1728  {
1729  selectionCopy = m_selectionTool->RequestSelection(
1730  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1731  {
1732  },
1733  !m_isFootprintEditor /* prompt user regarding locked items */ );
1734  }
1735 
1736  bool isHover = selectionCopy.IsHover();
1737 
1738  // in "alternative" mode, deletion is not just a simple list of selected items,
1739  // it removes whole tracks, not just segments
1740  if( isAlt && isHover
1741  && ( selectionCopy.HasType( PCB_TRACE_T ) || selectionCopy.HasType( PCB_VIA_T ) ) )
1742  {
1744  }
1745 
1746  if( selectionCopy.Empty() )
1747  return 0;
1748 
1749  // As we are about to remove items, they have to be removed from the selection first
1751 
1752  for( EDA_ITEM* item : selectionCopy )
1753  {
1754  PCB_GROUP* parentGroup = static_cast<BOARD_ITEM*>( item )->GetParentGroup();
1755 
1756  if( parentGroup )
1757  {
1758  m_commit->Modify( parentGroup );
1759  parentGroup->RemoveItem( static_cast<BOARD_ITEM*>( item ) );
1760  }
1761 
1762  switch( item->Type() )
1763  {
1764  case PCB_FP_TEXT_T:
1765  {
1766  FP_TEXT* text = static_cast<FP_TEXT*>( item );
1767  FOOTPRINT* parent = static_cast<FOOTPRINT*>( item->GetParent() );
1768 
1769  if( text->GetType() == FP_TEXT::TEXT_is_DIVERS )
1770  {
1771  m_commit->Modify( parent );
1772  getView()->Remove( text );
1773  parent->Remove( text );
1774  }
1775  }
1776  break;
1777 
1778  case PCB_PAD_T:
1779  {
1780  PAD* pad = static_cast<PAD*>( item );
1781  FOOTPRINT* parent = static_cast<FOOTPRINT*>( item->GetParent() );
1782 
1783  m_commit->Modify( parent );
1784  getView()->Remove( pad );
1785  parent->Remove( pad );
1786  }
1787  break;
1788 
1789  case PCB_FP_ZONE_T:
1790  {
1791  FP_ZONE* zone = static_cast<FP_ZONE*>( item );
1792  FOOTPRINT* parent = static_cast<FOOTPRINT*>( item->GetParent() );
1793 
1794  m_commit->Modify( parent );
1795  getView()->Remove( zone );
1796  parent->Remove( zone );
1797  }
1798  break;
1799 
1800  case PCB_ZONE_T:
1801  // We process the zones special so that cutouts can be deleted when the delete tool
1802  // is called from inside a cutout when the zone is selected.
1803  {
1804  // Only interact with cutouts when deleting and a single item is selected
1805  if( !isCut && selectionCopy.GetSize() == 1 )
1806  {
1808  ZONE* zone = static_cast<ZONE*>( item );
1809 
1810  int outlineIdx, holeIdx;
1811 
1812  if( zone->HitTestCutout( curPos, &outlineIdx, &holeIdx ) )
1813  {
1814  // Remove the cutout
1815  m_commit->Modify( zone );
1816  zone->RemoveCutout( outlineIdx, holeIdx );
1817  zone->UnFill();
1818  // TODO Refill zone when KiCad supports auto re-fill
1819 
1820  // Update the display
1821  zone->HatchBorder();
1822  canvas()->Refresh();
1823 
1824  // Restore the selection on the original zone
1826 
1827  break;
1828  }
1829  }
1830 
1831  // Remove the entire zone otherwise
1832  m_commit->Remove( item );
1833  }
1834  break;
1835 
1836  case PCB_GROUP_T:
1837  {
1838  PCB_GROUP* group = static_cast<PCB_GROUP*>( item );
1839 
1840  auto removeItem = [&]( BOARD_ITEM* bItem )
1841  {
1842  if( bItem->GetParent() && bItem->GetParent()->Type() == PCB_FOOTPRINT_T )
1843  {
1844  // Silently ignore delete of Reference or Value if they happen to be in
1845  // group.
1846  if( bItem->Type() == PCB_FP_TEXT_T )
1847  {
1848  if( static_cast<FP_TEXT*>( bItem )->GetType() != FP_TEXT::TEXT_is_DIVERS )
1849  return;
1850  }
1851 
1852  m_commit->Modify( bItem->GetParent() );
1853  getView()->Remove( bItem );
1854  bItem->GetParent()->Remove( bItem );
1855  }
1856  else
1857  {
1858  m_commit->Remove( bItem );
1859  }
1860  };
1861 
1862  removeItem( group );
1863 
1864  group->RunOnDescendants( [&]( BOARD_ITEM* aDescendant )
1865  {
1866  removeItem( aDescendant );
1867  });
1868  }
1869  break;
1870 
1871  default:
1872  m_commit->Remove( item );
1873  break;
1874  }
1875  }
1876 
1877  // If the entered group has been emptied then leave it.
1878  PCB_GROUP* enteredGroup = m_selectionTool->GetEnteredGroup();
1879 
1880  if( enteredGroup && enteredGroup->GetItems().empty() )
1882 
1883  if( isCut )
1884  m_commit->Push( _( "Cut" ) );
1885  else
1886  m_commit->Push( _( "Delete" ) );
1887 
1888  return 0;
1889 }
1890 
1891 
1892 int EDIT_TOOL::MoveExact( const TOOL_EVENT& aEvent )
1893 {
1894  if( isRouterActive() )
1895  {
1896  wxBell();
1897  return 0;
1898  }
1899 
1901  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1902  {
1903  // Iterate from the back so we don't have to worry about removals.
1904  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1905  {
1906  BOARD_ITEM* item = aCollector[i];
1907 
1908  if( item->Type() == PCB_MARKER_T )
1909  aCollector.Remove( item );
1910  }
1911 
1912  sTool->FilterCollectorForHierarchy( aCollector, true );
1913  },
1914  true /* prompt user regarding locked items */ );
1915 
1916  if( selection.Empty() )
1917  return 0;
1918 
1919  wxPoint translation;
1920  double rotation;
1921  ROTATION_ANCHOR rotationAnchor = selection.Size() > 1 ? ROTATE_AROUND_SEL_CENTER
1923 
1924  // TODO: Implement a visible bounding border at the edge
1925  auto sel_box = selection.GetBoundingBox();
1926 
1927  DIALOG_MOVE_EXACT dialog( frame(), translation, rotation, rotationAnchor, sel_box );
1928  int ret = dialog.ShowModal();
1929 
1930  if( ret == wxID_OK )
1931  {
1932  VECTOR2I rp = selection.GetCenter();
1933  wxPoint selCenter( rp.x, rp.y );
1934 
1935  // Make sure the rotation is from the right reference point
1936  selCenter += translation;
1937 
1938  // When editing footprints, all items have the same parent
1939  if( IsFootprintEditor() )
1940  m_commit->Modify( selection.Front() );
1941 
1942  for( EDA_ITEM* selItem : selection )
1943  {
1944  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selItem );
1945 
1946  if( !item->IsNew() && !IsFootprintEditor() )
1947  {
1948  m_commit->Modify( item );
1949 
1950  if( item->Type() == PCB_GROUP_T )
1951  {
1952  PCB_GROUP* group = static_cast<PCB_GROUP*>( item );
1953 
1954  group->RunOnDescendants( [&]( BOARD_ITEM* bItem )
1955  {
1956  m_commit->Modify( bItem );
1957  });
1958  }
1959  }
1960 
1961  if( !item->GetParent() || !item->GetParent()->IsSelected() )
1962  item->Move( translation );
1963 
1964  switch( rotationAnchor )
1965  {
1967  item->Rotate( item->GetPosition(), rotation );
1968  break;
1970  item->Rotate( selCenter, rotation );
1971  break;
1973  item->Rotate( (wxPoint) frame()->GetScreen()->m_LocalOrigin, rotation );
1974  break;
1976  item->Rotate( board()->GetDesignSettings().m_AuxOrigin, rotation );
1977  break;
1978  }
1979 
1980  if( !m_dragging )
1981  getView()->Update( item );
1982  }
1983 
1984  m_commit->Push( _( "Move exact" ) );
1985 
1986  if( selection.IsHover() )
1988 
1990 
1991  if( m_dragging )
1993  }
1994 
1995  return 0;
1996 }
1997 
1998 
1999 int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent )
2000 {
2001  if( isRouterActive() )
2002  {
2003  wxBell();
2004  return 0;
2005  }
2006 
2007  bool increment = aEvent.IsAction( &PCB_ACTIONS::duplicateIncrement );
2008 
2009  // Be sure that there is at least one item that we can modify
2011  []( const VECTOR2I&, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2012  {
2013  // Iterate from the back so we don't have to worry about removals.
2014  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
2015  {
2016  BOARD_ITEM* item = aCollector[i];
2017 
2018  if( item->Type() == PCB_MARKER_T )
2019  aCollector.Remove( item );
2020  }
2021 
2022  sTool->FilterCollectorForHierarchy( aCollector, true );
2023  } );
2024 
2025  if( selection.Empty() )
2026  return 0;
2027 
2028  // we have a selection to work on now, so start the tool process
2029  PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2030 
2031  // If the selection was given a hover, we do not keep the selection after completion
2032  bool is_hover = selection.IsHover();
2033 
2034  std::vector<BOARD_ITEM*> new_items;
2035  new_items.reserve( selection.Size() );
2036 
2037 
2038  // Each selected item is duplicated and pushed to new_items list
2039  // Old selection is cleared, and new items are then selected.
2040  for( EDA_ITEM* item : selection )
2041  {
2042  BOARD_ITEM* dupe_item = nullptr;
2043  BOARD_ITEM* orig_item = static_cast<BOARD_ITEM*>( item );
2044 
2045  if( m_isFootprintEditor )
2046  {
2047  FOOTPRINT* parentFootprint = editFrame->GetBoard()->GetFirstFootprint();
2048  dupe_item = parentFootprint->DuplicateItem( orig_item );
2049 
2050  if( increment && item->Type() == PCB_PAD_T
2051  && PAD_NAMING::PadCanHaveName( *static_cast<PAD*>( dupe_item ) ) )
2052  {
2053  PAD_TOOL* padTool = m_toolMgr->GetTool<PAD_TOOL>();
2054  wxString padName = padTool->GetLastPadName();
2055  padName = parentFootprint->GetNextPadName( padName );
2056  padTool->SetLastPadName( padName );
2057  static_cast<PAD*>( dupe_item )->SetName( padName );
2058  }
2059  }
2060  else if( orig_item->GetParent() && orig_item->GetParent()->Type() == PCB_FOOTPRINT_T )
2061  {
2062  FOOTPRINT* parentFootprint = static_cast<FOOTPRINT*>( orig_item->GetParent() );
2063 
2064  m_commit->Modify( parentFootprint );
2065  dupe_item = parentFootprint->DuplicateItem( orig_item, true /* add to parent */ );
2066  }
2067  else
2068  {
2069  switch( orig_item->Type() )
2070  {
2071  case PCB_FOOTPRINT_T:
2072  case PCB_TEXT_T:
2073  case PCB_SHAPE_T:
2074  case PCB_TRACE_T:
2075  case PCB_ARC_T:
2076  case PCB_VIA_T:
2077  case PCB_ZONE_T:
2078  case PCB_TARGET_T:
2079  case PCB_DIM_ALIGNED_T:
2080  case PCB_DIM_CENTER_T:
2081  case PCB_DIM_ORTHOGONAL_T:
2082  case PCB_DIM_LEADER_T:
2083  dupe_item = orig_item->Duplicate();
2084  break;
2085 
2086  case PCB_GROUP_T:
2087  dupe_item = static_cast<PCB_GROUP*>( orig_item )->DeepDuplicate();
2088  break;
2089 
2090  default:
2091  wxASSERT_MSG( false, wxString::Format( "Unhandled item type %d",
2092  orig_item->Type() ) );
2093  break;
2094  }
2095  }
2096 
2097  if( dupe_item )
2098  {
2099  if( dupe_item->Type() == PCB_GROUP_T )
2100  {
2101  static_cast<PCB_GROUP*>( dupe_item )->RunOnDescendants(
2102  [&]( BOARD_ITEM* bItem )
2103  {
2104  m_commit->Add( bItem );
2105  });
2106  }
2107 
2108  // Clear the selection flag here, otherwise the PCB_SELECTION_TOOL
2109  // will not properly select it later on
2110  dupe_item->ClearSelected();
2111 
2112  new_items.push_back( dupe_item );
2113  m_commit->Add( dupe_item );
2114  }
2115  }
2116 
2117  // Clear the old selection first
2119 
2120  // Select the new items
2121  m_toolMgr->RunAction( PCB_ACTIONS::selectItems, true, &new_items );
2122 
2123  // record the new items as added
2124  if( !selection.Empty() )
2125  {
2126  editFrame->DisplayToolMsg( wxString::Format( _( "Duplicated %d item(s)" ),
2127  (int) new_items.size() ) );
2128 
2129  // TODO(ISM): This line can't be used to activate the tool until we allow multiple activations
2130  // m_toolMgr->RunAction( PCB_ACTIONS::move, true );
2131  // Instead we have to create the event and call the tool's function
2132  // directly
2133 
2134  // If items were duplicated, pick them up
2135  // this works well for "dropping" copies around and pushes the commit
2137  Move( evt );
2138 
2139  // Deslect the duplicated item if we originally started as a hover selection
2140  if( is_hover )
2142  }
2143 
2144  return 0;
2145 }
2146 
2147 
2149 {
2150  if( isRouterActive() )
2151  {
2152  wxBell();
2153  return 0;
2154  }
2155 
2156  // Be sure that there is at least one item that we can modify
2158  []( const VECTOR2I&, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2159  {
2160  sTool->FilterCollectorForHierarchy( aCollector, true );
2161  } );
2162 
2163  if( selection.Empty() )
2164  return 0;
2165 
2166  // we have a selection to work on now, so start the tool process
2167  PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
2168  ARRAY_CREATOR array_creator( *editFrame, m_isFootprintEditor, selection );
2169  array_creator.Invoke();
2170 
2171  return 0;
2172 }
2173 
2174 
2176  PCB_SELECTION_TOOL* sTool )
2177 {
2178  for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
2179  {
2180  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( aCollector[i] );
2181 
2182  if( item->Type() != PCB_PAD_T )
2183  aCollector.Remove( i );
2184  }
2185 }
2186 
2187 
2189  PCB_SELECTION_TOOL* sTool )
2190 {
2191  for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
2192  {
2193  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( aCollector[i] );
2194 
2195  if( item->Type() != PCB_FOOTPRINT_T )
2196  aCollector.Remove( i );
2197  }
2198 }
2199 
2200 
2202 {
2203  if( m_dragging && aSelection.HasReferencePoint() )
2204  return false;
2205 
2206  // When there is only one item selected, the reference point is its position...
2207  if( aSelection.Size() == 1 )
2208  {
2209  auto item = static_cast<BOARD_ITEM*>( aSelection.Front() );
2210  auto pos = item->GetPosition();
2211  aSelection.SetReferencePoint( VECTOR2I( pos.x, pos.y ) );
2212  }
2213  // ...otherwise modify items with regard to the grid-snapped center position
2214  else
2215  {
2216  PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
2217  aSelection.SetReferencePoint( grid.BestSnapAnchor( aSelection.GetCenter(), nullptr ) );
2218  }
2219 
2220  return true;
2221 }
2222 
2223 
2224 bool EDIT_TOOL::pickReferencePoint( const wxString& aTooltip, const wxString& aSuccessMessage,
2225  const wxString& aCanceledMessage, VECTOR2I& aReferencePoint )
2226 {
2228  OPT<VECTOR2I> pickedPoint;
2229  bool done = false;
2230 
2231  m_statusPopup->SetText( aTooltip );
2232 
2233  picker->SetClickHandler(
2234  [&]( const VECTOR2D& aPoint ) -> bool
2235  {
2236  pickedPoint = aPoint;
2237 
2238  if( !aSuccessMessage.empty() )
2239  {
2240  m_statusPopup->SetText( aSuccessMessage );
2241  m_statusPopup->Expire( 800 );
2242  }
2243  else
2244  {
2245  m_statusPopup->Hide();
2246  }
2247 
2248  return false; // we don't need any more points
2249  } );
2250 
2251  picker->SetMotionHandler(
2252  [&]( const VECTOR2D& aPos )
2253  {
2254  m_statusPopup->Move( wxGetMousePosition() + wxPoint( 20, -50 ) );
2255  } );
2256 
2257  picker->SetCancelHandler(
2258  [&]()
2259  {
2260  if( !aCanceledMessage.empty() )
2261  {
2262  m_statusPopup->SetText( aCanceledMessage );
2263  m_statusPopup->Expire( 800 );
2264  }
2265  else
2266  {
2267  m_statusPopup->Hide();
2268  }
2269  } );
2270 
2271  picker->SetFinalizeHandler(
2272  [&]( const int& aFinalState )
2273  {
2274  done = true;
2275  } );
2276 
2277  m_statusPopup->Move( wxGetMousePosition() + wxPoint( 20, -50 ) );
2278  m_statusPopup->Popup();
2279 
2280  std::string tool = "";
2281  m_toolMgr->RunAction( ACTIONS::pickerTool, true, &tool );
2282 
2283  while( !done )
2284  {
2285  // Pass events unless we receive a null event, then we must shut down
2286  if( TOOL_EVENT* evt = Wait() )
2287  evt->SetPassEvent();
2288  else
2289  break;
2290  }
2291 
2292  // Ensure statusPopup is hidden after use and before deleting it:
2293  m_statusPopup->Hide();
2294 
2295  if( pickedPoint.is_initialized() )
2296  aReferencePoint = pickedPoint.get();
2297 
2298  return pickedPoint.is_initialized();
2299 }
2300 
2301 
2303 {
2304  std::string tool = "pcbnew.InteractiveEdit.selectReferencePoint";
2305  CLIPBOARD_IO io;
2306  PCB_GRID_HELPER grid( m_toolMgr, getEditFrame<PCB_BASE_EDIT_FRAME>()->GetMagneticItemsSettings() );
2307 
2308  frame()->PushTool( tool );
2309  Activate();
2310 
2312  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2313  {
2314  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
2315  {
2316  BOARD_ITEM* item = aCollector[i];
2317 
2318  // We can't copy both a footprint and its text in the same operation, so if
2319  // both are selected, remove the text
2320  if( item->Type() == PCB_FP_TEXT_T && aCollector.HasItem( item->GetParent() ) )
2321  aCollector.Remove( item );
2322  }
2323  },
2324  aEvent.IsAction( &ACTIONS::cut ) && !m_isFootprintEditor /* prompt user regarding locked items */ );
2325 
2326  if( !selection.Empty() )
2327  {
2328  std::vector<BOARD_ITEM*> items;
2329 
2330  for( EDA_ITEM* item : selection )
2331  items.push_back( static_cast<BOARD_ITEM*>( item ) );
2332 
2333  VECTOR2I refPoint;
2334 
2335  if( aEvent.IsAction( &PCB_ACTIONS::copyWithReference ) )
2336  {
2337  if( !pickReferencePoint( _( "Select reference point for the copy..." ),
2338  _( "Selection copied" ),
2339  _( "Copy cancelled" ),
2340  refPoint ) )
2341  return 0;
2342  }
2343  else
2344  {
2345  refPoint = grid.BestDragOrigin( getViewControls()->GetCursorPosition(), items );
2346  }
2347 
2348  selection.SetReferencePoint( refPoint );
2349 
2350  io.SetBoard( board() );
2352  frame()->SetStatusText( _( "Selection copied" ) );
2353  }
2354 
2355  frame()->PopTool( tool );
2356 
2357  return 0;
2358 }
2359 
2360 
2362 {
2363  if( !copyToClipboard( aEvent ) )
2364  {
2365  // N.B. Setting the CUT flag prevents lock filtering as we only want to delete the items
2366  // that were copied to the clipboard, no more, no fewer. Filtering for locked item, if
2367  // any will be done in the copyToClipboard() routine
2368  TOOL_EVENT evt( aEvent.Category(), aEvent.Action(), TOOL_ACTION_SCOPE::AS_GLOBAL );
2370  Remove( evt );
2371  }
2372 
2373  return 0;
2374 }
2375 
2376 
2378 {
2380  Go( &EDIT_TOOL::Move, PCB_ACTIONS::move.MakeEvent() );
2381  Go( &EDIT_TOOL::Drag, PCB_ACTIONS::drag45Degree.MakeEvent() );
2383  Go( &EDIT_TOOL::Rotate, PCB_ACTIONS::rotateCw.MakeEvent() );
2384  Go( &EDIT_TOOL::Rotate, PCB_ACTIONS::rotateCcw.MakeEvent() );
2385  Go( &EDIT_TOOL::Flip, PCB_ACTIONS::flip.MakeEvent() );
2386  Go( &EDIT_TOOL::Remove, ACTIONS::doDelete.MakeEvent() );
2387  Go( &EDIT_TOOL::Remove, PCB_ACTIONS::deleteFull.MakeEvent() );
2391  Go( &EDIT_TOOL::Duplicate, ACTIONS::duplicate.MakeEvent() );
2394  Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirror.MakeEvent() );
2397 
2398  Go( &EDIT_TOOL::copyToClipboard, ACTIONS::copy.MakeEvent() );
2400  Go( &EDIT_TOOL::cutToClipboard, ACTIONS::cut.MakeEvent() );
2401 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:148
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition: pcb_actions.h:70
long long GetValue()
Returns the value in internal units.
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:63
VECTOR2I GetReferencePoint() const
Definition: selection.h:259
PCB_GROUP * GetParentGroup() const
Definition: board_item.h:93
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
static bool ShowAlways(const SELECTION &aSelection)
The default condition function (always returns true).
void setTransitions() override
< Set up handlers for various events.
Definition: edit_tool.cpp:2377
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
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:1615
VECTOR2I m_cursor
Definition: edit_tool.h:193
void ClearReferencePoint()
Definition: selection.h:269
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:646
bool IsCurrentTool(const TOOL_ACTION &aAction) const
void SetOffset(const wxPoint &aOffset)
Definition: pad.h:248
Definition: track.h:343
virtual wxPoint GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: track.h:301
int DragArcTrack(const TOOL_EVENT &aEvent)
Drag-resize an arc (and change end points of connected straight segments).
Definition: edit_tool.cpp:326
int Properties(const TOOL_EVENT &aEvent)
Display properties window for the selected object.
Definition: edit_tool.cpp:1362
int Rotate(const TOOL_EVENT &aEvent)
Rotate currently selected items.
Definition: edit_tool.cpp:1408
static TOOL_ACTION move
move or drag an item
Definition: pcb_actions.h:96
static TOOL_ACTION deleteFull
Definition: pcb_actions.h:131
class ALIGNED_DIMENSION, a linear dimension (graphic item)
Definition: typeinfo.h:100
class LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:101
void RemoveCutout(int aOutlineIdx, int aHoleIdx)
Remove a cutout from the zone.
Definition: zone.cpp:790
bool IsHover() const
Definition: selection.h:72
double GetLineLength(const wxPoint &aPointA, const wxPoint &aPointB)
Return the length of a line segment defined by aPointA and aPointB.
Definition: trigo.h:223
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
class FP_TEXT, text in a footprint
Definition: typeinfo.h:92
static const KICAD_T Tracks[]
A scan list for only TRACKs.
Definition: collectors.h:299
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.
virtual VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const =0
Return the current mouse pointer position.
BOARD * board() const
static TOOL_ACTION editFpInFpEditor
Definition: pcb_actions.h:347
static TOOL_ACTION pageSettings
Definition: actions.h:59
REMOVE_FLAGS
Definition: actions.h:191
bool IsSelected() const
Definition: eda_item.h:173
static TOOL_ACTION changeTrackWidth
Update selected tracks & vias to the current track & via dimensions.
Definition: pcb_actions.h:115
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:194
This file is part of the common library.
static TOOL_ACTION doDelete
Definition: actions.h:75
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:82
void SetEnd(const wxPoint &aEnd)
Definition: track.h:112
bool HasType(KICAD_T aType) const
Checks if there is at least one item of requested kind.
Definition: selection.h:233
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:108
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
Definition: action_menu.cpp:72
std::unique_ptr< BOARD_COMMIT > m_commit
Definition: edit_tool.h:191
void ClearSelected()
Definition: eda_item.h:182
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
const wxPoint & GetStart() const
Definition: track.h:116
static constexpr double IU_PER_MM
Mock up a conversion function.
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:50
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:251
static SELECTION_CONDITION OnlyTypes(const KICAD_T aTypes[])
Create a functor that tests if the selected items are only of given types.
class CENTER_DIMENSION, a center point marking (graphic item)
Definition: typeinfo.h:102
int ChangeTrackWidth(const TOOL_EVENT &aEvent)
Definition: edit_tool.cpp:1089
virtual VECTOR2I GetCenter() const
Returns the center point of the selection area bounding box.
Definition: selection.h:139
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:46
static TOOL_ACTION dragFreeAngle
Definition: pcb_actions.h:142
Tool is invoked after being inactive.
Definition: tool_base.h:80
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:215
ITER end()
Definition: selection.h:63
virtual void OnEditItemRequest(BOARD_ITEM *aItem)=0
Install the corresponding dialog editor for the given item.
virtual void PushTool(const std::string &actionName)
NB: the definition of "tool" is different at the user level.
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:352
static TOOL_ACTION properties
Activation of the edit tool.
Definition: pcb_actions.h:121
Tool relating to pads and pad settings.
Definition: pad_tool.h:35
void Remove(BOARD_ITEM *aItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: footprint.cpp:521
TOOL_MENU & GetToolMenu()
class PCB_TEXT, text on a layer
Definition: typeinfo.h:91
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:141
static TOOL_ACTION selectConnection
Select tracks between junctions or expands an existing selection to pads or the entire connection.
Definition: pcb_actions.h:78
class ARC, an arc track segment on a copper layer
Definition: typeinfo.h:97
static TOOL_ACTION getAndPlace
Find an item and start moving.
Definition: pcb_actions.h:467
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.h:593
static TOOL_ACTION drag45Degree
Definition: pcb_actions.h:141
TOOL_EVENT_CATEGORY Category() const
Returns more specific information about the type of an event.
Definition: tool_event.h:248
static TOOL_ACTION mirror
Mirroring of selected items.
Definition: pcb_actions.h:112
virtual void SetCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true, bool aTriggeredByArrows=false, long aArrowCommand=0)=0
Move cursor to the requested position expressed in world coordinates.
static TOOL_ACTION changeFootprint
Definition: pcb_actions.h:327
static void mirrorPadX(PAD &aPad, const wxPoint &aMirrorPoint)
Mirror a pad in the vertical axis passing through a point (mirror left to right)
Definition: edit_tool.cpp:1502
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
class PAD, a pad in a footprint
Definition: typeinfo.h:89
bool HitTestDrawingSheetItems(KIGFX::VIEW *aView, const wxPoint &aPosition)
PCB_GROUP * GetEnteredGroup()
static TOOL_ACTION updateFootprint
Definition: pcb_actions.h:325
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition: seg.h:208
void SetFinalizeHandler(FINALIZE_HANDLER aHandler)
Set a handler for the finalize event.
Definition: picker_tool.h:102
PCB_SELECTION_TOOL * m_selectionTool
Definition: edit_tool.h:190
void UndoRedoBlock(bool aBlock=true)
Enable/disable undo and redo operations.
virtual MAGNETIC_SETTINGS * GetMagneticItemsSettings()
virtual wxPoint GetPosition() const
Definition: eda_item.h:302
void SetDelta(const wxSize &aSize)
Definition: pad.h:238
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.
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
void SetBoard(BOARD *aBoard)
const PCB_DISPLAY_OPTIONS & GetDisplayOptions() const
Display options control the way tracks, vias, outlines and other things are shown (for instance solid...
std::unordered_set< BOARD_ITEM * > & GetItems()
Definition: pcb_group.h:68
static bool NotEmpty(const SELECTION &aSelection)
Test if there are any items selected.
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).
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition: collector.h:115
search types array terminator (End Of Types)
Definition: typeinfo.h:81
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:77
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:206
CIRCLE & ConstructFromTanTanPt(const SEG &aLineA, const SEG &aLineB, const VECTOR2I &aP)
Constructs this circle such that it is tangent to the given segmetns and passes through the given poi...
Definition: circle.cpp:51
class TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
int Mirror(const TOOL_EVENT &aEvent)
Mirror the current selection.
Definition: edit_tool.cpp:1524
PADS & Pads()
Definition: footprint.h:164
const VC_SETTINGS & GetSettings() const
Apply VIEW_CONTROLS settings from an object.
bool IsNew() const
Definition: eda_item.h:169
void SetWidth(int aWidth)
Definition: track.h:109
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:70
static TOOL_ACTION moveExact
Activation of the exact move tool.
Definition: pcb_actions.h:124
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:2361
bool pickReferencePoint(const wxString &aTooltip, const wxString &aSuccessMessage, const wxString &aCanceledMessage, VECTOR2I &aReferencePoint)
Definition: edit_tool.cpp:2224
DS_PROXY_VIEW_ITEM * GetDrawingSheet() const
static TOOL_ACTION pickerTool
Definition: actions.h:158
void Append(EDA_ITEM *item)
Add an item to the end of the list.
Definition: collector.h:105
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:118
PCB_BASE_EDIT_FRAME * frame() const
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:519
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 SetParameter(T aParam)
Set a non-standard parameter assigned to the event.
Definition: tool_event.h:466
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:87
PCB_SELECTION & GetSelection()
Return the set of currently selected items.
ITER begin()
Definition: selection.h:62
static TOOL_ACTION copy
Definition: actions.h:70
int Flip(const TOOL_EVENT &aEvent)
Rotate currently selected items.
Definition: edit_tool.cpp:1622
void SetReferencePoint(const VECTOR2I &aP)
Definition: selection.h:264
static TOOL_ACTION duplicateIncrement
Activation of the duplication tool with incrementing (e.g. pad number)
Definition: pcb_actions.h:127
const wxPoint & GetOffset() const
Definition: pad.h:249
bool isInteractiveDragEnabled() const
Definition: edit_tool.cpp:263
int GetAndPlace(const TOOL_EVENT &aEvent)
Definition: edit_tool.cpp:212
VECTOR2I LineProject(const VECTOR2I &aP) const
Compute the perpendicular projection point of aP on a line passing through ends of the segment.
Definition: seg.h:404
static TOOL_ACTION rotateCw
Rotation of selected objects.
Definition: pcb_actions.h:105
LSET is a set of PCB_LAYER_IDs.
const wxPoint & GetMid() const
Definition: track.h:292
const PCB_SELECTION & selection() const
void SetFlags(STATUS_FLAGS aMask)
Definition: eda_item.h:203
void SetLastPadName(const wxString &aPadName)
Definition: pad_tool.h:63
FOOTPRINT * GetFirstFootprint() const
Gets the first footprint on the board or nullptr.
Definition: board.h:382
#define NULL
double GetOrientation() const
Return the rotation angle of the pad in a variety of units (the basic call returns tenths of degrees)...
Definition: pad.h:349
An extension of WX_TEXT_ENTRY_DIALOG that uses UNIT_BINDER to request a dimension (e....
const VECTOR2I GetArcMid(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, bool aMinArcAngle=true)
Returns the middle point of an arc, half-way between aStart and aEnd.
Definition: trigo.cpp:162
int doMoveSelection(TOOL_EVENT aEvent, bool aPickReference=false)
Definition: edit_tool.cpp:689
wxString GetLastPadName() const
Definition: pad_tool.h:62
bool updateModificationPoint(PCB_SELECTION &aSelection)
Definition: edit_tool.cpp:2201
bool m_dragging
Definition: edit_tool.h:192
virtual EDA_RECT GetBoundingBox() const
Definition: selection.h:180
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:927
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
virtual void Move(const wxPoint &aMoveVector)
Move this object.
Definition: board_item.h:277
virtual void PopTool(const std::string &actionName)
int ShowQuasiModal()
void AddItemToSel(BOARD_ITEM *aItem, bool aQuietMode=false)
Select all items on the board.
T Parameter() const
Return a non-standard parameter assigned to the event.
Definition: tool_event.h:448
bool PadCanHaveName(const PAD &aPad)
Check if a pad should be named.
Definition: pad_naming.cpp:26
const VECTOR2I & GetP0() const
Definition: shape_arc.h:95
Generic, UI-independent tool event.
Definition: tool_event.h:173
A collection of nets and the parameters used to route or test these nets.
Definition: netclass.h:46
static TOOL_ACTION createArray
Tool for creating an array of objects.
Definition: pcb_actions.h:391
static const int MIN_PRECISION_IU
This is the minimum precision for all the points in a shape.
Definition: shape.h:122
bool Contains(EDA_ITEM *aItem) const
Definition: selection.h:114
void SetMotionHandler(MOTION_HANDLER aHandler)
Set a handler for mouse motion.
Definition: picker_tool.h:82
FOOTPRINT * footprint() const
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:416
static TOOL_ACTION cut
Definition: actions.h:69
bool ToolStackIsEmpty()
Definition: tools_holder.h:117
bool HasItem(const EDA_ITEM *aItem) const
Tests if aItem has already been collected.
Definition: collector.h:203
int FilletTracks(const TOOL_EVENT &aEvent)
Fillet (i.e.
Definition: edit_tool.cpp:1159
bool Init() override
Init() is called once upon a registration of the tool.
Definition: edit_tool.cpp:101
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition: seg.h:437
An interface for classes handling user events controlling the view behavior such as zooming,...
int MoveWithReference(const TOOL_EVENT &aEvent)
Move an item but with a reference point selected first.
Definition: edit_tool.cpp:675
PAD_SHAPE GetShape() const
Definition: pad.h:169
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:279
void SetX0(int x)
Definition: pad.h:229
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
bool RemoveItem(BOARD_ITEM *aItem)
Remove item from group.
Definition: pcb_group.cpp:50
std::unique_ptr< STATUS_TEXT_POPUP > m_statusPopup
Definition: edit_tool.h:195
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
int Radius
Public to make access simpler.
Definition: circle.h:36
class ZONE, a copper pour area
Definition: typeinfo.h:105
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:209
Class Circle Represents basic circle geometry with utility geometry functions.
Definition: circle.h:33
virtual void Rotate(const wxPoint &aRotCentre, double aAngle)
Rotate this object.
Definition: board_item.h:294
int GetuViaDiameter() const
Definition: netclass.h:140
Global action (toolbar/main menu event, global shortcut)
Definition: tool_event.h:151
virtual KIGFX::VIEW_ITEM * GetItem(unsigned int aIdx) const override
Definition: selection.h:106
void ExitGroup(bool aSelectGroup=false)
Leave the currently entered group.
static TOOL_ACTION copyWithReference
copy command with manual reference point selection
Definition: pcb_actions.h:102
void SetMid(const wxPoint &aMid)
Definition: track.h:291
ROTATION_ANCHOR
bool m_isFootprintEditor
static TOOL_ACTION routerInlineDrag
Activation of the Push and Shove router (inline dragging mode)
Definition: pcb_actions.h:212
static TOOL_ACTION hideDynamicRatsnest
Definition: pcb_actions.h:460
virtual void SwapData(BOARD_ITEM *aImage) override
Swap data between aItem and aImage.
Definition: track.cpp:916
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
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:2175
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=NULL) override
Update the board display after modifying it by a python script (note: it is automatically called by a...
int Move(const TOOL_EVENT &aEvent)
Main loop in which events are handled.
Definition: edit_tool.cpp:663
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:122
class PCB_TARGET, a target (graphic item)
Definition: typeinfo.h:104
static TOOL_ACTION inlineBreakTrack
Breaks track when router is not activated.
Definition: pcb_actions.h:139
Generic tool for picking an item.
class FOOTPRINT, a footprint
Definition: typeinfo.h:88
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
Definition: edit_tool.cpp:77
BOARD * GetBoard()
void AddSeparator(int aOrder=ANY_ORDER)
Add a separator to the menu.
static TOOL_ACTION pasteSpecial
Definition: actions.h:72
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:101
void SetClickHandler(CLICK_HANDLER aHandler)
Set a handler for mouse click event.
Definition: picker_tool.h:71
static TOOL_ACTION rotateCcw
Definition: pcb_actions.h:106
const wxSize & GetDelta() const
Definition: pad.h:239
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
int MoveExact(const TOOL_EVENT &aEvent)
Invoke a dialog box to allow moving of the item by an exact amount.
Definition: edit_tool.cpp:1892
Definition: seg.h:41
OPT< std::string > GetCommandStr() const
Definition: tool_event.h:476
int Remove(const TOOL_EVENT &aEvent)
Delete currently selected items.
Definition: edit_tool.cpp:1704
static ROUTER * theRouter
Definition: pns_router.cpp:59
static TOOL_ACTION flip
Flipping of selected objects.
Definition: pcb_actions.h:109
double GetAngle() const
Definition: track.cpp:950
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.
SPECIAL_TOOLS_CONTEXT_MENU(TOOL_INTERACTIVE *aTool)
Definition: edit_tool.cpp:88
static const KICAD_T DraggableItems[]
A scan list for items that can be dragged.
Definition: collectors.h:314
virtual bool IsLocked() const
Definition: board_item.h:249
bool invokeInlineRouter(int aDragMode)
Definition: edit_tool.cpp:235
bool HasReferencePoint() const
Definition: selection.h:254
class MARKER_PCB, a marker used to show something
Definition: typeinfo.h:98
void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
void SetTitle(const wxString &aTitle) override
Set title for the menu.
Definition: action_menu.cpp:90
PCB_SELECTION & RequestSelection(CLIENT_SELECTION_FILTER aClientFilter, bool aConfirmLockedItems=false)
Return the current selection set, filtered according to aFlags and aClientFilter.
double m_MaxTangentAngleDeviation
Maximum angle between the tangent line of an arc track and a connected straight track in order to com...
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:2148
KIGFX::VIEW_CONTROLS * controls() const
int GetWidth() const
Definition: track.h:110
STATUS_FLAGS IsPointOnEnds(const wxPoint &point, int min_dist=0) const
Function IsPointOnEnds returns STARTPOINT if point if near (dist = min_dist) start point,...
Definition: track.cpp:188
Common, abstract interface for edit frames.
wxPoint GetPosition() const override
Definition: pad.h:177
Definition: track.h:262
#define _(s)
Definition: 3d_actions.cpp:33
long m_lastKeyboardCursorCommand
Position of the above event.
wxString GetNextPadName(const wxString &aLastPadName) const
Return the next available pad name in the footprint.
Definition: footprint.cpp:1700
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:241
bool m_Live3DRefresh
If true, 3D viewer will redraw on every modification operation.
virtual void Update3DView(bool aMarkDirty, bool aRefresh, const wxString *aTitle=nullptr)
Update the 3D view, if the viewer is opened by this frame.
void AddSubMenu(std::shared_ptr< ACTION_MENU > aSubMenu)
Store a submenu of this menu model.
Definition: tool_menu.cpp:52
bool m_lastKeyboardCursorPositionValid
ACTIONS::CURSOR_UP, ACTIONS::CURSOR_DOWN, etc.
bool IsToolActive() const
Definition: tool_base.cpp:31
void SetPosition(const wxPoint &aPos) override
Definition: pad.h:171
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition: pcb_actions.h:66
void Mirror(const wxPoint &aCentre, bool aMirrorAroundXAxis)
Mirror an edge of the footprint.
Definition: fp_shape.cpp:224
class ZONE, managed by a footprint
Definition: typeinfo.h:94
static SELECTION_CONDITION OnlyType(KICAD_T aType)
Create a functor that tests if the selected items are only of given type.
VECTOR2I A
Definition: seg.h:49
VECTOR2I Center
Public to make access simpler.
Definition: circle.h:37
PCBNEW_SETTINGS & Settings()
TOOL_EVENT MakeEvent() const
Return the event associated with the action (i.e.
Definition: tool_action.h:123
int Size() const
Returns the number of selected parts.
Definition: selection.h:128
bool isRouterActive() const
Definition: edit_tool.cpp:271
void Mirror(const wxPoint &aMirrorRef, bool aMirrorLeftRight)
Mirror the outlines relative to a given horizontal axis the layer is not changed.
Definition: zone.cpp:745
The selection tool: currently supports:
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:68
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:150
bool IsFootprintEditor() const
static wxPoint mirrorPointX(const wxPoint &aPoint, const wxPoint &aMirrorPoint)
Definition: edit_tool.cpp:1487
void ShowInfoBarError(const wxString &aErrorMsg, bool aShowCloseButton=false)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an error icon on the left o...
static TOOL_ACTION positionRelative
Activation of the position relative tool.
Definition: pcb_actions.h:239
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
const wxPoint & GetEnd() const
Definition: track.h:113
boost::optional< T > OPT
Definition: optional.h:7
virtual double GetLength() const
Function GetLength returns the length of the track using the hypotenuse calculation.
Definition: track.h:141
bool GetMoveWarpsCursor() const
Indicate that a move operation should warp the mouse pointer to the origin of the move object.
Definition: tools_holder.h:140
void Activate()
Run the tool.
void SetStart(const wxPoint &aStart)
Definition: track.h:115
int GetuViaDrill() const
Definition: netclass.h:144
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
virtual BOARD_ITEM * Duplicate() const
Create a copy of this BOARD_ITEM.
Definition: board_item.h:202
class ORTHOGONAL_DIMENSION, a linear dimension constrained to x/y
Definition: typeinfo.h:103
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:293
static TOOL_ACTION undo
Definition: actions.h:67
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:322
drawingsheet frame and titleblock
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:2302
static TOOL_ACTION updateLocalRatsnest
Definition: pcb_actions.h:461
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:2188
BOARD * GetBoard() const
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
ROUTER * Router() const
Definition: pad.h:60
bool UnFill()
Removes the zone filling.
Definition: zone.cpp:186
void SaveSelection(const PCB_SELECTION &selected, bool isFootprintEditor)
PCB_DRAW_PANEL_GAL * canvas() const
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:168
static TOOL_ACTION moveWithReference
move with a reference point
Definition: pcb_actions.h:99
int GetEventRotationAngle(const PCB_BASE_EDIT_FRAME &aFrame, const TOOL_EVENT &aEvt)
Function getEventRotationAngle()
void PostEvent(const TOOL_EVENT &aEvent)
Put an event to the event queue to be processed at the end of event processing cycle.
Definition: tool_manager.h:267
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
int Duplicate(const TOOL_EVENT &aEvent)
Duplicate the current selection and starts a move action.
Definition: edit_tool.cpp:1999
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.
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:173
static TOOL_ACTION selectAll
Definition: actions.h:73
const LSET GetSelectionLayers()
ROUTING_SETTINGS & Settings()
Definition: pns_router.h:184
#define IS_NEW
New item, just created.
Definition: eda_item.h:107
bool IsEmpty() const
Definition: board.h:353
const VECTOR2I & GetP1() const
Definition: shape_arc.h:96
static TOOL_ACTION paste
Definition: actions.h:71
A specialization of ZONE for use in footprints.
Definition: zone.h:943
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
static TOOL_ACTION duplicate
Definition: actions.h:74
Definition: track.h:83
void SetOrientation(double aAngle)
Set the rotation angle of the pad.
Definition: pad.cpp:578
static TOOL_ACTION refreshPreview
Definition: actions.h:109
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
void SetCancelHandler(CANCEL_HANDLER aHandler)
Set a handler for cancel events (ESC or context-menu Cancel).
Definition: picker_tool.h:91
EDA_ITEM * Front() const
Definition: selection.h:203
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:1503
int Side(const VECTOR2I &aP) const
Determine on which side of directed line passing via segment ends point aP lies.
Definition: seg.h:143
void DisplayToolMsg(const wxString &msg) override
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:163
VECTOR2I NearestPoint(const VECTOR2I &aP) const
Function NearestPoint()
Definition: circle.cpp:188
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition: view.h:404
VECTOR2I B
Definition: seg.h:50