KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_align_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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/gpl-3.0.html,
19 * or you may search the http://www.gnu.org website for the version 3 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <algorithm>
25
26#include <bitmaps.h>
27#include <sch_actions.h>
28#include <sch_collectors.h>
29#include <sch_commit.h>
30#include <sch_edit_frame.h>
31#include <sch_item.h>
32#include <sch_selection.h>
33#include <sch_selection_tool.h>
36#include <tool/tool_event.h>
39#include <view/view_controls.h>
40
41
43 SCH_TOOL_BASE<SCH_EDIT_FRAME>( "eeschema.Align" ),
44 m_alignMenu( nullptr )
45{
46}
47
48
53
54
56{
58
59 if( !m_alignMenu )
60 {
61 m_alignMenu = new CONDITIONAL_MENU( this );
63 m_alignMenu->SetUntranslatedTitle( _HKI( "Align" ) );
64
65 const auto canAlign = SELECTION_CONDITIONS::MoreThan( 1 );
66
67 m_alignMenu->AddItem( SCH_ACTIONS::alignLeft, canAlign );
68 m_alignMenu->AddItem( SCH_ACTIONS::alignCenterX, canAlign );
69 m_alignMenu->AddItem( SCH_ACTIONS::alignRight, canAlign );
70
71 m_alignMenu->AddSeparator( canAlign );
72 m_alignMenu->AddItem( SCH_ACTIONS::alignTop, canAlign );
73 m_alignMenu->AddItem( SCH_ACTIONS::alignCenterY, canAlign );
74 m_alignMenu->AddItem( SCH_ACTIONS::alignBottom, canAlign );
75 }
76
77 CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
78 selToolMenu.AddMenu( m_alignMenu, SELECTION_CONDITIONS::MoreThan( 1 ), 100 );
79
81
82 return true;
83}
84
85
86template< typename T >
87int SCH_ALIGN_TOOL::selectTarget( const std::vector<ITEM_BOX>& aItems,
88 const std::vector<ITEM_BOX>& aLocked, T aGetValue )
89{
91
92 if( !aLocked.empty() )
93 {
94 for( const ITEM_BOX& item : aLocked )
95 {
96 if( item.second.Contains( cursorPos ) )
97 return aGetValue( item );
98 }
99
100 return aGetValue( aLocked.front() );
101 }
102
103 for( const ITEM_BOX& item : aItems )
104 {
105 if( item.second.Contains( cursorPos ) )
106 return aGetValue( item );
107 }
108
109 return aGetValue( aItems.front() );
110}
111
112
113template< typename T >
114size_t SCH_ALIGN_TOOL::GetSelections( std::vector<ITEM_BOX>& aItemsToAlign,
115 std::vector<ITEM_BOX>& aLockedItems, T aCompare )
116{
117 SCH_SELECTION& selection = m_selectionTool->RequestSelection( SCH_COLLECTOR::MovableItems );
118
119 for( EDA_ITEM* item : selection )
120 {
121 if( !item->IsSCH_ITEM() )
122 continue;
123
124 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
125
126 if( schItem->GetParent() && schItem->GetParent()->IsSelected() )
127 continue;
128
129 BOX2I bbox = schItem->GetBoundingBox();
130
131 if( schItem->IsLocked() )
132 aLockedItems.emplace_back( schItem, bbox );
133 else
134 aItemsToAlign.emplace_back( schItem, bbox );
135 }
136
137 std::sort( aItemsToAlign.begin(), aItemsToAlign.end(), aCompare );
138 std::sort( aLockedItems.begin(), aLockedItems.end(), aCompare );
139
140 return aItemsToAlign.size();
141}
142
143
144void SCH_ALIGN_TOOL::moveItem( SCH_ITEM* aItem, const VECTOR2I& aDelta, SCH_COMMIT& aCommit )
145{
146 if( aDelta == VECTOR2I( 0, 0 ) )
147 return;
148
149 VECTOR2I delta = adjustDeltaForGrid( aItem, aDelta );
150
151 if( delta == VECTOR2I( 0, 0 ) )
152 return;
153
154 aCommit.Modify( aItem, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
155 aItem->Move( delta );
156 aItem->ClearFlags( IS_MOVING );
157 updateItem( aItem, true );
158}
159
160
162{
163 if( aDelta == VECTOR2I( 0, 0 ) )
164 return aDelta;
165
167 GRID_HELPER_GRIDS gridType = grid.GetItemGrid( aItem );
168
169 if( gridType != GRID_CONNECTABLE )
170 return aDelta;
171
172 VECTOR2I desiredPos = aItem->GetPosition() + aDelta;
173 VECTOR2I snappedPos = grid.AlignGrid( desiredPos, gridType );
174
175 return snappedPos - aItem->GetPosition();
176}
177
178
188
189
191{
192 std::vector<ITEM_BOX> itemsToAlign;
193 std::vector<ITEM_BOX> lockedItems;
194
195 if( !GetSelections( itemsToAlign, lockedItems,
196 []( const ITEM_BOX& lhs, const ITEM_BOX& rhs )
197 {
198 return lhs.second.GetTop() < rhs.second.GetTop();
199 } ) )
200 {
201 return 0;
202 }
203
204 SCH_COMMIT commit( m_toolMgr );
205
206 int targetTop = selectTarget( itemsToAlign, lockedItems,
207 []( const ITEM_BOX& item )
208 {
209 return item.second.GetTop();
210 } );
211
212 for( const ITEM_BOX& item : itemsToAlign )
213 {
214 int difference = targetTop - item.second.GetTop();
215 moveItem( item.first, VECTOR2I( 0, difference ), commit );
216 }
217
218 commit.Push( _( "Align to Top" ) );
219 return 0;
220}
221
222
224{
225 std::vector<ITEM_BOX> itemsToAlign;
226 std::vector<ITEM_BOX> lockedItems;
227
228 if( !GetSelections( itemsToAlign, lockedItems,
229 []( const ITEM_BOX& lhs, const ITEM_BOX& rhs )
230 {
231 return lhs.second.GetBottom() > rhs.second.GetBottom();
232 } ) )
233 {
234 return 0;
235 }
236
237 SCH_COMMIT commit( m_toolMgr );
238
239 int targetBottom = selectTarget( itemsToAlign, lockedItems,
240 []( const ITEM_BOX& item )
241 {
242 return item.second.GetBottom();
243 } );
244
245 for( const ITEM_BOX& item : itemsToAlign )
246 {
247 int difference = targetBottom - item.second.GetBottom();
248 moveItem( item.first, VECTOR2I( 0, difference ), commit );
249 }
250
251 commit.Push( _( "Align to Bottom" ) );
252 return 0;
253}
254
255
257{
258 std::vector<ITEM_BOX> itemsToAlign;
259 std::vector<ITEM_BOX> lockedItems;
260
261 if( !GetSelections( itemsToAlign, lockedItems,
262 []( const ITEM_BOX& lhs, const ITEM_BOX& rhs )
263 {
264 return lhs.second.GetLeft() < rhs.second.GetLeft();
265 } ) )
266 {
267 return 0;
268 }
269
270 SCH_COMMIT commit( m_toolMgr );
271
272 int targetLeft = selectTarget( itemsToAlign, lockedItems,
273 []( const ITEM_BOX& item )
274 {
275 return item.second.GetLeft();
276 } );
277
278 for( const ITEM_BOX& item : itemsToAlign )
279 {
280 int difference = targetLeft - item.second.GetLeft();
281 moveItem( item.first, VECTOR2I( difference, 0 ), commit );
282 }
283
284 commit.Push( _( "Align to Left" ) );
285 return 0;
286}
287
288
290{
291 std::vector<ITEM_BOX> itemsToAlign;
292 std::vector<ITEM_BOX> lockedItems;
293
294 if( !GetSelections( itemsToAlign, lockedItems,
295 []( const ITEM_BOX& lhs, const ITEM_BOX& rhs )
296 {
297 return lhs.second.GetRight() > rhs.second.GetRight();
298 } ) )
299 {
300 return 0;
301 }
302
303 SCH_COMMIT commit( m_toolMgr );
304
305 int targetRight = selectTarget( itemsToAlign, lockedItems,
306 []( const ITEM_BOX& item )
307 {
308 return item.second.GetRight();
309 } );
310
311 for( const ITEM_BOX& item : itemsToAlign )
312 {
313 int difference = targetRight - item.second.GetRight();
314 moveItem( item.first, VECTOR2I( difference, 0 ), commit );
315 }
316
317 commit.Push( _( "Align to Right" ) );
318 return 0;
319}
320
321
323{
324 std::vector<ITEM_BOX> itemsToAlign;
325 std::vector<ITEM_BOX> lockedItems;
326
327 if( !GetSelections( itemsToAlign, lockedItems,
328 []( const ITEM_BOX& lhs, const ITEM_BOX& rhs )
329 {
330 return lhs.second.Centre().x < rhs.second.Centre().x;
331 } ) )
332 {
333 return 0;
334 }
335
336 SCH_COMMIT commit( m_toolMgr );
337
338 int targetX = selectTarget( itemsToAlign, lockedItems,
339 []( const ITEM_BOX& item )
340 {
341 return item.second.Centre().x;
342 } );
343
344 for( const ITEM_BOX& item : itemsToAlign )
345 {
346 int difference = targetX - item.second.Centre().x;
347 moveItem( item.first, VECTOR2I( difference, 0 ), commit );
348 }
349
350 commit.Push( _( "Align to Middle" ) );
351 return 0;
352}
353
354
356{
357 std::vector<ITEM_BOX> itemsToAlign;
358 std::vector<ITEM_BOX> lockedItems;
359
360 if( !GetSelections( itemsToAlign, lockedItems,
361 []( const ITEM_BOX& lhs, const ITEM_BOX& rhs )
362 {
363 return lhs.second.Centre().y < rhs.second.Centre().y;
364 } ) )
365 {
366 return 0;
367 }
368
369 SCH_COMMIT commit( m_toolMgr );
370
371 int targetY = selectTarget( itemsToAlign, lockedItems,
372 []( const ITEM_BOX& item )
373 {
374 return item.second.Centre().y;
375 } );
376
377 for( const ITEM_BOX& item : itemsToAlign )
378 {
379 int difference = targetY - item.second.Centre().y;
380 moveItem( item.first, VECTOR2I( 0, difference ), commit );
381 }
382
383 commit.Push( _( "Align to Center" ) );
384 return 0;
385}
386
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:106
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
virtual VECTOR2I GetPosition() const
Definition eda_item.h:272
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:110
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:144
bool IsSelected() const
Definition eda_item.h:127
EDA_ITEM * GetParent() const
Definition eda_item.h:112
virtual bool IsLocked() const
Definition eda_item.h:120
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
static TOOL_ACTION alignTop
static TOOL_ACTION alignRight
static TOOL_ACTION alignBottom
static TOOL_ACTION alignLeft
static TOOL_ACTION alignCenterX
static TOOL_ACTION alignCenterY
int AlignTop(const TOOL_EVENT &aEvent)
int AlignLeft(const TOOL_EVENT &aEvent)
int selectTarget(const std::vector< ITEM_BOX > &aItems, const std::vector< ITEM_BOX > &aLocked, T aGetValue)
~SCH_ALIGN_TOOL() override
size_t GetSelections(std::vector< ITEM_BOX > &aItemsToAlign, std::vector< ITEM_BOX > &aLockedItems, T aCompare)
bool Init() override
Init() is called once upon a registration of the tool.
int AlignBottom(const TOOL_EVENT &aEvent)
VECTOR2I adjustDeltaForGrid(SCH_ITEM *aItem, const VECTOR2I &aDelta)
CONDITIONAL_MENU * m_alignMenu
int AlignRight(const TOOL_EVENT &aEvent)
std::pair< SCH_ITEM *, BOX2I > ITEM_BOX
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
int AlignCenterY(const TOOL_EVENT &aEvent)
int AlignCenterX(const TOOL_EVENT &aEvent)
void moveItem(SCH_ITEM *aItem, const VECTOR2I &aDelta, SCH_COMMIT &aCommit)
static const std::vector< KICAD_T > MovableItems
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
Schematic editor (Eeschema) main window.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:167
virtual void Move(const VECTOR2I &aMoveVector)
Move the item by aMoveVector to a new position.
Definition sch_item.h:377
void updateItem(EDA_ITEM *aItem, bool aUpdateRTree) const
bool Init() override
Init() is called once upon a registration of the tool.
SCH_TOOL_BASE(const std::string &aName)
SCH_SELECTION_TOOL * m_selectionTool
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...
KIGFX::VIEW_CONTROLS * getViewControls() const
Definition tool_base.cpp:44
Generic, UI-independent tool event.
Definition tool_event.h:171
void Go(int(SCH_EDIT_FRAME::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
#define _(s)
@ RECURSE
Definition eda_item.h:51
#define IS_MOVING
Item being moved.
GRID_HELPER_GRIDS
Definition grid_helper.h:44
@ GRID_CONNECTABLE
Definition grid_helper.h:48
#define _HKI(x)
Definition page_info.cpp:44
int delta
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695