KiCad PCB EDA Suite
placement_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) 2014-2016 CERN
5  * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
6  * @author Maciej Suminski <[email protected]>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 #include "tool/selection.h"
26 #include "placement_tool.h"
27 #include "pcb_actions.h"
28 #include "pcb_selection_tool.h"
29 
30 #include <ratsnest/ratsnest_data.h>
31 #include <tool/tool_manager.h>
32 
33 #include <pcb_edit_frame.h>
34 #include <board.h>
35 #include <board_commit.h>
36 #include <bitmaps.h>
37 
38 #include <confirm.h>
39 #include <menus_helpers.h>
40 
41 
43  TOOL_INTERACTIVE( "pcbnew.Placement" ),
44  m_selectionTool( nullptr ),
45  m_placementMenu( nullptr ),
46  m_frame( nullptr )
47 {
48 }
49 
51 {
52  delete m_placementMenu;
53 }
54 
55 
57 {
58  // Find the selection tool, so they can cooperate
60  m_frame = getEditFrame<PCB_BASE_FRAME>();
61 
62  // Create a context menu and make it available through selection tool
63  m_placementMenu = new ACTION_MENU( true, this );
65  m_placementMenu->SetTitle( _( "Align/Distribute" ) );
66 
67  // Add all align/distribute commands
71 
72  m_placementMenu->AppendSeparator();
76 
77  m_placementMenu->AppendSeparator();
80 
83 
84  return true;
85 }
86 
87 
88 template <class T>
90 {
91  ALIGNMENT_RECTS rects;
92 
93  for( EDA_ITEM* item : aItems )
94  {
95  BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
96 
97  if( item->Type() == PCB_FOOTPRINT_T )
98  {
99  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
100  rects.emplace_back( std::make_pair( boardItem,
101  footprint->GetBoundingBox( false, false ) ) );
102  }
103  else
104  {
105  rects.emplace_back( std::make_pair( boardItem, item->GetBoundingBox() ) );
106  }
107  }
108 
109  return rects;
110 }
111 
112 
113 template< typename T >
115  T aGetValue )
116 {
117  wxPoint curPos = (wxPoint) getViewControls()->GetCursorPosition();
118 
119  // Prefer locked items to unlocked items.
120  // Secondly, prefer items under the cursor to other items.
121 
122  if( aLocked.size() >= 1 )
123  {
124  for( const ALIGNMENT_RECT& item : aLocked )
125  {
126  if( item.second.Contains( curPos ) )
127  return aGetValue( item );
128  }
129 
130  return aGetValue( aLocked.front() );
131  }
132 
133  for( const ALIGNMENT_RECT& item : aItems )
134  {
135  if( item.second.Contains( curPos ) )
136  return aGetValue( item );
137  }
138 
139  return aGetValue( aItems.front() );
140 }
141 
142 
143 template< typename T >
145  ALIGNMENT_RECTS& aLockedItems, T aCompare )
146 {
148  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
149  {
150  // Iterate from the back so we don't have to worry about removals.
151  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
152  {
153  BOARD_ITEM* item = aCollector[i];
154 
155  if( item->Type() == PCB_MARKER_T )
156  aCollector.Remove( item );
157  }
158  } );
159 
160  std::vector<BOARD_ITEM*> lockedItems;
161  std::vector<BOARD_ITEM*> itemsToAlign;
162 
163  for( EDA_ITEM* item : selection )
164  {
165  BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
166 
167  // We do not lock items in the footprint editor
168  if( boardItem->IsLocked() && m_frame->IsType( FRAME_PCB_EDITOR ) )
169  {
170  // Locking a pad but not the footprint means that we align the footprint using
171  // the pad position. So we test for footprint locking here
172  if( boardItem->Type() == PCB_PAD_T && !boardItem->GetParent()->IsLocked() )
173  {
174  itemsToAlign.push_back( boardItem );
175  }
176  else
177  {
178  lockedItems.push_back( boardItem );
179  }
180  }
181  else
182  itemsToAlign.push_back( boardItem );
183  }
184 
185  aItemsToAlign = GetBoundingBoxes( itemsToAlign );
186  aLockedItems = GetBoundingBoxes( lockedItems );
187  std::sort( aItemsToAlign.begin(), aItemsToAlign.end(), aCompare );
188  std::sort( aLockedItems.begin(), aLockedItems.end(), aCompare );
189 
190  return aItemsToAlign.size();
191 }
192 
193 
195 {
196  ALIGNMENT_RECTS itemsToAlign;
197  ALIGNMENT_RECTS locked_items;
198 
199  if( !GetSelections( itemsToAlign, locked_items,
200  []( const ALIGNMENT_RECT left, const ALIGNMENT_RECT right)
201  {
202  return ( left.second.GetTop() < right.second.GetTop() );
203  } ) )
204  {
205  return 0;
206  }
207 
208  BOARD_COMMIT commit( m_frame );
209 
210  int targetTop = selectTarget( itemsToAlign, locked_items,
211  []( const ALIGNMENT_RECT& aVal )
212  {
213  return aVal.second.GetTop();
214  } );
215 
216  // Move the selected items
217  for( std::pair<BOARD_ITEM*, EDA_RECT>& i : itemsToAlign )
218  {
219  BOARD_ITEM* item = i.first;
220  int difference = targetTop - i.second.GetTop();
221 
222  if( item->GetParent() && item->GetParent()->IsSelected() )
223  continue;
224 
225  // Don't move a pad by itself unless editing the footprint
226  if( item->Type() == PCB_PAD_T && m_frame->IsType( FRAME_PCB_EDITOR ) )
227  item = item->GetParent();
228 
229  commit.Stage( item, CHT_MODIFY );
230  item->Move( wxPoint( 0, difference ) );
231  }
232 
233  commit.Push( _( "Align to top" ) );
234 
235  return 0;
236 }
237 
238 
240 {
241  ALIGNMENT_RECTS itemsToAlign;
242  ALIGNMENT_RECTS locked_items;
243 
244  if( !GetSelections( itemsToAlign, locked_items,
245  []( const ALIGNMENT_RECT left, const ALIGNMENT_RECT right)
246  {
247  return ( left.second.GetBottom() < right.second.GetBottom() );
248  } ) )
249  {
250  return 0;
251  }
252 
253  BOARD_COMMIT commit( m_frame );
254 
255  int targetBottom = selectTarget( itemsToAlign, locked_items,
256  []( const ALIGNMENT_RECT& aVal )
257  {
258  return aVal.second.GetBottom();
259  } );
260 
261  // Move the selected items
262  for( std::pair<BOARD_ITEM*, EDA_RECT>& i : itemsToAlign )
263  {
264  int difference = targetBottom - i.second.GetBottom();
265  BOARD_ITEM* item = i.first;
266 
267  if( item->GetParent() && item->GetParent()->IsSelected() )
268  continue;
269 
270  // Don't move a pad by itself unless editing the footprint
271  if( item->Type() == PCB_PAD_T && m_frame->IsType( FRAME_PCB_EDITOR ) )
272  item = item->GetParent();
273 
274  commit.Stage( item, CHT_MODIFY );
275  item->Move( wxPoint( 0, difference ) );
276  }
277 
278  commit.Push( _( "Align to bottom" ) );
279 
280  return 0;
281 }
282 
283 
285 {
286  // Because this tool uses bounding boxes and they aren't mirrored even when
287  // the view is mirrored, we need to call the other one if mirrored.
288  if( getView()->IsMirroredX() )
289  {
290  return doAlignRight();
291  }
292  else
293  {
294  return doAlignLeft();
295  }
296 }
297 
298 
300 {
301  ALIGNMENT_RECTS itemsToAlign;
302  ALIGNMENT_RECTS locked_items;
303 
304  if( !GetSelections( itemsToAlign, locked_items,
305  []( const ALIGNMENT_RECT left, const ALIGNMENT_RECT right)
306  {
307  return ( left.second.GetLeft() < right.second.GetLeft() );
308  } ) )
309  {
310  return 0;
311  }
312 
313  BOARD_COMMIT commit( m_frame );
314 
315  int targetLeft = selectTarget( itemsToAlign, locked_items,
316  []( const ALIGNMENT_RECT& aVal )
317  {
318  return aVal.second.GetLeft();
319  } );
320 
321  // Move the selected items
322  for( std::pair<BOARD_ITEM*, EDA_RECT>& i : itemsToAlign )
323  {
324  int difference = targetLeft - i.second.GetLeft();
325  BOARD_ITEM* item = i.first;
326 
327  if( item->GetParent() && item->GetParent()->IsSelected() )
328  continue;
329 
330  // Don't move a pad by itself unless editing the footprint
331  if( item->Type() == PCB_PAD_T && m_frame->IsType( FRAME_PCB_EDITOR ) )
332  item = item->GetParent();
333 
334  commit.Stage( item, CHT_MODIFY );
335  item->Move( wxPoint( difference, 0 ) );
336  }
337 
338  commit.Push( _( "Align to left" ) );
339 
340  return 0;
341 }
342 
343 
345 {
346  // Because this tool uses bounding boxes and they aren't mirrored even when
347  // the view is mirrored, we need to call the other one if mirrored.
348  if( getView()->IsMirroredX() )
349  {
350  return doAlignLeft();
351  }
352  else
353  {
354  return doAlignRight();
355  }
356 }
357 
358 
360 {
361  ALIGNMENT_RECTS itemsToAlign;
362  ALIGNMENT_RECTS locked_items;
363 
364  if( !GetSelections( itemsToAlign, locked_items,
365  []( const ALIGNMENT_RECT left, const ALIGNMENT_RECT right)
366  {
367  return ( left.second.GetRight() < right.second.GetRight() );
368  } ) )
369  {
370  return 0;
371  }
372 
373  BOARD_COMMIT commit( m_frame );
374 
375  int targetRight = selectTarget( itemsToAlign, locked_items,
376  []( const ALIGNMENT_RECT& aVal )
377  {
378  return aVal.second.GetRight();
379  } );
380 
381  // Move the selected items
382  for( std::pair<BOARD_ITEM*, EDA_RECT>& i : itemsToAlign )
383  {
384  int difference = targetRight - i.second.GetRight();
385  BOARD_ITEM* item = i.first;
386 
387  if( item->GetParent() && item->GetParent()->IsSelected() )
388  continue;
389 
390  // Don't move a pad by itself unless editing the footprint
391  if( item->Type() == PCB_PAD_T && m_frame->IsType( FRAME_PCB_EDITOR ) )
392  item = item->GetParent();
393 
394  commit.Stage( item, CHT_MODIFY );
395  item->Move( wxPoint( difference, 0 ) );
396  }
397 
398  commit.Push( _( "Align to right" ) );
399 
400  return 0;
401 }
402 
403 
405 {
406  ALIGNMENT_RECTS itemsToAlign;
407  ALIGNMENT_RECTS locked_items;
408 
409  if( !GetSelections( itemsToAlign, locked_items,
410  []( const ALIGNMENT_RECT left, const ALIGNMENT_RECT right)
411  {
412  return ( left.second.GetCenter().x < right.second.GetCenter().x );
413  } ) )
414  {
415  return 0;
416  }
417 
418  BOARD_COMMIT commit( m_frame );
419 
420  int targetX = selectTarget( itemsToAlign, locked_items,
421  []( const ALIGNMENT_RECT& aVal )
422  {
423  return aVal.second.GetCenter().x;
424  } );
425 
426  // Move the selected items
427  for( std::pair<BOARD_ITEM*, EDA_RECT>& i : itemsToAlign )
428  {
429  int difference = targetX - i.second.GetCenter().x;
430  BOARD_ITEM* item = i.first;
431 
432  if( item->GetParent() && item->GetParent()->IsSelected() )
433  continue;
434 
435  // Don't move a pad by itself unless editing the footprint
436  if( item->Type() == PCB_PAD_T && m_frame->IsType( FRAME_PCB_EDITOR ) )
437  item = item->GetParent();
438 
439  commit.Stage( item, CHT_MODIFY );
440  item->Move( wxPoint( difference, 0 ) );
441  }
442 
443  commit.Push( _( "Align to middle" ) );
444 
445  return 0;
446 }
447 
448 
450 {
451  ALIGNMENT_RECTS itemsToAlign;
452  ALIGNMENT_RECTS locked_items;
453 
454  if( !GetSelections( itemsToAlign, locked_items,
455  []( const ALIGNMENT_RECT left, const ALIGNMENT_RECT right)
456  {
457  return ( left.second.GetCenter().y < right.second.GetCenter().y );
458  } ) )
459  {
460  return 0;
461  }
462 
463  BOARD_COMMIT commit( m_frame );
464 
465  int targetY = selectTarget( itemsToAlign, locked_items,
466  []( const ALIGNMENT_RECT& aVal )
467  {
468  return aVal.second.GetCenter().y;
469  } );
470 
471  // Move the selected items
472  for( std::pair<BOARD_ITEM*, EDA_RECT>& i : itemsToAlign )
473  {
474  int difference = targetY - i.second.GetCenter().y;
475  BOARD_ITEM* item = i.first;
476 
477  if( item->GetParent() && item->GetParent()->IsSelected() )
478  continue;
479 
480  // Don't move a pad by itself unless editing the footprint
481  if( item->Type() == PCB_PAD_T && m_frame->IsType( FRAME_PCB_EDITOR ) )
482  item = item->GetParent();
483 
484  commit.Stage( item, CHT_MODIFY );
485  item->Move( wxPoint( 0, difference ) );
486  }
487 
488  commit.Push( _( "Align to center" ) );
489 
490  return 0;
491 }
492 
493 
495 {
497  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
498  {
499  // Iterate from the back so we don't have to worry about removals.
500  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
501  {
502  BOARD_ITEM* item = aCollector[i];
503 
504  if( item->Type() == PCB_MARKER_T )
505  aCollector.Remove( item );
506  }
507  },
508  m_frame->IsType( FRAME_PCB_EDITOR ) /* prompt user regarding locked items */ );
509 
510  if( selection.Size() <= 1 )
511  return 0;
512 
513  BOARD_COMMIT commit( m_frame );
514  ALIGNMENT_RECTS itemsToDistribute = GetBoundingBoxes( selection );
515 
516  // find the last item by reverse sorting
517  std::sort( itemsToDistribute.begin(), itemsToDistribute.end(),
518  [] ( const ALIGNMENT_RECT left, const ALIGNMENT_RECT right)
519  {
520  return ( left.second.GetRight() > right.second.GetRight() );
521  } );
522 
523  BOARD_ITEM* lastItem = itemsToDistribute.begin()->first;
524  const int maxRight = itemsToDistribute.begin()->second.GetRight();
525 
526  // sort to get starting order
527  std::sort( itemsToDistribute.begin(), itemsToDistribute.end(),
528  [] ( const ALIGNMENT_RECT left, const ALIGNMENT_RECT right)
529  {
530  return ( left.second.GetX() < right.second.GetX() );
531  } );
532 
533  const int minX = itemsToDistribute.begin()->second.GetX();
534  int totalGap = maxRight - minX;
535  int totalWidth = 0;
536 
537  for( std::pair<BOARD_ITEM*, EDA_RECT>& i : itemsToDistribute )
538  totalWidth += i.second.GetWidth();
539 
540  if( totalGap < totalWidth )
541  {
542  // the width of the items exceeds the gap (overlapping items) -> use center point spacing
543  doDistributeCentersHorizontally( itemsToDistribute, commit );
544  }
545  else
546  {
547  totalGap -= totalWidth;
548  doDistributeGapsHorizontally( itemsToDistribute, commit, lastItem, totalGap );
549  }
550 
551  commit.Push( _( "Distribute horizontally" ) );
552 
553  return 0;
554 }
555 
556 
558  BOARD_COMMIT& aCommit,
559  const BOARD_ITEM* lastItem,
560  int totalGap ) const
561 {
562  const int itemGap = totalGap / ( itemsToDistribute.size() - 1 );
563  int targetX = itemsToDistribute.begin()->second.GetX();
564 
565  for( std::pair<BOARD_ITEM*, EDA_RECT>& i : itemsToDistribute )
566  {
567  BOARD_ITEM* item = i.first;
568 
569  // cover the corner case where the last item is wider than the previous item and gap
570  if( lastItem == item )
571  continue;
572 
573  if( item->GetParent() && item->GetParent()->IsSelected() )
574  continue;
575 
576  // Don't move a pad by itself unless editing the footprint
577  if( item->Type() == PCB_PAD_T && m_frame->IsType( FRAME_PCB_EDITOR ) )
578  item = item->GetParent();
579 
580  int difference = targetX - i.second.GetX();
581  aCommit.Stage( item, CHT_MODIFY );
582  item->Move( wxPoint( difference, 0 ) );
583  targetX += ( i.second.GetWidth() + itemGap );
584  }
585 }
586 
587 
589  BOARD_COMMIT& aCommit ) const
590 {
591  std::sort( itemsToDistribute.begin(), itemsToDistribute.end(),
592  [] ( const ALIGNMENT_RECT left, const ALIGNMENT_RECT right)
593  {
594  return ( left.second.GetCenter().x < right.second.GetCenter().x );
595  } );
596 
597  const int totalGap = ( itemsToDistribute.end()-1 )->second.GetCenter().x
598  - itemsToDistribute.begin()->second.GetCenter().x;
599  const int itemGap = totalGap / ( itemsToDistribute.size() - 1 );
600  int targetX = itemsToDistribute.begin()->second.GetCenter().x;
601 
602  for( std::pair<BOARD_ITEM*, EDA_RECT>& i : itemsToDistribute )
603  {
604  BOARD_ITEM* item = i.first;
605 
606  if( item->GetParent() && item->GetParent()->IsSelected() )
607  continue;
608 
609  // Don't move a pad by itself unless editing the footprint
610  if( item->Type() == PCB_PAD_T && m_frame->IsType( FRAME_PCB_EDITOR ) )
611  item = item->GetParent();
612 
613  int difference = targetX - i.second.GetCenter().x;
614  aCommit.Stage( item, CHT_MODIFY );
615  item->Move( wxPoint( difference, 0 ) );
616  targetX += ( itemGap );
617  }
618 }
619 
620 
622 {
624  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
625  {
626  // Iterate from the back so we don't have to worry about removals.
627  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
628  {
629  BOARD_ITEM* item = aCollector[i];
630 
631  if( item->Type() == PCB_MARKER_T )
632  aCollector.Remove( item );
633  }
634  },
635  m_frame->IsType( FRAME_PCB_EDITOR ) /* prompt user regarding locked items */ );
636 
637  if( selection.Size() <= 1 )
638  return 0;
639 
640  BOARD_COMMIT commit( m_frame );
641  ALIGNMENT_RECTS itemsToDistribute = GetBoundingBoxes( selection );
642 
643  // find the last item by reverse sorting
644  std::sort( itemsToDistribute.begin(), itemsToDistribute.end(),
645  [] ( const ALIGNMENT_RECT left, const ALIGNMENT_RECT right)
646  {
647  return ( left.second.GetBottom() > right.second.GetBottom() );
648  } );
649 
650  BOARD_ITEM* lastItem = itemsToDistribute.begin()->first;
651  const int maxBottom = itemsToDistribute.begin()->second.GetBottom();
652 
653  // sort to get starting order
654  std::sort( itemsToDistribute.begin(), itemsToDistribute.end(),
655  [] ( const ALIGNMENT_RECT left, const ALIGNMENT_RECT right)
656  {
657  return ( left.second.GetCenter().y < right.second.GetCenter().y );
658  } );
659 
660  int minY = itemsToDistribute.begin()->second.GetY();
661  int totalGap = maxBottom - minY;
662  int totalHeight = 0;
663 
664  for( std::pair<BOARD_ITEM*, EDA_RECT>& i : itemsToDistribute )
665  totalHeight += i.second.GetHeight();
666 
667  if( totalGap < totalHeight )
668  {
669  // the width of the items exceeds the gap (overlapping items) -> use center point spacing
670  doDistributeCentersVertically( itemsToDistribute, commit );
671  }
672  else
673  {
674  totalGap -= totalHeight;
675  doDistributeGapsVertically( itemsToDistribute, commit, lastItem, totalGap );
676  }
677 
678  commit.Push( _( "Distribute vertically" ) );
679 
680  return 0;
681 }
682 
683 
685  BOARD_COMMIT& aCommit,
686  const BOARD_ITEM* lastItem,
687  int totalGap ) const
688 {
689  const int itemGap = totalGap / ( itemsToDistribute.size() - 1 );
690  int targetY = itemsToDistribute.begin()->second.GetY();
691 
692  for( std::pair<BOARD_ITEM*, EDA_RECT>& i : itemsToDistribute )
693  {
694  BOARD_ITEM* item = i.first;
695 
696  // cover the corner case where the last item is wider than the previous item and gap
697  if( lastItem == item )
698  continue;
699 
700  if( item->GetParent() && item->GetParent()->IsSelected() )
701  continue;
702 
703  // Don't move a pad by itself unless editing the footprint
704  if( item->Type() == PCB_PAD_T && m_frame->IsType( FRAME_PCB_EDITOR ) )
705  item = item->GetParent();
706 
707  int difference = targetY - i.second.GetY();
708  aCommit.Stage( item, CHT_MODIFY );
709  item->Move( wxPoint( 0, difference ) );
710  targetY += ( i.second.GetHeight() + itemGap );
711  }
712 }
713 
714 
716  BOARD_COMMIT& aCommit ) const
717 {
718  std::sort( itemsToDistribute.begin(), itemsToDistribute.end(),
719  [] ( const ALIGNMENT_RECT left, const ALIGNMENT_RECT right)
720  {
721  return ( left.second.GetCenter().y < right.second.GetCenter().y );
722  } );
723 
724  const int totalGap = ( itemsToDistribute.end()-1 )->second.GetCenter().y
725  - itemsToDistribute.begin()->second.GetCenter().y;
726  const int itemGap = totalGap / ( itemsToDistribute.size() - 1 );
727  int targetY = itemsToDistribute.begin()->second.GetCenter().y;
728 
729  for( std::pair<BOARD_ITEM*, EDA_RECT>& i : itemsToDistribute )
730  {
731  BOARD_ITEM* item = i.first;
732 
733  if( item->GetParent() && item->GetParent()->IsSelected() )
734  continue;
735 
736  // Don't move a pad by itself unless editing the footprint
737  if( item->Type() == PCB_PAD_T && m_frame->IsType( FRAME_PCB_EDITOR ) )
738  item = item->GetParent();
739 
740  int difference = targetY - i.second.GetCenter().y;
741  aCommit.Stage( item, CHT_MODIFY );
742  item->Move( wxPoint( 0, difference ) );
743  targetY += ( itemGap );
744  }
745 }
746 
747 
749 {
756 
760  PCB_ACTIONS::distributeVertically.MakeEvent() );
761 }
static TOOL_ACTION alignRight
Definition: pcb_actions.h:224
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
int AlignLeft(const TOOL_EVENT &aEvent)
Sets X coordinate of the selected items to the value of the left-most selected item X coordinate.
int selectTarget(ALIGNMENT_RECTS &aItems, ALIGNMENT_RECTS &aLocked, T aGetValue)
int AlignCenterY(const TOOL_EVENT &aEvent)
Set the y coordinate of the midpoint of each of the selected items to the value of the y coordinate o...
bool IsSelected() const
Definition: eda_item.h:123
Defines the structure of a menu based on ACTIONs.
Definition: action_menu.h:48
This file is part of the common library.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:49
PCB_SELECTION_TOOL * m_selectionTool
int AlignCenterX(const TOOL_EVENT &aEvent)
Set the x coordinate of the midpoint of each of the selected items to the value of the x coordinate o...
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
Definition: action_menu.cpp:73
static SELECTION_CONDITION MoreThan(int aNumber)
Create a functor that tests if the number of selected items is greater than the value given as parame...
virtual ~ALIGN_DISTRIBUTE_TOOL()
ACTION_MENU * m_placementMenu
Class that computes missing connections on a PCB.
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:46
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:214
static TOOL_ACTION alignCenterY
Definition: pcb_actions.h:226
TOOL_MENU & GetToolMenu()
int AlignBottom(const TOOL_EVENT &aEvent)
Sets Y coordinate of the selected items to the value of the bottom-most selected item Y coordinate.
class PAD, a pad in a footprint
Definition: typeinfo.h:89
static TOOL_ACTION distributeVertically
Definition: pcb_actions.h:228
void doDistributeCentersHorizontally(ALIGNMENT_RECTS &itemsToDistribute, BOARD_COMMIT &aCommit) const
Distribute selected items using an even spacing between the centers of their bounding boxes.
virtual bool IsLocked() const
Definition: board_item.cpp:64
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition: collector.h:110
static TOOL_ACTION alignBottom
Definition: pcb_actions.h:222
static TOOL_ACTION distributeHorizontally
Definition: pcb_actions.h:227
ALIGNMENT_RECTS GetBoundingBoxes(const T &aItems)
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:82
int DistributeHorizontally(const TOOL_EVENT &aEvent)
Distribute the selected items along the X axis.
int doAlignRight()
Align selected items using the right edge of their bounding boxes to the right-most item.
void doDistributeGapsHorizontally(ALIGNMENT_RECTS &itemsToDistribute, BOARD_COMMIT &aCommit, const BOARD_ITEM *lastItem, int totalGap) const
Distributes selected items using an even spacing between their bounding boxes.
int doAlignLeft()
Sets X coordinate of the selected items to the value of the left-most selected item X coordinate.
int GetY() const
Definition: board_item.h:70
std::vector< ALIGNMENT_RECT > ALIGNMENT_RECTS
virtual void Move(const wxPoint &aMoveVector)
Move this object.
Definition: board_item.h:236
int AlignTop(const TOOL_EVENT &aEvent)
Set Y coordinate of the selected items to the value of the top-most selected item Y coordinate.
Generic, UI-independent tool event.
Definition: tool_event.h:152
int DistributeVertically(const TOOL_EVENT &aEvent)
Distribute the selected items along the Y axis.
static TOOL_ACTION alignLeft
Definition: pcb_actions.h:223
#define _(s)
virtual wxPoint GetCenter() const
This defaults to the center of the bounding box if not overridden.
Definition: board_item.h:81
static TOOL_ACTION alignTop
Definition: pcb_actions.h:221
void doDistributeGapsVertically(ALIGNMENT_RECTS &itemsToDistribute, BOARD_COMMIT &aCommit, const BOARD_ITEM *lastItem, int totalGap) const
Distributes selected items using an even spacing between their bounding boxes.
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
class FOOTPRINT, a footprint
Definition: typeinfo.h:88
void doDistributeCentersVertically(ALIGNMENT_RECTS &itemsToDistribute, BOARD_COMMIT &aCommit) const
Distribute selected items using an even spacing between the centers of their bounding boxes.
class PCB_MARKER, a marker used to show something
Definition: typeinfo.h:98
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.
int GetX() const
Definition: board_item.h:64
PCB_BASE_FRAME * m_frame
bool IsType(FRAME_T aType) const
std::pair< BOARD_ITEM *, EDA_RECT > ALIGNMENT_RECT
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:240
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
const EDA_RECT GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: footprint.cpp:718
wxMenuItem * Add(const wxString &aLabel, int aId, BITMAPS aIcon)
Add a wxWidgets-style entry to the menu.
int Size() const
Returns the number of selected parts.
Definition: selection.h:104
The selection tool: currently supports:
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:100
virtual void Push(const wxString &aMessage=wxT("A commit"), bool aCreateUndoEntry=true, bool aSetDirtyBit=true) override
Revert the commit by restoring the modified items state.
int AlignRight(const TOOL_EVENT &aEvent)
Sets X coordinate of the selected items to the value of the right-most selected item X coordinate.
virtual const EDA_RECT GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition: eda_item.cpp:75
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
bool Init() override
Init() is called once upon a registration of the tool.
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:135
static TOOL_ACTION alignCenterX
Definition: pcb_actions.h:225
size_t GetSelections(ALIGNMENT_RECTS &aItemsToAlign, ALIGNMENT_RECTS &aLockedItems, T aCompare)
Populate two vectors with the sorted selection and sorted locked items.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
COMMIT & Stage(EDA_ITEM *aItem, CHANGE_TYPE aChangeType) override
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113