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