7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-02-18 22:29:38 +00:00
kicad/common/tool/selection_tool.cpp
Seth Hillbrand 0b2d4d4879 Revise Copyright statement to align with TLF
Recommendation is to avoid using the year nomenclature as this
information is already encoded in the git repo.  Avoids needing to
repeatly update.

Also updates AUTHORS.txt from current repo with contributor names
2025-01-01 14:12:04 -08:00

432 lines
12 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <bitmaps.h>
#include <widgets/ui_common.h>
#include <collector.h>
#include <tool/tool_manager.h>
#include <tool/actions.h>
#include <tool/selection_tool.h>
#include <view/view.h>
#include <eda_draw_frame.h>
#include <eda_item.h>
SELECTION_TOOL::SELECTION_TOOL( const std::string& aName ) :
TOOL_INTERACTIVE( aName ),
m_additive( false ),
m_subtractive( false ),
m_exclusive_or( false ),
m_multiple( false ),
m_skip_heuristics( false ),
m_highlight_modifier( false ),
m_drag_additive( false ),
m_drag_subtractive( false ),
m_canceledMenu( false )
{
}
void SELECTION_TOOL::setModifiersState( bool aShiftState, bool aCtrlState, bool aAltState )
{
// Set the configuration of m_additive, m_subtractive, m_exclusive_or from the state of
// modifier keys SHIFT and CTRL
// ALT key cannot be used on MSW because of a conflict with the system menu
m_subtractive = aCtrlState && aShiftState;
m_additive = !aCtrlState && aShiftState;
if( ctrlClickHighlights() )
{
m_exclusive_or = false;
m_highlight_modifier = aCtrlState && !aShiftState;
}
else
{
m_exclusive_or = aCtrlState && !aShiftState;
m_highlight_modifier = false;
}
// Drag is more forgiving and allows either Ctrl+Drag or Shift+Drag to add to the selection
// Note, however that we cannot provide disambiguation at the same time as the box selection
m_drag_additive = ( aCtrlState || aShiftState ) && !aAltState;
m_drag_subtractive = aCtrlState && aShiftState && !aAltState;
// While the ALT key has some conflicts under MSW (and some flavors of Linux WMs), it remains
// useful for users who only use tap-click rather than holding the button. We disable it for
// windows because it flashes the disambiguation menu without showing data
#ifndef __WINDOWS__
m_skip_heuristics = aAltState;
#else
m_skip_heuristics = false;
#endif
}
bool SELECTION_TOOL::hasModifier()
{
return m_subtractive || m_additive || m_exclusive_or;
}
int SELECTION_TOOL::UpdateMenu( const TOOL_EVENT& aEvent )
{
ACTION_MENU* actionMenu = aEvent.Parameter<ACTION_MENU*>();
CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( actionMenu );
if( conditionalMenu )
conditionalMenu->Evaluate( selection() );
if( actionMenu )
actionMenu->UpdateAll();
return 0;
}
int SELECTION_TOOL::AddItemToSel( const TOOL_EVENT& aEvent )
{
AddItemToSel( aEvent.Parameter<EDA_ITEM*>() );
selection().SetIsHover( false );
return 0;
}
void SELECTION_TOOL::AddItemToSel( EDA_ITEM* aItem, bool aQuietMode )
{
if( aItem )
{
select( aItem );
// Inform other potentially interested tools
if( !aQuietMode )
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
}
}
int SELECTION_TOOL::ReselectItem( const TOOL_EVENT& aEvent )
{
RemoveItemFromSel( aEvent.Parameter<EDA_ITEM*>() );
selection().SetIsHover( false );
AddItemToSel( aEvent.Parameter<EDA_ITEM*>() );
selection().SetIsHover( false );
return 0;
}
int SELECTION_TOOL::AddItemsToSel( const TOOL_EVENT& aEvent )
{
AddItemsToSel( aEvent.Parameter<EDA_ITEMS*>(), false );
selection().SetIsHover( false );
return 0;
}
void SELECTION_TOOL::AddItemsToSel( EDA_ITEMS* aList, bool aQuietMode )
{
if( aList )
{
for( EDA_ITEM* item : *aList )
select( item );
// Inform other potentially interested tools
if( !aQuietMode )
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
}
}
int SELECTION_TOOL::RemoveItemFromSel( const TOOL_EVENT& aEvent )
{
RemoveItemFromSel( aEvent.Parameter<EDA_ITEM*>() );
selection().SetIsHover( false );
return 0;
}
void SELECTION_TOOL::RemoveItemFromSel( EDA_ITEM* aItem, bool aQuietMode )
{
if( aItem )
{
unselect( aItem );
// Inform other potentially interested tools
if( !aQuietMode )
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
}
}
int SELECTION_TOOL::RemoveItemsFromSel( const TOOL_EVENT& aEvent )
{
RemoveItemsFromSel( aEvent.Parameter<EDA_ITEMS*>(), false );
selection().SetIsHover( false );
return 0;
}
void SELECTION_TOOL::RemoveItemsFromSel( EDA_ITEMS* aList, bool aQuietMode )
{
if( aList )
{
for( EDA_ITEM* item : *aList )
unselect( item );
// Inform other potentially interested tools
if( !aQuietMode )
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
}
}
void SELECTION_TOOL::RemoveItemsFromSel( std::vector<KIID>* aList, bool aQuietMode )
{
EDA_ITEMS removeItems;
for( EDA_ITEM* item : selection() )
{
if( alg::contains( *aList, item->m_Uuid ) )
removeItems.push_back( item );
}
RemoveItemsFromSel( &removeItems, aQuietMode );
}
void SELECTION_TOOL::BrightenItem( EDA_ITEM* aItem )
{
highlight( aItem, BRIGHTENED );
}
void SELECTION_TOOL::UnbrightenItem( EDA_ITEM* aItem )
{
unhighlight( aItem, BRIGHTENED );
}
void SELECTION_TOOL::onDisambiguationExpire( wxTimerEvent& aEvent )
{
// If there is a multiple selection then it's more likely that we're seeing a paused drag
// than a long-click.
if( selection().GetSize() >= 2 && !hasModifier() )
return;
// If another tool has since started running then we don't want to interrupt
if( !getEditFrame<EDA_DRAW_FRAME>()->ToolStackIsEmpty() )
return;
m_toolMgr->ProcessEvent( EVENTS::DisambiguatePoint );
}
int SELECTION_TOOL::SelectionMenu( const TOOL_EVENT& aEvent )
{
COLLECTOR* collector = aEvent.Parameter<COLLECTOR*>();
if( !doSelectionMenu( collector ) )
collector->m_MenuCancelled = true;
return 0;
}
bool SELECTION_TOOL::doSelectionMenu( COLLECTOR* aCollector )
{
UNITS_PROVIDER* unitsProvider = getEditFrame<EDA_DRAW_FRAME>();
EDA_ITEM* current = nullptr;
SELECTION highlightGroup;
bool selectAll = false;
bool expandSelection = false;
highlightGroup.SetLayer( LAYER_SELECT_OVERLAY );
getView()->Add( &highlightGroup );
do
{
/// The user has requested the full, non-limited list of selection items
if( expandSelection )
aCollector->Combine();
expandSelection = false;
int limit = std::min( 100, aCollector->GetCount() );
ACTION_MENU menu( true );
for( int i = 0; i < limit; ++i )
{
EDA_ITEM* item = ( *aCollector )[i];
wxString menuText;
if( i < 9 )
{
#ifdef __WXMAC__
menuText = wxString::Format( "%s\t%d",
item->GetItemDescription( unitsProvider, false ),
i + 1 );
#else
menuText = wxString::Format( "&%d %s\t%d",
i + 1,
item->GetItemDescription( unitsProvider, false ),
i + 1 );
#endif
}
else
{
menuText = item->GetItemDescription( unitsProvider, false );
}
menu.Add( menuText, i + 1, item->GetMenuImage() );
}
menu.AppendSeparator();
menu.Add( _( "Select &All\tA" ), limit + 1, BITMAPS::INVALID_BITMAP );
if( !expandSelection && aCollector->HasAdditionalItems() )
menu.Add( _( "&Expand Selection\tE" ), limit + 2, BITMAPS::INVALID_BITMAP );
if( aCollector->m_MenuTitle.Length() )
{
menu.SetTitle( aCollector->m_MenuTitle );
menu.SetIcon( BITMAPS::info );
menu.DisplayTitle( true );
}
else
{
menu.DisplayTitle( false );
}
SetContextMenu( &menu, CMENU_NOW );
while( TOOL_EVENT* evt = Wait() )
{
if( evt->Action() == TA_CHOICE_MENU_UPDATE )
{
if( selectAll )
{
for( int i = 0; i < aCollector->GetCount(); ++i )
unhighlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
}
else if( current )
{
unhighlight( current, BRIGHTENED, &highlightGroup );
}
int id = *evt->GetCommandId();
// User has pointed an item, so show it in a different way
if( id > 0 && id <= limit )
{
current = ( *aCollector )[id - 1];
highlight( current, BRIGHTENED, &highlightGroup );
}
else
{
current = nullptr;
}
// User has pointed on the "Select All" option
if( id == limit + 1 )
{
for( int i = 0; i < aCollector->GetCount(); ++i )
highlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
selectAll = true;
}
else
{
selectAll = false;
}
}
else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
{
if( selectAll )
{
for( int i = 0; i < aCollector->GetCount(); ++i )
unhighlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
}
else if( current )
{
unhighlight( current, BRIGHTENED, &highlightGroup );
}
std::optional<int> id = evt->GetCommandId();
// User has selected the "Select All" option
if( id == limit + 1 )
{
selectAll = true;
current = nullptr;
}
// User has selected the "Expand Selection" option
else if( id == limit + 2 )
{
selectAll = false;
current = nullptr;
expandSelection = true;
}
// User has selected an item, so this one will be returned
else if( id && ( *id > 0 ) && ( *id <= limit ) )
{
selectAll = false;
current = ( *aCollector )[*id - 1];
}
// User has cancelled the menu (either by <esc> or clicking out of it)
else
{
selectAll = false;
current = nullptr;
}
}
else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
{
break;
}
getView()->UpdateItems();
getEditFrame<EDA_DRAW_FRAME>()->GetCanvas()->Refresh();
}
} while( expandSelection );
getView()->Remove( &highlightGroup );
if( selectAll )
{
return true;
}
else if( current )
{
aCollector->Empty();
aCollector->Append( current );
return true;
}
return false;
}