1387 lines
40 KiB
C++
1387 lines
40 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2019 CERN
|
|
* Copyright (C) 2019-2020 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 2
|
|
* 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/old-licenses/gpl-2.0.html
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
|
|
#include <class_libentry.h>
|
|
#include <core/typeinfo.h>
|
|
#include <ee_actions.h>
|
|
#include <ee_collectors.h>
|
|
#include <ee_selection_tool.h>
|
|
#include <eeschema_id.h> // For MAX_SELECT_ITEM_IDS
|
|
#include <lib_edit_frame.h>
|
|
#include <lib_item.h>
|
|
#include <lib_view_frame.h>
|
|
#include <math/util.h>
|
|
#include <menus_helpers.h>
|
|
#include <painter.h>
|
|
#include <preview_items/selection_area.h>
|
|
#include <sch_base_frame.h>
|
|
#include <sch_component.h>
|
|
#include <sch_edit_frame.h>
|
|
#include <sch_field.h>
|
|
#include <sch_item.h>
|
|
#include <sch_line.h>
|
|
#include <sch_sheet.h>
|
|
#include <tool/tool_event.h>
|
|
#include <tool/tool_manager.h>
|
|
#include <tools/sch_line_wire_bus_tool.h>
|
|
#include <view/view.h>
|
|
#include <view/view_controls.h>
|
|
#include <view/view_group.h>
|
|
|
|
|
|
SELECTION_CONDITION EE_CONDITIONS::Empty = [] (const SELECTION& aSelection )
|
|
{
|
|
return aSelection.Empty();
|
|
};
|
|
|
|
|
|
SELECTION_CONDITION EE_CONDITIONS::Idle = [] (const SELECTION& aSelection )
|
|
{
|
|
return ( !aSelection.Front() || aSelection.Front()->GetEditFlags() == 0 );
|
|
};
|
|
|
|
|
|
SELECTION_CONDITION EE_CONDITIONS::IdleSelection = [] (const SELECTION& aSelection )
|
|
{
|
|
return ( aSelection.Front() && aSelection.Front()->GetEditFlags() == 0 );
|
|
};
|
|
|
|
|
|
SELECTION_CONDITION EE_CONDITIONS::SingleSymbol = [] (const SELECTION& aSel )
|
|
{
|
|
if( aSel.GetSize() == 1 )
|
|
{
|
|
SCH_COMPONENT* comp = dynamic_cast<SCH_COMPONENT*>( aSel.Front() );
|
|
|
|
if( comp )
|
|
return !comp->GetPartRef() || !comp->GetPartRef()->IsPower();
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
|
|
SELECTION_CONDITION EE_CONDITIONS::SingleDeMorganSymbol = [] ( const SELECTION& aSel )
|
|
{
|
|
if( aSel.GetSize() == 1 )
|
|
{
|
|
SCH_COMPONENT* comp = dynamic_cast<SCH_COMPONENT*>( aSel.Front() );
|
|
|
|
if( comp )
|
|
return comp->GetPartRef() && comp->GetPartRef()->HasConversion();
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
|
|
SELECTION_CONDITION EE_CONDITIONS::SingleMultiUnitSymbol = [] ( const SELECTION& aSel )
|
|
{
|
|
if( aSel.GetSize() == 1 )
|
|
{
|
|
SCH_COMPONENT* comp = dynamic_cast<SCH_COMPONENT*>( aSel.Front() );
|
|
|
|
if( comp )
|
|
return comp->GetPartRef() && comp->GetPartRef()->GetUnitCount() >= 2;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
|
|
#define HITTEST_THRESHOLD_PIXELS 5
|
|
|
|
|
|
EE_SELECTION_TOOL::EE_SELECTION_TOOL() :
|
|
TOOL_INTERACTIVE( "eeschema.InteractiveSelection" ),
|
|
m_frame( nullptr ),
|
|
m_additive( false ),
|
|
m_subtractive( false ),
|
|
m_exclusive_or( false ),
|
|
m_multiple( false ),
|
|
m_skip_heuristics( false ),
|
|
m_isLibEdit( false ),
|
|
m_isLibView( false ),
|
|
m_unit( 0 ),
|
|
m_convert( 0 )
|
|
{
|
|
}
|
|
|
|
|
|
EE_SELECTION_TOOL::~EE_SELECTION_TOOL()
|
|
{
|
|
getView()->Remove( &m_selection );
|
|
}
|
|
|
|
using E_C = EE_CONDITIONS;
|
|
|
|
bool EE_SELECTION_TOOL::Init()
|
|
{
|
|
m_frame = getEditFrame<SCH_BASE_FRAME>();
|
|
|
|
LIB_VIEW_FRAME* libViewFrame = dynamic_cast<LIB_VIEW_FRAME*>( m_frame );
|
|
LIB_EDIT_FRAME* libEditFrame = dynamic_cast<LIB_EDIT_FRAME*>( m_frame );
|
|
|
|
if( libEditFrame )
|
|
{
|
|
m_isLibEdit = true;
|
|
m_unit = libEditFrame->GetUnit();
|
|
m_convert = libEditFrame->GetConvert();
|
|
}
|
|
else
|
|
m_isLibView = libViewFrame != nullptr;
|
|
|
|
|
|
static KICAD_T wireOrBusTypes[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT };
|
|
|
|
auto wireSelection = E_C::MoreThan( 0 ) && E_C::OnlyType( SCH_LINE_LOCATE_WIRE_T );
|
|
auto busSelection = E_C::MoreThan( 0 ) && E_C::OnlyType( SCH_LINE_LOCATE_BUS_T );
|
|
auto wireOrBusSelection = E_C::MoreThan( 0 ) && E_C::OnlyTypes( wireOrBusTypes );
|
|
auto sheetSelection = E_C::Count( 1 ) && E_C::OnlyType( SCH_SHEET_T );
|
|
auto schEditCondition = [this] ( const SELECTION& aSel ) {
|
|
return !m_isLibEdit && !m_isLibView;
|
|
};
|
|
auto belowRootSheetCondition = [this] ( const SELECTION& aSel ) {
|
|
return !m_isLibEdit && !m_isLibView && g_CurrentSheet->Last() != g_RootSheet;
|
|
};
|
|
auto havePartCondition = [ this ] ( const SELECTION& sel ) {
|
|
return m_isLibEdit && ( (LIB_EDIT_FRAME*) m_frame )->GetCurPart();
|
|
};
|
|
|
|
auto& menu = m_menu.GetMenu();
|
|
|
|
menu.AddItem( EE_ACTIONS::enterSheet, sheetSelection && EE_CONDITIONS::Idle, 1 );
|
|
menu.AddItem( EE_ACTIONS::explicitCrossProbe, sheetSelection && EE_CONDITIONS::Idle, 1 );
|
|
menu.AddItem( EE_ACTIONS::leaveSheet, belowRootSheetCondition, 1 );
|
|
|
|
menu.AddSeparator( 100 );
|
|
menu.AddItem( EE_ACTIONS::drawWire, schEditCondition && EE_CONDITIONS::Empty, 100 );
|
|
menu.AddItem( EE_ACTIONS::drawBus, schEditCondition && EE_CONDITIONS::Empty, 100 );
|
|
|
|
menu.AddSeparator( 100 );
|
|
menu.AddItem( EE_ACTIONS::finishWire, SCH_LINE_WIRE_BUS_TOOL::IsDrawingWire, 100 );
|
|
|
|
menu.AddSeparator( 100 );
|
|
menu.AddItem( EE_ACTIONS::finishBus, SCH_LINE_WIRE_BUS_TOOL::IsDrawingBus, 100 );
|
|
|
|
menu.AddSeparator( 200 );
|
|
menu.AddItem( EE_ACTIONS::selectConnection, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
|
|
menu.AddItem( EE_ACTIONS::placeJunction, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
|
|
menu.AddItem( EE_ACTIONS::placeLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
|
|
menu.AddItem( EE_ACTIONS::placeGlobalLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
|
|
menu.AddItem( EE_ACTIONS::placeHierLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
|
|
menu.AddItem( EE_ACTIONS::breakWire, wireSelection && EE_CONDITIONS::Idle, 250 );
|
|
menu.AddItem( EE_ACTIONS::breakBus, busSelection && EE_CONDITIONS::Idle, 250 );
|
|
menu.AddItem( EE_ACTIONS::importSheetPin, sheetSelection && EE_CONDITIONS::Idle, 250 );
|
|
|
|
menu.AddSeparator( 400 );
|
|
menu.AddItem( EE_ACTIONS::symbolProperties, havePartCondition && EE_CONDITIONS::Empty, 400 );
|
|
menu.AddItem( EE_ACTIONS::pinTable, havePartCondition && EE_CONDITIONS::Empty, 400 );
|
|
|
|
menu.AddSeparator( 1000 );
|
|
m_frame->AddStandardSubMenus( m_menu );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void EE_SELECTION_TOOL::Reset( RESET_REASON aReason )
|
|
{
|
|
m_frame = getEditFrame<SCH_BASE_FRAME>();
|
|
|
|
if( aReason == TOOL_BASE::MODEL_RELOAD )
|
|
{
|
|
// Remove pointers to the selected items from containers without changing their
|
|
// properties (as they are already deleted while a new sheet is loaded)
|
|
m_selection.Clear();
|
|
getView()->GetPainter()->GetSettings()->SetHighlight( false );
|
|
|
|
LIB_EDIT_FRAME* libEditFrame = dynamic_cast<LIB_EDIT_FRAME*>( m_frame );
|
|
LIB_VIEW_FRAME* libViewFrame = dynamic_cast<LIB_VIEW_FRAME*>( m_frame );
|
|
|
|
if( libEditFrame )
|
|
{
|
|
m_isLibEdit = true;
|
|
m_unit = libEditFrame->GetUnit();
|
|
m_convert = libEditFrame->GetConvert();
|
|
}
|
|
else
|
|
m_isLibView = libViewFrame != nullptr;
|
|
}
|
|
else
|
|
// Restore previous properties of selected items and remove them from containers
|
|
ClearSelection();
|
|
|
|
// Reinsert the VIEW_GROUP, in case it was removed from the VIEW
|
|
getView()->Remove( &m_selection );
|
|
getView()->Add( &m_selection );
|
|
}
|
|
|
|
|
|
int EE_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( m_selection );
|
|
|
|
if( actionMenu )
|
|
actionMenu->UpdateAll();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
const KICAD_T movableSchematicItems[] =
|
|
{
|
|
SCH_MARKER_T,
|
|
SCH_JUNCTION_T,
|
|
SCH_NO_CONNECT_T,
|
|
SCH_BUS_BUS_ENTRY_T,
|
|
SCH_BUS_WIRE_ENTRY_T,
|
|
SCH_LINE_T,
|
|
SCH_BITMAP_T,
|
|
SCH_TEXT_T,
|
|
SCH_LABEL_T,
|
|
SCH_GLOBAL_LABEL_T,
|
|
SCH_HIER_LABEL_T,
|
|
SCH_FIELD_T,
|
|
SCH_COMPONENT_T,
|
|
SCH_SHEET_PIN_T,
|
|
SCH_SHEET_T,
|
|
EOT
|
|
};
|
|
|
|
|
|
const KICAD_T movableSymbolItems[] =
|
|
{
|
|
LIB_ARC_T,
|
|
LIB_CIRCLE_T,
|
|
LIB_TEXT_T,
|
|
LIB_RECTANGLE_T,
|
|
LIB_POLYLINE_T,
|
|
LIB_BEZIER_T,
|
|
LIB_PIN_T,
|
|
LIB_FIELD_T,
|
|
EOT
|
|
};
|
|
|
|
|
|
int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
|
|
{
|
|
|
|
// Main loop: keep receiving events
|
|
while( TOOL_EVENT* evt = Wait() )
|
|
{
|
|
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;
|
|
|
|
// Is the user requesting that the selection list include all possible
|
|
// items without removing less likely selection candidates
|
|
m_skip_heuristics = !!evt->Modifier( MD_ALT );
|
|
|
|
// Single click? Select single object
|
|
if( evt->IsClick( BUT_LEFT ) )
|
|
{
|
|
m_frame->FocusOnItem( nullptr );
|
|
|
|
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
|
|
else if( evt->IsClick( BUT_RIGHT ) )
|
|
{
|
|
bool selectionCancelled = false;
|
|
|
|
if( m_selection.Empty() ||
|
|
!m_selection.GetBoundingBox().Contains( wxPoint( evt->Position() ) ) )
|
|
{
|
|
ClearSelection();
|
|
SelectPoint( evt->Position(), EE_COLLECTOR::AllItems, &selectionCancelled );
|
|
m_selection.SetIsHover( true );
|
|
}
|
|
|
|
if( !selectionCancelled )
|
|
m_menu.ShowContextMenu( m_selection );
|
|
}
|
|
|
|
// double click? Display the properties window
|
|
else if( evt->IsDblClick( BUT_LEFT ) )
|
|
{
|
|
m_frame->FocusOnItem( nullptr );
|
|
|
|
if( m_selection.Empty() )
|
|
SelectPoint( evt->Position());
|
|
|
|
EDA_ITEM* item = m_selection.Front();
|
|
|
|
if( item && item->Type() == SCH_SHEET_T )
|
|
m_toolMgr->RunAction( EE_ACTIONS::enterSheet );
|
|
else
|
|
m_toolMgr->RunAction( EE_ACTIONS::properties );
|
|
}
|
|
|
|
// drag with LMB? Select multiple objects (or at least draw a selection box) or drag them
|
|
else if( evt->IsDrag( BUT_LEFT ) )
|
|
{
|
|
m_frame->FocusOnItem( nullptr );
|
|
|
|
if( m_additive || m_subtractive || m_exclusive_or || m_frame->GetDragSelects() )
|
|
{
|
|
selectMultiple();
|
|
}
|
|
else
|
|
{
|
|
// selection is empty? try to start dragging the item under the point where drag
|
|
// started
|
|
if( m_isLibEdit && m_selection.Empty() )
|
|
m_selection = RequestSelection( movableSymbolItems );
|
|
else if( m_selection.Empty() )
|
|
m_selection = RequestSelection( movableSchematicItems );
|
|
|
|
// Check if dragging has started within any of selected items bounding box
|
|
if( selectionContains( evt->Position() ) )
|
|
{
|
|
// Yes -> run the move tool and wait till it finishes
|
|
if( m_isLibEdit )
|
|
m_toolMgr->InvokeTool( "eeschema.SymbolMoveTool" );
|
|
else
|
|
m_toolMgr->InvokeTool( "eeschema.InteractiveMove" );
|
|
}
|
|
else
|
|
{
|
|
// No -> drag a selection box
|
|
selectMultiple();
|
|
}
|
|
}
|
|
}
|
|
|
|
// context sub-menu selection? Handle unit selection or bus unfolding
|
|
else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CHOICE_MENU_CHOICE )
|
|
{
|
|
if( evt->GetCommandId().get() >= ID_POPUP_SCH_SELECT_UNIT_CMP
|
|
&& evt->GetCommandId().get() <= ID_POPUP_SCH_SELECT_UNIT_CMP_MAX )
|
|
{
|
|
SCH_COMPONENT* component = dynamic_cast<SCH_COMPONENT*>( m_selection.Front() );
|
|
int unit = evt->GetCommandId().get() - ID_POPUP_SCH_SELECT_UNIT_CMP;
|
|
|
|
if( component )
|
|
static_cast<SCH_EDIT_FRAME*>( m_frame )->SelectUnit( component, unit );
|
|
}
|
|
else if( evt->GetCommandId().get() >= ID_POPUP_SCH_UNFOLD_BUS
|
|
&& evt->GetCommandId().get() <= ID_POPUP_SCH_UNFOLD_BUS_END )
|
|
{
|
|
wxString* net = new wxString( *evt->Parameter<wxString*>() );
|
|
m_toolMgr->RunAction( EE_ACTIONS::unfoldBus, true, net );
|
|
}
|
|
|
|
}
|
|
|
|
else if( evt->IsCancelInteractive() )
|
|
{
|
|
m_frame->FocusOnItem( nullptr );
|
|
|
|
ClearSelection();
|
|
}
|
|
|
|
else if( evt->Action() == TA_UNDO_REDO_PRE )
|
|
{
|
|
m_frame->FocusOnItem( nullptr );
|
|
|
|
ClearSelection();
|
|
}
|
|
|
|
else
|
|
evt->SetPassEvent();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
EE_SELECTION& EE_SELECTION_TOOL::GetSelection()
|
|
{
|
|
return m_selection;
|
|
}
|
|
|
|
|
|
EDA_ITEM* EE_SELECTION_TOOL::SelectPoint( const VECTOR2I& aWhere, const KICAD_T* aFilterList,
|
|
bool* aSelectionCancelledFlag, bool aCheckLocked,
|
|
bool aAdd, bool aSubtract, bool aExclusiveOr )
|
|
{
|
|
EE_COLLECTOR collector;
|
|
|
|
collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
|
|
|
|
if( m_isLibEdit )
|
|
{
|
|
auto part = static_cast<LIB_EDIT_FRAME*>( m_frame )->GetCurPart();
|
|
|
|
if( !part )
|
|
return nullptr;
|
|
|
|
collector.Collect( part->GetDrawItems(), aFilterList, (wxPoint) aWhere, m_unit, m_convert );
|
|
}
|
|
else
|
|
collector.Collect( m_frame->GetScreen(), aFilterList, (wxPoint) aWhere, m_unit, m_convert );
|
|
|
|
// Post-process collected items
|
|
for( int i = collector.GetCount() - 1; i >= 0; --i )
|
|
{
|
|
if( !Selectable( collector[ i ] ) )
|
|
{
|
|
collector.Remove( i );
|
|
continue;
|
|
}
|
|
|
|
if( aCheckLocked && collector[ i ]->IsLocked() )
|
|
{
|
|
collector.Remove( i );
|
|
continue;
|
|
}
|
|
|
|
// SelectPoint, unlike other selection routines, can select line ends
|
|
if( collector[ i ]->Type() == SCH_LINE_T )
|
|
{
|
|
SCH_LINE* line = (SCH_LINE*) collector[ i ];
|
|
line->ClearFlags( STARTPOINT | ENDPOINT );
|
|
|
|
if( HitTestPoints( line->GetStartPoint(), (wxPoint) aWhere, collector.m_Threshold ) )
|
|
line->SetFlags( STARTPOINT );
|
|
else if (HitTestPoints( line->GetEndPoint(), (wxPoint) aWhere, collector.m_Threshold ) )
|
|
line->SetFlags( ENDPOINT );
|
|
else
|
|
line->SetFlags( STARTPOINT | ENDPOINT );
|
|
}
|
|
}
|
|
|
|
m_selection.ClearReferencePoint();
|
|
|
|
// Apply some ugly heuristics to avoid disambiguation menus whenever possible
|
|
if( collector.GetCount() > 1 && !m_skip_heuristics )
|
|
{
|
|
GuessSelectionCandidates( collector, aWhere );
|
|
}
|
|
|
|
// If still more than one item we're going to have to ask the user.
|
|
if( collector.GetCount() > 1 )
|
|
{
|
|
collector.m_MenuTitle = _( "Clarify Selection" );
|
|
// Must call selectionMenu via RunAction() to avoid event-loop contention
|
|
m_toolMgr->RunAction( EE_ACTIONS::selectionMenu, true, &collector );
|
|
|
|
if( collector.m_MenuCancelled )
|
|
{
|
|
if( aSelectionCancelledFlag )
|
|
*aSelectionCancelledFlag = true;
|
|
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if( !aAdd && !aSubtract && !aExclusiveOr )
|
|
ClearSelection();
|
|
|
|
if( collector.GetCount() == 1 )
|
|
{
|
|
EDA_ITEM* item = collector[ 0 ];
|
|
|
|
if( aSubtract || ( aExclusiveOr && item->IsSelected() ) )
|
|
{
|
|
unselect( item );
|
|
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
|
|
return nullptr;
|
|
}
|
|
else
|
|
{
|
|
select( item );
|
|
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
|
|
return item;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
void EE_SELECTION_TOOL::GuessSelectionCandidates( EE_COLLECTOR& collector, const VECTOR2I& aPos )
|
|
{
|
|
// There are certain parent/child and enclosure combinations that can be handled
|
|
// automatically.
|
|
|
|
// Prefer exact hits to sloppy ones
|
|
int exactHits = 0;
|
|
|
|
for( int i = collector.GetCount() - 1; i >= 0; --i )
|
|
{
|
|
EDA_ITEM* item = collector[ i ];
|
|
|
|
if( item->HitTest( (wxPoint) aPos, 0 ) )
|
|
exactHits++;
|
|
}
|
|
|
|
if( exactHits > 0 && exactHits < collector.GetCount() )
|
|
{
|
|
for( int i = collector.GetCount() - 1; i >= 0; --i )
|
|
{
|
|
EDA_ITEM* item = collector[ i ];
|
|
|
|
if( !item->HitTest( (wxPoint) aPos, 0 ) )
|
|
collector.Remove( item );
|
|
}
|
|
}
|
|
|
|
// Prefer a non-sheet to a sheet
|
|
for( int i = 0; collector.GetCount() == 2 && i < 2; ++i )
|
|
{
|
|
EDA_ITEM* item = collector[ i ];
|
|
EDA_ITEM* other = collector[ ( i + 1 ) % 2 ];
|
|
|
|
if( item->Type() != SCH_SHEET_T && other->Type() == SCH_SHEET_T )
|
|
collector.Remove( other );
|
|
}
|
|
|
|
// Prefer a symbol to a pin or the opposite, when both a symbol and a pin are selected
|
|
// We need to be able to select only a pin:
|
|
// - to display its characteristics (especially if an ERC is attached to the pin)
|
|
// - for cross probing, to select the corresponding pad.
|
|
// Note also the case happens only in schematic editor. In symbol editor, the symbol
|
|
// itself is never selected
|
|
for( int i = 0; collector.GetCount() == 2 && i < 2; ++i )
|
|
{
|
|
EDA_ITEM* item = collector[ i ];
|
|
EDA_ITEM* other = collector[ ( i + 1 ) % 2 ];
|
|
|
|
if( item->Type() == SCH_COMPONENT_T && other->Type() == SCH_PIN_T )
|
|
{
|
|
if( !m_isLibEdit && m_frame->eeconfig()->m_Selection.select_pin_selects_symbol )
|
|
collector.Remove( other );
|
|
else
|
|
collector.Remove( item );
|
|
}
|
|
}
|
|
|
|
// Prefer a field to a symbol
|
|
for( int i = 0; collector.GetCount() == 2 && i < 2; ++i )
|
|
{
|
|
EDA_ITEM* item = collector[ i ];
|
|
EDA_ITEM* other = collector[ ( i + 1 ) % 2 ];
|
|
|
|
if( item->Type() == SCH_FIELD_T && other->Type() == SCH_COMPONENT_T )
|
|
collector.Remove( other );
|
|
}
|
|
|
|
// No need for multiple wires at a single point; if there's a junction select that;
|
|
// otherwise any of the wires will do
|
|
bool junction = false;
|
|
bool wiresOnly = true;
|
|
|
|
for( EDA_ITEM* item : collector )
|
|
{
|
|
if( item->Type() == SCH_JUNCTION_T )
|
|
junction = true;
|
|
else if( item->Type() != SCH_LINE_T )
|
|
wiresOnly = false;
|
|
}
|
|
|
|
if( wiresOnly )
|
|
{
|
|
for( int j = collector.GetCount() - 1; j >= 0; --j )
|
|
{
|
|
if( junction && collector[ j ]->Type() != SCH_JUNCTION_T )
|
|
collector.Remove( j );
|
|
else if( !junction && j > 0 )
|
|
collector.Remove( j );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
EE_SELECTION& EE_SELECTION_TOOL::RequestSelection( const KICAD_T aFilterList[] )
|
|
{
|
|
if( m_selection.Empty() )
|
|
{
|
|
VECTOR2D cursorPos = getViewControls()->GetCursorPosition( true );
|
|
|
|
ClearSelection();
|
|
SelectPoint( cursorPos, aFilterList );
|
|
m_selection.SetIsHover( true );
|
|
m_selection.ClearReferencePoint();
|
|
}
|
|
else // Trim an existing selection by aFilterList
|
|
{
|
|
for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
|
|
{
|
|
EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
|
|
|
|
if( !item->IsType( aFilterList ) )
|
|
{
|
|
unselect( item );
|
|
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
|
|
}
|
|
}
|
|
}
|
|
|
|
updateReferencePoint();
|
|
|
|
return m_selection;
|
|
}
|
|
|
|
|
|
void EE_SELECTION_TOOL::updateReferencePoint()
|
|
{
|
|
VECTOR2I refP( 0, 0 );
|
|
|
|
if( m_selection.Size() > 0 )
|
|
{
|
|
if( m_isLibEdit )
|
|
refP = static_cast<LIB_ITEM*>( m_selection.GetTopLeftItem() )->GetPosition();
|
|
else
|
|
refP = static_cast<SCH_ITEM*>( m_selection.GetTopLeftItem() )->GetPosition();
|
|
}
|
|
|
|
m_selection.SetReferencePoint( refP );
|
|
}
|
|
|
|
|
|
bool EE_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();
|
|
|
|
KIGFX::PREVIEW::SELECTION_AREA area;
|
|
view->Add( &area );
|
|
|
|
while( TOOL_EVENT* evt = Wait() )
|
|
{
|
|
if( evt->IsCancelInteractive() || evt->IsActivate() )
|
|
{
|
|
cancelled = true;
|
|
break;
|
|
}
|
|
|
|
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 );
|
|
getViewControls()->SetAutoPan( true );
|
|
}
|
|
|
|
if( evt->IsMouseUp( BUT_LEFT ) )
|
|
{
|
|
getViewControls()->SetAutoPan( false );
|
|
|
|
// 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;
|
|
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> sheetPins;
|
|
|
|
// Filter the view items based on the selection box
|
|
BOX2I selectionBox = area.ViewBBox();
|
|
view->Query( selectionBox, selectedItems ); // Get the list of selected items
|
|
|
|
// Sheet pins aren't in the view; add them by hand
|
|
for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : selectedItems )
|
|
{
|
|
SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( pair.first );
|
|
|
|
if( sheet )
|
|
{
|
|
int layer = pair.second;
|
|
|
|
for( SCH_SHEET_PIN* pin : sheet->GetPins() )
|
|
sheetPins.emplace_back( KIGFX::VIEW::LAYER_ITEM_PAIR( pin, layer ) );
|
|
}
|
|
}
|
|
|
|
selectedItems.insert( selectedItems.end(), sheetPins.begin(), sheetPins.end() );
|
|
|
|
int width = area.GetEnd().x - area.GetOrigin().x;
|
|
int height = area.GetEnd().y - area.GetOrigin().y;
|
|
|
|
/* 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
|
|
*/
|
|
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 ) );
|
|
|
|
selectionRect.Normalize();
|
|
|
|
for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : selectedItems )
|
|
{
|
|
EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( pair.first );
|
|
|
|
if( item && Selectable( item ) && item->HitTest( selectionRect, windowSelection ) )
|
|
{
|
|
if( m_subtractive || ( m_exclusive_or && item->IsSelected() ) )
|
|
{
|
|
unselect( item );
|
|
anySubtracted = true;
|
|
}
|
|
else
|
|
{
|
|
select( item );
|
|
item->SetFlags( STARTPOINT | ENDPOINT );
|
|
anyAdded = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_selection.SetIsHover( false );
|
|
|
|
// Inform other potentially interested tools
|
|
if( anyAdded )
|
|
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
|
|
|
|
if( anySubtracted )
|
|
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
|
|
|
|
break; // Stop waiting for events
|
|
}
|
|
}
|
|
|
|
getViewControls()->SetAutoPan( false );
|
|
|
|
// Stop drawing the selection box
|
|
view->Remove( &area );
|
|
m_multiple = false; // Multiple selection mode is inactive
|
|
|
|
if( !cancelled )
|
|
m_selection.ClearReferencePoint();
|
|
|
|
return cancelled;
|
|
}
|
|
|
|
|
|
static KICAD_T nodeTypes[] =
|
|
{
|
|
SCH_COMPONENT_LOCATE_POWER_T,
|
|
SCH_PIN_T,
|
|
SCH_LINE_LOCATE_WIRE_T,
|
|
SCH_LINE_LOCATE_BUS_T,
|
|
SCH_BUS_WIRE_ENTRY_T,
|
|
SCH_BUS_BUS_ENTRY_T,
|
|
SCH_LABEL_T,
|
|
SCH_HIER_LABEL_T,
|
|
SCH_GLOBAL_LABEL_T,
|
|
SCH_SHEET_PIN_T,
|
|
SCH_JUNCTION_T,
|
|
EOT
|
|
};
|
|
|
|
|
|
EDA_ITEM* EE_SELECTION_TOOL::GetNode( VECTOR2I aPosition )
|
|
{
|
|
EE_COLLECTOR collector;
|
|
|
|
//TODO(snh): Reimplement after exposing KNN interface
|
|
int thresholdMax = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
|
|
|
|
for( int threshold : { 0, thresholdMax/2, thresholdMax } )
|
|
{
|
|
collector.m_Threshold = threshold;
|
|
collector.Collect( m_frame->GetScreen(), nodeTypes, (wxPoint) aPosition );
|
|
|
|
if( collector.GetCount() > 0 )
|
|
break;
|
|
}
|
|
|
|
return collector.GetCount() ? collector[ 0 ] : nullptr;
|
|
}
|
|
|
|
|
|
int EE_SELECTION_TOOL::SelectNode( const TOOL_EVENT& aEvent )
|
|
{
|
|
VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !aEvent.Modifier( MD_ALT ) );
|
|
|
|
SelectPoint( cursorPos, nodeTypes );
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int EE_SELECTION_TOOL::SelectConnection( const TOOL_EVENT& aEvent )
|
|
{
|
|
static KICAD_T wiresAndBuses[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT };
|
|
|
|
RequestSelection( wiresAndBuses );
|
|
|
|
if( m_selection.Empty() )
|
|
return 0;
|
|
|
|
SCH_LINE* line = (SCH_LINE*) m_selection.Front();
|
|
EDA_ITEMS items;
|
|
|
|
m_frame->GetScreen()->ClearDrawingState();
|
|
auto conns = m_frame->GetScreen()->MarkConnections( line );
|
|
|
|
for( auto item : conns )
|
|
select( item );
|
|
|
|
if( m_selection.GetSize() > 1 )
|
|
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int EE_SELECTION_TOOL::AddItemToSel( const TOOL_EVENT& aEvent )
|
|
{
|
|
AddItemToSel( aEvent.Parameter<EDA_ITEM*>() );
|
|
m_selection.SetIsHover( false );
|
|
return 0;
|
|
}
|
|
|
|
|
|
void EE_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 EE_SELECTION_TOOL::AddItemsToSel( const TOOL_EVENT& aEvent )
|
|
{
|
|
AddItemsToSel( aEvent.Parameter<EDA_ITEMS*>(), false );
|
|
m_selection.SetIsHover( false );
|
|
return 0;
|
|
}
|
|
|
|
|
|
void EE_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 EE_SELECTION_TOOL::RemoveItemFromSel( const TOOL_EVENT& aEvent )
|
|
{
|
|
RemoveItemFromSel( aEvent.Parameter<EDA_ITEM*>() );
|
|
m_selection.SetIsHover( false );
|
|
return 0;
|
|
}
|
|
|
|
|
|
void EE_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 EE_SELECTION_TOOL::RemoveItemsFromSel( const TOOL_EVENT& aEvent )
|
|
{
|
|
RemoveItemsFromSel( aEvent.Parameter<EDA_ITEMS*>(), false );
|
|
m_selection.SetIsHover( false );
|
|
return 0;
|
|
}
|
|
|
|
|
|
void EE_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 EE_SELECTION_TOOL::BrightenItem( EDA_ITEM* aItem )
|
|
{
|
|
highlight( aItem, BRIGHTENED );
|
|
}
|
|
|
|
|
|
void EE_SELECTION_TOOL::UnbrightenItem( EDA_ITEM* aItem )
|
|
{
|
|
unhighlight( aItem, BRIGHTENED );
|
|
}
|
|
|
|
|
|
int EE_SELECTION_TOOL::ClearSelection( const TOOL_EVENT& aEvent )
|
|
{
|
|
ClearSelection();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void EE_SELECTION_TOOL::RebuildSelection()
|
|
{
|
|
m_selection.Clear();
|
|
|
|
if( m_isLibEdit )
|
|
{
|
|
LIB_PART* start = static_cast<LIB_EDIT_FRAME*>( m_frame )->GetCurPart();
|
|
|
|
for( LIB_ITEM& item : start->GetDrawItems() )
|
|
{
|
|
if( item.IsSelected() )
|
|
select( static_cast<EDA_ITEM*>( &item ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( auto item : m_frame->GetScreen()->Items() )
|
|
{
|
|
// If the field and component are selected, only use the component
|
|
if( item->IsSelected() )
|
|
{
|
|
select( item );
|
|
}
|
|
else
|
|
{
|
|
if( item->Type() == SCH_COMPONENT_T )
|
|
{
|
|
for( SCH_FIELD& field : static_cast<SCH_COMPONENT*>( item )->GetFields() )
|
|
{
|
|
if( field.IsSelected() )
|
|
select( &field );
|
|
}
|
|
}
|
|
|
|
if( item->Type() == SCH_SHEET_T )
|
|
{
|
|
for( SCH_FIELD& field : static_cast<SCH_SHEET*>( item )->GetFields() )
|
|
{
|
|
if( field.IsSelected() )
|
|
select( &field );
|
|
}
|
|
|
|
for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
|
|
{
|
|
if( pin->IsSelected() )
|
|
select( pin );
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
updateReferencePoint();
|
|
|
|
// Inform other potentially interested tools
|
|
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
|
|
}
|
|
|
|
|
|
int EE_SELECTION_TOOL::SelectionMenu( const TOOL_EVENT& aEvent )
|
|
{
|
|
EE_COLLECTOR* collector = aEvent.Parameter<EE_COLLECTOR*>();
|
|
|
|
if( !doSelectionMenu( collector ) )
|
|
collector->m_MenuCancelled = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool EE_SELECTION_TOOL::doSelectionMenu( EE_COLLECTOR* aCollector )
|
|
{
|
|
EDA_ITEM* current = nullptr;
|
|
ACTION_MENU menu( true );
|
|
|
|
int limit = std::min( MAX_SELECT_ITEM_IDS, aCollector->GetCount() );
|
|
|
|
for( int i = 0; i < limit; ++i )
|
|
{
|
|
wxString text;
|
|
EDA_ITEM* item = ( *aCollector )[i];
|
|
text = item->GetSelectMenuText( m_frame->GetUserUnits() );
|
|
|
|
wxString menuText = wxString::Format("&%d. %s", i + 1, text );
|
|
menu.Add( menuText, i + 1, item->GetMenuImage() );
|
|
}
|
|
|
|
if( aCollector->m_MenuTitle.Length() )
|
|
menu.SetTitle( aCollector->m_MenuTitle );
|
|
|
|
menu.SetIcon( info_xpm );
|
|
menu.DisplayTitle( true );
|
|
SetContextMenu( &menu, CMENU_NOW );
|
|
|
|
while( TOOL_EVENT* evt = Wait() )
|
|
{
|
|
if( evt->Action() == TA_CHOICE_MENU_UPDATE )
|
|
{
|
|
if( current )
|
|
unhighlight( current, BRIGHTENED );
|
|
|
|
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 );
|
|
}
|
|
else
|
|
{
|
|
current = nullptr;
|
|
}
|
|
}
|
|
else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
|
|
{
|
|
if( current )
|
|
unhighlight( current, BRIGHTENED );
|
|
|
|
OPT<int> id = evt->GetCommandId();
|
|
|
|
// User has selected an item, so this one will be returned
|
|
if( id && ( *id > 0 ) )
|
|
current = ( *aCollector )[*id - 1];
|
|
else
|
|
current = nullptr;
|
|
|
|
break;
|
|
}
|
|
|
|
getView()->UpdateItems();
|
|
m_frame->GetCanvas()->Refresh();
|
|
}
|
|
|
|
if( current )
|
|
{
|
|
unhighlight( current, BRIGHTENED );
|
|
|
|
getView()->UpdateItems();
|
|
m_frame->GetCanvas()->Refresh();
|
|
|
|
aCollector->Empty();
|
|
aCollector->Append( current );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool EE_SELECTION_TOOL::Selectable( const EDA_ITEM* aItem, bool checkVisibilityOnly ) const
|
|
{
|
|
// NOTE: in the future this is where eeschema layer/itemtype visibility will be handled
|
|
LIB_EDIT_FRAME* symbeditFrame = dynamic_cast< LIB_EDIT_FRAME* >( m_frame );
|
|
|
|
switch( aItem->Type() )
|
|
{
|
|
case SCH_PIN_T:
|
|
if( !static_cast<const SCH_PIN*>( aItem )->IsVisible() && !m_frame->GetShowAllPins() )
|
|
return false;
|
|
break;
|
|
|
|
case LIB_PART_T: // In libedit we do not want to select the symbol itself.
|
|
return false;
|
|
|
|
case LIB_FIELD_T:
|
|
{
|
|
if( symbeditFrame )
|
|
{
|
|
LIB_PART* currentPart = symbeditFrame->GetCurPart();
|
|
|
|
// Nothing in derived symbols is editable at the moment.
|
|
if( currentPart && currentPart->IsAlias() )
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case LIB_ARC_T:
|
|
case LIB_CIRCLE_T:
|
|
case LIB_TEXT_T:
|
|
case LIB_RECTANGLE_T:
|
|
case LIB_POLYLINE_T:
|
|
case LIB_BEZIER_T:
|
|
case LIB_PIN_T:
|
|
{
|
|
if( symbeditFrame )
|
|
{
|
|
LIB_ITEM* lib_item = (LIB_ITEM*) aItem;
|
|
|
|
if( lib_item->GetUnit() && lib_item->GetUnit() != symbeditFrame->GetUnit() )
|
|
return false;
|
|
|
|
if( lib_item->GetConvert() && lib_item->GetConvert() != symbeditFrame->GetConvert() )
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SCH_MARKER_T: // Always selectable
|
|
return true;
|
|
|
|
default: // Suppress warnings
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void EE_SELECTION_TOOL::ClearSelection()
|
|
{
|
|
if( m_selection.Empty() )
|
|
return;
|
|
|
|
while( m_selection.GetSize() )
|
|
unhighlight( (EDA_ITEM*) m_selection.Front(), SELECTED, &m_selection );
|
|
|
|
getView()->Update( &m_selection );
|
|
|
|
m_selection.SetIsHover( false );
|
|
m_selection.ClearReferencePoint();
|
|
|
|
// Inform other potentially interested tools
|
|
m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
|
|
}
|
|
|
|
|
|
void EE_SELECTION_TOOL::select( EDA_ITEM* aItem )
|
|
{
|
|
highlight( aItem, SELECTED, &m_selection );
|
|
}
|
|
|
|
|
|
void EE_SELECTION_TOOL::unselect( EDA_ITEM* aItem )
|
|
{
|
|
unhighlight( aItem, SELECTED, &m_selection );
|
|
}
|
|
|
|
|
|
void EE_SELECTION_TOOL::highlight( EDA_ITEM* aItem, int aMode, EE_SELECTION* aGroup )
|
|
{
|
|
KICAD_T itemType = aItem->Type();
|
|
|
|
if( aMode == SELECTED )
|
|
aItem->SetSelected();
|
|
else if( aMode == BRIGHTENED )
|
|
aItem->SetBrightened();
|
|
|
|
if( aGroup )
|
|
aGroup->Add( aItem );
|
|
|
|
// Highlight pins and fields. (All the other component children are currently only
|
|
// represented in the LIB_PART and will inherit the settings of the parent component.)
|
|
if( itemType == SCH_COMPONENT_T )
|
|
{
|
|
SCH_PIN_PTRS pins = static_cast<SCH_COMPONENT*>( aItem )->GetSchPins( g_CurrentSheet );
|
|
|
|
for( SCH_PIN* pin : pins )
|
|
{
|
|
if( aMode == SELECTED )
|
|
pin->SetSelected();
|
|
else if( aMode == BRIGHTENED )
|
|
pin->SetBrightened();
|
|
}
|
|
|
|
for( SCH_FIELD& field : static_cast<SCH_COMPONENT*>( aItem )->GetFields() )
|
|
{
|
|
if( aMode == SELECTED )
|
|
field.SetSelected();
|
|
else if( aMode == BRIGHTENED )
|
|
field.SetBrightened();
|
|
}
|
|
}
|
|
else if( itemType == SCH_SHEET_T )
|
|
{
|
|
for( SCH_FIELD& field : static_cast<SCH_SHEET*>( aItem )->GetFields() )
|
|
{
|
|
if( aMode == SELECTED )
|
|
field.SetSelected();
|
|
else if( aMode == BRIGHTENED )
|
|
field.SetBrightened();
|
|
}
|
|
|
|
for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( aItem )->GetPins() )
|
|
{
|
|
if( aMode == SELECTED )
|
|
pin->SetSelected();
|
|
else if( aMode == BRIGHTENED )
|
|
pin->SetBrightened();
|
|
}
|
|
}
|
|
|
|
if( itemType == SCH_PIN_T || itemType == SCH_FIELD_T || itemType == SCH_SHEET_PIN_T )
|
|
getView()->Update( aItem->GetParent() );
|
|
else
|
|
getView()->Update( aItem );
|
|
}
|
|
|
|
|
|
void EE_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, EE_SELECTION* aGroup )
|
|
{
|
|
KICAD_T itemType = aItem->Type();
|
|
|
|
if( aMode == SELECTED )
|
|
aItem->ClearSelected();
|
|
else if( aMode == BRIGHTENED )
|
|
aItem->ClearBrightened();
|
|
|
|
if( aGroup )
|
|
aGroup->Remove( aItem );
|
|
|
|
// Unhighlight pins and fields. (All the other component children are currently only
|
|
// represented in the LIB_PART.)
|
|
if( itemType == SCH_COMPONENT_T )
|
|
{
|
|
SCH_PIN_PTRS pins = static_cast<SCH_COMPONENT*>( aItem )->GetSchPins( g_CurrentSheet );
|
|
|
|
for( SCH_PIN* pin : pins )
|
|
{
|
|
if( aMode == SELECTED )
|
|
pin->ClearSelected();
|
|
else if( aMode == BRIGHTENED )
|
|
pin->ClearBrightened();
|
|
}
|
|
|
|
for( SCH_FIELD& field : static_cast<SCH_COMPONENT*>( aItem )->GetFields() )
|
|
{
|
|
if( aMode == SELECTED )
|
|
field.ClearSelected();
|
|
else if( aMode == BRIGHTENED )
|
|
field.ClearBrightened();
|
|
}
|
|
}
|
|
else if( itemType == SCH_SHEET_T )
|
|
{
|
|
for( SCH_FIELD& field : static_cast<SCH_SHEET*>( aItem )->GetFields() )
|
|
{
|
|
if( aMode == SELECTED )
|
|
field.ClearSelected();
|
|
else if( aMode == BRIGHTENED )
|
|
field.ClearBrightened();
|
|
}
|
|
|
|
for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( aItem )->GetPins() )
|
|
{
|
|
if( aMode == SELECTED )
|
|
pin->ClearSelected();
|
|
else if( aMode == BRIGHTENED )
|
|
pin->ClearBrightened();
|
|
}
|
|
}
|
|
|
|
if( itemType == SCH_PIN_T || itemType == SCH_FIELD_T || itemType == SCH_SHEET_PIN_T )
|
|
getView()->Update( aItem->GetParent() );
|
|
else
|
|
getView()->Update( aItem );
|
|
}
|
|
|
|
|
|
bool EE_SELECTION_TOOL::selectionContains( const VECTOR2I& aPoint ) const
|
|
{
|
|
const unsigned GRIP_MARGIN = 20;
|
|
VECTOR2I margin = getView()->ToWorld( VECTOR2I( 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;
|
|
}
|
|
|
|
|
|
void EE_SELECTION_TOOL::setTransitions()
|
|
{
|
|
Go( &EE_SELECTION_TOOL::UpdateMenu, ACTIONS::updateMenu.MakeEvent() );
|
|
|
|
Go( &EE_SELECTION_TOOL::Main, EE_ACTIONS::selectionActivate.MakeEvent() );
|
|
Go( &EE_SELECTION_TOOL::SelectNode, EE_ACTIONS::selectNode.MakeEvent() );
|
|
Go( &EE_SELECTION_TOOL::SelectConnection, EE_ACTIONS::selectConnection.MakeEvent() );
|
|
Go( &EE_SELECTION_TOOL::ClearSelection, EE_ACTIONS::clearSelection.MakeEvent() );
|
|
|
|
Go( &EE_SELECTION_TOOL::AddItemToSel, EE_ACTIONS::addItemToSel.MakeEvent() );
|
|
Go( &EE_SELECTION_TOOL::AddItemsToSel, EE_ACTIONS::addItemsToSel.MakeEvent() );
|
|
Go( &EE_SELECTION_TOOL::RemoveItemFromSel, EE_ACTIONS::removeItemFromSel.MakeEvent() );
|
|
Go( &EE_SELECTION_TOOL::RemoveItemsFromSel, EE_ACTIONS::removeItemsFromSel.MakeEvent() );
|
|
Go( &EE_SELECTION_TOOL::SelectionMenu, EE_ACTIONS::selectionMenu.MakeEvent() );
|
|
}
|
|
|
|
|