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 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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <bitmaps.h>
21#include <widgets/ui_common.h>
22#include <collector.h>
23#include <tool/tool_manager.h>
24#include <tool/actions.h>
25#include <tool/selection_tool.h>
26#include <view/view.h>
27#include <eda_draw_frame.h>
28#include <eda_item.h>
29
30
31SELECTION_TOOL::SELECTION_TOOL( const std::string& aName ) :
32 TOOL_INTERACTIVE( aName ),
33 m_additive( false ),
34 m_subtractive( false ),
35 m_exclusive_or( false ),
36 m_multiple( false ),
37 m_skip_heuristics( false ),
38 m_highlight_modifier( false ),
39 m_drag_additive( false ),
40 m_drag_subtractive( false ),
41 m_canceledMenu( false )
42{
43}
44
45void SELECTION_TOOL::setModifiersState( bool aShiftState, bool aCtrlState, bool aAltState )
46{
47 // Set the configuration of m_additive, m_subtractive, m_exclusive_or from the state of
48 // modifier keys SHIFT and CTRL
49
50 // ALT key cannot be used on MSW because of a conflict with the system menu
51
52 m_subtractive = aCtrlState && aShiftState;
53 m_additive = !aCtrlState && aShiftState;
54
56 {
57 m_exclusive_or = false;
58 m_highlight_modifier = aCtrlState && !aShiftState;
59 }
60 else
61 {
62 m_exclusive_or = aCtrlState && !aShiftState;
64 }
65
66 // Drag is more forgiving and allows either Ctrl+Drag or Shift+Drag to add to the selection
67 // Note, however that we cannot provide disambiguation at the same time as the box selection
68 m_drag_additive = ( aCtrlState || aShiftState ) && !aAltState;
69 m_drag_subtractive = aCtrlState && aShiftState && !aAltState;
70
71 // While the ALT key has some conflicts under MSW (and some flavors of Linux WMs), it remains
72 // useful for users who only use tap-click rather than holding the button. We disable it for
73 // windows because it flashes the disambiguation menu without showing data
74#ifndef __WINDOWS__
75 m_skip_heuristics = aAltState;
76#else
77 m_skip_heuristics = false;
78#endif
79
80}
81
82
87
88
90{
91 ACTION_MENU* actionMenu = aEvent.Parameter<ACTION_MENU*>();
92 CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( actionMenu );
93
94 if( conditionalMenu )
95 conditionalMenu->Evaluate( selection() );
96
97 if( actionMenu )
98 actionMenu->UpdateAll();
99
100 return 0;
101}
102
103
105{
106 AddItemToSel( aEvent.Parameter<EDA_ITEM*>() );
107 selection().SetIsHover( false );
108 return 0;
109}
110
111
112void SELECTION_TOOL::AddItemToSel( EDA_ITEM* aItem, bool aQuietMode )
113{
114 if( aItem )
115 {
116 select( aItem );
117
118 // Inform other potentially interested tools
119 if( !aQuietMode )
120 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
121 }
122}
123
124
126{
128 selection().SetIsHover( false );
129
130 AddItemToSel( aEvent.Parameter<EDA_ITEM*>() );
131 selection().SetIsHover( false );
132
133 return 0;
134}
135
136
138{
139 AddItemsToSel( aEvent.Parameter<EDA_ITEMS*>(), false );
140 selection().SetIsHover( false );
141 return 0;
142}
143
144
145void SELECTION_TOOL::AddItemsToSel( EDA_ITEMS* aList, bool aQuietMode )
146{
147 if( aList )
148 {
149 for( EDA_ITEM* item : *aList )
150 {
151 // select() dereferences before the item reaches the selection, so nulls die here
152 if( item )
153 select( item );
154 }
155
156 // Inform other potentially interested tools
157 if( !aQuietMode )
158 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
159 }
160}
161
162
164{
166 selection().SetIsHover( false );
167 return 0;
168}
169
170
171void SELECTION_TOOL::RemoveItemFromSel( EDA_ITEM* aItem, bool aQuietMode )
172{
173 if( aItem )
174 {
175 unselect( aItem );
176
177 // Inform other potentially interested tools
178 if( !aQuietMode )
179 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
180 }
181}
182
183
185{
186 RemoveItemsFromSel( aEvent.Parameter<EDA_ITEMS*>(), false );
187 selection().SetIsHover( false );
188 return 0;
189}
190
191
192void SELECTION_TOOL::RemoveItemsFromSel( EDA_ITEMS* aList, bool aQuietMode )
193{
194 if( aList )
195 {
196 for( EDA_ITEM* item : *aList )
197 unselect( item );
198
199 // Inform other potentially interested tools
200 if( !aQuietMode )
201 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
202 }
203}
204
205
206void SELECTION_TOOL::RemoveItemsFromSel( std::vector<KIID>* aList, bool aQuietMode )
207{
208 EDA_ITEMS removeItems;
209
210 for( EDA_ITEM* item : selection() )
211 {
212 if( alg::contains( *aList, item->m_Uuid ) )
213 removeItems.push_back( item );
214 }
215
216 RemoveItemsFromSel( &removeItems, aQuietMode );
217}
218
219
221{
222 highlight( aItem, BRIGHTENED );
223}
224
225
227{
228 unhighlight( aItem, BRIGHTENED );
229}
230
231
232void SELECTION_TOOL::onDisambiguationExpire( wxTimerEvent& aEvent )
233{
234 // If there is a multiple selection then it's more likely that we're seeing a paused drag
235 // than a long-click.
236 if( selection().GetSize() >= 2 && !hasModifier() )
237 return;
238
239 // If another tool has since started running then we don't want to interrupt
240 if( !getEditFrame<EDA_DRAW_FRAME>()->ToolStackIsEmpty() )
241 return;
242
243 m_toolMgr->ProcessEvent( EVENTS::DisambiguatePoint );
244
245 // Timer events bypass EDA_DRAW_PANEL_GAL::OnEvent, which is what normally repaints the selection
246 getEditFrame<EDA_DRAW_FRAME>()->GetCanvas()->Refresh();
247}
248
249
251{
252 COLLECTOR* collector = aEvent.Parameter<COLLECTOR*>();
253
254 if( !doSelectionMenu( collector ) )
255 collector->m_MenuCancelled = true;
256
257 return 0;
258}
259
260
262{
264 EDA_ITEM* current = nullptr;
265 SELECTION highlightGroup;
266 bool selectAll = false;
267 bool showMoreChoices = false;
268
269 highlightGroup.SetLayer( LAYER_SELECT_OVERLAY );
270 getView()->Add( &highlightGroup );
271
272 do
273 {
276 if( showMoreChoices )
277 {
278 aCollector->Combine();
279
280 // prime event loop so we don't have to wait around for a mouse-moved
281 m_toolMgr->PrimeTool( { 0, 0 } );
282
283 showMoreChoices = false;
284 }
285
286 int limit = std::min( 100, aCollector->GetCount() );
287 ACTION_MENU menu( true );
288
289 auto getItemDescription =
290 [&]( EDA_ITEM* item, int itemIdx )
291 {
292 wxString desc = item->GetItemDescription( unitsProvider, false );
293
294 if( item->Type() == PCB_FOOTPRINT_T )
295 {
296 for( int i = 0; i < limit; ++i )
297 {
298 if( i == itemIdx )
299 continue;
300
301 EDA_ITEM* other = ( *aCollector )[i];
302
303 if( other->GetItemDescription( unitsProvider, false ) == desc )
304 return item->DisambiguateItemDescription( unitsProvider, false );
305 }
306 }
307
308 return desc;
309 };
310
311 for( int i = 0; i < limit; ++i )
312 {
313 EDA_ITEM* item = ( *aCollector )[i];
314 wxString menuText;
315
316 if( i < 9 )
317 {
318#ifdef __WXMAC__
319 menuText = wxString::Format( "%s\t%d",
320 getItemDescription( item, i ),
321 i + 1 );
322#else
323 menuText = wxString::Format( "&%d %s\t%d",
324 i + 1,
325 getItemDescription( item, i ),
326 i + 1 );
327#endif
328 }
329 else
330 {
331 menuText = getItemDescription( item, i );
332 }
333
334 menu.Add( menuText, i + 1, item->GetMenuImage() );
335 }
336
337 menu.AppendSeparator();
338 menu.Add( _( "Select &All\tA" ), limit + 1, BITMAPS::INVALID_BITMAP );
339
340 if( !showMoreChoices && aCollector->HasAdditionalItems() )
341 menu.Add( _( "Show &More Choices...\tM" ), limit + 2, BITMAPS::INVALID_BITMAP );
342
343 if( aCollector->m_MenuTitle.Length() )
344 {
345 menu.SetTitle( aCollector->m_MenuTitle );
346 menu.SetIcon( BITMAPS::info );
347 menu.DisplayTitle( true );
348 }
349 else
350 {
351 menu.DisplayTitle( false );
352 }
353
354 SetContextMenu( &menu, CMENU_NOW );
355
356 while( TOOL_EVENT* evt = Wait() )
357 {
358 if( evt->Action() == TA_CHOICE_MENU_UPDATE )
359 {
360 if( selectAll )
361 {
362 for( int i = 0; i < aCollector->GetCount(); ++i )
363 unhighlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
364 }
365 else if( current )
366 {
367 unhighlight( current, BRIGHTENED, &highlightGroup );
368 }
369
370 int id = *evt->GetCommandId();
371
372 // User has pointed an item, so show it in a different way
373 if( id > 0 && id <= limit )
374 {
375 current = ( *aCollector )[id - 1];
376 highlight( current, BRIGHTENED, &highlightGroup );
377 }
378 else
379 {
380 current = nullptr;
381 }
382
383 // User has pointed on the "Select All" option
384 if( id == limit + 1 )
385 {
386 for( int i = 0; i < aCollector->GetCount(); ++i )
387 highlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
388
389 selectAll = true;
390 }
391 else
392 {
393 selectAll = false;
394 }
395 }
396 else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
397 {
398 if( selectAll )
399 {
400 for( int i = 0; i < aCollector->GetCount(); ++i )
401 unhighlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
402 }
403 else if( current )
404 {
405 unhighlight( current, BRIGHTENED, &highlightGroup );
406 }
407
408 std::optional<int> id = evt->GetCommandId();
409
410 // User has selected the "Select All" option
411 if( id == limit + 1 )
412 {
413 selectAll = true;
414 current = nullptr;
415 }
416 // User has selected the "Expand Selection" option
417 else if( id == limit + 2 )
418 {
419 selectAll = false;
420 current = nullptr;
421 showMoreChoices = true;
422 }
423 // User has selected an item, so this one will be returned
424 else if( id && ( *id > 0 ) && ( *id <= limit ) )
425 {
426 selectAll = false;
427 current = ( *aCollector )[*id - 1];
428 }
429 // User has cancelled the menu (either by <esc> or clicking out of it)
430 else
431 {
432 selectAll = false;
433 current = nullptr;
434 }
435 }
436 else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
437 {
438 break;
439 }
440
441 getView()->UpdateItems();
442 getEditFrame<EDA_DRAW_FRAME>()->GetCanvas()->Refresh();
443 }
444 }
445 while( showMoreChoices );
446
447 getView()->Remove( &highlightGroup );
448
449 if( selectAll )
450 {
451 return true;
452 }
453 else if( current )
454 {
455 aCollector->Empty();
456 aCollector->Append( current );
457 return true;
458 }
459
460 return false;
461}
@ INVALID_BITMAP
Define the structure of a menu based on ACTIONs.
Definition action_menu.h:43
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.
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
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:45
bool m_MenuCancelled
Definition collector.h:237
void Empty()
Clear the list.
Definition collector.h:87
int GetCount() const
Return the number of objects in the list.
Definition collector.h:79
wxString m_MenuTitle
Definition collector.h:236
bool HasAdditionalItems()
Test if the collector has heuristic backup items.
Definition collector.h:130
void Combine()
Re-combine the backup list into the main list of the collector.
Definition collector.h:138
void Append(EDA_ITEM *item)
Add an item to the end of the list.
Definition collector.h:97
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:98
virtual wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const
Return a user-visible description string of this item.
Definition eda_item.cpp:304
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
virtual wxString DisambiguateItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const
Definition eda_item.h:458
virtual BITMAPS GetMenuImage() const
Return a pointer to an image to be used in menus.
Definition eda_item.cpp:524
static const TOOL_EVENT DisambiguatePoint
Used to inform tool that it should display the disambiguation menu.
Definition actions.h:362
static const TOOL_EVENT SelectedEvent
Definition actions.h:345
static const TOOL_EVENT UnselectedEvent
Definition actions.h:346
virtual void SetLayer(int aLayer)
Set layer used to draw the group.
Definition view_group.h:91
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition view.cpp:297
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition view.cpp:412
void UpdateItems()
Iterate through the list of items that asked for updating and updates them.
Definition view.cpp:1614
bool m_multiple
Multiple selection mode is active.
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
bool doSelectionMenu(COLLECTOR *aCollector)
bool m_drag_additive
Add multiple items to selection.
bool m_exclusive_or
Items' selection state should be toggled.
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)
bool m_subtractive
Items should be removed from selection.
SELECTION_TOOL(const std::string &aName)
bool m_highlight_modifier
Select highlight net on left click.
bool m_skip_heuristics
Show disambiguation menu for all items under the cursor rather than trying to narrow them down first ...
virtual void unhighlight(EDA_ITEM *aItem, int aHighlightMode, SELECTION *aGroup=nullptr)=0
Unhighlight the item visually.
int ReselectItem(const TOOL_EVENT &aEvent)
bool m_drag_subtractive
Remove multiple from selection.
bool m_additive
Items should be added to sel (instead of replacing).
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()
Determine if ctrl-click is highlight net or XOR selection.
bool m_canceledMenu
Sets to true if the disambiguation menu was canceled.
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:80
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:182
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
KIGFX::VIEW * getView() const
Return the instance of KIGFX::VIEW object used in the application.
Definition tool_base.cpp:34
Generic, UI-independent tool event.
Definition tool_event.h:167
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:484
void SetContextMenu(ACTION_MENU *aMenu, CONTEXT_MENU_TRIGGER aTrigger=CMENU_BUTTON)
Assign a context menu and tells when it should be activated.
TOOL_INTERACTIVE(TOOL_ID aId, const std::string &aName)
Create a tool with given id & name.
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.
#define _(s)
#define BRIGHTENED
item is drawn with a bright contour
@ LAYER_SELECT_OVERLAY
Selected items overlay.
Definition layer_ids.h:276
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
std::vector< EDA_ITEM * > EDA_ITEMS
@ TA_CHOICE_MENU_CHOICE
Context menu choice.
Definition tool_event.h:94
@ TA_CHOICE_MENU_UPDATE
Context menu update.
Definition tool_event.h:90
@ TA_CHOICE_MENU_CLOSED
Context menu is closed, no matter whether anything has been chosen or not.
Definition tool_event.h:97
@ CMENU_NOW
Right now (after TOOL_INTERACTIVE::SetContextMenu).
Definition tool_event.h:152
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:78
Functions to provide common constants and other functions to assist in making a consistent UI.