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