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