|
|
/*
* This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2021-2023 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>
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 ), i + 1 );#else
menuText = wxString::Format( "&%d %s\t%d", i + 1, item->GetItemDescription( unitsProvider ), i + 1 );#endif
} else { menuText = item->GetItemDescription( unitsProvider ); }
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;}
|