KiCad PCB EDA Suite
Loading...
Searching...
No Matches
selection_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) 2021-2023 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 <bitmaps.h>
25#include <widgets/ui_common.h>
26#include <collector.h>
27#include <tool/tool_manager.h>
28#include <tool/actions.h>
29#include <tool/selection_tool.h>
30#include <view/view.h>
31#include <eda_draw_frame.h>
32
33
34SELECTION_TOOL::SELECTION_TOOL( const std::string& aName ) :
35 TOOL_INTERACTIVE( aName ),
36 m_additive( false ),
37 m_subtractive( false ),
38 m_exclusive_or( false ),
39 m_multiple( false ),
40 m_skip_heuristics( false ),
41 m_highlight_modifier( false ),
42 m_drag_additive( false ),
43 m_drag_subtractive( false ),
44 m_canceledMenu( false )
45{
46}
47
48void SELECTION_TOOL::setModifiersState( bool aShiftState, bool aCtrlState, bool aAltState )
49{
50 // Set the configuration of m_additive, m_subtractive, m_exclusive_or from the state of
51 // modifier keys SHIFT and CTRL
52
53 // ALT key cannot be used on MSW because of a conflict with the system menu
54
55 m_subtractive = aCtrlState && aShiftState;
56 m_additive = !aCtrlState && aShiftState;
57
59 {
60 m_exclusive_or = false;
61 m_highlight_modifier = aCtrlState && !aShiftState;
62 }
63 else
64 {
65 m_exclusive_or = aCtrlState && !aShiftState;
67 }
68
69 // Drag is more forgiving and allows either Ctrl+Drag or Shift+Drag to add to the selection
70 // Note, however that we cannot provide disambiguation at the same time as the box selection
71 m_drag_additive = ( aCtrlState || aShiftState ) && !aAltState;
72 m_drag_subtractive = aCtrlState && aShiftState && !aAltState;
73
74 // While the ALT key has some conflicts under MSW (and some flavors of Linux WMs), it remains
75 // useful for users who only use tap-click rather than holding the button. We disable it for
76 // windows because it flashes the disambiguation menu without showing data
77#ifndef __WINDOWS__
78 m_skip_heuristics = aAltState;
79#else
80 m_skip_heuristics = false;
81#endif
82
83}
84
85
87{
89}
90
91
93{
94 ACTION_MENU* actionMenu = aEvent.Parameter<ACTION_MENU*>();
95 CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( actionMenu );
96
97 if( conditionalMenu )
98 conditionalMenu->Evaluate( selection() );
99
100 if( actionMenu )
101 actionMenu->UpdateAll();
102
103 return 0;
104}
105
106
108{
109 AddItemToSel( aEvent.Parameter<EDA_ITEM*>() );
110 selection().SetIsHover( false );
111 return 0;
112}
113
114
115void SELECTION_TOOL::AddItemToSel( EDA_ITEM* aItem, bool aQuietMode )
116{
117 if( aItem )
118 {
119 select( aItem );
120
121 // Inform other potentially interested tools
122 if( !aQuietMode )
124 }
125}
126
127
129{
131 selection().SetIsHover( false );
132
133 AddItemToSel( aEvent.Parameter<EDA_ITEM*>() );
134 selection().SetIsHover( false );
135
136 return 0;
137}
138
139
141{
142 AddItemsToSel( aEvent.Parameter<EDA_ITEMS*>(), false );
143 selection().SetIsHover( false );
144 return 0;
145}
146
147
148void SELECTION_TOOL::AddItemsToSel( EDA_ITEMS* aList, bool aQuietMode )
149{
150 if( aList )
151 {
152 for( EDA_ITEM* item : *aList )
153 select( item );
154
155 // Inform other potentially interested tools
156 if( !aQuietMode )
158 }
159}
160
161
163{
165 selection().SetIsHover( false );
166 return 0;
167}
168
169
170void SELECTION_TOOL::RemoveItemFromSel( EDA_ITEM* aItem, bool aQuietMode )
171{
172 if( aItem )
173 {
174 unselect( aItem );
175
176 // Inform other potentially interested tools
177 if( !aQuietMode )
179 }
180}
181
182
184{
185 RemoveItemsFromSel( aEvent.Parameter<EDA_ITEMS*>(), false );
186 selection().SetIsHover( false );
187 return 0;
188}
189
190
191void SELECTION_TOOL::RemoveItemsFromSel( EDA_ITEMS* aList, bool aQuietMode )
192{
193 if( aList )
194 {
195 for( EDA_ITEM* item : *aList )
196 unselect( item );
197
198 // Inform other potentially interested tools
199 if( !aQuietMode )
201 }
202}
203
204
205void SELECTION_TOOL::RemoveItemsFromSel( std::vector<KIID>* aList, bool aQuietMode )
206{
207 EDA_ITEMS removeItems;
208
209 for( EDA_ITEM* item : selection() )
210 {
211 if( alg::contains( *aList, item->m_Uuid ) )
212 removeItems.push_back( item );
213 }
214
215 RemoveItemsFromSel( &removeItems, aQuietMode );
216}
217
218
220{
221 highlight( aItem, BRIGHTENED );
222}
223
224
226{
227 unhighlight( aItem, BRIGHTENED );
228}
229
230
231void SELECTION_TOOL::onDisambiguationExpire( wxTimerEvent& aEvent )
232{
233 // If there is a multiple selection then it's more likely that we're seeing a paused drag
234 // than a long-click.
235 if( selection().GetSize() >= 2 && !hasModifier() )
236 return;
237
238 // If another tool has since started running then we don't want to interrupt
239 if( !getEditFrame<EDA_DRAW_FRAME>()->ToolStackIsEmpty() )
240 return;
241
243}
244
245
247{
248 COLLECTOR* collector = aEvent.Parameter<COLLECTOR*>();
249
250 if( !doSelectionMenu( collector ) )
251 collector->m_MenuCancelled = true;
252
253 return 0;
254}
255
256
258{
259 UNITS_PROVIDER* unitsProvider = getEditFrame<EDA_DRAW_FRAME>();
260 EDA_ITEM* current = nullptr;
261 SELECTION highlightGroup;
262 bool selectAll = false;
263 bool expandSelection = false;
264
265 highlightGroup.SetLayer( LAYER_SELECT_OVERLAY );
266 getView()->Add( &highlightGroup );
267
268 do
269 {
271 if( expandSelection )
272 aCollector->Combine();
273
274 expandSelection = false;
275
276 int limit = std::min( 100, aCollector->GetCount() );
277 ACTION_MENU menu( true );
278
279 for( int i = 0; i < limit; ++i )
280 {
281 EDA_ITEM* item = ( *aCollector )[i];
282 wxString menuText;
283
284 if( i < 9 )
285 {
286#ifdef __WXMAC__
287 menuText = wxString::Format( "%s\t%d",
288 item->GetItemDescription( unitsProvider ),
289 i + 1 );
290#else
291 menuText = wxString::Format( "&%d %s\t%d",
292 i + 1,
293 item->GetItemDescription( unitsProvider ),
294 i + 1 );
295#endif
296 }
297 else
298 {
299 menuText = item->GetItemDescription( unitsProvider );
300 }
301
302 menu.Add( menuText, i + 1, item->GetMenuImage() );
303 }
304
305 menu.AppendSeparator();
306 menu.Add( _( "Select &All\tA" ), limit + 1, BITMAPS::INVALID_BITMAP );
307
308 if( !expandSelection && aCollector->HasAdditionalItems() )
309 menu.Add( _( "&Expand Selection\tE" ), limit + 2, BITMAPS::INVALID_BITMAP );
310
311 if( aCollector->m_MenuTitle.Length() )
312 {
313 menu.SetTitle( aCollector->m_MenuTitle );
314 menu.SetIcon( BITMAPS::info );
315 menu.DisplayTitle( true );
316 }
317 else
318 {
319 menu.DisplayTitle( false );
320 }
321
322 SetContextMenu( &menu, CMENU_NOW );
323
324 while( TOOL_EVENT* evt = Wait() )
325 {
326 if( evt->Action() == TA_CHOICE_MENU_UPDATE )
327 {
328 if( selectAll )
329 {
330 for( int i = 0; i < aCollector->GetCount(); ++i )
331 unhighlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
332 }
333 else if( current )
334 {
335 unhighlight( current, BRIGHTENED, &highlightGroup );
336 }
337
338 int id = *evt->GetCommandId();
339
340 // User has pointed an item, so show it in a different way
341 if( id > 0 && id <= limit )
342 {
343 current = ( *aCollector )[id - 1];
344 highlight( current, BRIGHTENED, &highlightGroup );
345 }
346 else
347 {
348 current = nullptr;
349 }
350
351 // User has pointed on the "Select All" option
352 if( id == limit + 1 )
353 {
354 for( int i = 0; i < aCollector->GetCount(); ++i )
355 highlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
356
357 selectAll = true;
358 }
359 else
360 {
361 selectAll = false;
362 }
363 }
364 else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
365 {
366 if( selectAll )
367 {
368 for( int i = 0; i < aCollector->GetCount(); ++i )
369 unhighlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
370 }
371 else if( current )
372 {
373 unhighlight( current, BRIGHTENED, &highlightGroup );
374 }
375
376 std::optional<int> id = evt->GetCommandId();
377
378 // User has selected the "Select All" option
379 if( id == limit + 1 )
380 {
381 selectAll = true;
382 current = nullptr;
383 }
384 // User has selected the "Expand Selection" option
385 else if( id == limit + 2 )
386 {
387 selectAll = false;
388 current = nullptr;
389 expandSelection = true;
390 }
391 // User has selected an item, so this one will be returned
392 else if( id && ( *id > 0 ) && ( *id <= limit ) )
393 {
394 selectAll = false;
395 current = ( *aCollector )[*id - 1];
396 }
397 // User has cancelled the menu (either by <esc> or clicking out of it)
398 else
399 {
400 selectAll = false;
401 current = nullptr;
402 }
403 }
404 else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
405 {
406 break;
407 }
408
409 getView()->UpdateItems();
410 getEditFrame<EDA_DRAW_FRAME>()->GetCanvas()->Refresh();
411 }
412 } while( expandSelection );
413
414 getView()->Remove( &highlightGroup );
415
416 if( selectAll )
417 {
418 return true;
419 }
420 else if( current )
421 {
422 aCollector->Empty();
423 aCollector->Append( current );
424 return true;
425 }
426
427 return false;
428}
429
430
Defines the structure of a menu based on ACTIONs.
Definition: action_menu.h:49
void DisplayTitle(bool aDisplay=true)
Decide whether a title for a pop up menu should be displayed.
void UpdateAll()
Run update handlers for the menu and its submenus.
void SetTitle(const wxString &aTitle) override
Set title for the menu.
Definition: action_menu.cpp:92
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
Definition: action_menu.cpp:78
wxMenuItem * Add(const wxString &aLabel, int aId, BITMAPS aIcon)
Add a wxWidgets-style entry to the menu.
An abstract class that will find and hold all the objects according to an inspection done by the Insp...
Definition: collector.h:48
bool m_MenuCancelled
Definition: collector.h:237
void Empty()
Clear the list.
Definition: collector.h:89
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:81
wxString m_MenuTitle
Definition: collector.h:236
bool HasAdditionalItems()
Test if the collector has heuristic backup items.
Definition: collector.h:132
void Combine()
Re-combine the backup list into the main list of the collector.
Definition: collector.h:140
void Append(EDA_ITEM *item)
Add an item to the end of the list.
Definition: collector.h:99
void Evaluate(const SELECTION &aSelection)
Update the contents of the menu based on the supplied conditions.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:88
virtual wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider) const
Return a user-visible description string of this item.
Definition: eda_item.cpp:108
virtual BITMAPS GetMenuImage() const
Return a pointer to an image to be used in menus.
Definition: eda_item.cpp:288
static const TOOL_EVENT DisambiguatePoint
Used for hotkey feedback.
Definition: actions.h:277
static const TOOL_EVENT SelectedEvent
Definition: actions.h:260
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:261
virtual void SetLayer(int aLayer)
Set layer used to draw the group.
Definition: view_group.h:99
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:315
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:354
void UpdateItems()
Iterate through the list of items that asked for updating and updates them.
Definition: view.cpp:1435
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
bool doSelectionMenu(COLLECTOR *aCollector)
int AddItemsToSel(const TOOL_EVENT &aEvent)
void BrightenItem(EDA_ITEM *aItem)
virtual void unselect(EDA_ITEM *aItem)=0
Take necessary action mark an item as unselected.
int AddItemToSel(const TOOL_EVENT &aEvent)
int UpdateMenu(const TOOL_EVENT &aEvent)
Update a menu's state based on the current selection.
void setModifiersState(bool aShiftState, bool aCtrlState, bool aAltState)
Set the configuration of m_additive, m_subtractive, m_exclusive_or, m_skip_heuristics from the state ...
void UnbrightenItem(EDA_ITEM *aItem)
virtual void select(EDA_ITEM *aItem)=0
Take necessary action mark an item as selected.
int SelectionMenu(const TOOL_EVENT &aEvent)
Show a popup menu to trim the COLLECTOR passed as aEvent's parameter down to a single item.
int RemoveItemsFromSel(const TOOL_EVENT &aEvent)
SELECTION_TOOL(const std::string &aName)
virtual void unhighlight(EDA_ITEM *aItem, int aHighlightMode, SELECTION *aGroup=nullptr)=0
Unhighlight the item visually.
int ReselectItem(const TOOL_EVENT &aEvent)
virtual void highlight(EDA_ITEM *aItem, int aHighlightMode, SELECTION *aGroup=nullptr)=0
Highlight the item visually.
virtual SELECTION & selection()=0
Return a reference to the selection.
bool hasModifier()
True if a selection modifier is enabled, false otherwise.
virtual bool ctrlClickHighlights()
Determines if ctrl-click is highlight net or XOR selection.
void onDisambiguationExpire(wxTimerEvent &aEvent)
Start the process to show our disambiguation menu once the user has kept the mouse down for the minim...
void SetIsHover(bool aIsHover)
Definition: selection.h:78
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:216
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
Generic, UI-independent tool event.
Definition: tool_event.h:167
T Parameter() const
Return a parameter assigned to the event.
Definition: tool_event.h:460
void SetContextMenu(ACTION_MENU *aMenu, CONTEXT_MENU_TRIGGER aTrigger=CMENU_BUTTON)
Assign a context menu and tells when it should be activated.
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.
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
#define _(s)
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
Definition: eda_item.h:532
#define BRIGHTENED
item is drawn with a bright contour
@ LAYER_SELECT_OVERLAY
currently selected items overlay
Definition: layer_ids.h:223
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:100
@ CMENU_NOW
Definition: tool_event.h:152
@ TA_CHOICE_MENU_CHOICE
Definition: tool_event.h:97
@ TA_CHOICE_MENU_UPDATE
Definition: tool_event.h:93
@ TA_CHOICE_MENU_CLOSED
Definition: tool_event.h:100
Functions to provide common constants and other functions to assist in making a consistent UI.