Homogenize selection tools around our HIG.

In particular make addative and exclusive-or selections work the
same way.

Also give Highlight Net a hotkey now that it can't use ctrl-click
anymore.
This commit is contained in:
Jeff Young 2019-07-09 20:50:40 +01:00
parent b0b9c5ab63
commit 483dc24e87
13 changed files with 237 additions and 557 deletions

View File

@ -40,14 +40,13 @@ public:
};
static GESTURE_PSEUDO_ACTION* g_gesturePseudoActions[] = {
new GESTURE_PSEUDO_ACTION( _( "Highlight Net" ), MD_CTRL + PSEUDO_WXK_CLICK ),
new GESTURE_PSEUDO_ACTION( _( "Clear Net Highlighting" ), MD_CTRL + PSEUDO_WXK_CLICK ),
new GESTURE_PSEUDO_ACTION( _( "Pan Left/Right" ), MD_CTRL + PSEUDO_WXK_WHEEL ),
new GESTURE_PSEUDO_ACTION( _( "Pan Up/Down" ), MD_SHIFT + PSEUDO_WXK_WHEEL ),
new GESTURE_PSEUDO_ACTION( _( "Finish Drawing" ), PSEUDO_WXK_DBLCLICK ),
new GESTURE_PSEUDO_ACTION( _( "Show Clarify Selection Menu" ), MD_ALT + PSEUDO_WXK_CLICK ),
new GESTURE_PSEUDO_ACTION( _( "Add to Selection" ), MD_SHIFT + PSEUDO_WXK_CLICK ),
new GESTURE_PSEUDO_ACTION( _( "Remove from Selection" ), MD_CTRL + PSEUDO_WXK_CLICK ),
new GESTURE_PSEUDO_ACTION( _( "Toggle Selection State" ), MD_CTRL + PSEUDO_WXK_CLICK ),
new GESTURE_PSEUDO_ACTION( _( "Remove from Selection" ), MD_SHIFT + MD_CTRL + PSEUDO_WXK_CLICK ),
new GESTURE_PSEUDO_ACTION( _( "Ignore Grid Snaps" ), MD_ALT ),
new GESTURE_PSEUDO_ACTION( _( "Ignore Other Snaps" ), MD_SHIFT ),
};

View File

@ -35,6 +35,7 @@ struct SELECTION_COLORS
COLOR4D normal;
COLOR4D additive;
COLOR4D subtract;
COLOR4D exclusiveOr;
COLOR4D outline_l2r;
COLOR4D outline_r2l;
};
@ -44,6 +45,7 @@ static const SELECTION_COLORS selectionColorScheme[2] = {
COLOR4D( 0.3, 0.3, 0.7, 0.3 ), // Slight blue
COLOR4D( 0.3, 0.7, 0.3, 0.3 ), // Slight green
COLOR4D( 0.7, 0.3, 0.3, 0.3 ), // Slight red
COLOR4D( 0.7, 0.3, 0.3, 0.3 ), // Slight red
COLOR4D( 1.0, 1.0, 0.4, 1.0 ), // yellow
COLOR4D( 0.4, 0.4, 1.0, 1.0 ) // blue
@ -52,6 +54,7 @@ static const SELECTION_COLORS selectionColorScheme[2] = {
COLOR4D( 0.5, 0.3, 1.0, 0.5 ), // Slight blue
COLOR4D( 0.5, 1.0, 0.5, 0.5 ), // Slight green
COLOR4D( 1.0, 0.5, 0.5, 0.5 ), // Slight red
COLOR4D( 1.0, 0.5, 0.5, 0.5 ), // Slight red
COLOR4D( 0.7, 0.7, 0.0, 1.0 ), // yellow
COLOR4D( 0.1, 0.1, 1.0, 1.0 ) // blue
@ -61,30 +64,13 @@ static const SELECTION_COLORS selectionColorScheme[2] = {
SELECTION_AREA::SELECTION_AREA() :
m_additive( false ),
m_subtractive( false )
m_subtractive( false ),
m_exclusiveOr( false )
{
}
void SELECTION_AREA::SetAdditive( bool aAdditive )
{
m_additive = aAdditive;
if( m_additive )
m_subtractive = false;
}
void SELECTION_AREA::SetSubtractive( bool aSubtractive )
{
m_subtractive = aSubtractive;
if( m_subtractive )
m_additive = false;
}
const BOX2I SELECTION_AREA::ViewBBox() const
{
BOX2I tmp;
@ -106,17 +92,13 @@ void SELECTION_AREA::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
// Set the fill of the selection rectangle
// based on the selection mode
if( m_additive )
{
gal.SetFillColor( scheme.additive );
}
else if( m_subtractive )
{
gal.SetFillColor( scheme.subtract );
}
else if( m_exclusiveOr )
gal.SetFillColor( scheme.exclusiveOr );
else
{
gal.SetFillColor( scheme.normal );
}
gal.SetIsStroke( true );
gal.SetIsFill( true );

View File

@ -508,7 +508,10 @@ TOOL_ACTION EE_ACTIONS::simTune( "eeschema.Simulation.tune",
_( "Select a value to be tuned" ), "" );
TOOL_ACTION EE_ACTIONS::highlightNet( "eeschema.EditorControl.highlightNet",
AS_GLOBAL );
AS_GLOBAL,
'`', "",
_( "Highlight Net" ), _( "Highlight net under cursor" ),
net_highlight_schematic_xpm );
TOOL_ACTION EE_ACTIONS::clearHighlight( "eeschema.EditorControl.clearHighlight",
AS_GLOBAL );

View File

@ -124,6 +124,7 @@ EE_SELECTION_TOOL::EE_SELECTION_TOOL() :
m_frame( nullptr ),
m_additive( false ),
m_subtractive( false ),
m_exclusive_or( false ),
m_multiple( false ),
m_skip_heuristics( false ),
m_isLibEdit( false ),
@ -288,13 +289,14 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
if( m_frame->ToolStackIsEmpty() )
m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_ARROW );
// Should selected items be added to the current selection or
// become the new selection (discarding previously selected items)
m_additive = evt->Modifier( MD_SHIFT );
m_additive = m_subtractive = m_exclusive_or = false;
// Should selected items be REMOVED from the current selection?
// This will be ignored if the SHIFT modifier is pressed
m_subtractive = !m_additive && evt->Modifier( MD_CTRL );
if( evt->Modifier( MD_SHIFT ) && evt->Modifier( MD_CTRL ) )
m_subtractive = true;
else if( evt->Modifier( MD_SHIFT ) )
m_additive = true;
else if( evt->Modifier( MD_CTRL ) )
m_exclusive_or = true;
// Is the user requesting that the selection list include all possible
// items without removing less likely selection candidates
@ -303,18 +305,8 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
// Single click? Select single object
if( evt->IsClick( BUT_LEFT ) )
{
if( evt->Modifier( MD_CTRL ) && dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
{
m_toolMgr->RunAction( EE_ACTIONS::highlightNet, true );
}
else
{
// If no modifier keys are pressed, clear the selection
if( !m_additive )
ClearSelection();
SelectPoint( evt->Position());
}
SelectPoint( evt->Position(), EE_COLLECTOR::AllItems, nullptr, false,
m_additive, m_subtractive, m_exclusive_or );
}
// right click? if there is any object - show the context menu
@ -349,22 +341,17 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
// drag with LMB? Select multiple objects (or at least draw a selection box) or drag them
else if( evt->IsDrag( BUT_LEFT ) )
{
bool empty = m_selection.Empty();
// selection is empty? try to start dragging the item under the point where drag started
if( empty )
{
m_selection = RequestSelection( movableItems );
empty = m_selection.Empty();
}
// selection STILL empty? attempt a rectangle multi-selection
if( m_additive || m_subtractive || empty || m_frame->GetDragAlwaysSelects() )
if( m_additive || m_subtractive || m_exclusive_or || m_frame->GetDragAlwaysSelects() )
{
selectMultiple();
}
else
{
// selection is empty? try to start dragging the item under the point where drag
// started
if( m_selection.Empty() )
m_selection = RequestSelection( movableItems );
// Check if dragging has started within any of selected items bounding box
if( selectionContains( evt->Position() ) )
{
@ -373,8 +360,8 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
}
else
{
// No -> clear the selection list
ClearSelection();
// No -> drag a selection box
selectMultiple();
}
}
}
@ -428,7 +415,8 @@ EE_SELECTION& EE_SELECTION_TOOL::GetSelection()
EDA_ITEM* EE_SELECTION_TOOL::SelectPoint( const VECTOR2I& aWhere, const KICAD_T* aFilterList,
bool* aSelectionCancelledFlag, bool aCheckLocked )
bool* aSelectionCancelledFlag, bool aCheckLocked,
bool aAdd, bool aSubtract, bool aExclusiveOr )
{
EDA_ITEM* start;
EE_COLLECTOR collector;
@ -445,8 +433,6 @@ EDA_ITEM* EE_SELECTION_TOOL::SelectPoint( const VECTOR2I& aWhere, const KICAD_T*
collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
collector.Collect( start, aFilterList, (wxPoint) aWhere, m_unit, m_convert );
bool anyCollected = collector.GetCount() > 0;
// Post-process collected items
for( int i = collector.GetCount() - 1; i >= 0; --i )
{
@ -501,17 +487,27 @@ EDA_ITEM* EE_SELECTION_TOOL::SelectPoint( const VECTOR2I& aWhere, const KICAD_T*
}
}
if( !aAdd && !aSubtract && !aExclusiveOr )
ClearSelection();
if( collector.GetCount() == 1 )
{
EDA_ITEM* item = collector[ 0 ];
toggleSelection( item );
return item;
if( aSubtract || ( aExclusiveOr && item->IsSelected() ) )
{
unselect( item );
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
return nullptr;
}
else
{
select( item );
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
return item;
}
}
if( !m_additive && anyCollected )
ClearSelection();
return nullptr;
}
@ -609,7 +605,10 @@ EE_SELECTION& EE_SELECTION_TOOL::RequestSelection( const KICAD_T aFilterList[] )
EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
if( !item->IsType( aFilterList ) )
toggleSelection( item );
{
unselect( item );
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
}
}
}
@ -666,11 +665,15 @@ bool EE_SELECTION_TOOL::selectMultiple()
if( evt->IsDrag( BUT_LEFT ) )
{
if( !m_additive && !m_subtractive && !m_exclusive_or )
ClearSelection();
// Start drawing a selection box
area.SetOrigin( evt->DragOrigin() );
area.SetEnd( evt->Position() );
area.SetAdditive( m_additive );
area.SetSubtractive( m_subtractive );
area.SetExclusiveOr( m_exclusive_or );
view->SetVisible( &area, true );
view->Update( &area );
@ -701,12 +704,14 @@ bool EE_SELECTION_TOOL::selectMultiple()
* Right > Left : Select objects that are crossed by selection
*/
bool windowSelection = width >= 0;
bool anyAdded = false;
bool anySubtracted = false;
if( view->IsMirroredX() )
windowSelection = !windowSelection;
// Construct an EDA_RECT to determine EDA_ITEM selection
EDA_RECT selectionRect( (wxPoint)area.GetOrigin(), wxSize( width, height ) );
EDA_RECT selectionRect( (wxPoint) area.GetOrigin(), wxSize( width, height ) );
selectionRect.Normalize();
@ -719,12 +724,16 @@ bool EE_SELECTION_TOOL::selectMultiple()
if( item->HitTest( selectionRect, windowSelection ) )
{
if( m_subtractive )
if( m_subtractive || ( m_exclusive_or && item->IsSelected() ) )
{
unselect( item );
anySubtracted = true;
}
else
{
select( item );
item->SetFlags( STARTPOINT | ENDPOINT );
anyAdded = true;
}
}
}
@ -732,9 +741,12 @@ bool EE_SELECTION_TOOL::selectMultiple()
m_selection.SetIsHover( false );
// Inform other potentially interested tools
if( !m_selection.Empty() )
if( anyAdded )
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
if( anySubtracted )
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
break; // Stop waiting for events
}
}
@ -1116,35 +1128,6 @@ void EE_SELECTION_TOOL::ClearSelection()
}
void EE_SELECTION_TOOL::toggleSelection( EDA_ITEM* aItem, bool aForce )
{
if( aItem->IsSelected() )
{
unselect( aItem );
// Inform other potentially interested tools
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
}
else
{
if( !m_additive )
ClearSelection();
// Prevent selection of invisible or inactive items
if( aForce || selectable( aItem ) )
{
select( aItem );
// Inform other potentially interested tools
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
}
}
if( m_frame )
m_frame->GetCanvas()->ForceRefresh();
}
void EE_SELECTION_TOOL::select( EDA_ITEM* aItem )
{
highlight( aItem, SELECTED, &m_selection );

View File

@ -101,7 +101,8 @@ public:
*/
EDA_ITEM* SelectPoint( const VECTOR2I& aWhere,
const KICAD_T* aFilterList = EE_COLLECTOR::AllItems,
bool* aSelectionCancelledFlag = NULL, bool aCheckLocked = false );
bool* aSelectionCancelledFlag = NULL, bool aCheckLocked = false,
bool aAdd = false, bool aSubtract = false, bool aExclusiveOr = false );
int AddItemToSel( const TOOL_EVENT& aEvent );
void AddItemToSel( EDA_ITEM* aItem, bool aQuietMode = false );
@ -169,15 +170,6 @@ private:
*/
bool doSelectionMenu( EE_COLLECTOR* aItems );
/**
* Function toggleSelection()
* Changes selection status of a given item.
*
* @param aItem is the item to have selection status changed.
* @param aForce causes the toggle to happen without checking selectability
*/
void toggleSelection( EDA_ITEM* aItem, bool aForce = false );
/**
* Function selectable()
* Checks conditions for an item to be selected.
@ -242,6 +234,7 @@ private:
bool m_additive; // Items should be added to selection (instead of replacing)
bool m_subtractive; // Items should be removed from selection
bool m_exclusive_or; // Items' selection state should be toggled
bool m_multiple; // Multiple selection mode is active
bool m_skip_heuristics; // Heuristics are not allowed when choosing item under cursor

View File

@ -131,9 +131,6 @@ TOOL_ACTION GERBVIEW_ACTIONS::selectionActivate( "gerbview.InteractiveSelection"
AS_GLOBAL, 0, "",
"", "", NULL, AF_ACTIVATE ); // No description, it is not supposed to be shown anywhere
TOOL_ACTION GERBVIEW_ACTIONS::selectionCursor( "gerbview.InteractiveSelection.Cursor",
AS_GLOBAL );
TOOL_ACTION GERBVIEW_ACTIONS::selectItem( "gerbview.InteractiveSelection.SelectItem",
AS_GLOBAL );

View File

@ -33,7 +33,6 @@ using namespace std::placeholders;
#include <bitmaps.h>
#include <tool/tool_event.h>
#include <tool/tool_manager.h>
#include <preview_items/bright_box.h>
#include <preview_items/ruler_item.h>
#include <preview_items/selection_area.h>
#include <gerbview_id.h>
@ -108,10 +107,12 @@ private:
GERBVIEW_SELECTION_TOOL::GERBVIEW_SELECTION_TOOL() :
TOOL_INTERACTIVE( "gerbview.InteractiveSelection" ),
m_frame( NULL ), m_additive( false ), m_subtractive( false ),
m_frame( NULL ),
m_additive( false ),
m_subtractive( false ),
m_exclusive_or( false ),
m_multiple( false )
{
// these members are initialized to avoid warnings about non initialized vars
m_preliminary = true;
}
@ -131,7 +132,6 @@ int GERBVIEW_SELECTION_TOOL::UpdateMenu( const TOOL_EVENT& aEvent )
}
GERBVIEW_SELECTION_TOOL::~GERBVIEW_SELECTION_TOOL()
{
getView()->Remove( &m_selection );
@ -186,6 +186,15 @@ int GERBVIEW_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
if( m_frame->ToolStackIsEmpty() )
m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_ARROW );
m_additive = m_subtractive = m_exclusive_or = false;
if( evt->Modifier( MD_SHIFT ) && evt->Modifier( MD_CTRL ) )
m_subtractive = true;
else if( evt->Modifier( MD_SHIFT ) )
m_additive = true;
else if( evt->Modifier( MD_CTRL ) )
m_exclusive_or = true;
// This is kind of hacky: activate RMB drag on any event.
// There doesn't seem to be any other good way to tell when another tool
// is canceled and control returns to the selection tool, except by the
@ -204,9 +213,6 @@ int GERBVIEW_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
// single click? Select single object
if( evt->IsClick( BUT_LEFT ) )
{
if( !m_additive )
clearSelection();
selectPoint( evt->Position() );
}
@ -244,34 +250,6 @@ GERBVIEW_SELECTION& GERBVIEW_SELECTION_TOOL::GetSelection()
}
void GERBVIEW_SELECTION_TOOL::toggleSelection( EDA_ITEM* aItem )
{
if( aItem->IsSelected() )
{
unselect( aItem );
// Inform other potentially interested tools
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
}
else
{
if( !m_additive )
clearSelection();
// Prevent selection of invisible or inactive items
if( selectable( aItem ) )
{
select( aItem );
// Inform other potentially interested tools
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
}
}
m_frame->GetCanvas()->ForceRefresh();
}
bool GERBVIEW_SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag )
{
EDA_ITEM* item = NULL;
@ -280,8 +258,6 @@ bool GERBVIEW_SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag
collector.Collect( model, GERBER_COLLECTOR::AllItems, wxPoint( aWhere.x, aWhere.y ) );
bool anyCollected = collector.GetCount() != 0;
// Remove unselectable items
for( int i = collector.GetCount() - 1; i >= 0; --i )
{
@ -289,42 +265,39 @@ bool GERBVIEW_SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag
collector.Remove( i );
}
switch( collector.GetCount() )
if( collector.GetCount() > 1 )
{
case 0:
if( !m_additive && anyCollected )
clearSelection();
if( aOnDrag )
Wait( TOOL_EVENT( TC_ANY, TA_MOUSE_UP, BUT_LEFT ) );
return false;
item = disambiguationMenu( &collector );
case 1:
toggleSelection( collector[0] );
return true;
default:
// Let's see if there is still disambiguation in selection..
if( collector.GetCount() == 1 )
if( item )
{
toggleSelection( collector[0] );
collector.Empty();
collector.Append( item );
}
}
if( !m_additive && !m_subtractive && !m_exclusive_or )
clearSelection();
if( collector.GetCount() == 1 )
{
item = collector[ 0 ];
if( m_subtractive || ( m_exclusive_or && item->IsSelected() ) )
{
unselect( item );
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
return false;
}
else
{
select( item );
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
return true;
}
else if( collector.GetCount() > 1 )
{
if( aOnDrag )
Wait( TOOL_EVENT( TC_ANY, TA_MOUSE_UP, BUT_LEFT ) );
item = disambiguationMenu( &collector );
if( item )
{
toggleSelection( item );
return true;
}
}
break;
}
return false;
@ -343,120 +316,14 @@ bool GERBVIEW_SELECTION_TOOL::selectCursor( bool aSelectAlways )
}
bool GERBVIEW_SELECTION_TOOL::selectMultiple()
{
bool cancelled = false; // Was the tool cancelled while it was running?
m_multiple = true; // Multiple selection mode is active
KIGFX::VIEW* view = getView();
getViewControls()->SetAutoPan( true );
KIGFX::PREVIEW::SELECTION_AREA area;
view->Add( &area );
while( TOOL_EVENT* evt = Wait() )
{
if( evt->IsCancelInteractive() )
{
cancelled = true;
break;
}
if( evt->IsDrag( BUT_LEFT ) )
{
// Start drawing a selection box
area.SetOrigin( evt->DragOrigin() );
area.SetEnd( evt->Position() );
area.SetAdditive( m_additive );
area.SetSubtractive( m_subtractive );
view->SetVisible( &area, true );
view->Update( &area );
}
if( evt->IsMouseUp( BUT_LEFT ) )
{
// End drawing the selection box
view->SetVisible( &area, false );
// Mark items within the selection box as selected
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
// Filter the view items based on the selection box
BOX2I selectionBox = area.ViewBBox();
view->Query( selectionBox, selectedItems ); // Get the list of selected items
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR>::iterator it, it_end;
int width = area.GetEnd().x - area.GetOrigin().x;
int height = area.GetEnd().y - area.GetOrigin().y;
// Construct an EDA_RECT to determine EDA_ITEM selection
EDA_RECT selectionRect( wxPoint( area.GetOrigin().x, area.GetOrigin().y ),
wxSize( width, height ) );
selectionRect.Normalize();
for( it = selectedItems.begin(), it_end = selectedItems.end(); it != it_end; ++it )
{
auto item = static_cast<GERBER_DRAW_ITEM*>( it->first );
if( !item || !selectable( item ) )
continue;
/* Selection mode depends on direction of drag-selection:
* Left > Right : Select objects that are fully enclosed by selection
* Right > Left : Select objects that are crossed by selection
*/
if( item->HitTest( selectionRect, width >= 0 ) )
{
if( m_subtractive )
unselect( item );
else
select( item );
}
}
// Inform other potentially interested tools
if( !m_selection.Empty() )
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
break; // Stop waiting for events
}
}
// Stop drawing the selection box
view->Remove( &area );
m_multiple = false; // Multiple selection mode is inactive
getViewControls()->SetAutoPan( false );
return cancelled;
}
void GERBVIEW_SELECTION_TOOL::setTransitions()
{
Go( &GERBVIEW_SELECTION_TOOL::UpdateMenu, ACTIONS::updateMenu.MakeEvent() );
Go( &GERBVIEW_SELECTION_TOOL::Main, GERBVIEW_ACTIONS::selectionActivate.MakeEvent() );
Go( &GERBVIEW_SELECTION_TOOL::CursorSelection, GERBVIEW_ACTIONS::selectionCursor.MakeEvent() );
Go( &GERBVIEW_SELECTION_TOOL::ClearSelection, GERBVIEW_ACTIONS::selectionClear.MakeEvent() );
Go( &GERBVIEW_SELECTION_TOOL::SelectItem, GERBVIEW_ACTIONS::selectItem.MakeEvent() );
Go( &GERBVIEW_SELECTION_TOOL::UnselectItem, GERBVIEW_ACTIONS::unselectItem.MakeEvent() );
Go( &GERBVIEW_SELECTION_TOOL::MeasureTool, ACTIONS::measureTool.MakeEvent() );
}
int GERBVIEW_SELECTION_TOOL::CursorSelection( const TOOL_EVENT& aEvent )
{
if( m_selection.Empty() ) // Try to find an item that could be modified
{
selectCursor( true );
clearSelection();
return 0;
}
return 0;
Go( &GERBVIEW_SELECTION_TOOL::UpdateMenu, ACTIONS::updateMenu.MakeEvent() );
Go( &GERBVIEW_SELECTION_TOOL::Main, GERBVIEW_ACTIONS::selectionActivate.MakeEvent() );
Go( &GERBVIEW_SELECTION_TOOL::ClearSelection, GERBVIEW_ACTIONS::selectionClear.MakeEvent() );
Go( &GERBVIEW_SELECTION_TOOL::SelectItem, GERBVIEW_ACTIONS::selectItem.MakeEvent() );
Go( &GERBVIEW_SELECTION_TOOL::UnselectItem, GERBVIEW_ACTIONS::unselectItem.MakeEvent() );
Go( &GERBVIEW_SELECTION_TOOL::MeasureTool, ACTIONS::measureTool.MakeEvent() );
}
@ -474,12 +341,9 @@ int GERBVIEW_SELECTION_TOOL::SelectItems( const TOOL_EVENT& aEvent )
if( items )
{
// Perform individual selection of each item
// before processing the event.
// Perform individual selection of each item before processing the event.
for( auto item : *items )
{
select( item );
}
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
}
@ -511,12 +375,9 @@ int GERBVIEW_SELECTION_TOOL::UnselectItems( const TOOL_EVENT& aEvent )
if( items )
{
// Perform individual unselection of each item
// before processing the event
// Perform individual unselection of each item before processing the event
for( auto item : *items )
{
unselect( item );
}
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
}
@ -557,28 +418,6 @@ void GERBVIEW_SELECTION_TOOL::clearSelection()
}
void GERBVIEW_SELECTION_TOOL::zoomFitSelection( void )
{
//Should recalculate the view to zoom in on the selection
auto selectionBox = m_selection.ViewBBox();
auto view = getView();
VECTOR2D screenSize = view->ToWorld( m_frame->GetCanvas()->GetClientSize(), false );
if( !( selectionBox.GetWidth() == 0 ) || !( selectionBox.GetHeight() == 0 ) )
{
VECTOR2D vsize = selectionBox.GetSize();
double scale = view->GetScale() / std::max( fabs( vsize.x / screenSize.x ),
fabs( vsize.y / screenSize.y ) );
view->SetScale( scale );
view->SetCenter( selectionBox.Centre() );
view->Add( &m_selection );
}
m_frame->GetCanvas()->ForceRefresh();
}
EDA_ITEM* GERBVIEW_SELECTION_TOOL::disambiguationMenu( GERBER_COLLECTOR* aCollector )
{
EDA_ITEM* current = NULL;
@ -684,9 +523,7 @@ bool GERBVIEW_SELECTION_TOOL::selectable( const EDA_ITEM* aItem ) const
void GERBVIEW_SELECTION_TOOL::select( EDA_ITEM* aItem )
{
if( aItem->IsSelected() )
{
return;
}
m_selection.Add( aItem );
getView()->Add( &m_selection );
@ -732,25 +569,6 @@ void GERBVIEW_SELECTION_TOOL::unselectVisually( EDA_ITEM* aItem )
}
bool GERBVIEW_SELECTION_TOOL::selectionContains( const VECTOR2I& aPoint ) const
{
const unsigned GRIP_MARGIN = 20;
VECTOR2D margin = getView()->ToWorld( VECTOR2D( GRIP_MARGIN, GRIP_MARGIN ), false );
// Check if the point is located within any of the currently selected items bounding boxes
for( auto item : m_selection )
{
BOX2I itemBox = item->ViewBBox();
itemBox.Inflate( margin.x, margin.y ); // Give some margin for gripping an item
if( itemBox.Contains( aPoint ) )
return true;
}
return false;
}
int GERBVIEW_SELECTION_TOOL::MeasureTool( const TOOL_EVENT& aEvent )
{
auto& view = *getView();

View File

@ -73,9 +73,6 @@ public:
*/
GERBVIEW_SELECTION& GetSelection();
///> Select a single item under cursor event handler.
int CursorSelection( const TOOL_EVENT& aEvent );
///> Clear current selection event handler.
int ClearSelection( const TOOL_EVENT& aEvent );
@ -97,9 +94,6 @@ public:
///> Sets up handlers for various events.
void setTransitions() override;
///> Zooms the screen to center and fit the current selection.
void zoomFitSelection( void );
private:
/**
* Function selectPoint()
@ -123,15 +117,6 @@ private:
*/
bool selectCursor( bool aSelectAlways = false );
/**
* Function selectMultiple()
* Handles drawing a selection box that allows one to select many items at
* the same time.
*
* @return true if the function was cancelled (i.e. CancelEvent was received).
*/
bool selectMultiple();
/**
* Function clearSelection()
* Clears the current selection.
@ -147,14 +132,6 @@ private:
*/
EDA_ITEM* disambiguationMenu( GERBER_COLLECTOR* aItems );
/**
* Function toggleSelection()
* Changes selection status of a given item.
*
* @param aItem is the item to have selection status changed.
*/
void toggleSelection( EDA_ITEM* aItem );
/**
* Function selectable()
* Checks conditions for an item to be selected.
@ -193,39 +170,14 @@ private:
*/
void unselectVisually( EDA_ITEM* aItem );
/**
* Function selectionContains()
* Checks if the given point is placed within any of selected items' bounding box.
*
* @return True if the given point is contained in any of selected items' bouding box.
*/
bool selectionContains( const VECTOR2I& aPoint ) const;
GERBVIEW_FRAME* m_frame; // Pointer to the parent frame.
GERBVIEW_SELECTION m_selection; // Current state of selection.
/**
* Function guessSelectionCandidates()
* Tries to guess best selection candidates in case multiple items are clicked, by
* doing some braindead heuristics.
* @param aCollector is the collector that has a list of items to be queried.
*/
void guessSelectionCandidates( GERBER_COLLECTOR& aCollector ) const;
/// Pointer to the parent frame.
GERBVIEW_FRAME* m_frame;
/// Current state of selection.
GERBVIEW_SELECTION m_selection;
/// Flag saying if items should be added to the current selection or rather replace it.
bool m_additive;
/// Flag saying if items should be removed from the current selection
bool m_subtractive;
/// Flag saying if multiple selection mode is active.
bool m_multiple;
/// Determines if the selection is preliminary or final.
bool m_preliminary;
bool m_additive; // Items should be added to selection (instead of replacing)
bool m_subtractive; // Items should be removed from selection
bool m_exclusive_or; // Items' selection state should be toggled
bool m_multiple; // Multiple selection mode is active
bool m_preliminary; // Determines if the selection is preliminary or final.
};
#endif

View File

@ -80,11 +80,9 @@ public:
VECTOR2I GetEnd() const { return m_end; }
bool IsAdditive() const { return m_additive; }
bool IsSubtractive() const { return m_subtractive; }
void SetAdditive( bool aAdditive );
void SetSubtractive( bool aSubtractive );
void SetAdditive( bool aAdditive ) { m_additive = aAdditive; }
void SetSubtractive( bool aSubtractive ) { m_subtractive = aSubtractive; }
void SetExclusiveOr( bool aExclusiveOr ) { m_exclusiveOr = aExclusiveOr; }
void ViewDraw( int aLayer, KIGFX::VIEW* aView ) const override final;
@ -92,6 +90,7 @@ private:
bool m_additive;
bool m_subtractive;
bool m_exclusiveOr;
VECTOR2I m_origin, m_end;
};

View File

@ -32,7 +32,6 @@
#include <tool/selection.h>
#include <tools/pl_actions.h>
#include <ws_data_model.h>
#include <ws_painter.h>
#include <ws_draw_item.h>
#include <collector.h>
#include "pl_selection_tool.h"
@ -58,6 +57,7 @@ PL_SELECTION_TOOL::PL_SELECTION_TOOL() :
m_frame( nullptr ),
m_additive( false ),
m_subtractive( false ),
m_exclusive_or( false ),
m_multiple( false ),
m_skip_heuristics( false )
{
@ -114,13 +114,14 @@ int PL_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
if( m_frame->ToolStackIsEmpty() )
m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_ARROW );
// Should selected items be added to the current selection or
// become the new selection (discarding previously selected items)
m_additive = evt->Modifier( MD_SHIFT );
m_additive = m_subtractive = m_exclusive_or = false;
// Should selected items be REMOVED from the current selection?
// This will be ignored if the SHIFT modifier is pressed
m_subtractive = !m_additive && evt->Modifier( MD_CTRL );
if( evt->Modifier( MD_SHIFT ) && evt->Modifier( MD_CTRL ) )
m_subtractive = true;
else if( evt->Modifier( MD_SHIFT ) )
m_additive = true;
else if( evt->Modifier( MD_CTRL ) )
m_exclusive_or = true;
// Is the user requesting that the selection list include all possible
// items without removing less likely selection candidates
@ -129,10 +130,6 @@ int PL_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
// Single click? Select single object
if( evt->IsClick( BUT_LEFT ) )
{
// If no modifier keys are pressed, clear the selection
if( !m_additive )
ClearSelection();
SelectPoint( evt->Position());
}
@ -160,7 +157,7 @@ int PL_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
// drag with LMB? Select multiple objects (or at least draw a selection box) or drag them
else if( evt->IsDrag( BUT_LEFT ) )
{
if( m_additive || m_subtractive || m_selection.Empty() )
if( m_additive || m_subtractive || m_exclusive_or || m_selection.Empty() )
{
selectMultiple();
}
@ -223,8 +220,6 @@ EDA_ITEM* PL_SELECTION_TOOL::SelectPoint( const VECTOR2I& aWhere, bool* aSelecti
}
}
bool anyCollected = collector.GetCount() != 0;
m_selection.ClearReferencePoint();
// Apply some ugly heuristics to avoid disambiguation menus whenever possible
@ -250,17 +245,27 @@ EDA_ITEM* PL_SELECTION_TOOL::SelectPoint( const VECTOR2I& aWhere, bool* aSelecti
}
}
if( !m_additive && !m_subtractive && !m_exclusive_or )
ClearSelection();
if( collector.GetCount() == 1 )
{
EDA_ITEM* item = collector[ 0 ];
toggleSelection( item );
return item;
if( m_subtractive || ( m_exclusive_or && item->IsSelected() ) )
{
unselect( item );
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
return nullptr;
}
else
{
select( item );
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
return item;
}
}
if( !m_additive && anyCollected )
ClearSelection();
return nullptr;
}
@ -316,11 +321,15 @@ bool PL_SELECTION_TOOL::selectMultiple()
if( evt->IsDrag( BUT_LEFT ) )
{
if( !m_additive && !m_subtractive && !m_exclusive_or )
ClearSelection();
// Start drawing a selection box
area.SetOrigin( evt->DragOrigin() );
area.SetEnd( evt->Position() );
area.SetAdditive( m_additive );
area.SetSubtractive( m_subtractive );
area.SetExclusiveOr( m_exclusive_or );
view->SetVisible( &area, true );
view->Update( &area );
@ -342,6 +351,8 @@ bool PL_SELECTION_TOOL::selectMultiple()
* Right > Left : Select objects that are crossed by selection
*/
bool windowSelection = width >= 0 ? true : false;
bool anyAdded = false;
bool anySubtracted = false;
// Construct an EDA_RECT to determine EDA_ITEM selection
EDA_RECT selectionRect( (wxPoint)area.GetOrigin(), wxSize( width, height ) );
@ -354,18 +365,27 @@ bool PL_SELECTION_TOOL::selectMultiple()
{
if( item->HitTest( selectionRect, windowSelection ) )
{
if( m_subtractive )
if( m_subtractive || ( m_exclusive_or && item->IsSelected() ) )
{
unselect( item );
anySubtracted = true;
}
else
{
select( item );
anyAdded = true;
}
}
}
}
// Inform other potentially interested tools
if( !m_selection.Empty() )
if( anyAdded )
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
if( anySubtracted )
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
break; // Stop waiting for events
}
}
@ -609,31 +629,6 @@ void PL_SELECTION_TOOL::ClearSelection()
}
void PL_SELECTION_TOOL::toggleSelection( EDA_ITEM* aItem )
{
if( aItem->IsSelected() )
{
unselect( aItem );
// Inform other potentially interested tools
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
}
else
{
if( !m_additive )
ClearSelection();
select( aItem );
// Inform other potentially interested tools
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
}
if( m_frame )
m_frame->GetCanvas()->ForceRefresh();
}
void PL_SELECTION_TOOL::select( EDA_ITEM* aItem )
{
highlight( aItem, SELECTED, &m_selection );

View File

@ -49,7 +49,7 @@ class PL_SELECTION_TOOL : public TOOL_INTERACTIVE
{
public:
PL_SELECTION_TOOL();
~PL_SELECTION_TOOL() { }
~PL_SELECTION_TOOL() override { }
/// @copydoc TOOL_BASE::Init()
bool Init() override;
@ -149,14 +149,6 @@ private:
*/
bool doSelectionMenu( COLLECTOR* aItems );
/**
* Function toggleSelection()
* Changes selection status of a given item.
*
* @param aItem is the item to have selection status changed.
*/
void toggleSelection( EDA_ITEM* aItem );
/**
* Function select()
* Takes necessary action mark an item as selected.
@ -208,6 +200,7 @@ private:
bool m_additive; // Items should be added to selection (instead of replacing)
bool m_subtractive; // Items should be removed from selection
bool m_exclusive_or; // Items' selection state should be toggled
bool m_multiple; // Multiple selection mode is active
bool m_skip_heuristics; // Heuristics are not allowed when choosing item under cursor
};

View File

@ -23,27 +23,22 @@
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <limits>
#include <limits>
#include <functional>
using namespace std::placeholders;
#include <class_board.h>
#include <class_board_item.h>
#include <class_track.h>
#include <class_module.h>
#include <class_pcb_text.h>
#include <class_drawsegment.h>
#include <class_zone.h>
#include <pcb_edit_frame.h>
#include <collectors.h>
#include <confirm.h>
#include <dialog_find.h>
#include <dialog_block_options.h>
#include <class_draw_panel_gal.h>
#include <view/view_controls.h>
#include <view/view_group.h>
#include <preview_items/selection_area.h>
#include <painter.h>
#include <bitmaps.h>
@ -119,6 +114,7 @@ SELECTION_TOOL::SELECTION_TOOL() :
m_frame( NULL ),
m_additive( false ),
m_subtractive( false ),
m_exclusive_or( false ),
m_multiple( false ),
m_skip_heuristics( false ),
m_locked( true ),
@ -193,13 +189,15 @@ int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
if( m_frame->ToolStackIsEmpty() )
m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_ARROW );
// Should selected items be added to the current selection or
// become the new selection (discarding previously selected items)
m_additive = evt->Modifier( MD_SHIFT );
bool dragAlwaysSelects = getEditFrame<PCB_BASE_FRAME>()->Settings().m_DragSelects;
m_additive = m_subtractive = m_exclusive_or = false;
// Should selected items be REMOVED from the current selection?
// This will be ignored if the SHIFT modifier is pressed
m_subtractive = !m_additive && evt->Modifier( MD_CTRL );
if( evt->Modifier( MD_SHIFT ) && evt->Modifier( MD_CTRL ) )
m_subtractive = true;
else if( evt->Modifier( MD_SHIFT ) )
m_additive = true;
else if( evt->Modifier( MD_CTRL ) )
m_exclusive_or = true;
// Is the user requesting that the selection list include all possible
// items without removing less likely selection candidates
@ -208,18 +206,7 @@ int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
// Single click? Select single object
if( evt->IsClick( BUT_LEFT ) )
{
if( evt->Modifier( MD_CTRL ) && !m_editModules )
{
m_toolMgr->RunAction( PCB_ACTIONS::highlightNet, true );
}
else
{
// If no modifier keys are pressed, clear the selection
if( !m_additive )
clearSelection();
selectPoint( evt->Position() );
}
selectPoint( evt->Position() );
}
// right click? if there is any object - show the context menu
@ -249,28 +236,17 @@ int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
// drag with LMB? Select multiple objects (or at least draw a selection box) or drag them
else if( evt->IsDrag( BUT_LEFT ) )
{
if( m_additive || m_subtractive )
if( m_additive || m_subtractive || m_exclusive_or || dragAlwaysSelects )
{
selectMultiple();
}
else if( m_selection.Empty() )
{
// There is nothing selected, so try to select something
if( getEditFrame<PCB_BASE_FRAME>()->Settings().m_DragSelects || !selectCursor() )
{
// If nothings has been selected, user wants to select more or selection
// box is preferred to dragging - draw selection box
selectMultiple();
}
else
{
m_selection.SetIsHover( true );
m_toolMgr->InvokeTool( "pcbnew.InteractiveEdit" );
}
}
else
{
// selection is empty? try to start dragging the item under the point where drag
// started
if( m_selection.Empty() && selectCursor() )
m_selection.SetIsHover( true );
// Check if dragging has started within any of selected items bounding box
if( selectionContains( evt->Position() ) )
{
@ -279,8 +255,8 @@ int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
}
else
{
// No -> clear the selection list
clearSelection();
// No -> drag a selection box
selectMultiple();
}
}
}
@ -316,7 +292,8 @@ PCBNEW_SELECTION& SELECTION_TOOL::GetSelection()
PCBNEW_SELECTION& SELECTION_TOOL::RequestSelection( CLIENT_SELECTION_FILTER aClientFilter,
std::vector<BOARD_ITEM*>* aFiltered, bool aConfirmLockedItems )
std::vector<BOARD_ITEM*>* aFiltered,
bool aConfirmLockedItems )
{
bool selectionEmpty = m_selection.Empty();
m_selection.SetIsHover( selectionEmpty );
@ -346,15 +323,17 @@ PCBNEW_SELECTION& SELECTION_TOOL::RequestSelection( CLIENT_SELECTION_FILTER aCli
* This can happen if the locked pads select the module instead
*/
std::vector<EDA_ITEM*> new_items;
std::set_difference( collector.begin(), collector.end(), m_selection.begin(), m_selection.end(),
std::back_inserter( new_items ) );
std::set_difference( collector.begin(), collector.end(),
m_selection.begin(), m_selection.end(),
std::back_inserter( new_items ) );
/**
* The second step is to find the items that were removed by the client filter
*/
std::vector<EDA_ITEM*> diff;
std::set_difference( m_selection.begin(), m_selection.end(), collector.begin(), collector.end(),
std::back_inserter( diff ) );
std::set_difference( m_selection.begin(), m_selection.end(),
collector.begin(), collector.end(),
std::back_inserter( diff ) );
if( aFiltered )
{
@ -379,34 +358,6 @@ PCBNEW_SELECTION& SELECTION_TOOL::RequestSelection( CLIENT_SELECTION_FILTER aCli
}
void SELECTION_TOOL::toggleSelection( BOARD_ITEM* aItem, bool aForce )
{
if( aItem->IsSelected() )
{
unselect( aItem );
// Inform other potentially interested tools
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
}
else
{
if( !m_additive )
clearSelection();
// Prevent selection of invisible or inactive items
if( aForce || selectable( aItem ) )
{
select( aItem );
// Inform other potentially interested tools
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
}
}
if( m_frame )
m_frame->GetCanvas()->ForceRefresh();
}
const GENERAL_COLLECTORS_GUIDE SELECTION_TOOL::getCollectorsGuide() const
{
GENERAL_COLLECTORS_GUIDE guide( board()->GetVisibleLayers(),
@ -446,8 +397,6 @@ bool SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag,
m_editModules ? GENERAL_COLLECTOR::ModuleItems : GENERAL_COLLECTOR::AllBoardItems,
wxPoint( aWhere.x, aWhere.y ), guide );
bool anyCollected = collector.GetCount() != 0;
// Remove unselectable items
for( int i = collector.GetCount() - 1; i >= 0; --i )
{
@ -472,9 +421,7 @@ bool SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag,
if( collector.GetCount() > 1 )
{
if( aOnDrag )
{
Wait( TOOL_EVENT( TC_ANY, TA_MOUSE_UP, BUT_LEFT ) );
}
if( !doSelectionMenu( &collector, _( "Clarify Selection" ) ) )
{
@ -485,17 +432,27 @@ bool SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag,
}
}
if( !m_additive && !m_subtractive && !m_exclusive_or )
clearSelection();
if( collector.GetCount() == 1 )
{
BOARD_ITEM* item = collector[ 0 ];
toggleSelection( item );
return true;
if( m_subtractive || ( m_exclusive_or && item->IsSelected() ) )
{
unselect( item );
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
return false;
}
else
{
select( item );
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
return true;
}
}
if( !m_additive && anyCollected )
clearSelection();
return false;
}
@ -531,11 +488,15 @@ bool SELECTION_TOOL::selectMultiple()
if( evt->IsDrag( BUT_LEFT ) )
{
if( !m_additive && !m_subtractive && !m_exclusive_or )
clearSelection();
// Start drawing a selection box
area.SetOrigin( evt->DragOrigin() );
area.SetEnd( evt->Position() );
area.SetAdditive( m_additive );
area.SetSubtractive( m_subtractive );
area.SetExclusiveOr( m_exclusive_or );
view->SetVisible( &area, true );
view->Update( &area );
@ -566,13 +527,14 @@ bool SELECTION_TOOL::selectMultiple()
* Right > Left : Select objects that are crossed by selection
*/
bool windowSelection = width >= 0 ? true : false;
bool anyAdded = false;
bool anySubtracted = false;
if( view->IsMirroredX() )
windowSelection = !windowSelection;
// Construct an EDA_RECT to determine BOARD_ITEM selection
EDA_RECT selectionRect( wxPoint( area.GetOrigin().x, area.GetOrigin().y ),
wxSize( width, height ) );
EDA_RECT selectionRect( (wxPoint) area.GetOrigin(), wxSize( width, height ) );
selectionRect.Normalize();
@ -585,17 +547,28 @@ bool SELECTION_TOOL::selectMultiple()
if( item->HitTest( selectionRect, windowSelection ) )
{
if( m_subtractive )
if( m_subtractive || ( m_exclusive_or && item->IsSelected() ) )
{
unselect( item );
anySubtracted = true;
}
else
{
select( item );
anyAdded = true;
}
}
}
m_selection.SetIsHover( false );
// Inform other potentially interested tools
if( !m_selection.Empty() )
if( anyAdded )
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
if( anySubtracted )
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
break; // Stop waiting for events
}
}
@ -1142,7 +1115,8 @@ int SELECTION_TOOL::findMove( const TOOL_EVENT& aEvent )
{
KIGFX::VIEW_CONTROLS* viewCtrls = getViewControls();
clearSelection();
toggleSelection( module, true );
select( module );
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
auto cursorPosition = viewCtrls->GetCursorPosition( false );

View File

@ -261,15 +261,6 @@ private:
*/
BOARD_ITEM* pickSmallestComponent( GENERAL_COLLECTOR* aCollector );
/**
* Function toggleSelection()
* Changes selection status of a given item.
*
* @param aItem is the item to have selection status changed.
* @param aForce causes the toggle to happen without checking selectability
*/
void toggleSelection( BOARD_ITEM* aItem, bool aForce = false );
/**
* Function selectable()
* Checks conditions for an item to be selected.
@ -347,6 +338,7 @@ private:
bool m_additive; // Items should be added to selection (instead of replacing)
bool m_subtractive; // Items should be removed from selection
bool m_exclusive_or; // Items' selection state should be toggled
bool m_multiple; // Multiple selection mode is active
bool m_skip_heuristics; // Heuristics are not allowed when choosing item under cursor
bool m_locked; // Other tools are not allowed to modify locked items