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  * @author Maciej Suminski <maciej.suminski@cern.ch>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include "placement_tool.h"
26 #include "pcb_actions.h"
27 #include "selection_tool.h"
28 #include <tool/tool_manager.h>
29 
30 #include <wxPcbStruct.h>
31 #include <class_board.h>
32 #include <ratsnest_data.h>
33 #include <board_commit.h>
34 #include <bitmaps.h>
35 
36 #include <confirm.h>
37 #include <menus_helpers.h>
38 
39 // Placement tool
40 TOOL_ACTION PCB_ACTIONS::alignTop( "pcbnew.AlignAndDistribute.alignTop",
41  AS_GLOBAL, 0,
42  _( "Align to Top" ),
43  _( "Aligns selected items to the top edge" ), up_xpm );
44 
45 TOOL_ACTION PCB_ACTIONS::alignBottom( "pcbnew.AlignAndDistribute.alignBottom",
46  AS_GLOBAL, 0,
47  _( "Align to Bottom" ),
48  _( "Aligns selected items to the bottom edge" ), down_xpm );
49 
50 TOOL_ACTION PCB_ACTIONS::alignLeft( "pcbnew.AlignAndDistribute.alignLeft",
51  AS_GLOBAL, 0,
52  _( "Align to Left" ),
53  _( "Aligns selected items to the left edge" ), left_xpm );
54 
55 TOOL_ACTION PCB_ACTIONS::alignRight( "pcbnew.AlignAndDistribute.alignRight",
56  AS_GLOBAL, 0,
57  _( "Align to Right" ),
58  _( "Aligns selected items to the right edge" ), right_xpm );
59 
60 TOOL_ACTION PCB_ACTIONS::distributeHorizontally( "pcbnew.AlignAndDistribute.distributeHorizontally",
61  AS_GLOBAL, 0,
62  _( "Distribute Horizontally" ),
63  _( "Distributes selected items along the horizontal axis" ), distribute_horizontal_xpm );
64 
65 TOOL_ACTION PCB_ACTIONS::distributeVertically( "pcbnew.AlignAndDistribute.distributeVertically",
66  AS_GLOBAL, 0,
67  _( "Distribute Vertically" ),
68  _( "Distributes selected items along the vertical axis" ), distribute_vertical_xpm );
69 
70 
72  TOOL_INTERACTIVE( "pcbnew.Placement" ), m_selectionTool( NULL ), m_placementMenu( NULL )
73 {
74 }
75 
77 {
78  delete m_placementMenu;
79 }
80 
81 
83 {
84  // Find the selection tool, so they can cooperate
85  m_selectionTool = static_cast<SELECTION_TOOL*>( m_toolMgr->FindTool( "pcbnew.InteractiveSelection" ) );
86 
87  if( !m_selectionTool )
88  {
89  DisplayError( NULL, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
90  return false;
91  }
92 
93  // Create a context menu and make it available through selection tool
95  m_placementMenu->SetIcon( align_items_xpm );
96  m_placementMenu->SetTitle( _( "Align/distribute" ) );
97 
98  // Add all align/distribute commands
103  m_placementMenu->AppendSeparator();
106 
109 
110  return true;
111 }
112 
113 
115 {
116  const SELECTION& selection = m_selectionTool->GetSelection();
117 
118  if( selection.Size() <= 1 )
119  return 0;
120 
121  BOARD_COMMIT commit( getEditFrame<PCB_BASE_FRAME>() );
122  commit.StageItems( selection, CHT_MODIFY );
123 
124  // Compute the highest point of selection - it will be the edge of alignment
125  int top = selection.Front()->GetBoundingBox().GetY();
126 
127  for( int i = 1; i < selection.Size(); ++i )
128  {
129  int currentTop = selection[i]->GetBoundingBox().GetY();
130 
131  if( top > currentTop ) // Y decreases when going up
132  top = currentTop;
133  }
134 
135  // Move the selected items
136  for( auto i : selection )
137  {
138  auto item = static_cast<BOARD_ITEM*>( i );
139 
140  int difference = top - item->GetBoundingBox().GetY();
141 
142  item->Move( wxPoint( 0, difference ) );
143  }
144 
145  commit.Push( _( "Align to top" ) );
146 
147  return 0;
148 }
149 
150 
152 {
153  const SELECTION& selection = m_selectionTool->GetSelection();
154 
155  if( selection.Size() <= 1 )
156  return 0;
157 
158  BOARD_COMMIT commit( getEditFrame<PCB_BASE_FRAME>() );
159  commit.StageItems( selection, CHT_MODIFY );
160 
161  // Compute the lowest point of selection - it will be the edge of alignment
162  int bottom = selection.Front()->GetBoundingBox().GetBottom();
163 
164  for( int i = 1; i < selection.Size(); ++i )
165  {
166  int currentBottom = selection[i]->GetBoundingBox().GetBottom();
167 
168  if( bottom < currentBottom ) // Y increases when going down
169  bottom = currentBottom;
170  }
171 
172  // Move the selected items
173  for( auto i : selection )
174  {
175  auto item = static_cast<BOARD_ITEM*>( i );
176 
177  int difference = bottom - item->GetBoundingBox().GetBottom();
178 
179  item->Move( wxPoint( 0, difference ) );
180  }
181 
182  commit.Push( _( "Align to bottom" ) );
183 
184  return 0;
185 }
186 
187 
189 {
190  // Because this tool uses bounding boxes and they aren't mirrored even when
191  // the view is mirrored, we need to call the other one if mirrored.
192  if( getView()->IsMirroredX() )
193  {
194  return doAlignRight();
195  }
196  else
197  {
198  return doAlignLeft();
199  }
200 }
201 
202 
204 {
205  const SELECTION& selection = m_selectionTool->GetSelection();
206 
207  if( selection.Size() <= 1 )
208  return 0;
209 
210  BOARD_COMMIT commit( getEditFrame<PCB_BASE_FRAME>() );
211  commit.StageItems( selection, CHT_MODIFY );
212 
213  // Compute the leftmost point of selection - it will be the edge of alignment
214  int left = selection.Front()->GetBoundingBox().GetX();
215 
216  for( int i = 1; i < selection.Size(); ++i )
217  {
218  int currentLeft = selection[i]->GetBoundingBox().GetX();
219 
220  if( left > currentLeft ) // X decreases when going left
221  left = currentLeft;
222  }
223 
224  // Move the selected items
225  for( auto i : selection )
226  {
227  auto item = static_cast<BOARD_ITEM*>( i );
228 
229  int difference = left - item->GetBoundingBox().GetX();
230 
231  item->Move( wxPoint( difference, 0 ) );
232  }
233 
234  commit.Push( _( "Align to left" ) );
235 
236  return 0;
237 }
238 
239 
241 {
242  // Because this tool uses bounding boxes and they aren't mirrored even when
243  // the view is mirrored, we need to call the other one if mirrored.
244  if( getView()->IsMirroredX() )
245  {
246  return doAlignLeft();
247  }
248  else
249  {
250  return doAlignRight();
251  }
252 }
253 
254 
256 {
257  const SELECTION& selection = m_selectionTool->GetSelection();
258 
259  if( selection.Size() <= 1 )
260  return 0;
261 
262  BOARD_COMMIT commit( getEditFrame<PCB_BASE_FRAME>() );
263  commit.StageItems( selection, CHT_MODIFY );
264 
265  // Compute the rightmost point of selection - it will be the edge of alignment
266  int right = selection.Front()->GetBoundingBox().GetRight();
267 
268  for( int i = 1; i < selection.Size(); ++i )
269  {
270  int currentRight = selection[i]->GetBoundingBox().GetRight();
271 
272  if( right < currentRight ) // X increases when going right
273  right = currentRight;
274  }
275 
276  // Move the selected items
277  for( auto i : selection )
278  {
279  auto item = static_cast<BOARD_ITEM*>( i );
280 
281  int difference = right - item->GetBoundingBox().GetRight();
282 
283  item->Move( wxPoint( difference, 0 ) );
284  }
285 
286  commit.Push( _( "Align to right" ) );
287 
288  return 0;
289 }
290 
291 
292 static bool compareX( const BOARD_ITEM* aA, const BOARD_ITEM* aB )
293 {
294  return aA->GetBoundingBox().Centre().x < aB->GetBoundingBox().Centre().x;
295 }
296 
297 
298 static bool compareY( const BOARD_ITEM* aA, const BOARD_ITEM* aB )
299 {
300  return aA->GetBoundingBox().Centre().y < aB->GetBoundingBox().Centre().y;
301 }
302 
303 
305 {
306  const SELECTION& selection = m_selectionTool->GetSelection();
307 
308  if( selection.Size() <= 1 )
309  return 0;
310 
311  BOARD_COMMIT commit( getEditFrame<PCB_BASE_FRAME>() );
312  commit.StageItems( selection, CHT_MODIFY );
313 
314  // Prepare a list, so the items can be sorted by their X coordinate
315  std::vector<BOARD_ITEM*> itemsList;
316 
317  for( auto item : selection )
318  itemsList.push_back( static_cast<BOARD_ITEM*>( item ) );
319 
320  // Sort items by X coordinate
321  std::sort(itemsList.begin(), itemsList.end(), compareX );
322 
323  // Expected X coordinate for the next item (=minX)
324  int position = itemsList.front()->GetBoundingBox().Centre().x;
325 
326  // X coordinate for the last item
327  const int maxX = itemsList.back()->GetBoundingBox().Centre().x;
328 
329  // Distance between items
330  const int distance = ( maxX - position ) / ( itemsList.size() - 1 );
331 
332  for( auto item : itemsList )
333  {
334  int difference = position - item->GetBoundingBox().Centre().x;
335 
336  item->Move( wxPoint( difference, 0 ) );
337 
338  position += distance;
339  }
340 
341  commit.Push( _( "Distribute horizontally" ) );
342 
343  return 0;
344 }
345 
346 
348 {
349  const SELECTION& selection = m_selectionTool->GetSelection();
350 
351  if( selection.Size() <= 1 )
352  return 0;
353 
354  BOARD_COMMIT commit( getEditFrame<PCB_BASE_FRAME>() );
355  commit.StageItems( selection, CHT_MODIFY );
356 
357  // Prepare a list, so the items can be sorted by their Y coordinate
358  std::vector<BOARD_ITEM*> itemsList;
359 
360  for( auto item : selection )
361  itemsList.push_back( static_cast<BOARD_ITEM*>( item ) );
362 
363  // Sort items by Y coordinate
364  std::sort( itemsList.begin(), itemsList.end(), compareY );
365 
366  // Expected Y coordinate for the next item (=minY)
367  int position = (*itemsList.begin())->GetBoundingBox().Centre().y;
368 
369  // Y coordinate for the last item
370  const int maxY = (*itemsList.rbegin())->GetBoundingBox().Centre().y;
371 
372  // Distance between items
373  const int distance = ( maxY - position ) / ( itemsList.size() - 1 );
374 
375  for( auto item : itemsList )
376  {
377  int difference = position - item->GetBoundingBox().Centre().y;
378 
379  item->Move( wxPoint( 0, difference ) );
380 
381  position += distance;
382  }
383 
384  commit.Push( _( "Distribute vertically" ) );
385 
386  return 0;
387 }
388 
389 
391 {
396 
399 }
static TOOL_ACTION alignRight
Align items to the right edge of selection bounding box.
Definition: pcb_actions.h:229
int AlignLeft(const TOOL_EVENT &aEvent)
Function AlignLeft() Sets X coordinate of the selected items to the value of the left-most selected i...
virtual const EDA_RECT GetBoundingBox() const
Function GetBoundingBox returns the orthogonal, bounding box of this object for display purposes...
TOOL_BASE * FindTool(int aId) const
Function FindTool() Searches for a tool with given ID.
This file is part of the common library.
Class BOARD_ITEM is a base class for any item which can be embedded within the BOARD container class...
static SELECTION_CONDITION MoreThan(int aNumber)
Function MoreThan Creates a functor that tests if the number of selected items is greater than the va...
Class CONTEXT_MENU.
Definition: context_menu.h:44
virtual ~ALIGN_DISTRIBUTE_TOOL()
Class SELECTION_TOOL.
Class BOARD to handle a board.
Class that computes missing connections on a PCB.
CONDITIONAL_MENU & GetMenu()
Function GetMenu.
Definition: tool_menu.cpp:49
EDA_ITEM * Front() const
Definition: selection.h:144
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:215
int AlignBottom(const TOOL_EVENT &aEvent)
Function AlignBottom() Sets Y coordinate of the selected items to the value of the bottom-most select...
static TOOL_ACTION distributeVertically
Distributes items evenly along the vertical axis.
Definition: pcb_actions.h:235
CONTEXT_MENU * m_placementMenu
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Function Go()
virtual void Push(const wxString &aMessage=wxT("A commit"), bool aCreateUndoEntry=true) override
Executes the changes.
static TOOL_ACTION alignBottom
Align items to the bottom edge of selection bounding box.
Definition: pcb_actions.h:223
wxMenuItem * Add(const wxString &aLabel, int aId, const BITMAP_OPAQUE *aIcon=NULL)
Function Add() Adds an entry to the menu.
static TOOL_ACTION distributeHorizontally
Distributes items evenly along the horizontal axis.
Definition: pcb_actions.h:232
static bool compareX(const BOARD_ITEM *aA, const BOARD_ITEM *aB)
int DistributeHorizontally(const TOOL_EVENT &aEvent)
Function DistributeHorizontally() Distributes the selected items along the X axis.
int doAlignRight()
Aligns selected items using the right edge of their bounding boxes to the right-most item...
int doAlignLeft()
Sets X coordinate of the selected items to the value of the left-most selected item X coordinate...
int AlignTop(const TOOL_EVENT &aEvent)
Function AlignTop() Sets Y coordinate of the selected items to the value of the top-most selected ite...
Class TOOL_EVENT.
Definition: tool_event.h:162
int DistributeVertically(const TOOL_EVENT &aEvent)
Function DistributeVertically() Distributes the selected items along the Y axis.
COMMIT & StageItems(const Range &aRange, CHANGE_TYPE aChangeType)
Definition: commit.h:116
static TOOL_ACTION alignLeft
Align items to the left edge of selection bounding box.
Definition: pcb_actions.h:226
SELECTION & GetSelection()
Function GetSelection()
EDA_RECT GetBoundingBox() const
Definition: selection.cpp:73
int GetBottom() const
static TOOL_ACTION alignTop
Align items to the top edge of selection bounding box.
Definition: pcb_actions.h:220
wxPoint Centre() const
int GetRight() const
All active tools
Definition: tool_event.h:138
KIGFX::VIEW * getView() const
Function getView()
Definition: tool_base.cpp:35
static bool compareY(const BOARD_ITEM *aA, const BOARD_ITEM *aB)
void SetIcon(const BITMAP_OPAQUE *aIcon)
Function SetIcon() Assigns an icon for the entry.
void AddMenu(CONTEXT_MENU *aMenu, bool aExpand=false, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Function AddMenu()
void setTransitions() override
Sets up handlers for various events.
void SetTitle(const wxString &aTitle) override
Function SetTitle() Sets title for the context menu.
Class TOOL_ACTION.
Definition: tool_action.h:46
int GetX() const
int GetY() const
SELECTION_TOOL * m_selectionTool
int AlignRight(const TOOL_EVENT &aEvent)
Function AlignRight() Sets X coordinate of the selected items to the value of the right-most selected...
int Size() const
Returns the number of selected parts.
Definition: selection.h:112
bool Init() override
Function Init() Init() is called once upon a registration of the tool.
void DisplayError(wxWindow *parent, const wxString &text, int displaytime)
Function DisplayError displays an error or warning message box with aMessage.
Definition: confirm.cpp:73