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  true /* prompt user regarding locked items */ );
294 
295  if( selection.Empty() )
296  return 0;
297 
298  if( selection.Size() == 1 && selection.Front()->Type() == PCB_ARC_T )
299  {
300  // TODO: This really should be done in PNS to ensure DRC is maintained, but for now
301  // it allows interactive editing of an arc track
302  return DragArcTrack( aEvent );
303  }
304  else
305  {
306  invokeInlineRouter( mode );
307  }
308 
309  return 0;
310 }
311 
312 
314 {
316 
317  if( selection.Size() != 1 || selection.Front()->Type() != PCB_ARC_T )
318  return 0;
319 
320  PCB_ARC* theArc = static_cast<PCB_ARC*>( selection.Front() );
321  double arcAngleDegrees = std::abs( theArc->GetAngle() ) / 10.0;
322 
323  if( arcAngleDegrees + ADVANCED_CFG::GetCfg().m_MaxTangentAngleDeviation >= 180.0 )
324  {
326  wxString::Format( _( "Unable to resize arc tracks %.1f degrees or greater." ),
327  180.0 - ADVANCED_CFG::GetCfg().m_MaxTangentAngleDeviation ) );
328  return 0; // don't bother with > 180 degree arcs
329  }
330 
332 
333  Activate();
334  // Must be done after Activate() so that it gets set into the correct context
335  controls->ShowCursor( true );
336  controls->SetAutoPan( true );
337 
338  bool restore_state = false;
339  VECTOR2I arcCenter = theArc->GetCenter();
340  SEG tanStart = SEG( arcCenter, theArc->GetStart() ).PerpendicularSeg( theArc->GetStart() );
341  SEG tanEnd = SEG( arcCenter, theArc->GetEnd() ).PerpendicularSeg( theArc->GetEnd() );
342 
343  //Ensure the tangent segments are in the correct orientation
344  VECTOR2I tanIntersect = tanStart.IntersectLines( tanEnd ).get();
345  tanStart.A = tanIntersect;
346  tanStart.B = theArc->GetStart();
347  tanEnd.A = tanIntersect;
348  tanEnd.B = theArc->GetEnd();
349 
350  KICAD_T track_types[] = { PCB_PAD_T, PCB_VIA_T, PCB_TRACE_T, PCB_ARC_T, EOT };
351 
352  auto getUniqueTrackAtAnchorCollinear =
353  [&]( const VECTOR2I& aAnchor, const SEG& aCollinearSeg ) -> PCB_TRACK*
354  {
355  auto conn = board()->GetConnectivity();
356 
357  // Allow items at a distance within the width of the arc track
358  int allowedDeviation = theArc->GetWidth();
359 
360  std::vector<BOARD_CONNECTED_ITEM*> itemsOnAnchor;
361 
362  for( int i = 0; i < 3; i++ )
363  {
364  itemsOnAnchor = conn->GetConnectedItemsAtAnchor( theArc, aAnchor, track_types,
365  allowedDeviation );
366  allowedDeviation /= 2;
367 
368  if( itemsOnAnchor.size() == 1 )
369  break;
370  }
371 
372  PCB_TRACK* retval = nullptr;
373 
374  if( itemsOnAnchor.size() == 1 && itemsOnAnchor.front()->Type() == PCB_TRACE_T )
375  {
376  retval = static_cast<PCB_TRACK*>( itemsOnAnchor.front() );
377  SEG trackSeg( retval->GetStart(), retval->GetEnd() );
378 
379  // Allow deviations in colinearity as defined in ADVANCED_CFG
380  if( trackSeg.AngleDegrees( aCollinearSeg )
382  {
383  retval = nullptr;
384  }
385  }
386 
387  if( !retval )
388  {
389  retval = new PCB_TRACK( theArc->GetParent() );
390  retval->SetStart( (wxPoint) aAnchor );
391  retval->SetEnd( (wxPoint) aAnchor );
392  retval->SetNet( theArc->GetNet() );
393  retval->SetLayer( theArc->GetLayer() );
394  retval->SetWidth( theArc->GetWidth() );
395  retval->SetLocked( theArc->IsLocked() );
396  retval->SetFlags( IS_NEW );
397  getView()->Add( retval );
398  }
399 
400  return retval;
401  };
402 
403  PCB_TRACK* trackOnStart = getUniqueTrackAtAnchorCollinear( theArc->GetStart(), tanStart);
404  PCB_TRACK* trackOnEnd = getUniqueTrackAtAnchorCollinear( theArc->GetEnd(), tanEnd );
405 
406  // Make copies of items to be edited
407  PCB_ARC* theArcCopy = new PCB_ARC( *theArc );
408  PCB_TRACK* trackOnStartCopy = new PCB_TRACK( *trackOnStart );
409  PCB_TRACK* trackOnEndCopy = new PCB_TRACK( *trackOnEnd );
410 
411  if( trackOnStart->GetLength() != 0 )
412  {
413  tanStart.A = trackOnStart->GetStart();
414  tanStart.B = trackOnStart->GetEnd();
415  }
416 
417  if( trackOnEnd->GetLength() != 0 )
418  {
419  tanEnd.A = trackOnEnd->GetStart();
420  tanEnd.B = trackOnEnd->GetEnd();
421  }
422 
423  // Recalculate intersection point
424  tanIntersect = tanStart.IntersectLines( tanEnd ).get();
425 
426  auto isTrackStartClosestToArcStart =
427  [&]( PCB_TRACK* aTrack ) -> bool
428  {
429  double trackStartToArcStart = GetLineLength( aTrack->GetStart(), theArc->GetStart() );
430  double trackEndToArcStart = GetLineLength( aTrack->GetEnd(), theArc->GetStart() );
431 
432  return trackStartToArcStart < trackEndToArcStart;
433  };
434 
435  bool isStartTrackOnStartPt = isTrackStartClosestToArcStart( trackOnStart );
436  bool isEndTrackOnStartPt = isTrackStartClosestToArcStart( trackOnEnd );
437 
438  // Calculate constraints
439  //======================
440  // maxTanCircle is the circle with maximum radius that is tangent to the two adjacent straight
441  // tracks and whose tangent points are constrained within the original tracks and their
442  // projected intersection points.
443  //
444  // The cursor will be constrained first within the isosceles triangle formed by the segments
445  // cSegTanStart, cSegTanEnd and cSegChord. After that it will be constrained to be outside
446  // maxTanCircle.
447  //
448  //
449  // ____________ <-cSegTanStart
450  // / * . ' *
451  // cSegTanEnd-> / * . ' *
452  // /* . ' <-cSegChord *
453  // /. '
454  // /* *
455  //
456  // * c * <-maxTanCircle
457  //
458  // * *
459  //
460  // * *
461  // * *
462  // * *
463  //
464 
465  auto getFurthestPointToTanInterstect =
466  [&]( VECTOR2I& aPointA, VECTOR2I& aPointB ) -> VECTOR2I
467  {
468  if( ( aPointA - tanIntersect ).EuclideanNorm()
469  > ( aPointB - tanIntersect ).EuclideanNorm() )
470  {
471  return aPointA;
472  }
473  else
474  {
475  return aPointB;
476  }
477  };
478 
479  CIRCLE maxTanCircle;
480  VECTOR2I tanStartPoint = getFurthestPointToTanInterstect( tanStart.A, tanStart.B );
481  VECTOR2I tanEndPoint = getFurthestPointToTanInterstect( tanEnd.A, tanEnd.B );
482  VECTOR2I tempTangentPoint = tanEndPoint;
483 
484  if( getFurthestPointToTanInterstect( tanStartPoint, tanEndPoint ) == tanEndPoint )
485  tempTangentPoint = tanStartPoint;
486 
487  maxTanCircle.ConstructFromTanTanPt( tanStart, tanEnd, tempTangentPoint );
488  VECTOR2I maxTanPtStart = tanStart.LineProject( maxTanCircle.Center );
489  VECTOR2I maxTanPtEnd = tanEnd.LineProject( maxTanCircle.Center );
490 
491  SEG cSegTanStart( maxTanPtStart, tanIntersect );
492  SEG cSegTanEnd( maxTanPtEnd, tanIntersect );
493  SEG cSegChord( maxTanPtStart, maxTanPtEnd );
494 
495  int cSegTanStartSide = cSegTanStart.Side( theArc->GetMid() );
496  int cSegTanEndSide = cSegTanEnd.Side( theArc->GetMid() );
497  int cSegChordSide = cSegChord.Side( theArc->GetMid() );
498 
499  bool eatFirstMouseUp = true;
500 
501  // Start the tool loop
502  while( TOOL_EVENT* evt = Wait() )
503  {
505 
506  // Constrain cursor within the isosceles triangle
507  if( cSegTanStartSide != cSegTanStart.Side( m_cursor )
508  || cSegTanEndSide != cSegTanEnd.Side( m_cursor )
509  || cSegChordSide != cSegChord.Side( m_cursor ) )
510  {
511  std::vector<VECTOR2I> possiblePoints;
512 
513  possiblePoints.push_back( cSegTanEnd.NearestPoint( m_cursor ) );
514  possiblePoints.push_back( cSegChord.NearestPoint( m_cursor ) );
515 
516  VECTOR2I closest = cSegTanStart.NearestPoint( m_cursor );
517 
518  for( VECTOR2I candidate : possiblePoints )
519  {
520  if( ( candidate - m_cursor ).SquaredEuclideanNorm()
521  < ( closest - m_cursor ).SquaredEuclideanNorm() )
522  {
523  closest = candidate;
524  }
525  }
526 
527  m_cursor = closest;
528  }
529 
530  // Constrain cursor to be outside maxTanCircle
531  if( ( m_cursor - maxTanCircle.Center ).EuclideanNorm() < maxTanCircle.Radius )
532  m_cursor = maxTanCircle.NearestPoint( m_cursor );
533 
535 
536  // Calculate resulting object coordinates
537  CIRCLE circlehelper;
538  circlehelper.ConstructFromTanTanPt( cSegTanStart, cSegTanEnd, m_cursor );
539 
540  VECTOR2I newCenter = circlehelper.Center;
541  VECTOR2I newStart = cSegTanStart.LineProject( newCenter );
542  VECTOR2I newEnd = cSegTanEnd.LineProject( newCenter );
543  VECTOR2I newMid = CalcArcMid( newStart, newEnd, newCenter );
544 
545  // Update objects
546  theArc->SetStart( (wxPoint) newStart );
547  theArc->SetEnd( (wxPoint) newEnd );
548  theArc->SetMid( (wxPoint) newMid );
549 
550  if( isStartTrackOnStartPt )
551  trackOnStart->SetStart( (wxPoint) newStart );
552  else
553  trackOnStart->SetEnd( (wxPoint) newStart );
554 
555  if( isEndTrackOnStartPt )
556  trackOnEnd->SetStart( (wxPoint) newEnd );
557  else
558  trackOnEnd->SetEnd( (wxPoint) newEnd );
559 
560  // Update view
561  getView()->Update( trackOnStart );
562  getView()->Update( trackOnEnd );
563  getView()->Update( theArc );
564 
565  // Handle events
566  if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
567  {
568  eatFirstMouseUp = false;
569  }
570  else if( evt->IsCancelInteractive() || evt->IsActivate() )
571  {
572  restore_state = true; // Canceling the tool means that items have to be restored
573  break; // Finish
574  }
575  else if( evt->IsAction( &ACTIONS::undo ) )
576  {
577  restore_state = true; // Perform undo locally
578  break; // Finish
579  }
580  else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT )
581  || evt->IsDblClick( BUT_LEFT ) )
582  {
583  // Eat mouse-up/-click events that leaked through from the lock dialog
584  if( eatFirstMouseUp && evt->Parameter<intptr_t>() != ACTIONS::CURSOR_CLICK )
585  {
586  eatFirstMouseUp = false;
587  continue;
588  }
589 
590  break; // Finish
591  }
592  }
593 
594  // Ensure we only do one commit operation on each object
595  auto processTrack =
596  [&]( PCB_TRACK* aTrack, PCB_TRACK* aTrackCopy, int aMaxLengthIU ) -> bool
597  {
598  if( aTrack->IsNew() )
599  {
600  getView()->Remove( aTrack );
601 
602  if( aTrack->GetLength() <= aMaxLengthIU )
603  {
604  delete aTrack;
605  delete aTrackCopy;
606  aTrack = nullptr;
607  aTrackCopy = nullptr;
608  return false;
609  }
610  else
611  {
612  m_commit->Add( aTrack );
613  delete aTrackCopy;
614  aTrackCopy = nullptr;
615  return true;
616  }
617  }
618  else if( aTrack->GetLength() <= aMaxLengthIU )
619  {
620  aTrack->SwapData( aTrackCopy ); //restore the original before notifying COMMIT
621  m_commit->Remove( aTrack );
622  delete aTrackCopy;
623  aTrackCopy = nullptr;
624  return false;
625  }
626  else
627  {
628  m_commit->Modified( aTrack, aTrackCopy );
629  }
630 
631  return true;
632  };
633 
634  // Amend the end points of the arc if we delete the joining tracks
635  wxPoint newStart = trackOnStart->GetStart();
636  wxPoint newEnd = trackOnEnd->GetStart();
637 
638  if( isStartTrackOnStartPt )
639  newStart = trackOnStart->GetEnd();
640 
641  if( isEndTrackOnStartPt )
642  newEnd = trackOnEnd->GetEnd();
643 
644  int maxLengthIU = KiROUND( ADVANCED_CFG::GetCfg().m_MaxTrackLengthToKeep * IU_PER_MM );
645 
646  if( !processTrack( trackOnStart, trackOnStartCopy, maxLengthIU ) )
647  theArc->SetStart( newStart );
648 
649  if( !processTrack( trackOnEnd, trackOnEndCopy, maxLengthIU ) )
650  theArc->SetEnd( newEnd );
651 
652  processTrack( theArc, theArcCopy, 0 ); // only delete the arc if start and end points coincide
653 
654  // Should we commit?
655  if( restore_state )
656  m_commit->Revert();
657  else
658  m_commit->Push( _( "Drag Arc Track" ) );
659 
660  return 0;
661 }
662 
663 
664 int EDIT_TOOL::Move( const TOOL_EVENT& aEvent )
665 {
666  if( isRouterActive() )
667  {
668  wxBell();
669  return 0;
670  }
671 
672  return doMoveSelection( aEvent );
673 }
674 
675 
677 {
678  if( isRouterActive() )
679  {
680  wxBell();
681  return 0;
682  }
683 
684  return doMoveSelection( aEvent, true );
685 }
686 
687 
688 // Note: aEvent MUST NOT be const&; the source will get de-allocated if we go into the picker's
689 // event loop.
690 int EDIT_TOOL::doMoveSelection( TOOL_EVENT aEvent, bool aPickReference )
691 {
692  PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
694  VECTOR2I originalCursorPos = controls->GetCursorPosition();
695 
696  // Be sure that there is at least one item that we can modify. If nothing was selected before,
697  // try looking for the stuff under mouse cursor (i.e. KiCad old-style hover selection)
699  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
700  {
701  sTool->FilterCollectorForMarkers( aCollector );
702  sTool->FilterCollectorForHierarchy( aCollector, true );
703  },
704  // Prompt user regarding locked items if in board editor and in free-pad-mode (if
705  // we're not in free-pad mode we delay this until the second RequestSelection()).
707 
708  if( m_dragging || selection.Empty() )
709  return 0;
710 
711  LSET item_layers = selection.GetSelectionLayers();
712  bool is_hover = selection.IsHover(); // N.B. This must be saved before the second call
713  // to RequestSelection() below
714  VECTOR2I pickedReferencePoint;
715 
716  // Now filter out pads if not in free pads mode. We cannot do this in the first
717  // RequestSelection() as we need the item_layers when a pad is the selection front.
718  if( !m_isFootprintEditor && !frame()->Settings().m_AllowFreePads )
719  {
721  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
722  {
723  sTool->FilterCollectorForMarkers( aCollector );
724  sTool->FilterCollectorForHierarchy( aCollector, true );
725  sTool->FilterCollectorForFreePads( aCollector );
726  },
727  true /* prompt user regarding locked items */ );
728  }
729 
730  if( selection.Empty() )
731  return 0;
732 
733  std::string tool = aEvent.GetCommandStr().get();
734  editFrame->PushTool( tool );
735 
736  Activate();
737  // Must be done after Activate() so that it gets set into the correct context
738  controls->ShowCursor( true );
739  controls->SetAutoPan( true );
740 
741  if( aPickReference && !pickReferencePoint( _( "Select reference point for move..." ), "", "",
742  pickedReferencePoint ) )
743  {
744  if( is_hover )
746 
747  editFrame->PopTool( tool );
748  return 0;
749  }
750 
751  std::vector<BOARD_ITEM*> sel_items; // All the items operated on by the move below
752  std::vector<BOARD_ITEM*> orig_items; // All the original items in the selection
753 
754  for( EDA_ITEM* item : selection )
755  {
756  BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( item );
757  FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( item );
758 
759  if( boardItem )
760  {
761  orig_items.push_back( boardItem );
762  sel_items.push_back( boardItem );
763  }
764 
765  if( footprint )
766  {
767  for( PAD* pad : footprint->Pads() )
768  sel_items.push_back( pad );
769 
770  // Clear this flag here; it will be set by the netlist updater if the footprint is new
771  // so that it was skipped in the initial connectivity update in OnNetlistChanged
773  }
774  }
775 
776  bool restore_state = false;
777  VECTOR2I originalPos;
778  VECTOR2I totalMovement;
780  TOOL_EVENT* evt = &aEvent;
781  VECTOR2I prevPos;
782 
783  bool lock45 = false;
784  bool eatFirstMouseUp = true;
785  bool hasRedrawn3D = false;
786  bool allowRedraw3D = editFrame->GetDisplayOptions().m_Live3DRefresh;
787 
788  // Prime the pump
790 
791  // Main loop: keep receiving events
792  do
793  {
794  VECTOR2I movement;
796  grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
797  grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
798 
799  if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
800  eatFirstMouseUp = false;
801 
802  if( evt->IsAction( &PCB_ACTIONS::move ) || evt->IsMotion() || evt->IsDrag( BUT_LEFT )
803  || evt->IsAction( &ACTIONS::refreshPreview )
804  || evt->IsAction( &PCB_ACTIONS::moveWithReference ) )
805  {
806  if( m_dragging && evt->Category() == TC_MOUSE )
807  {
808  bool redraw3D = false;
809 
810  VECTOR2I mousePos( controls->GetMousePosition() );
811 
812  m_cursor = grid.BestSnapAnchor( mousePos, item_layers, sel_items );
813 
815  {
817 
818  // The arrow keys are by definition SINGLE AXIS. Do not allow the other
819  // axis to be snapped to the grid.
820  if( action == ACTIONS::CURSOR_LEFT || action == ACTIONS::CURSOR_RIGHT )
821  m_cursor.y = prevPos.y;
822  else if( action == ACTIONS::CURSOR_UP || action == ACTIONS::CURSOR_DOWN )
823  m_cursor.x = prevPos.x;
824  }
825 
827  originalPos = m_cursor;
828 
829  if( lock45 )
830  {
831  VECTOR2I moveVector = m_cursor - originalPos;
832  m_cursor = originalPos + GetVectorSnapped45( moveVector );
833  }
834 
837 
838  movement = m_cursor - prevPos;
839  prevPos = m_cursor;
840  totalMovement += movement;
841 
842  // Drag items to the current cursor position
843  for( EDA_ITEM* item : sel_items )
844  {
845  // Don't double move footprint pads, fields, etc.
846  //
847  // For PCB_GROUP_T, we make sure the selection includes only the top level
848  // group and not its descendants.
849  if( !item->GetParent() || !item->GetParent()->IsSelected() )
850  static_cast<BOARD_ITEM*>( item )->Move( movement );
851 
852  if( !redraw3D && item->Type() == PCB_FOOTPRINT_T )
853  redraw3D = true;
854  }
855 
856  if( redraw3D && allowRedraw3D )
857  {
858  editFrame->Update3DView( false, true );
859  hasRedrawn3D = true;
860  }
861 
863  }
864  else if( !m_dragging && !evt->IsAction( &ACTIONS::refreshPreview ) )
865  {
866  // Prepare to start dragging
867  if( !( evt->IsAction( &PCB_ACTIONS::move )
868  || evt->IsAction( &PCB_ACTIONS::moveWithReference ) )
869  && ( editFrame->Settings().m_TrackDragAction != TRACK_DRAG_ACTION::MOVE ) )
870  {
872  break;
873  }
874 
875  m_dragging = true;
876 
877  // When editing footprints, all items have the same parent
878  if( IsFootprintEditor() )
879  {
880  m_commit->Modify( selection.Front() );
881  }
882  else
883  {
884  // Save items, so changes can be undone
885  for( EDA_ITEM* item : selection )
886  {
887  // Don't double move footprint pads, fields, etc.
888  //
889  // For PCB_GROUP_T, the parent is the board.
890  if( item->GetParent() && item->GetParent()->IsSelected() )
891  continue;
892 
893  m_commit->Modify( item );
894 
895  // If moving a group, record position of all the descendants for undo
896  if( item->Type() == PCB_GROUP_T )
897  {
898  PCB_GROUP* group = static_cast<PCB_GROUP*>( item );
899  group->RunOnDescendants( [&]( BOARD_ITEM* bItem )
900  {
901  m_commit->Modify( bItem );
902  });
903  }
904  }
905  }
906 
907  editFrame->UndoRedoBlock( true );
909 
911  {
912  // start moving with the reference point attached to the cursor
913  grid.SetAuxAxes( false );
914 
915  if( lock45 )
916  {
917  VECTOR2I moveVector = m_cursor - originalPos;
918  m_cursor = originalPos + GetVectorSnapped45( moveVector );
919  }
920 
921  movement = m_cursor - selection.GetReferencePoint();
922 
923  // Drag items to the current cursor position
924  for( EDA_ITEM* item : selection )
925  {
926  // Don't double move footprint pads, fields, etc.
927  if( item->GetParent() && item->GetParent()->IsSelected() )
928  continue;
929 
930  static_cast<BOARD_ITEM*>( item )->Move( movement );
931  }
932 
934  }
935  else
936  {
937  std::vector<BOARD_ITEM*> items;
938 
939  for( EDA_ITEM* item : selection )
940  items.push_back( static_cast<BOARD_ITEM*>( item ) );
941 
942  m_cursor = grid.BestDragOrigin( originalCursorPos, items );
943 
944  // Set the current cursor position to the first dragged item origin, so the
945  // movement vector could be computed later
946  if( aPickReference )
947  {
948  selection.SetReferencePoint( pickedReferencePoint );
949  controls->ForceCursorPosition( true, pickedReferencePoint );
950  m_cursor = pickedReferencePoint;
951  }
952  else
953  {
954  // Check if user wants to warp the mouse to origin of moved object
955  if( !editFrame->GetMoveWarpsCursor() )
956  m_cursor = originalCursorPos; // No, so use original mouse pos instead
957 
959  grid.SetAuxAxes( true, m_cursor );
960  }
961 
962  originalPos = m_cursor;
963 
964  }
965 
967 
968  prevPos = m_cursor;
969  controls->SetAutoPan( true );
971  }
972 
974  new VECTOR2I( movement ) );
975  }
976  else if( evt->IsCancelInteractive() || evt->IsActivate() )
977  {
978  if( m_dragging && evt->IsCancelInteractive() )
979  evt->SetPassEvent( false );
980 
981  restore_state = true; // Canceling the tool means that items have to be restored
982  break; // Finish
983  }
984  else if( evt->IsAction( &ACTIONS::undo ) )
985  {
986  restore_state = true; // Perform undo locally
987  break; // Finish
988  }
989  else if( evt->IsAction( &ACTIONS::doDelete ) || evt->IsAction( &ACTIONS::cut ) )
990  {
991  // Dispatch TOOL_ACTIONs
992  evt->SetPassEvent();
993  break; // finish -- there is no further processing for removed items
994  }
995  else if( evt->IsAction( &ACTIONS::duplicate ) )
996  {
997  evt->SetPassEvent();
998  break; // finish -- Duplicate tool will start a new Move with the dup'ed items
999  }
1000  else if( evt->IsAction( &PCB_ACTIONS::rotateCw )
1001  || evt->IsAction( &PCB_ACTIONS::rotateCcw )
1002  || evt->IsAction( &PCB_ACTIONS::flip )
1003  || evt->IsAction( &PCB_ACTIONS::mirror ) )
1004  {
1005  eatFirstMouseUp = false;
1006  evt->SetPassEvent();
1007  }
1008  else if( evt->IsAction( &PCB_ACTIONS::moveExact ) )
1009  {
1010  // Reset positions so the Move Exactly is from the start.
1011  for( EDA_ITEM* item : selection )
1012  {
1013  BOARD_ITEM* i = static_cast<BOARD_ITEM*>( item );
1014  i->Move( -totalMovement );
1015  }
1016 
1017  break; // finish -- we moved exactly, so we are finished
1018  }
1019  else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) )
1020  {
1021  // Eat mouse-up/-click events that leaked through from the lock dialog
1022  if( eatFirstMouseUp && evt->Parameter<intptr_t>() != ACTIONS::CURSOR_CLICK )
1023  {
1024  eatFirstMouseUp = false;
1025  continue;
1026  }
1027 
1028  break; // finish
1029  }
1030  else if( evt->IsAction( &PCB_ACTIONS::toggle45 ) )
1031  {
1032  lock45 = !lock45;
1033  evt->SetPassEvent( false );
1034  }
1035  else
1036  {
1037  evt->SetPassEvent();
1038  }
1039 
1040  } while( ( evt = Wait() ) ); // Assignment (instead of equality test) is intentional
1041 
1042  controls->ForceCursorPosition( false );
1043  controls->ShowCursor( false );
1044  controls->SetAutoPan( false );
1045 
1046  m_dragging = false;
1047  editFrame->UndoRedoBlock( false );
1048 
1049  if( hasRedrawn3D && restore_state )
1050  editFrame->Update3DView( false, true );
1051 
1052  // Discard reference point when selection is "dropped" onto the board
1054 
1055  // TODO: there's an encapsulation leak here: this commit often has more than just the move
1056  // in it; for instance it might have a paste, append board, etc. as well.
1057  if( restore_state )
1058  m_commit->Revert();
1059  else
1060  m_commit->Push( _( "Drag" ) );
1061 
1062  // Remove the dynamic ratsnest from the screen
1064 
1065  // Unselect all items to update flags
1067 
1068  // Reselect items if they were already selected and we completed the move
1069  if( !is_hover && !restore_state )
1070  m_toolMgr->RunAction( PCB_ACTIONS::selectItems, true, &orig_items );
1071 
1072  editFrame->PopTool( tool );
1073  editFrame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1074 
1075  return restore_state ? -1 : 0;
1076 }
1077 
1078 
1080 {
1082  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1083  {
1084  // Iterate from the back so we don't have to worry about removals.
1085  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1086  {
1087  BOARD_ITEM* item = aCollector[ i ];
1088 
1089  if( !dynamic_cast<PCB_TRACK*>( item ) )
1090  aCollector.Remove( item );
1091  }
1092  },
1093  true /* prompt user regarding locked items */ );
1094 
1095  for( EDA_ITEM* item : selection )
1096  {
1097  if( item->Type() == PCB_VIA_T )
1098  {
1099  PCB_VIA* via = static_cast<PCB_VIA*>( item );
1100 
1101  m_commit->Modify( via );
1102 
1103  int new_width;
1104  int new_drill;
1105 
1106  if( via->GetViaType() == VIATYPE::MICROVIA )
1107  {
1108  NETCLASS* netClass = via->GetNetClass();
1109 
1110  new_width = netClass->GetuViaDiameter();
1111  new_drill = netClass->GetuViaDrill();
1112  }
1113  else
1114  {
1115  new_width = board()->GetDesignSettings().GetCurrentViaSize();
1116  new_drill = board()->GetDesignSettings().GetCurrentViaDrill();
1117  }
1118 
1119  via->SetDrill( new_drill );
1120  via->SetWidth( new_width );
1121  }
1122  else if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
1123  {
1124  PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item );
1125 
1126  wxCHECK( track, 0 );
1127 
1128  m_commit->Modify( track );
1129 
1130  int new_width = board()->GetDesignSettings().GetCurrentTrackWidth();
1131  track->SetWidth( new_width );
1132  }
1133  }
1134 
1135  m_commit->Push( _("Edit track width/via size") );
1136 
1137  if( selection.IsHover() )
1138  {
1140 
1141  // Notify other tools of the changes -- This updates the visual ratsnest
1143  }
1144 
1145  return 0;
1146 }
1147 
1148 
1150 {
1151  // Store last used fillet radius to allow pressing "enter" if repeat fillet is required
1152  static long long filletRadiusIU = 0;
1153 
1155  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1156  {
1157  // Iterate from the back so we don't have to worry about removals.
1158  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1159  {
1160  BOARD_ITEM* item = aCollector[i];
1161 
1162  if( !dynamic_cast<PCB_TRACK*>( item ) )
1163  aCollector.Remove( item );
1164  }
1165  },
1166  true /* prompt user regarding locked items */ );
1167 
1168  if( selection.Size() < 2 )
1169  {
1170  frame()->ShowInfoBarMsg( _( "At least two straight track segments must be selected." ) );
1171  return 0;
1172  }
1173 
1174  WX_UNIT_ENTRY_DIALOG dia( frame(), _( "Enter fillet radius:" ), _( "Fillet Tracks" ),
1175  filletRadiusIU );
1176 
1177  if( dia.ShowModal() == wxID_CANCEL )
1178  return 0;
1179 
1180  filletRadiusIU = dia.GetValue();
1181 
1182  if( filletRadiusIU == 0 )
1183  {
1184  frame()->ShowInfoBarMsg( _( "A radius of zero was entered.\n"
1185  "The fillet operation was not performed." ) );
1186  return 0;
1187  }
1188 
1189  struct FILLET_OP
1190  {
1191  PCB_TRACK* t1;
1192  PCB_TRACK* t2;
1193  // Start point of track is modified after PCB_ARC is added, otherwise the end point:
1194  bool t1Start = true;
1195  bool t2Start = true;
1196  };
1197 
1198  std::vector<FILLET_OP> filletOperations;
1199  KICAD_T track_types[] = { PCB_PAD_T, PCB_VIA_T, PCB_TRACE_T, PCB_ARC_T, EOT };
1200  bool operationPerformedOnAtLeastOne = false;
1201  bool didOneAttemptFail = false;
1202  std::set<PCB_TRACK*> processedTracks;
1203 
1204  for( auto it = selection.begin(); it != selection.end(); it++ )
1205  {
1206  PCB_TRACK* track = dyn_cast<PCB_TRACK*>( *it );
1207 
1208  if( !track || track->Type() != PCB_TRACE_T || track->GetLength() == 0 )
1209  {
1210  continue;
1211  }
1212 
1213  auto processFilletOp =
1214  [&]( bool aStartPoint )
1215  {
1216  wxPoint anchor = ( aStartPoint ) ? track->GetStart() : track->GetEnd();
1217  auto connectivity = board()->GetConnectivity();
1218  auto itemsOnAnchor = connectivity->GetConnectedItemsAtAnchor( track, anchor,
1219  track_types );
1220 
1221  if( itemsOnAnchor.size() > 0
1222  && selection.Contains( itemsOnAnchor.at( 0 ) )
1223  && itemsOnAnchor.at( 0 )->Type() == PCB_TRACE_T )
1224  {
1225  PCB_TRACK* trackOther = dyn_cast<PCB_TRACK*>( itemsOnAnchor.at( 0 ) );
1226 
1227  // Make sure we don't fillet the same pair of tracks twice
1228  if( processedTracks.find( trackOther ) == processedTracks.end() )
1229  {
1230  if( itemsOnAnchor.size() == 1 )
1231  {
1232  FILLET_OP filletOp;
1233  filletOp.t1 = track;
1234  filletOp.t2 = trackOther;
1235  filletOp.t1Start = aStartPoint;
1236  filletOp.t2Start = track->IsPointOnEnds( filletOp.t2->GetStart() );
1237  filletOperations.push_back( filletOp );
1238  }
1239  else
1240  {
1241  // User requested to fillet these two tracks but not possible as
1242  // there are other elements connected at that point
1243  didOneAttemptFail = true;
1244  }
1245  }
1246  }
1247  };
1248 
1249  processFilletOp( true ); // on the start point of track
1250  processFilletOp( false ); // on the end point of track
1251 
1252  processedTracks.insert( track );
1253  }
1254 
1255  std::vector<BOARD_ITEM*> itemsToAddToSelection;
1256 
1257  for( FILLET_OP filletOp : filletOperations )
1258  {
1259  PCB_TRACK* track1 = filletOp.t1;
1260  PCB_TRACK* track2 = filletOp.t2;
1261 
1262  bool trackOnStart = track1->IsPointOnEnds( track2->GetStart() );
1263  bool trackOnEnd = track1->IsPointOnEnds( track2->GetEnd() );
1264 
1265  if( trackOnStart && trackOnEnd )
1266  continue; // Ignore duplicate tracks
1267 
1268  if( ( trackOnStart || trackOnEnd ) && track1->GetLayer() == track2->GetLayer() )
1269  {
1270  SEG t1Seg( track1->GetStart(), track1->GetEnd() );
1271  SEG t2Seg( track2->GetStart(), track2->GetEnd() );
1272 
1273  if( t1Seg.ApproxCollinear( t2Seg ) )
1274  continue;
1275 
1276  SHAPE_ARC sArc( t1Seg, t2Seg, filletRadiusIU );
1277 
1278  wxPoint t1newPoint, t2newPoint;
1279 
1280  auto setIfPointOnSeg =
1281  []( wxPoint& aPointToSet, SEG aSegment, VECTOR2I aVecToTest )
1282  {
1283  VECTOR2I segToVec = aSegment.NearestPoint( aVecToTest ) - aVecToTest;
1284 
1285  // Find out if we are on the segment (minimum precision)
1286  if( segToVec.EuclideanNorm() < SHAPE_ARC::MIN_PRECISION_IU )
1287  {
1288  aPointToSet.x = aVecToTest.x;
1289  aPointToSet.y = aVecToTest.y;
1290  return true;
1291  }
1292 
1293  return false;
1294  };
1295 
1296  //Do not draw a fillet if the end points of the arc are not within the track segments
1297  if( !setIfPointOnSeg( t1newPoint, t1Seg, sArc.GetP0() )
1298  && !setIfPointOnSeg( t2newPoint, t2Seg, sArc.GetP0() ) )
1299  {
1300  didOneAttemptFail = true;
1301  continue;
1302  }
1303 
1304  if( !setIfPointOnSeg( t1newPoint, t1Seg, sArc.GetP1() )
1305  && !setIfPointOnSeg( t2newPoint, t2Seg, sArc.GetP1() ) )
1306  {
1307  didOneAttemptFail = true;
1308  continue;
1309  }
1310 
1311  PCB_ARC* tArc = new PCB_ARC( frame()->GetBoard(), &sArc );
1312  tArc->SetLayer( track1->GetLayer() );
1313  tArc->SetWidth( track1->GetWidth() );
1314  tArc->SetNet( track1->GetNet() );
1315  tArc->SetLocked( track1->IsLocked() );
1316  m_commit->Add( tArc );
1317  itemsToAddToSelection.push_back( tArc );
1318 
1319  m_commit->Modify( track1 );
1320  m_commit->Modify( track2 );
1321 
1322  if( filletOp.t1Start )
1323  track1->SetStart( t1newPoint );
1324  else
1325  track1->SetEnd( t1newPoint );
1326 
1327  if( filletOp.t2Start )
1328  track2->SetStart( t2newPoint );
1329  else
1330  track2->SetEnd( t2newPoint );
1331 
1332  operationPerformedOnAtLeastOne = true;
1333  }
1334  }
1335 
1336  m_commit->Push( _( "Fillet Tracks" ) );
1337 
1338  //select the newly created arcs
1339  for( BOARD_ITEM* item : itemsToAddToSelection )
1340  m_selectionTool->AddItemToSel( item );
1341 
1342  if( !operationPerformedOnAtLeastOne )
1343  frame()->ShowInfoBarMsg( _( "Unable to fillet the selected track segments." ) );
1344  else if( didOneAttemptFail )
1345  frame()->ShowInfoBarMsg( _( "Some of the track segments could not be filleted." ) );
1346 
1347  return 0;
1348 }
1349 
1350 
1351 int EDIT_TOOL::Properties( const TOOL_EVENT& aEvent )
1352 {
1353  PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1355  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1356  {
1357  } );
1358 
1359  // Tracks & vias are treated in a special way:
1361  {
1362  DIALOG_TRACK_VIA_PROPERTIES dlg( editFrame, selection, *m_commit );
1363  dlg.ShowQuasiModal(); // QuasiModal required for NET_SELECTOR
1364  }
1365  else if( selection.Size() == 1 )
1366  {
1367  // Display properties dialog
1368  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
1369 
1370  // Do not handle undo buffer, it is done by the properties dialogs
1371  editFrame->OnEditItemRequest( item );
1372 
1373  // Notify other tools of the changes
1375  }
1376  else if( selection.Size() == 0 && getView()->IsLayerVisible( LAYER_DRAWINGSHEET ) )
1377  {
1378  DS_PROXY_VIEW_ITEM* ds = editFrame->GetCanvas()->GetDrawingSheet();
1379  VECTOR2D cursorPos = getViewControls()->GetCursorPosition( false );
1380 
1381  if( ds && ds->HitTestDrawingSheetItems( getView(), (wxPoint) cursorPos ) )
1383  else
1385  }
1386 
1387  if( selection.IsHover() )
1388  {
1390  }
1391  else
1392  {
1393  // Check for items becoming invisible and drop them from the selection.
1394 
1395  LSET visible = editFrame->GetBoard()->GetVisibleLayers();
1396 
1397  for( EDA_ITEM* eda_item : selection )
1398  {
1399  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( eda_item );
1400 
1401  if( !( item->GetLayerSet() & visible ).any() )
1403  }
1404  }
1405 
1406  return 0;
1407 }
1408 
1409 
1410 int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
1411 {
1412  if( isRouterActive() )
1413  {
1414  wxBell();
1415  return 0;
1416  }
1417 
1418  PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1419 
1420  // Be sure that there is at least one item that we can modify. If nothing was selected before,
1421  // try looking for the stuff under mouse cursor (i.e. KiCad old-style hover selection)
1423  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1424  {
1425  sTool->FilterCollectorForHierarchy( aCollector, true );
1426  sTool->FilterCollectorForMarkers( aCollector );
1427  },
1428  // Prompt user regarding locked items if in board editor and in free-pad-mode (if
1429  // we're not in free-pad mode we delay this until the second RequestSelection()).
1431 
1432  if( selection.Empty() )
1433  return 0;
1434 
1435  OPT<VECTOR2I> oldRefPt = boost::make_optional<VECTOR2I>( false, VECTOR2I( 0, 0 ) );
1436  bool is_hover = selection.IsHover(); // N.B. This must be saved before the second
1437  // call to RequestSelection() below
1438 
1440  oldRefPt = selection.GetReferencePoint();
1441 
1442  // Now filter out pads if not in free pads mode. We cannot do this in the first
1443  // RequestSelection() as we need the reference point when a pad is the selection front.
1444  if( !m_isFootprintEditor && !frame()->Settings().m_AllowFreePads )
1445  {
1447  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1448  {
1449  sTool->FilterCollectorForMarkers( aCollector );
1450  sTool->FilterCollectorForHierarchy( aCollector, true );
1451  sTool->FilterCollectorForFreePads( aCollector );
1452  },
1453  true /* prompt user regarding locked items */ );
1454  }
1455 
1456  // Did we filter everything out? If so, don't try to operate further
1457  if( selection.Empty() )
1458  return 0;
1459 
1461 
1463  const int rotateAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *editFrame, aEvent );
1464 
1465  // When editing footprints, all items have the same parent
1466  if( IsFootprintEditor() )
1467  m_commit->Modify( selection.Front() );
1468 
1469  for( EDA_ITEM* item : selection )
1470  {
1471  if( !item->IsNew() && !IsFootprintEditor() )
1472  {
1473  m_commit->Modify( item );
1474 
1475  // If rotating a group, record position of all the descendants for undo
1476  if( item->Type() == PCB_GROUP_T )
1477  {
1478  static_cast<PCB_GROUP*>( item )->RunOnDescendants(
1479  [&]( BOARD_ITEM* bItem )
1480  {
1481  m_commit->Modify( bItem );
1482  });
1483  }
1484  }
1485 
1486  static_cast<BOARD_ITEM*>( item )->Rotate( refPt, rotateAngle );
1487  }
1488 
1489  if( !m_dragging )
1490  m_commit->Push( _( "Rotate" ) );
1491 
1492  if( is_hover && !m_dragging )
1494 
1496 
1497  if( m_dragging )
1499 
1500  // Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
1501  // to this now invalid reference
1502  if( oldRefPt )
1503  selection.SetReferencePoint( *oldRefPt );
1504  else
1506 
1507  return 0;
1508 }
1509 
1510 
1514 static wxPoint mirrorPointX( const wxPoint& aPoint, const wxPoint& aMirrorPoint )
1515 {
1516  wxPoint mirrored = aPoint;
1517 
1518  mirrored.x -= aMirrorPoint.x;
1519  mirrored.x = -mirrored.x;
1520  mirrored.x += aMirrorPoint.x;
1521 
1522  return mirrored;
1523 }
1524 
1525 
1529 static void mirrorPadX( PAD& aPad, const wxPoint& aMirrorPoint )
1530 {
1531  if( aPad.GetShape() == PAD_SHAPE::CUSTOM )
1532  aPad.FlipPrimitives( true ); // mirror primitives left to right
1533 
1534  wxPoint tmpPt = mirrorPointX( aPad.GetPosition(), aMirrorPoint );
1535  aPad.SetPosition( tmpPt );
1536 
1537  aPad.SetX0( aPad.GetPosition().x );
1538 
1539  tmpPt = aPad.GetOffset();
1540  tmpPt.x = -tmpPt.x;
1541  aPad.SetOffset( tmpPt );
1542 
1543  auto tmpz = aPad.GetDelta();
1544  tmpz.x = -tmpz.x;
1545  aPad.SetDelta( tmpz );
1546 
1547  aPad.SetOrientation( -aPad.GetOrientation() );
1548 }
1549 
1550 
1551 int EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
1552 {
1553  if( isRouterActive() )
1554  {
1555  wxBell();
1556  return 0;
1557  }
1558 
1560  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1561  {
1562  sTool->FilterCollectorForMarkers( aCollector );
1563  sTool->FilterCollectorForHierarchy( aCollector, true );
1564  sTool->FilterCollectorForFreePads( aCollector );
1565  },
1566  !m_dragging /* prompt user regarding locked items */ );
1567 
1568  if( selection.Empty() )
1569  return 0;
1570 
1572  auto refPoint = selection.GetReferencePoint();
1573  wxPoint mirrorPoint( refPoint.x, refPoint.y );
1574 
1575  // When editing footprints, all items have the same parent
1576  if( IsFootprintEditor() )
1577  m_commit->Modify( selection.Front() );
1578 
1579  for( EDA_ITEM* item : selection )
1580  {
1581  // only modify items we can mirror
1582  switch( item->Type() )
1583  {
1584  case PCB_FP_SHAPE_T:
1585  case PCB_FP_TEXT_T:
1586  case PCB_FP_ZONE_T:
1587  case PCB_PAD_T:
1588  // Only create undo entry for items on the board
1589  if( !item->IsNew() && !IsFootprintEditor() )
1590  m_commit->Modify( item );
1591 
1592  break;
1593  default:
1594  continue;
1595  }
1596 
1597  // modify each object as necessary
1598  switch( item->Type() )
1599  {
1600  case PCB_FP_SHAPE_T:
1601  {
1602  FP_SHAPE* shape = static_cast<FP_SHAPE*>( item );
1603  shape->Mirror( mirrorPoint, false );
1604  break;
1605  }
1606 
1607  case PCB_FP_ZONE_T:
1608  {
1609  FP_ZONE* zone = static_cast<FP_ZONE*>( item );
1610  zone->Mirror( mirrorPoint, false );
1611  break;
1612  }
1613 
1614  case PCB_FP_TEXT_T:
1615  {
1616  FP_TEXT* text = static_cast<FP_TEXT*>( item );
1617  text->Mirror( mirrorPoint, false );
1618  break;
1619  }
1620 
1621  case PCB_PAD_T:
1622  {
1623  PAD* pad = static_cast<PAD*>( item );
1624  mirrorPadX( *pad, mirrorPoint );
1625  break;
1626  }
1627 
1628  default:
1629  // it's likely the commit object is wrong if you get here
1630  // Unsure if PCB_GROUP_T needs special attention here.
1631  assert( false );
1632  break;
1633  }
1634  }
1635 
1636  if( !m_dragging )
1637  m_commit->Push( _( "Mirror" ) );
1638 
1639  if( selection.IsHover() && !m_dragging )
1641 
1643 
1644  if( m_dragging )
1646 
1647  return 0;
1648 }
1649 
1650 
1651 int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent )
1652 {
1653  if( isRouterActive() )
1654  {
1655  wxBell();
1656  return 0;
1657  }
1658 
1660  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1661  {
1662  sTool->FilterCollectorForMarkers( aCollector );
1663  sTool->FilterCollectorForHierarchy( aCollector, true );
1664  sTool->FilterCollectorForFreePads( aCollector );
1665  },
1666  !m_dragging /* prompt user regarding locked items */ );
1667 
1668  if( selection.Empty() )
1669  return 0;
1670 
1671  OPT<VECTOR2I> oldRefPt = boost::make_optional<VECTOR2I>( false, VECTOR2I( 0, 0 ) );
1672 
1674  oldRefPt = selection.GetReferencePoint();
1675 
1677 
1678  // Flip around the anchor for footprints, and the bounding box center for board items
1679  VECTOR2I refPt = IsFootprintEditor() ? VECTOR2I( 0, 0 ) : selection.GetCenter();
1680 
1681  // If only one item selected, flip around the selection or item anchor point (instead
1682  // of the bounding box center) to avoid moving the item anchor
1683  if( selection.GetSize() == 1 )
1684  {
1686  refPt = selection.GetReferencePoint();
1687  else
1688  refPt = static_cast<BOARD_ITEM*>( selection.GetItem( 0 ) )->GetPosition();
1689  }
1690 
1691  bool leftRight = frame()->Settings().m_FlipLeftRight;
1692 
1693  // When editing footprints, all items have the same parent
1694  if( IsFootprintEditor() )
1695  m_commit->Modify( selection.Front() );
1696 
1697  for( EDA_ITEM* item : selection )
1698  {
1699  if( !item->IsNew() && !IsFootprintEditor() )
1700  m_commit->Modify( item );
1701 
1702  if( item->Type() == PCB_GROUP_T )
1703  {
1704  static_cast<PCB_GROUP*>( item )->RunOnDescendants( [&]( BOARD_ITEM* bItem )
1705  {
1706  m_commit->Modify( bItem );
1707  });
1708  }
1709 
1710  static_cast<BOARD_ITEM*>( item )->Flip( refPt, leftRight );
1711  }
1712 
1713  if( !m_dragging )
1714  m_commit->Push( _( "Change Side / Flip" ) );
1715 
1716  if( selection.IsHover() && !m_dragging )
1718 
1720 
1721  if( m_dragging )
1723 
1724  // Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
1725  // to this now invalid reference
1726  if( oldRefPt )
1727  selection.SetReferencePoint( *oldRefPt );
1728  else
1730 
1731  return 0;
1732 }
1733 
1734 
1735 int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent )
1736 {
1737  if( isRouterActive() )
1738  {
1740  return 0;
1741  }
1742 
1743  std::vector<BOARD_ITEM*> lockedItems;
1744  Activate();
1745 
1746  // get a copy instead of reference (as we're going to clear the selection before removing items)
1747  PCB_SELECTION selectionCopy;
1750 
1751  // If we are in a "Cut" operation, then the copied selection exists already and we want to
1752  // delete exactly that; no more, no fewer. Any filtering for locked items must be done in
1753  // the copyToClipboard() routine.
1754  if( isCut )
1755  {
1756  selectionCopy = m_selectionTool->GetSelection();
1757  }
1758  else
1759  {
1760  // When not in free-pad mode we normally auto-promote selected pads to their parent
1761  // footprints. But this is probably a little too dangerous for a destructive operation,
1762  // so we just do the promotion but not the deletion (allowing for a second delete to do
1763  // it if that's what the user wanted).
1764  selectionCopy = m_selectionTool->RequestSelection(
1765  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1766  {
1767  } );
1768 
1769  size_t beforeFPCount = selectionCopy.CountType( PCB_FOOTPRINT_T );
1770 
1772  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1773  {
1774  sTool->FilterCollectorForFreePads( aCollector );
1775  } );
1776 
1777  if( !selectionCopy.IsHover()
1778  && m_selectionTool->GetSelection().CountType( PCB_FOOTPRINT_T ) > beforeFPCount )
1779  {
1780  wxBell();
1781  canvas()->Refresh();
1782  return 0;
1783  }
1784 
1785  // In "alternative" mode, we expand selected track items to their full connection.
1786  if( isAlt && ( selectionCopy.HasType( PCB_TRACE_T ) || selectionCopy.HasType( PCB_VIA_T ) ) )
1787  {
1789  }
1790 
1791  // Finally run RequestSelection() one more time to find out what user wants to do about
1792  // locked objects.
1793  selectionCopy = m_selectionTool->RequestSelection(
1794  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1795  {
1796  sTool->FilterCollectorForFreePads( aCollector );
1797  },
1798  true /* prompt user regarding locked items */ );
1799  }
1800 
1801  // As we are about to remove items, they have to be removed from the selection first
1803 
1804  for( EDA_ITEM* item : selectionCopy )
1805  {
1806  PCB_GROUP* parentGroup = static_cast<BOARD_ITEM*>( item )->GetParentGroup();
1807 
1808  if( parentGroup )
1809  {
1810  m_commit->Modify( parentGroup );
1811  parentGroup->RemoveItem( static_cast<BOARD_ITEM*>( item ) );
1812  }
1813 
1814  switch( item->Type() )
1815  {
1816  case PCB_FP_TEXT_T:
1817  {
1818  FP_TEXT* text = static_cast<FP_TEXT*>( item );
1819  FOOTPRINT* parent = static_cast<FOOTPRINT*>( item->GetParent() );
1820 
1821  if( text->GetType() == FP_TEXT::TEXT_is_DIVERS )
1822  {
1823  m_commit->Modify( parent );
1824  getView()->Remove( text );
1825  parent->Remove( text );
1826  }
1827 
1828  break;
1829  }
1830 
1831  case PCB_PAD_T:
1832  if( IsFootprintEditor() || frame()->Settings().m_AllowFreePads )
1833  {
1834  PAD* pad = static_cast<PAD*>( item );
1835  FOOTPRINT* parent = static_cast<FOOTPRINT*>( item->GetParent() );
1836 
1837  m_commit->Modify( parent );
1838  getView()->Remove( pad );
1839  parent->Remove( pad );
1840  }
1841 
1842  break;
1843 
1844  case PCB_FP_ZONE_T:
1845  {
1846  FP_ZONE* zone = static_cast<FP_ZONE*>( item );
1847  FOOTPRINT* parent = static_cast<FOOTPRINT*>( item->GetParent() );
1848 
1849  m_commit->Modify( parent );
1850  getView()->Remove( zone );
1851  parent->Remove( zone );
1852  break;
1853  }
1854 
1855  case PCB_ZONE_T:
1856  // We process the zones special so that cutouts can be deleted when the delete tool
1857  // is called from inside a cutout when the zone is selected.
1858  {
1859  // Only interact with cutouts when deleting and a single item is selected
1860  if( !isCut && selectionCopy.GetSize() == 1 )
1861  {
1863  ZONE* zone = static_cast<ZONE*>( item );
1864 
1865  int outlineIdx, holeIdx;
1866 
1867  if( zone->HitTestCutout( curPos, &outlineIdx, &holeIdx ) )
1868  {
1869  // Remove the cutout
1870  m_commit->Modify( zone );
1871  zone->RemoveCutout( outlineIdx, holeIdx );
1872  zone->UnFill();
1873 
1874  // TODO Refill zone when KiCad supports auto re-fill
1875 
1876  // Update the display
1877  zone->HatchBorder();
1878  canvas()->Refresh();
1879 
1880  // Restore the selection on the original zone
1882 
1883  break;
1884  }
1885  }
1886 
1887  // Remove the entire zone otherwise
1888  m_commit->Remove( item );
1889  break;
1890  }
1891 
1892  case PCB_GROUP_T:
1893  {
1894  PCB_GROUP* group = static_cast<PCB_GROUP*>( item );
1895 
1896  auto removeItem =
1897  [&]( BOARD_ITEM* bItem )
1898  {
1899  if( bItem->GetParent() && bItem->GetParent()->Type() == PCB_FOOTPRINT_T )
1900  {
1901  // Silently ignore delete of Reference or Value if they happen to be
1902  // in group.
1903  if( bItem->Type() == PCB_FP_TEXT_T )
1904  {
1905  FP_TEXT* textItem = static_cast<FP_TEXT*>( bItem );
1906 
1907  if( textItem->GetType() != FP_TEXT::TEXT_is_DIVERS )
1908  return;
1909  }
1910  else if( bItem->Type() == PCB_PAD_T )
1911  {
1913  return;
1914  }
1915 
1916  m_commit->Modify( bItem->GetParent() );
1917  getView()->Remove( bItem );
1918  bItem->GetParent()->Remove( bItem );
1919  }
1920  else
1921  {
1922  m_commit->Remove( bItem );
1923  }
1924  };
1925 
1926  removeItem( group );
1927 
1928  group->RunOnDescendants( [&]( BOARD_ITEM* aDescendant )
1929  {
1930  removeItem( aDescendant );
1931  });
1932  break;
1933  }
1934 
1935  default:
1936  m_commit->Remove( item );
1937  break;
1938  }
1939  }
1940 
1941  // If the entered group has been emptied then leave it.
1942  PCB_GROUP* enteredGroup = m_selectionTool->GetEnteredGroup();
1943 
1944  if( enteredGroup && enteredGroup->GetItems().empty() )
1946 
1947  if( isCut )
1948  m_commit->Push( _( "Cut" ) );
1949  else
1950  m_commit->Push( _( "Delete" ) );
1951 
1952  return 0;
1953 }
1954 
1955 
1956 int EDIT_TOOL::MoveExact( const TOOL_EVENT& aEvent )
1957 {
1958  if( isRouterActive() )
1959  {
1960  wxBell();
1961  return 0;
1962  }
1963 
1965  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1966  {
1967  sTool->FilterCollectorForMarkers( aCollector );
1968  sTool->FilterCollectorForHierarchy( aCollector, true );
1969  },
1970  true /* prompt user regarding locked items */ );
1971 
1972  if( selection.Empty() )
1973  return 0;
1974 
1975  wxPoint translation;
1976  double rotation;
1977  ROTATION_ANCHOR rotationAnchor = selection.Size() > 1 ? ROTATE_AROUND_SEL_CENTER
1979 
1980  // TODO: Implement a visible bounding border at the edge
1981  auto sel_box = selection.GetBoundingBox();
1982 
1983  DIALOG_MOVE_EXACT dialog( frame(), translation, rotation, rotationAnchor, sel_box );
1984  int ret = dialog.ShowModal();
1985 
1986  if( ret == wxID_OK )
1987  {
1988  VECTOR2I rp = selection.GetCenter();
1989  wxPoint selCenter( rp.x, rp.y );
1990 
1991  // Make sure the rotation is from the right reference point
1992  selCenter += translation;
1993 
1994  if( !frame()->GetDisplayOptions().m_DisplayInvertYAxis )
1995  rotation *= -1.0;
1996 
1997  // When editing footprints, all items have the same parent
1998  if( IsFootprintEditor() )
1999  m_commit->Modify( selection.Front() );
2000 
2001  for( EDA_ITEM* selItem : selection )
2002  {
2003  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selItem );
2004 
2005  if( !item->IsNew() && !IsFootprintEditor() )
2006  {
2007  m_commit->Modify( item );
2008 
2009  if( item->Type() == PCB_GROUP_T )
2010  {
2011  PCB_GROUP* group = static_cast<PCB_GROUP*>( item );
2012 
2013  group->RunOnDescendants( [&]( BOARD_ITEM* bItem )
2014  {
2015  m_commit->Modify( bItem );
2016  });
2017  }
2018  }
2019 
2020  if( !item->GetParent() || !item->GetParent()->IsSelected() )
2021  item->Move( translation );
2022 
2023  switch( rotationAnchor )
2024  {
2026  item->Rotate( item->GetPosition(), rotation );
2027  break;
2029  item->Rotate( selCenter, rotation );
2030  break;
2032  item->Rotate( (wxPoint) frame()->GetScreen()->m_LocalOrigin, rotation );
2033  break;
2035  item->Rotate( board()->GetDesignSettings().GetAuxOrigin(), rotation );
2036  break;
2037  }
2038 
2039  if( !m_dragging )
2040  getView()->Update( item );
2041  }
2042 
2043  m_commit->Push( _( "Move exact" ) );
2044 
2045  if( selection.IsHover() )
2047 
2049 
2050  if( m_dragging )
2052  }
2053 
2054  return 0;
2055 }
2056 
2057 
2058 int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent )
2059 {
2060  if( isRouterActive() )
2061  {
2062  wxBell();
2063  return 0;
2064  }
2065 
2066  bool increment = aEvent.IsAction( &PCB_ACTIONS::duplicateIncrement );
2067 
2068  // Be sure that there is at least one item that we can modify
2070  []( const VECTOR2I&, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2071  {
2072  sTool->FilterCollectorForMarkers( aCollector );
2073  sTool->FilterCollectorForHierarchy( aCollector, true );
2074  } );
2075 
2076  if( selection.Empty() )
2077  return 0;
2078 
2079  // we have a selection to work on now, so start the tool process
2080  PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2081 
2082  // If the selection was given a hover, we do not keep the selection after completion
2083  bool is_hover = selection.IsHover();
2084 
2085  std::vector<BOARD_ITEM*> new_items;
2086  new_items.reserve( selection.Size() );
2087 
2088  // Each selected item is duplicated and pushed to new_items list
2089  // Old selection is cleared, and new items are then selected.
2090  for( EDA_ITEM* item : selection )
2091  {
2092  BOARD_ITEM* dupe_item = nullptr;
2093  BOARD_ITEM* orig_item = static_cast<BOARD_ITEM*>( item );
2094 
2095  if( m_isFootprintEditor )
2096  {
2097  FOOTPRINT* parentFootprint = editFrame->GetBoard()->GetFirstFootprint();
2098  dupe_item = parentFootprint->DuplicateItem( orig_item );
2099 
2100  if( increment && dupe_item->Type() == PCB_PAD_T
2101  && static_cast<PAD*>( dupe_item )->CanHaveNumber() )
2102  {
2103  PAD_TOOL* padTool = m_toolMgr->GetTool<PAD_TOOL>();
2104  wxString padNumber = padTool->GetLastPadNumber();
2105  padNumber = parentFootprint->GetNextPadNumber( padNumber );
2106  padTool->SetLastPadNumber( padNumber );
2107  static_cast<PAD*>( dupe_item )->SetNumber( padNumber );
2108  }
2109  }
2110  else if( orig_item->GetParent() && orig_item->GetParent()->Type() == PCB_FOOTPRINT_T )
2111  {
2112  FOOTPRINT* parentFootprint = static_cast<FOOTPRINT*>( orig_item->GetParent() );
2113 
2114  m_commit->Modify( parentFootprint );
2115  dupe_item = parentFootprint->DuplicateItem( orig_item, true /* add to parent */ );
2116  }
2117  else
2118  {
2119  switch( orig_item->Type() )
2120  {
2121  case PCB_FOOTPRINT_T:
2122  case PCB_TEXT_T:
2123  case PCB_SHAPE_T:
2124  case PCB_TRACE_T:
2125  case PCB_ARC_T:
2126  case PCB_VIA_T:
2127  case PCB_ZONE_T:
2128  case PCB_TARGET_T:
2129  case PCB_DIM_ALIGNED_T:
2130  case PCB_DIM_CENTER_T:
2131  case PCB_DIM_ORTHOGONAL_T:
2132  case PCB_DIM_LEADER_T:
2133  dupe_item = orig_item->Duplicate();
2134  break;
2135 
2136  case PCB_GROUP_T:
2137  dupe_item = static_cast<PCB_GROUP*>( orig_item )->DeepDuplicate();
2138  break;
2139 
2140  default:
2141  wxASSERT_MSG( false, wxString::Format( "Unhandled item type %d",
2142  orig_item->Type() ) );
2143  break;
2144  }
2145  }
2146 
2147  if( dupe_item )
2148  {
2149  if( dupe_item->Type() == PCB_GROUP_T )
2150  {
2151  static_cast<PCB_GROUP*>( dupe_item )->RunOnDescendants(
2152  [&]( BOARD_ITEM* bItem )
2153  {
2154  m_commit->Add( bItem );
2155  });
2156  }
2157 
2158  // Clear the selection flag here, otherwise the PCB_SELECTION_TOOL
2159  // will not properly select it later on
2160  dupe_item->ClearSelected();
2161 
2162  new_items.push_back( dupe_item );
2163  m_commit->Add( dupe_item );
2164  }
2165  }
2166 
2167  // Clear the old selection first
2169 
2170  // Select the new items
2171  m_toolMgr->RunAction( PCB_ACTIONS::selectItems, true, &new_items );
2172 
2173  // record the new items as added
2174  if( !selection.Empty() )
2175  {
2176  editFrame->DisplayToolMsg( wxString::Format( _( "Duplicated %d item(s)" ),
2177  (int) new_items.size() ) );
2178 
2179  // TODO(ISM): This line can't be used to activate the tool until we allow multiple
2180  // activations.
2181  // m_toolMgr->RunAction( PCB_ACTIONS::move, true );
2182  // Instead we have to create the event and call the tool's function
2183  // directly
2184 
2185  // If items were duplicated, pick them up
2186  // this works well for "dropping" copies around and pushes the commit
2188  Move( evt );
2189 
2190  // Deslect the duplicated item if we originally started as a hover selection
2191  if( is_hover )
2193  }
2194 
2195  return 0;
2196 }
2197 
2198 
2200 {
2201  if( isRouterActive() )
2202  {
2203  wxBell();
2204  return 0;
2205  }
2206 
2207  // Be sure that there is at least one item that we can modify
2209  []( const VECTOR2I&, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2210  {
2211  sTool->FilterCollectorForMarkers( aCollector );
2212  sTool->FilterCollectorForHierarchy( aCollector, true );
2213  } );
2214 
2215  if( selection.Empty() )
2216  return 0;
2217 
2218  // we have a selection to work on now, so start the tool process
2219  PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
2220  ARRAY_CREATOR array_creator( *editFrame, m_isFootprintEditor, selection, m_toolMgr );
2221  array_creator.Invoke();
2222 
2223  return 0;
2224 }
2225 
2226 
2228  PCB_SELECTION_TOOL* sTool )
2229 {
2230  for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
2231  {
2232  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( aCollector[i] );
2233 
2234  if( item->Type() != PCB_PAD_T )
2235  aCollector.Remove( i );
2236  }
2237 }
2238 
2239 
2241  PCB_SELECTION_TOOL* sTool )
2242 {
2243  for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
2244  {
2245  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( aCollector[i] );
2246 
2247  if( item->Type() != PCB_FOOTPRINT_T )
2248  aCollector.Remove( i );
2249  }
2250 }
2251 
2252 
2254 {
2255  if( m_dragging && aSelection.HasReferencePoint() )
2256  return false;
2257 
2258  // Can't modify an empty group
2259  if( aSelection.Empty() )
2260  return false;
2261 
2262  // When there is only one item selected, the reference point is its position...
2263  if( aSelection.Size() == 1 )
2264  {
2265  auto item = static_cast<BOARD_ITEM*>( aSelection.Front() );
2266  auto pos = item->GetPosition();
2267  aSelection.SetReferencePoint( VECTOR2I( pos.x, pos.y ) );
2268  }
2269  // ...otherwise modify items with regard to the grid-snapped center position
2270  else
2271  {
2272  PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
2273  aSelection.SetReferencePoint( grid.BestSnapAnchor( aSelection.GetCenter(), nullptr ) );
2274  }
2275 
2276  return true;
2277 }
2278 
2279 
2280 bool EDIT_TOOL::pickReferencePoint( const wxString& aTooltip, const wxString& aSuccessMessage,
2281  const wxString& aCanceledMessage, VECTOR2I& aReferencePoint )
2282 {
2284  OPT<VECTOR2I> pickedPoint;
2285  bool done = false;
2286 
2287  m_statusPopup->SetText( aTooltip );
2288 
2290  picker->SetSnapping( true );
2291 
2292  picker->SetClickHandler(
2293  [&]( const VECTOR2D& aPoint ) -> bool
2294  {
2295  pickedPoint = aPoint;
2296 
2297  if( !aSuccessMessage.empty() )
2298  {
2299  m_statusPopup->SetText( aSuccessMessage );
2300  m_statusPopup->Expire( 800 );
2301  }
2302  else
2303  {
2304  m_statusPopup->Hide();
2305  }
2306 
2307  return false; // we don't need any more points
2308  } );
2309 
2310  picker->SetMotionHandler(
2311  [&]( const VECTOR2D& aPos )
2312  {
2313  m_statusPopup->Move( wxGetMousePosition() + wxPoint( 20, -50 ) );
2314  } );
2315 
2316  picker->SetCancelHandler(
2317  [&]()
2318  {
2319  if( !aCanceledMessage.empty() )
2320  {
2321  m_statusPopup->SetText( aCanceledMessage );
2322  m_statusPopup->Expire( 800 );
2323  }
2324  else
2325  {
2326  m_statusPopup->Hide();
2327  }
2328  } );
2329 
2330  picker->SetFinalizeHandler(
2331  [&]( const int& aFinalState )
2332  {
2333  done = true;
2334  } );
2335 
2336  m_statusPopup->Move( wxGetMousePosition() + wxPoint( 20, -50 ) );
2337  m_statusPopup->Popup();
2338 
2339  std::string tool = "";
2340  m_toolMgr->RunAction( ACTIONS::pickerTool, true, &tool );
2341 
2342  while( !done )
2343  {
2344  // Pass events unless we receive a null event, then we must shut down
2345  if( TOOL_EVENT* evt = Wait() )
2346  evt->SetPassEvent();
2347  else
2348  break;
2349  }
2350 
2351  // Ensure statusPopup is hidden after use and before deleting it:
2352  m_statusPopup->Hide();
2353 
2354  if( pickedPoint.is_initialized() )
2355  aReferencePoint = pickedPoint.get();
2356 
2357  return pickedPoint.is_initialized();
2358 }
2359 
2360 
2362 {
2363  std::string tool = "pcbnew.InteractiveEdit.selectReferencePoint";
2364  CLIPBOARD_IO io;
2366  getEditFrame<PCB_BASE_EDIT_FRAME>()->GetMagneticItemsSettings() );
2367 
2368  frame()->PushTool( tool );
2369  Activate();
2370 
2372  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2373  {
2374  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
2375  {
2376  BOARD_ITEM* item = aCollector[i];
2377 
2378  // We can't copy both a footprint and its text in the same operation, so if
2379  // both are selected, remove the text
2380  if( item->Type() == PCB_FP_TEXT_T && aCollector.HasItem( item->GetParent() ) )
2381  aCollector.Remove( item );
2382  }
2383  },
2384 
2385  // Prompt user regarding locked items.
2386  aEvent.IsAction( &ACTIONS::cut ) && !m_isFootprintEditor );
2387 
2388  if( !selection.Empty() )
2389  {
2390  std::vector<BOARD_ITEM*> items;
2391 
2392  for( EDA_ITEM* item : selection )
2393  items.push_back( static_cast<BOARD_ITEM*>( item ) );
2394 
2395  VECTOR2I refPoint;
2396 
2397  if( aEvent.IsAction( &PCB_ACTIONS::copyWithReference ) )
2398  {
2399  if( !pickReferencePoint( _( "Select reference point for the copy..." ),
2400  _( "Selection copied" ),
2401  _( "Copy canceled" ),
2402  refPoint ) )
2403  {
2404  frame()->PopTool( tool );
2405  return 0;
2406  }
2407  }
2408  else
2409  {
2410  refPoint = grid.BestDragOrigin( getViewControls()->GetCursorPosition(), items );
2411  }
2412 
2413  selection.SetReferencePoint( refPoint );
2414 
2415  io.SetBoard( board() );
2417  frame()->SetStatusText( _( "Selection copied" ) );
2418  }
2419 
2420  frame()->PopTool( tool );
2421 
2422  return 0;
2423 }
2424 
2425 
2427 {
2428  if( !copyToClipboard( aEvent ) )
2429  {
2430  // N.B. Setting the CUT flag prevents lock filtering as we only want to delete the items
2431  // that were copied to the clipboard, no more, no fewer. Filtering for locked item, if
2432  // any will be done in the copyToClipboard() routine
2433  TOOL_EVENT evt( aEvent.Category(), aEvent.Action(), TOOL_ACTION_SCOPE::AS_GLOBAL );
2435  Remove( evt );
2436  }
2437 
2438  return 0;
2439 }
2440 
2441 
2443 {
2445  Go( &EDIT_TOOL::Move, PCB_ACTIONS::move.MakeEvent() );
2446  Go( &EDIT_TOOL::Drag, PCB_ACTIONS::drag45Degree.MakeEvent() );
2448  Go( &EDIT_TOOL::Rotate, PCB_ACTIONS::rotateCw.MakeEvent() );
2449  Go( &EDIT_TOOL::Rotate, PCB_ACTIONS::rotateCcw.MakeEvent() );
2450  Go( &EDIT_TOOL::Flip, PCB_ACTIONS::flip.MakeEvent() );
2451  Go( &EDIT_TOOL::Remove, ACTIONS::doDelete.MakeEvent() );
2452  Go( &EDIT_TOOL::Remove, PCB_ACTIONS::deleteFull.MakeEvent() );
2456  Go( &EDIT_TOOL::Duplicate, ACTIONS::duplicate.MakeEvent() );
2459  Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirror.MakeEvent() );
2462 
2463  Go( &EDIT_TOOL::copyToClipboard, ACTIONS::copy.MakeEvent() );
2465  Go( &EDIT_TOOL::cutToClipboard, ACTIONS::cut.MakeEvent() );
2466 }
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:2442
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:194
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:680
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:280
void SetLastPadNumber(const wxString &aPadNumber)
Definition: pad_tool.h:63
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:1351
int Rotate(const TOOL_EVENT &aEvent)
Rotate currently selected items.
Definition: edit_tool.cpp:1410
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:804
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:163
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:190
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:1079
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:1529
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:223
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:237
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:1551
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:2426
bool pickReferencePoint(const wxString &aTooltip, const wxString &aSuccessMessage, const wxString &aCanceledMessage, VECTOR2I &aReferencePoint)
Definition: edit_tool.cpp:2280
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:525
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:1651
void SetReferencePoint(const VECTOR2I &aP)
Definition: selection.h:189
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:268
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:505
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:690
bool updateModificationPoint(PCB_SELECTION &aSelection)
Definition: edit_tool.cpp:2253
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:941
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:238
virtual void PopTool(const std::string &actionName)
int ShowQuasiModal()
void SetMid(const wxPoint &aMid)
Definition: pcb_track.h:270
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:62
int FilletTracks(const TOOL_EVENT &aEvent)
Fillet (i.e.
Definition: edit_tool.cpp:1149
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:227
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:676
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:213
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
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:2227
int Move(const TOOL_EVENT &aEvent)
Main loop in which events are handled.
Definition: edit_tool.cpp:664
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:1956
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:1735
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:903
bool invokeInlineRouter(int aDragMode)
Definition: edit_tool.cpp:235
double GetAngle() const
Definition: pcb_track.cpp:937
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:2199
KIGFX::VIEW_CONTROLS * controls() const
int GetAttributes() const
Definition: footprint.h:236
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:279
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:759
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:1514
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.
virtual BOARD_ITEM * Duplicate() const
Create a copy of this BOARD_ITEM.
Definition: board_item.h:171
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:2361
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:2240
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:135
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:2058
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:142
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:271
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:945
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:613
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:147
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:313
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
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