2013-08-02 14:53:50 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
2017-01-17 16:46:33 +00:00
|
|
|
* Copyright (C) 2013-2017 CERN
|
2013-08-02 14:53:50 +00:00
|
|
|
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
2013-09-19 15:02:57 +00:00
|
|
|
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
2013-08-02 14:53:50 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
2015-02-18 00:10:20 +00:00
|
|
|
#include <limits>
|
2013-08-02 14:53:50 +00:00
|
|
|
|
2016-06-29 10:23:11 +00:00
|
|
|
#include <functional>
|
|
|
|
using namespace std::placeholders;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2013-08-02 14:53:50 +00:00
|
|
|
#include <class_board.h>
|
|
|
|
#include <class_board_item.h>
|
2013-09-04 08:56:06 +00:00
|
|
|
#include <class_track.h>
|
2013-08-02 14:53:50 +00:00
|
|
|
#include <class_module.h>
|
2015-02-18 00:10:20 +00:00
|
|
|
#include <class_pcb_text.h>
|
|
|
|
#include <class_drawsegment.h>
|
2013-08-02 14:53:50 +00:00
|
|
|
|
|
|
|
#include <wxPcbStruct.h>
|
|
|
|
#include <collectors.h>
|
2014-07-09 14:50:31 +00:00
|
|
|
#include <confirm.h>
|
2014-10-13 13:25:16 +00:00
|
|
|
#include <dialog_find.h>
|
2014-07-09 14:50:31 +00:00
|
|
|
|
|
|
|
#include <class_draw_panel_gal.h>
|
2013-09-02 14:29:10 +00:00
|
|
|
#include <view/view_controls.h>
|
2013-10-02 08:21:05 +00:00
|
|
|
#include <view/view_group.h>
|
2013-09-17 09:32:47 +00:00
|
|
|
#include <painter.h>
|
2013-08-02 14:53:50 +00:00
|
|
|
|
2013-09-16 07:52:47 +00:00
|
|
|
#include <tool/tool_event.h>
|
|
|
|
#include <tool/tool_manager.h>
|
2015-03-10 08:36:04 +00:00
|
|
|
#include <ratsnest_data.h>
|
2013-08-02 14:53:50 +00:00
|
|
|
|
|
|
|
#include "selection_tool.h"
|
|
|
|
#include "selection_area.h"
|
2013-09-16 09:00:59 +00:00
|
|
|
#include "bright_box.h"
|
2013-09-27 18:52:34 +00:00
|
|
|
#include "common_actions.h"
|
2013-08-02 14:53:50 +00:00
|
|
|
|
2015-03-10 08:36:04 +00:00
|
|
|
class SELECT_MENU: public CONTEXT_MENU
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
SELECT_MENU()
|
|
|
|
{
|
2017-01-23 13:47:49 +00:00
|
|
|
SetTitle( _( "Select..." ) );
|
2015-03-10 08:36:04 +00:00
|
|
|
Add( COMMON_ACTIONS::selectConnection );
|
2015-05-18 11:48:10 +00:00
|
|
|
Add( COMMON_ACTIONS::selectCopper );
|
2015-03-10 08:36:04 +00:00
|
|
|
Add( COMMON_ACTIONS::selectNet );
|
2017-02-08 16:32:41 +00:00
|
|
|
Add( COMMON_ACTIONS::selectSameSheet );
|
2015-03-10 08:36:04 +00:00
|
|
|
}
|
2017-01-23 13:47:31 +00:00
|
|
|
|
|
|
|
private:
|
2017-02-08 16:32:41 +00:00
|
|
|
|
|
|
|
void update() override
|
|
|
|
{
|
|
|
|
SELECTION_TOOL* selTool = getToolManager()->GetTool<SELECTION_TOOL>();
|
|
|
|
|
|
|
|
// lines like this make me really think about a better name for SELECTION_CONDITIONS class
|
|
|
|
bool selEnabled = ( SELECTION_CONDITIONS::OnlyType( PCB_VIA_T )
|
|
|
|
|| SELECTION_CONDITIONS::OnlyType( PCB_TRACE_T ) )
|
|
|
|
( selTool->GetSelection() );
|
|
|
|
|
|
|
|
bool sheetSelEnabled = ( SELECTION_CONDITIONS::OnlyType( PCB_MODULE_T ) )
|
|
|
|
( selTool->GetSelection() );
|
|
|
|
|
|
|
|
Enable( getMenuId( COMMON_ACTIONS::selectNet ), selEnabled );
|
|
|
|
Enable( getMenuId( COMMON_ACTIONS::selectCopper ), selEnabled );
|
|
|
|
Enable( getMenuId( COMMON_ACTIONS::selectConnection ), selEnabled );
|
|
|
|
Enable( getMenuId( COMMON_ACTIONS::selectSameSheet ), sheetSelEnabled );
|
|
|
|
}
|
2017-01-23 13:47:31 +00:00
|
|
|
CONTEXT_MENU* create() const override
|
|
|
|
{
|
|
|
|
return new SELECT_MENU();
|
|
|
|
}
|
2015-03-10 08:36:04 +00:00
|
|
|
};
|
|
|
|
|
2015-03-10 16:05:38 +00:00
|
|
|
|
2013-08-02 14:53:50 +00:00
|
|
|
SELECTION_TOOL::SELECTION_TOOL() :
|
2016-05-10 16:09:39 +00:00
|
|
|
PCB_TOOL( "pcbnew.InteractiveSelection" ),
|
|
|
|
m_frame( NULL ), m_additive( false ), m_multiple( false ),
|
2017-01-11 17:22:53 +00:00
|
|
|
m_locked( true ), m_menu( *this )
|
2015-04-30 08:46:05 +00:00
|
|
|
{
|
2015-05-03 06:31:14 +00:00
|
|
|
// Do not leave uninitialized members:
|
|
|
|
m_preliminary = false;
|
2015-04-30 08:46:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SELECTION_TOOL::~SELECTION_TOOL()
|
|
|
|
{
|
2017-01-18 21:04:35 +00:00
|
|
|
getView()->Remove( &m_selection );
|
2015-04-30 08:46:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool SELECTION_TOOL::Init()
|
2013-08-06 07:31:08 +00:00
|
|
|
{
|
2017-01-11 10:15:25 +00:00
|
|
|
using S_C = SELECTION_CONDITIONS;
|
|
|
|
|
2017-02-08 16:32:41 +00:00
|
|
|
auto showSelectMenuFunctor = ( S_C::OnlyType( PCB_VIA_T ) ||
|
|
|
|
S_C::OnlyType( PCB_TRACE_T ) ||
|
|
|
|
S_C::OnlyType( PCB_MODULE_T ) ) &&
|
|
|
|
S_C::Count( 1 );
|
2017-01-11 10:15:25 +00:00
|
|
|
|
2017-01-11 17:22:53 +00:00
|
|
|
auto selectMenu = std::make_shared<SELECT_MENU>();
|
|
|
|
selectMenu->SetTool( this );
|
|
|
|
m_menu.AddSubMenu( selectMenu );
|
2015-04-30 08:46:05 +00:00
|
|
|
|
2017-01-11 17:22:53 +00:00
|
|
|
auto& menu = m_menu.GetMenu();
|
2015-08-07 16:20:49 +00:00
|
|
|
|
2017-01-23 13:47:49 +00:00
|
|
|
menu.AddMenu( selectMenu.get(), false, showSelectMenuFunctor );
|
2017-01-11 17:22:53 +00:00
|
|
|
// only show separator if there is a Select menu to show above it
|
|
|
|
menu.AddSeparator( showSelectMenuFunctor, 1000 );
|
2015-08-07 16:20:49 +00:00
|
|
|
|
2017-01-11 17:22:53 +00:00
|
|
|
m_menu.AddStandardSubMenus( *getEditFrame<PCB_BASE_FRAME>() );
|
2015-04-30 08:46:05 +00:00
|
|
|
|
|
|
|
return true;
|
2013-08-02 14:53:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-12-09 09:42:38 +00:00
|
|
|
void SELECTION_TOOL::Reset( RESET_REASON aReason )
|
2013-08-02 14:53:50 +00:00
|
|
|
{
|
2016-01-20 14:22:09 +00:00
|
|
|
m_frame = getEditFrame<PCB_BASE_FRAME>();
|
|
|
|
m_locked = true;
|
|
|
|
m_preliminary = true;
|
|
|
|
|
2015-03-10 17:31:23 +00:00
|
|
|
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 board is loaded)
|
2016-11-04 21:29:47 +00:00
|
|
|
m_selection.Clear();
|
2015-06-04 12:54:08 +00:00
|
|
|
getView()->GetPainter()->GetSettings()->SetHighlight( false );
|
2015-03-10 17:31:23 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
// Restore previous properties of selected items and remove them from containers
|
|
|
|
clearSelection();
|
2013-12-03 16:11:22 +00:00
|
|
|
|
2013-10-02 09:21:17 +00:00
|
|
|
// Reinsert the VIEW_GROUP, in case it was removed from the VIEW
|
2017-01-18 21:04:35 +00:00
|
|
|
getView()->Remove( &m_selection );
|
|
|
|
getView()->Add( &m_selection );
|
2013-09-26 16:38:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-14 20:28:47 +00:00
|
|
|
int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
|
2013-08-02 14:53:50 +00:00
|
|
|
{
|
2013-08-06 07:31:08 +00:00
|
|
|
// Main loop: keep receiving events
|
|
|
|
while( OPT_TOOL_EVENT evt = Wait() )
|
|
|
|
{
|
2013-09-19 15:02:57 +00:00
|
|
|
// Should selected items be added to the current selection or
|
|
|
|
// become the new selection (discarding previously selected items)
|
2013-10-14 18:40:36 +00:00
|
|
|
m_additive = evt->Modifier( MD_SHIFT );
|
2013-08-09 08:18:48 +00:00
|
|
|
|
2013-08-06 07:31:08 +00:00
|
|
|
// single click? Select single object
|
2014-07-09 11:50:27 +00:00
|
|
|
if( evt->IsClick( BUT_LEFT ) )
|
2013-12-03 14:57:09 +00:00
|
|
|
{
|
2014-08-06 07:33:10 +00:00
|
|
|
if( evt->Modifier( MD_CTRL ) && !m_editModules )
|
2014-04-04 15:40:00 +00:00
|
|
|
{
|
2015-06-18 15:51:50 +00:00
|
|
|
m_toolMgr->RunAction( COMMON_ACTIONS::highlightNet, true );
|
2014-04-04 15:40:00 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( !m_additive )
|
2014-07-09 11:50:27 +00:00
|
|
|
clearSelection();
|
2013-12-03 14:57:09 +00:00
|
|
|
|
2015-07-09 15:11:34 +00:00
|
|
|
selectPoint( evt->Position() );
|
2014-04-04 15:40:00 +00:00
|
|
|
}
|
2013-12-03 14:57:09 +00:00
|
|
|
}
|
|
|
|
|
2014-02-07 19:44:34 +00:00
|
|
|
// right click? if there is any object - show the context menu
|
|
|
|
else if( evt->IsClick( BUT_RIGHT ) )
|
|
|
|
{
|
2015-04-30 08:46:07 +00:00
|
|
|
bool emptySelection = m_selection.Empty();
|
|
|
|
|
|
|
|
if( emptySelection )
|
2015-07-09 15:11:34 +00:00
|
|
|
selectPoint( evt->Position() );
|
2014-02-07 19:44:34 +00:00
|
|
|
|
2017-01-11 17:22:53 +00:00
|
|
|
m_menu.ShowContextMenu( m_selection );
|
2015-04-30 08:46:08 +00:00
|
|
|
|
2015-04-30 08:46:07 +00:00
|
|
|
m_preliminary = emptySelection;
|
2014-02-07 19:44:34 +00:00
|
|
|
}
|
|
|
|
|
2014-02-04 16:27:00 +00:00
|
|
|
// double click? Display the properties window
|
2013-12-03 14:57:09 +00:00
|
|
|
else if( evt->IsDblClick( BUT_LEFT ) )
|
|
|
|
{
|
|
|
|
if( m_selection.Empty() )
|
2015-07-09 15:11:34 +00:00
|
|
|
selectPoint( evt->Position() );
|
2013-12-03 14:57:09 +00:00
|
|
|
|
2014-02-28 14:46:05 +00:00
|
|
|
m_toolMgr->RunAction( COMMON_ACTIONS::properties );
|
2013-12-03 14:57:09 +00:00
|
|
|
}
|
2013-08-06 07:31:08 +00:00
|
|
|
|
2013-09-02 14:29:10 +00:00
|
|
|
// drag with LMB? Select multiple objects (or at least draw a selection box) or drag them
|
2013-12-03 14:57:09 +00:00
|
|
|
else if( evt->IsDrag( BUT_LEFT ) )
|
2013-09-02 14:29:10 +00:00
|
|
|
{
|
2014-05-14 07:56:35 +00:00
|
|
|
if( m_additive )
|
2013-09-02 14:29:10 +00:00
|
|
|
{
|
2015-04-30 08:46:07 +00:00
|
|
|
m_preliminary = false;
|
|
|
|
|
2014-05-14 07:56:35 +00:00
|
|
|
selectMultiple();
|
|
|
|
}
|
|
|
|
else if( m_selection.Empty() )
|
|
|
|
{
|
2015-04-30 08:46:07 +00:00
|
|
|
m_preliminary = false;
|
|
|
|
|
2014-05-14 07:56:35 +00:00
|
|
|
// There is nothing selected, so try to select something
|
2015-07-09 15:11:34 +00:00
|
|
|
if( !selectCursor() )
|
2014-03-18 15:25:46 +00:00
|
|
|
{
|
|
|
|
// If nothings has been selected or user wants to select more
|
|
|
|
// draw the selection box
|
|
|
|
selectMultiple();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_toolMgr->InvokeTool( "pcbnew.InteractiveEdit" );
|
|
|
|
}
|
2013-09-02 14:29:10 +00:00
|
|
|
}
|
2014-03-18 15:25:46 +00:00
|
|
|
|
2013-09-02 14:29:10 +00:00
|
|
|
else
|
|
|
|
{
|
2013-09-27 14:23:43 +00:00
|
|
|
// Check if dragging has started within any of selected items bounding box
|
2014-02-27 15:13:27 +00:00
|
|
|
if( selectionContains( evt->Position() ) )
|
2013-09-12 08:24:23 +00:00
|
|
|
{
|
2013-09-27 14:23:43 +00:00
|
|
|
// Yes -> run the move tool and wait till it finishes
|
2013-12-03 15:09:03 +00:00
|
|
|
m_toolMgr->InvokeTool( "pcbnew.InteractiveEdit" );
|
2013-09-19 15:02:57 +00:00
|
|
|
}
|
2013-09-12 08:24:23 +00:00
|
|
|
else
|
2013-09-19 15:02:57 +00:00
|
|
|
{
|
2013-09-27 14:23:43 +00:00
|
|
|
// No -> clear the selection list
|
2014-07-09 11:50:27 +00:00
|
|
|
clearSelection();
|
2013-09-19 15:02:57 +00:00
|
|
|
}
|
2013-09-02 14:29:10 +00:00
|
|
|
}
|
|
|
|
}
|
2014-07-09 11:50:27 +00:00
|
|
|
|
2017-01-18 08:04:11 +00:00
|
|
|
else if( evt->IsCancel() || evt->Action() == TA_UNDO_REDO_PRE )
|
2014-07-09 11:50:27 +00:00
|
|
|
{
|
|
|
|
clearSelection();
|
|
|
|
}
|
2015-03-10 08:36:04 +00:00
|
|
|
|
2015-04-30 08:46:07 +00:00
|
|
|
else if( evt->Action() == TA_CONTEXT_MENU_CLOSED )
|
|
|
|
{
|
|
|
|
if( m_preliminary )
|
|
|
|
clearSelection();
|
2015-07-24 07:42:46 +00:00
|
|
|
|
2017-01-11 17:22:53 +00:00
|
|
|
m_menu.CloseContextMenu( evt );
|
2015-04-30 08:46:07 +00:00
|
|
|
}
|
2013-08-06 07:31:08 +00:00
|
|
|
}
|
|
|
|
|
2013-12-03 16:11:22 +00:00
|
|
|
// This tool is supposed to be active forever
|
|
|
|
assert( false );
|
2013-10-02 08:21:05 +00:00
|
|
|
|
2013-08-06 07:31:08 +00:00
|
|
|
return 0;
|
2013-08-02 14:53:50 +00:00
|
|
|
}
|
|
|
|
|
2013-08-06 07:31:08 +00:00
|
|
|
|
2016-11-04 21:29:47 +00:00
|
|
|
SELECTION& SELECTION_TOOL::GetSelection()
|
2016-01-20 14:22:09 +00:00
|
|
|
{
|
|
|
|
// The selected items list has been requested, so it is no longer preliminary
|
|
|
|
m_preliminary = false;
|
|
|
|
|
2016-11-04 21:29:47 +00:00
|
|
|
auto items = m_selection.GetItems();
|
|
|
|
|
2016-01-20 14:22:09 +00:00
|
|
|
// Filter out not modifiable items
|
2016-12-09 11:04:32 +00:00
|
|
|
for( auto item : items )
|
2016-01-20 14:22:09 +00:00
|
|
|
{
|
|
|
|
if( !modifiable( item ) )
|
2016-02-08 14:12:59 +00:00
|
|
|
{
|
2016-11-04 21:29:47 +00:00
|
|
|
m_selection.Remove( item );
|
2016-02-08 14:12:59 +00:00
|
|
|
}
|
2016-01-20 14:22:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return m_selection;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-09 11:50:27 +00:00
|
|
|
void SELECTION_TOOL::toggleSelection( BOARD_ITEM* aItem )
|
|
|
|
{
|
|
|
|
if( aItem->IsSelected() )
|
|
|
|
{
|
2014-11-21 10:49:54 +00:00
|
|
|
unselect( aItem );
|
2014-07-09 11:50:27 +00:00
|
|
|
|
|
|
|
// Inform other potentially interested tools
|
2015-02-14 20:28:47 +00:00
|
|
|
m_toolMgr->ProcessEvent( UnselectedEvent );
|
2014-07-09 11:50:27 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( !m_additive )
|
|
|
|
clearSelection();
|
|
|
|
|
|
|
|
// Prevent selection of invisible or inactive items
|
|
|
|
if( selectable( aItem ) )
|
|
|
|
{
|
|
|
|
select( aItem );
|
|
|
|
|
|
|
|
// Inform other potentially interested tools
|
2015-02-14 20:28:47 +00:00
|
|
|
m_toolMgr->ProcessEvent( SelectedEvent );
|
2014-07-09 11:50:27 +00:00
|
|
|
}
|
|
|
|
}
|
2016-05-09 08:41:11 +00:00
|
|
|
|
|
|
|
m_frame->GetGalCanvas()->ForceRefresh();
|
2014-07-09 11:50:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-09 15:11:34 +00:00
|
|
|
bool SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag )
|
2013-08-02 14:53:50 +00:00
|
|
|
{
|
2013-08-06 08:30:09 +00:00
|
|
|
BOARD_ITEM* item;
|
2014-07-09 11:50:27 +00:00
|
|
|
GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
|
2013-08-02 14:53:50 +00:00
|
|
|
GENERAL_COLLECTOR collector;
|
2014-07-09 14:50:31 +00:00
|
|
|
|
2016-11-04 21:29:47 +00:00
|
|
|
collector.Collect( board(),
|
2016-06-08 09:24:46 +00:00
|
|
|
m_editModules ? GENERAL_COLLECTOR::ModuleItems : GENERAL_COLLECTOR::AllBoardItems,
|
|
|
|
wxPoint( aWhere.x, aWhere.y ), guide );
|
2013-08-06 07:31:08 +00:00
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
bool anyCollected = collector.GetCount() != 0;
|
|
|
|
|
|
|
|
// Remove unselectable items
|
|
|
|
for( int i = collector.GetCount() - 1; i >= 0; --i )
|
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
if( !selectable( collector[i] ) || ( aOnDrag && collector[i]->IsLocked() ) )
|
2015-02-18 00:10:20 +00:00
|
|
|
collector.Remove( i );
|
|
|
|
}
|
|
|
|
|
2013-08-06 08:30:09 +00:00
|
|
|
switch( collector.GetCount() )
|
2013-08-02 14:53:50 +00:00
|
|
|
{
|
2013-08-06 07:31:08 +00:00
|
|
|
case 0:
|
2015-02-18 00:10:20 +00:00
|
|
|
if( !m_additive && anyCollected )
|
2014-07-09 11:50:27 +00:00
|
|
|
clearSelection();
|
2014-03-18 15:25:46 +00:00
|
|
|
|
|
|
|
return false;
|
2013-08-06 07:31:08 +00:00
|
|
|
|
|
|
|
case 1:
|
2013-08-09 08:18:48 +00:00
|
|
|
toggleSelection( collector[0] );
|
2014-03-18 15:25:46 +00:00
|
|
|
|
|
|
|
return true;
|
2013-08-06 07:31:08 +00:00
|
|
|
|
2015-03-10 08:36:04 +00:00
|
|
|
default:
|
2015-02-18 00:10:20 +00:00
|
|
|
// Apply some ugly heuristics to avoid disambiguation menus whenever possible
|
|
|
|
guessSelectionCandidates( collector );
|
2014-05-13 09:22:51 +00:00
|
|
|
|
2013-09-12 08:46:22 +00:00
|
|
|
// Let's see if there is still disambiguation in selection..
|
|
|
|
if( collector.GetCount() == 1 )
|
|
|
|
{
|
|
|
|
toggleSelection( collector[0] );
|
2014-03-18 15:25:46 +00:00
|
|
|
|
|
|
|
return true;
|
2013-09-12 08:46:22 +00:00
|
|
|
}
|
2015-02-18 16:53:46 +00:00
|
|
|
else if( collector.GetCount() > 1 )
|
2013-09-12 08:46:22 +00:00
|
|
|
{
|
2015-02-18 00:10:20 +00:00
|
|
|
if( aOnDrag )
|
2015-04-30 08:46:07 +00:00
|
|
|
Wait( TOOL_EVENT( TC_ANY, TA_MOUSE_UP, BUT_LEFT ) );
|
2015-03-10 08:36:04 +00:00
|
|
|
|
2013-09-12 08:46:22 +00:00
|
|
|
item = disambiguationMenu( &collector );
|
2013-09-26 16:38:58 +00:00
|
|
|
|
2013-09-12 08:46:22 +00:00
|
|
|
if( item )
|
2014-03-18 15:25:46 +00:00
|
|
|
{
|
2013-09-12 08:46:22 +00:00
|
|
|
toggleSelection( item );
|
2013-10-14 14:13:35 +00:00
|
|
|
|
2014-03-18 15:25:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2013-08-06 07:31:08 +00:00
|
|
|
break;
|
2013-08-02 14:53:50 +00:00
|
|
|
}
|
2014-03-18 15:25:46 +00:00
|
|
|
|
|
|
|
return false;
|
2013-08-02 14:53:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-09 15:11:34 +00:00
|
|
|
bool SELECTION_TOOL::selectCursor( bool aSelectAlways )
|
|
|
|
{
|
|
|
|
if( aSelectAlways || m_selection.Empty() )
|
|
|
|
{
|
|
|
|
clearSelection();
|
2015-07-09 18:04:54 +00:00
|
|
|
selectPoint( getViewControls()->GetCursorPosition() );
|
2015-07-09 15:11:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return !m_selection.Empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-06 14:04:12 +00:00
|
|
|
bool SELECTION_TOOL::selectMultiple()
|
2013-08-02 14:53:50 +00:00
|
|
|
{
|
2013-09-27 14:23:43 +00:00
|
|
|
bool cancelled = false; // Was the tool cancelled while it was running?
|
2013-09-19 15:02:57 +00:00
|
|
|
m_multiple = true; // Multiple selection mode is active
|
2013-11-29 08:37:23 +00:00
|
|
|
KIGFX::VIEW* view = getView();
|
2013-09-19 15:02:57 +00:00
|
|
|
getViewControls()->SetAutoPan( true );
|
2013-08-06 07:31:08 +00:00
|
|
|
|
2015-04-30 08:46:08 +00:00
|
|
|
SELECTION_AREA area;
|
|
|
|
view->Add( &area );
|
2013-08-06 07:31:08 +00:00
|
|
|
|
2013-09-19 15:02:57 +00:00
|
|
|
while( OPT_TOOL_EVENT evt = Wait() )
|
2013-08-06 07:31:08 +00:00
|
|
|
{
|
|
|
|
if( evt->IsCancel() )
|
2013-09-06 14:04:12 +00:00
|
|
|
{
|
|
|
|
cancelled = true;
|
2013-08-06 07:31:08 +00:00
|
|
|
break;
|
2013-09-06 14:04:12 +00:00
|
|
|
}
|
2013-08-06 07:31:08 +00:00
|
|
|
|
2013-10-15 08:41:00 +00:00
|
|
|
if( evt->IsDrag( BUT_LEFT ) )
|
2013-08-06 07:31:08 +00:00
|
|
|
{
|
2013-08-09 08:18:48 +00:00
|
|
|
if( !m_additive )
|
2014-07-09 11:50:27 +00:00
|
|
|
clearSelection();
|
2013-08-09 08:18:48 +00:00
|
|
|
|
2013-08-08 17:42:19 +00:00
|
|
|
// Start drawing a selection box
|
2015-04-30 08:46:08 +00:00
|
|
|
area.SetOrigin( evt->DragOrigin() );
|
|
|
|
area.SetEnd( evt->Position() );
|
2016-12-02 17:58:12 +00:00
|
|
|
view->SetVisible( &area, true );
|
|
|
|
view->Update( &area );
|
2013-08-06 07:31:08 +00:00
|
|
|
}
|
|
|
|
|
2013-10-15 08:41:00 +00:00
|
|
|
if( evt->IsMouseUp( BUT_LEFT ) )
|
2013-08-06 07:31:08 +00:00
|
|
|
{
|
2015-02-18 00:10:20 +00:00
|
|
|
// End drawing the selection box
|
2016-12-02 17:58:12 +00:00
|
|
|
view->SetVisible( &area, false );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2013-09-19 15:02:57 +00:00
|
|
|
// Mark items within the selection box as selected
|
2013-11-29 08:37:23 +00:00
|
|
|
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
|
2015-04-30 08:46:08 +00:00
|
|
|
BOX2I selectionBox = area.ViewBBox();
|
2013-09-19 15:02:57 +00:00
|
|
|
view->Query( selectionBox, selectedItems ); // Get the list of selected items
|
2013-08-08 17:42:19 +00:00
|
|
|
|
2013-11-29 08:37:23 +00:00
|
|
|
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR>::iterator it, it_end;
|
2013-10-14 14:13:35 +00:00
|
|
|
|
2013-08-08 17:42:19 +00:00
|
|
|
for( it = selectedItems.begin(), it_end = selectedItems.end(); it != it_end; ++it )
|
|
|
|
{
|
|
|
|
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it->first );
|
|
|
|
|
2013-09-19 15:02:57 +00:00
|
|
|
// Add only those items that are visible and fully within the selection box
|
2014-03-06 09:43:40 +00:00
|
|
|
if( !item->IsSelected() && selectable( item ) &&
|
|
|
|
selectionBox.Contains( item->ViewBBox() ) )
|
|
|
|
{
|
2013-12-18 13:33:34 +00:00
|
|
|
select( item );
|
2014-03-06 09:43:40 +00:00
|
|
|
}
|
2013-08-08 17:42:19 +00:00
|
|
|
}
|
2013-09-26 16:38:58 +00:00
|
|
|
|
2015-07-03 18:58:12 +00:00
|
|
|
if( m_selection.Size() == 1 )
|
2016-11-04 21:29:47 +00:00
|
|
|
m_frame->SetCurItem( m_selection.Front() );
|
2015-07-03 18:58:12 +00:00
|
|
|
else
|
|
|
|
m_frame->SetCurItem( NULL );
|
2013-09-27 14:23:43 +00:00
|
|
|
|
2016-01-20 14:22:09 +00:00
|
|
|
// Inform other potentially interested tools
|
2014-02-27 16:27:58 +00:00
|
|
|
if( !m_selection.Empty() )
|
2015-02-14 20:28:47 +00:00
|
|
|
m_toolMgr->ProcessEvent( SelectedEvent );
|
2014-02-27 16:27:58 +00:00
|
|
|
|
2013-12-04 09:58:51 +00:00
|
|
|
break; // Stop waiting for events
|
2013-08-06 07:31:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-13 13:11:26 +00:00
|
|
|
// Stop drawing the selection box
|
2015-04-30 08:46:08 +00:00
|
|
|
view->Remove( &area );
|
2013-09-19 15:02:57 +00:00
|
|
|
m_multiple = false; // Multiple selection mode is inactive
|
|
|
|
getViewControls()->SetAutoPan( false );
|
2013-09-06 14:04:12 +00:00
|
|
|
|
|
|
|
return cancelled;
|
2013-08-02 14:53:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-30 08:46:03 +00:00
|
|
|
void SELECTION_TOOL::SetTransitions()
|
2014-07-09 11:50:27 +00:00
|
|
|
{
|
|
|
|
Go( &SELECTION_TOOL::Main, COMMON_ACTIONS::selectionActivate.MakeEvent() );
|
2014-11-21 10:49:28 +00:00
|
|
|
Go( &SELECTION_TOOL::CursorSelection, COMMON_ACTIONS::selectionCursor.MakeEvent() );
|
2014-07-09 11:50:27 +00:00
|
|
|
Go( &SELECTION_TOOL::ClearSelection, COMMON_ACTIONS::selectionClear.MakeEvent() );
|
2014-11-21 10:50:13 +00:00
|
|
|
Go( &SELECTION_TOOL::SelectItem, COMMON_ACTIONS::selectItem.MakeEvent() );
|
|
|
|
Go( &SELECTION_TOOL::UnselectItem, COMMON_ACTIONS::unselectItem.MakeEvent() );
|
2014-10-13 13:25:16 +00:00
|
|
|
Go( &SELECTION_TOOL::find, COMMON_ACTIONS::find.MakeEvent() );
|
2014-10-13 13:22:14 +00:00
|
|
|
Go( &SELECTION_TOOL::findMove, COMMON_ACTIONS::findMove.MakeEvent() );
|
2015-03-10 08:36:04 +00:00
|
|
|
Go( &SELECTION_TOOL::selectConnection, COMMON_ACTIONS::selectConnection.MakeEvent() );
|
2015-05-18 11:48:10 +00:00
|
|
|
Go( &SELECTION_TOOL::selectCopper, COMMON_ACTIONS::selectCopper.MakeEvent() );
|
2015-03-10 08:36:04 +00:00
|
|
|
Go( &SELECTION_TOOL::selectNet, COMMON_ACTIONS::selectNet.MakeEvent() );
|
2017-02-08 16:32:41 +00:00
|
|
|
Go( &SELECTION_TOOL::selectSameSheet, COMMON_ACTIONS::selectSameSheet.MakeEvent() );
|
2014-07-09 11:50:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
SELECTION_LOCK_FLAGS SELECTION_TOOL::CheckLock()
|
2014-07-09 14:50:31 +00:00
|
|
|
{
|
2014-07-15 15:33:19 +00:00
|
|
|
if( !m_locked || m_editModules )
|
2015-02-18 00:10:20 +00:00
|
|
|
return SELECTION_UNLOCKED;
|
2014-07-09 14:50:31 +00:00
|
|
|
|
|
|
|
bool containsLocked = false;
|
|
|
|
|
|
|
|
// Check if the selection contains locked items
|
2016-12-09 11:04:32 +00:00
|
|
|
for( const auto& item : m_selection )
|
2014-07-09 14:50:31 +00:00
|
|
|
{
|
|
|
|
switch( item->Type() )
|
|
|
|
{
|
|
|
|
case PCB_MODULE_T:
|
|
|
|
if( static_cast<MODULE*>( item )->IsLocked() )
|
|
|
|
containsLocked = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PCB_MODULE_EDGE_T:
|
|
|
|
case PCB_MODULE_TEXT_T:
|
|
|
|
if( static_cast<MODULE*>( item->GetParent() )->IsLocked() )
|
|
|
|
containsLocked = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: // suppress warnings
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
if( containsLocked )
|
2014-07-09 14:50:31 +00:00
|
|
|
{
|
2015-07-30 11:49:35 +00:00
|
|
|
if( IsOK( m_frame, _( "Selection contains locked items. Do you want to continue?" ) ) )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
|
|
|
m_locked = false;
|
|
|
|
return SELECTION_LOCK_OVERRIDE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return SELECTION_LOCKED;
|
2014-07-09 14:50:31 +00:00
|
|
|
}
|
2015-03-10 08:36:04 +00:00
|
|
|
|
2014-07-09 14:50:31 +00:00
|
|
|
m_locked = false;
|
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
return SELECTION_UNLOCKED;
|
2014-07-09 14:50:31 +00:00
|
|
|
}
|
|
|
|
|
2015-04-30 08:46:07 +00:00
|
|
|
|
2015-02-14 20:28:47 +00:00
|
|
|
int SELECTION_TOOL::CursorSelection( const TOOL_EVENT& aEvent )
|
2014-07-09 11:50:27 +00:00
|
|
|
{
|
2017-02-09 08:47:32 +00:00
|
|
|
bool sanitize = (bool) aEvent.Parameter<intptr_t>();
|
|
|
|
|
|
|
|
if( m_selection.Empty() ) // Try to find an item that could be modified
|
|
|
|
{
|
|
|
|
selectCursor( true );
|
|
|
|
|
|
|
|
if( CheckLock() == SELECTION_LOCKED )
|
|
|
|
{
|
|
|
|
clearSelection();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( sanitize )
|
|
|
|
SanitizeSelection();
|
2014-07-09 11:50:27 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-07-09 13:10:32 +00:00
|
|
|
|
2015-02-14 20:28:47 +00:00
|
|
|
int SELECTION_TOOL::ClearSelection( const TOOL_EVENT& aEvent )
|
2014-07-09 11:50:27 +00:00
|
|
|
{
|
|
|
|
clearSelection();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-30 08:46:07 +00:00
|
|
|
|
2015-02-14 20:28:47 +00:00
|
|
|
int SELECTION_TOOL::SelectItem( const TOOL_EVENT& aEvent )
|
2014-11-21 10:50:13 +00:00
|
|
|
{
|
|
|
|
// Check if there is an item to be selected
|
2015-04-30 08:46:01 +00:00
|
|
|
BOARD_ITEM* item = aEvent.Parameter<BOARD_ITEM*>();
|
2014-11-21 10:50:13 +00:00
|
|
|
|
|
|
|
if( item )
|
|
|
|
{
|
|
|
|
select( item );
|
|
|
|
|
|
|
|
// Inform other potentially interested tools
|
2015-02-14 20:28:47 +00:00
|
|
|
m_toolMgr->ProcessEvent( SelectedEvent );
|
2014-11-21 10:50:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-30 08:46:07 +00:00
|
|
|
|
2015-02-14 20:28:47 +00:00
|
|
|
int SELECTION_TOOL::UnselectItem( const TOOL_EVENT& aEvent )
|
2014-11-21 10:50:13 +00:00
|
|
|
{
|
|
|
|
// Check if there is an item to be selected
|
2015-04-30 08:46:01 +00:00
|
|
|
BOARD_ITEM* item = aEvent.Parameter<BOARD_ITEM*>();
|
2014-11-21 10:50:13 +00:00
|
|
|
|
|
|
|
if( item )
|
|
|
|
{
|
|
|
|
unselect( item );
|
|
|
|
|
|
|
|
// Inform other potentially interested tools
|
2015-02-14 20:28:47 +00:00
|
|
|
m_toolMgr->ProcessEvent( UnselectedEvent );
|
2014-11-21 10:50:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2014-07-09 13:10:32 +00:00
|
|
|
|
2015-03-10 08:36:04 +00:00
|
|
|
|
|
|
|
int SELECTION_TOOL::selectConnection( const TOOL_EVENT& aEvent )
|
2015-05-18 11:48:10 +00:00
|
|
|
{
|
2017-02-04 06:44:17 +00:00
|
|
|
if( !selectCursor() )
|
2015-07-09 15:11:34 +00:00
|
|
|
return 0;
|
|
|
|
|
2017-02-04 06:44:17 +00:00
|
|
|
// copy the selection, since we're going to iterate and modify
|
|
|
|
auto selection = m_selection.GetItems();
|
2015-05-18 11:48:10 +00:00
|
|
|
|
2017-02-04 06:44:17 +00:00
|
|
|
for( auto item : selection )
|
2015-05-18 11:48:10 +00:00
|
|
|
{
|
2017-02-04 06:44:17 +00:00
|
|
|
// only TRACK items can be checked for trivial connections
|
|
|
|
if( item->Type() == PCB_TRACE_T || item->Type() == PCB_VIA_T )
|
|
|
|
{
|
|
|
|
TRACK& trackItem = static_cast<TRACK&>( *item );
|
|
|
|
selectAllItemsConnectedToTrack( trackItem );
|
|
|
|
}
|
2015-05-18 11:48:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Inform other potentially interested tools
|
2017-02-04 06:44:17 +00:00
|
|
|
if( m_selection.Size() > 0 )
|
|
|
|
m_toolMgr->ProcessEvent( SelectedEvent );
|
2015-05-18 11:48:10 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int SELECTION_TOOL::selectCopper( const TOOL_EVENT& aEvent )
|
2015-03-10 08:36:04 +00:00
|
|
|
{
|
2017-02-04 06:44:17 +00:00
|
|
|
if( !selectCursor( ) )
|
2015-07-09 15:11:34 +00:00
|
|
|
return 0;
|
|
|
|
|
2017-02-04 06:44:17 +00:00
|
|
|
// copy the selection, since we're going to iterate and modify
|
|
|
|
auto selection = m_selection.GetItems();
|
2015-03-10 08:36:04 +00:00
|
|
|
|
2017-02-04 06:44:17 +00:00
|
|
|
for( auto item : selection )
|
|
|
|
{
|
|
|
|
// only connected items can be traversed in the ratsnest
|
|
|
|
if ( item->IsConnected() )
|
|
|
|
{
|
|
|
|
auto& connItem = static_cast<BOARD_CONNECTED_ITEM&>( *item );
|
2015-03-10 08:36:04 +00:00
|
|
|
|
2017-02-04 06:44:17 +00:00
|
|
|
selectAllItemsConnectedToItem( connItem );
|
|
|
|
}
|
|
|
|
}
|
2015-03-10 08:36:04 +00:00
|
|
|
|
|
|
|
// Inform other potentially interested tools
|
2017-02-04 06:44:17 +00:00
|
|
|
if( m_selection.Size() > 0 )
|
2016-01-20 14:22:09 +00:00
|
|
|
m_toolMgr->ProcessEvent( SelectedEvent );
|
2015-03-10 08:36:04 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-04 06:44:17 +00:00
|
|
|
void SELECTION_TOOL::selectAllItemsConnectedToTrack( TRACK& aSourceTrack )
|
2015-03-10 08:36:04 +00:00
|
|
|
{
|
2017-02-04 06:44:17 +00:00
|
|
|
int segmentCount;
|
|
|
|
TRACK* trackList = board()->MarkTrace( &aSourceTrack, &segmentCount,
|
|
|
|
nullptr, nullptr, true );
|
2015-07-09 15:11:34 +00:00
|
|
|
|
2017-02-04 06:44:17 +00:00
|
|
|
for( int i = 0; i < segmentCount; ++i )
|
|
|
|
{
|
|
|
|
select( trackList );
|
|
|
|
trackList = trackList->Next();
|
|
|
|
}
|
|
|
|
}
|
2016-11-04 21:29:47 +00:00
|
|
|
|
2015-07-09 15:11:34 +00:00
|
|
|
|
2017-02-04 06:44:17 +00:00
|
|
|
void SELECTION_TOOL::selectAllItemsConnectedToItem( BOARD_CONNECTED_ITEM& aSourceItem )
|
|
|
|
{
|
|
|
|
RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
|
2015-03-10 08:36:04 +00:00
|
|
|
std::list<BOARD_CONNECTED_ITEM*> itemsList;
|
2017-02-04 06:44:17 +00:00
|
|
|
ratsnest->GetConnectedItems( &aSourceItem, itemsList, (RN_ITEM_TYPE)( RN_TRACKS | RN_VIAS ) );
|
|
|
|
|
|
|
|
for( BOARD_CONNECTED_ITEM* i : itemsList )
|
|
|
|
select( i );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SELECTION_TOOL::selectAllItemsOnNet( int aNetCode )
|
|
|
|
{
|
2015-03-10 08:36:04 +00:00
|
|
|
RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
|
2017-02-04 06:44:17 +00:00
|
|
|
std::list<BOARD_CONNECTED_ITEM*> itemsList;
|
2015-03-10 08:36:04 +00:00
|
|
|
|
2017-02-04 06:44:17 +00:00
|
|
|
ratsnest->GetNetItems( aNetCode, itemsList, (RN_ITEM_TYPE)( RN_TRACKS | RN_VIAS ) );
|
2015-03-10 08:36:04 +00:00
|
|
|
|
2016-06-29 20:07:55 +00:00
|
|
|
for( BOARD_CONNECTED_ITEM* i : itemsList )
|
2015-03-10 08:36:04 +00:00
|
|
|
select( i );
|
2017-02-04 06:44:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int SELECTION_TOOL::selectNet( const TOOL_EVENT& aEvent )
|
|
|
|
{
|
|
|
|
if( !selectCursor() )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// copy the selection, since we're going to iterate and modify
|
|
|
|
auto selection = m_selection.GetItems();
|
|
|
|
|
|
|
|
for( auto item : selection )
|
|
|
|
{
|
|
|
|
// only connected items get a net code
|
|
|
|
if( item->IsConnected() )
|
|
|
|
{
|
|
|
|
auto& connItem = static_cast<BOARD_CONNECTED_ITEM&>( *item );
|
|
|
|
|
|
|
|
selectAllItemsOnNet( connItem.GetNetCode() );
|
|
|
|
}
|
|
|
|
}
|
2015-03-10 08:36:04 +00:00
|
|
|
|
|
|
|
// Inform other potentially interested tools
|
2017-02-08 16:32:41 +00:00
|
|
|
if( m_selection.Size() > 0 )
|
|
|
|
m_toolMgr->ProcessEvent( SelectedEvent );
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SELECTION_TOOL::selectSameSheet( const TOOL_EVENT& aEvent )
|
|
|
|
{
|
|
|
|
if( !selectCursor( true ) )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// this function currently only supports modules since they are only
|
|
|
|
// on one sheet.
|
|
|
|
auto item = m_selection.Front();
|
|
|
|
if( item->Type() != PCB_MODULE_T )
|
|
|
|
return 0;
|
|
|
|
if( !item )
|
|
|
|
return 0;
|
|
|
|
auto mod = dynamic_cast<MODULE*>( item );
|
|
|
|
|
|
|
|
clearSelection();
|
|
|
|
|
|
|
|
// get the lowest subsheet name for this.
|
|
|
|
wxString sheetPath = mod->GetPath();
|
|
|
|
sheetPath = sheetPath.BeforeLast( '/' );
|
|
|
|
sheetPath = sheetPath.AfterLast( '/' );
|
|
|
|
|
|
|
|
auto modules = board()->m_Modules.GetFirst();
|
|
|
|
std::list<MODULE*> modList;
|
|
|
|
|
|
|
|
// store all modules that are on that sheet
|
|
|
|
for( MODULE* item = modules; item; item = item->Next() )
|
|
|
|
{
|
|
|
|
if ( item != NULL && item->GetPath().Contains( sheetPath ) )
|
|
|
|
{
|
|
|
|
modList.push_back( item );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Generate a list of all pads, and of all nets they belong to.
|
|
|
|
std::list<int> netcodeList;
|
|
|
|
for( MODULE* mod : modList )
|
|
|
|
{
|
|
|
|
for( D_PAD* pad = mod->Pads().GetFirst(); pad; pad = pad->Next() )
|
|
|
|
{
|
|
|
|
if( pad->IsConnected() )
|
|
|
|
{
|
|
|
|
netcodeList.push_back( pad->GetNetCode() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove all duplicates
|
|
|
|
netcodeList.sort();
|
|
|
|
netcodeList.unique();
|
|
|
|
|
|
|
|
// now we need to find all modules that are connected to each of these nets
|
|
|
|
// then we need to determine if these modules are in the list of modules
|
|
|
|
// belonging to this sheet ( modList )
|
|
|
|
RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
|
|
|
|
std::list<int> removeCodeList;
|
|
|
|
for( int netCode : netcodeList )
|
|
|
|
{
|
|
|
|
std::list<BOARD_CONNECTED_ITEM*> netPads;
|
|
|
|
ratsnest->GetNetItems( netCode, netPads, (RN_ITEM_TYPE)( RN_PADS ) );
|
|
|
|
for( BOARD_CONNECTED_ITEM* item : netPads )
|
|
|
|
{
|
|
|
|
bool found = ( std::find( modList.begin(), modList.end(), item->GetParent() ) != modList.end() );
|
|
|
|
if( !found )
|
|
|
|
{
|
|
|
|
// if we cannot find the module of the pad in the modList
|
|
|
|
// then we can assume that that module is not located in the same
|
|
|
|
// schematic, therefore invalidate this netcode.
|
|
|
|
removeCodeList.push_back( netCode );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove all duplicates
|
|
|
|
removeCodeList.sort();
|
|
|
|
removeCodeList.unique();
|
|
|
|
|
|
|
|
for( int removeCode : removeCodeList )
|
|
|
|
{
|
|
|
|
netcodeList.remove( removeCode );
|
|
|
|
}
|
|
|
|
|
|
|
|
std::list<BOARD_CONNECTED_ITEM*> localConnectionList;
|
|
|
|
for( int netCode : netcodeList )
|
|
|
|
{
|
|
|
|
ratsnest->GetNetItems( netCode, localConnectionList, (RN_ITEM_TYPE)( RN_TRACKS | RN_VIAS ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
for( BOARD_ITEM* i : modList )
|
|
|
|
{
|
|
|
|
if( i != NULL )
|
|
|
|
select( i );
|
|
|
|
}
|
|
|
|
for( BOARD_CONNECTED_ITEM* i : localConnectionList )
|
|
|
|
{
|
|
|
|
if( i != NULL )
|
|
|
|
select( i );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Inform other potentially interested tools
|
2017-02-04 06:44:17 +00:00
|
|
|
if( m_selection.Size() > 0 )
|
2016-01-20 14:22:09 +00:00
|
|
|
m_toolMgr->ProcessEvent( SelectedEvent );
|
2015-03-10 08:36:04 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-10-13 13:25:16 +00:00
|
|
|
void SELECTION_TOOL::findCallback( BOARD_ITEM* aItem )
|
|
|
|
{
|
|
|
|
clearSelection();
|
|
|
|
|
|
|
|
if( aItem )
|
2015-01-29 17:23:54 +00:00
|
|
|
{
|
|
|
|
select( aItem );
|
2015-06-18 15:51:52 +00:00
|
|
|
EDA_RECT bbox = aItem->GetBoundingBox();
|
|
|
|
BOX2D viewport( VECTOR2D( bbox.GetOrigin() ), VECTOR2D( bbox.GetSize() ) );
|
|
|
|
getView()->SetViewport( viewport );
|
2015-01-29 17:23:54 +00:00
|
|
|
|
|
|
|
// Inform other potentially interested tools
|
2015-02-14 20:28:47 +00:00
|
|
|
m_toolMgr->ProcessEvent( SelectedEvent );
|
2015-01-29 17:23:54 +00:00
|
|
|
}
|
2014-10-13 13:25:16 +00:00
|
|
|
|
|
|
|
m_frame->GetGalCanvas()->ForceRefresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-14 20:28:47 +00:00
|
|
|
int SELECTION_TOOL::find( const TOOL_EVENT& aEvent )
|
2014-10-13 13:25:16 +00:00
|
|
|
{
|
|
|
|
DIALOG_FIND dlg( m_frame );
|
|
|
|
dlg.EnableWarp( false );
|
2016-06-29 10:23:11 +00:00
|
|
|
dlg.SetCallback( std::bind( &SELECTION_TOOL::findCallback, this, _1 ) );
|
2014-10-13 13:25:16 +00:00
|
|
|
dlg.ShowModal();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-14 20:28:47 +00:00
|
|
|
int SELECTION_TOOL::findMove( const TOOL_EVENT& aEvent )
|
2014-10-13 13:22:14 +00:00
|
|
|
{
|
2015-12-20 18:47:52 +00:00
|
|
|
MODULE* module = m_frame->GetFootprintFromBoardByReference();
|
2014-10-13 13:22:14 +00:00
|
|
|
|
|
|
|
if( module )
|
|
|
|
{
|
|
|
|
clearSelection();
|
|
|
|
toggleSelection( module );
|
2017-02-06 10:16:36 +00:00
|
|
|
|
|
|
|
// Place event on module origin first, so the generic anchor snap
|
|
|
|
// doesn't just choose the closest pin for us
|
|
|
|
// Don't warp the view - we want the component to
|
|
|
|
// "teleport" to cursor, not move to the components position
|
|
|
|
getViewControls()->ForceCursorPosition( true, module->GetPosition() );
|
|
|
|
|
|
|
|
// pick the component up and start moving
|
2014-10-13 13:22:14 +00:00
|
|
|
m_toolMgr->InvokeTool( "pcbnew.InteractiveEdit" );
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-09 11:50:27 +00:00
|
|
|
void SELECTION_TOOL::clearSelection()
|
|
|
|
{
|
2016-02-08 14:12:59 +00:00
|
|
|
if( m_selection.Empty() )
|
|
|
|
return;
|
|
|
|
|
2016-12-09 11:04:32 +00:00
|
|
|
for( auto item : m_selection )
|
2016-11-04 21:29:47 +00:00
|
|
|
unselectVisually( item );
|
2014-07-09 11:50:27 +00:00
|
|
|
|
2016-11-04 21:29:47 +00:00
|
|
|
m_selection.Clear();
|
2014-07-09 11:50:27 +00:00
|
|
|
|
2014-07-09 14:50:31 +00:00
|
|
|
m_frame->SetCurItem( NULL );
|
|
|
|
m_locked = true;
|
2014-07-09 11:50:27 +00:00
|
|
|
|
|
|
|
// Inform other potentially interested tools
|
2015-02-14 20:28:47 +00:00
|
|
|
m_toolMgr->ProcessEvent( ClearedEvent );
|
2014-07-09 11:50:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-22 13:05:37 +00:00
|
|
|
BOARD_ITEM* SELECTION_TOOL::disambiguationMenu( GENERAL_COLLECTOR* aCollector )
|
2013-08-02 14:53:50 +00:00
|
|
|
{
|
2013-08-08 17:41:20 +00:00
|
|
|
BOARD_ITEM* current = NULL;
|
2016-12-02 17:58:12 +00:00
|
|
|
BRIGHT_BOX brightBox;
|
2013-09-26 16:38:58 +00:00
|
|
|
CONTEXT_MENU menu;
|
2013-08-06 07:31:08 +00:00
|
|
|
|
2016-12-02 17:58:12 +00:00
|
|
|
getView()->Add( &brightBox );
|
|
|
|
|
2013-08-06 07:31:08 +00:00
|
|
|
int limit = std::min( 10, aCollector->GetCount() );
|
2013-10-14 14:13:35 +00:00
|
|
|
|
2013-08-06 07:31:08 +00:00
|
|
|
for( int i = 0; i < limit; ++i )
|
|
|
|
{
|
|
|
|
wxString text;
|
2013-09-12 08:46:22 +00:00
|
|
|
BOARD_ITEM* item = ( *aCollector )[i];
|
2013-09-17 11:47:33 +00:00
|
|
|
text = item->GetSelectMenuText();
|
2015-05-18 11:48:13 +00:00
|
|
|
menu.Add( text, i + 1 );
|
2013-08-06 07:31:08 +00:00
|
|
|
}
|
|
|
|
|
2013-09-26 16:38:58 +00:00
|
|
|
menu.SetTitle( _( "Clarify selection" ) );
|
2017-01-23 13:47:49 +00:00
|
|
|
menu.DisplayTitle( true );
|
2013-09-26 16:38:58 +00:00
|
|
|
SetContextMenu( &menu, CMENU_NOW );
|
2013-08-06 07:31:08 +00:00
|
|
|
|
2013-09-16 09:00:59 +00:00
|
|
|
while( OPT_TOOL_EVENT evt = Wait() )
|
2013-08-06 07:31:08 +00:00
|
|
|
{
|
2013-10-14 18:40:36 +00:00
|
|
|
if( evt->Action() == TA_CONTEXT_MENU_UPDATE )
|
2013-08-06 07:31:08 +00:00
|
|
|
{
|
|
|
|
if( current )
|
2013-08-08 10:30:00 +00:00
|
|
|
current->ClearBrightened();
|
2013-08-06 07:31:08 +00:00
|
|
|
|
|
|
|
int id = *evt->GetCommandId();
|
2013-08-08 10:30:00 +00:00
|
|
|
|
2013-09-19 15:02:57 +00:00
|
|
|
// User has pointed an item, so show it in a different way
|
2015-05-18 11:48:13 +00:00
|
|
|
if( id > 0 && id <= limit )
|
2013-08-06 07:31:08 +00:00
|
|
|
{
|
2015-05-18 11:48:13 +00:00
|
|
|
current = ( *aCollector )[id - 1];
|
2013-08-08 10:30:00 +00:00
|
|
|
current->SetBrightened();
|
|
|
|
}
|
|
|
|
else
|
2014-02-27 16:27:58 +00:00
|
|
|
{
|
2013-08-06 07:31:08 +00:00
|
|
|
current = NULL;
|
2014-02-27 16:27:58 +00:00
|
|
|
}
|
2013-08-08 10:30:00 +00:00
|
|
|
}
|
2013-10-14 18:40:36 +00:00
|
|
|
else if( evt->Action() == TA_CONTEXT_MENU_CHOICE )
|
2013-08-06 07:31:08 +00:00
|
|
|
{
|
2014-07-09 13:10:32 +00:00
|
|
|
boost::optional<int> id = evt->GetCommandId();
|
2013-08-06 07:31:08 +00:00
|
|
|
|
2013-09-27 14:23:43 +00:00
|
|
|
// User has selected an item, so this one will be returned
|
2015-05-18 11:48:13 +00:00
|
|
|
if( id && ( *id > 0 ) )
|
|
|
|
current = ( *aCollector )[*id - 1];
|
2015-10-07 13:32:36 +00:00
|
|
|
else
|
|
|
|
current = NULL;
|
2013-08-08 10:30:00 +00:00
|
|
|
|
2013-09-16 09:00:59 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-09-19 15:02:57 +00:00
|
|
|
// Draw a mark to show which item is available to be selected
|
2013-09-16 09:00:59 +00:00
|
|
|
if( current && current->IsBrightened() )
|
|
|
|
{
|
2016-12-09 11:04:32 +00:00
|
|
|
brightBox.SetItem( current );
|
2016-12-02 17:58:12 +00:00
|
|
|
getView()->SetVisible( &brightBox, true );
|
|
|
|
// getView()->Hide( &brightBox, false );
|
|
|
|
getView()->Update( &brightBox, KIGFX::GEOMETRY );
|
2016-12-05 22:54:41 +00:00
|
|
|
getView()->MarkTargetDirty( KIGFX::TARGET_OVERLAY );
|
2013-08-06 07:31:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-02 17:58:12 +00:00
|
|
|
getView()->Remove( &brightBox );
|
|
|
|
|
|
|
|
|
2013-09-16 09:00:59 +00:00
|
|
|
return current;
|
2013-08-06 07:31:08 +00:00
|
|
|
}
|
2013-09-04 08:56:06 +00:00
|
|
|
|
|
|
|
|
2014-02-04 15:03:56 +00:00
|
|
|
BOARD_ITEM* SELECTION_TOOL::pickSmallestComponent( GENERAL_COLLECTOR* aCollector )
|
|
|
|
{
|
|
|
|
int count = aCollector->GetPrimaryCount(); // try to use preferred layer
|
|
|
|
|
|
|
|
if( 0 == count )
|
|
|
|
count = aCollector->GetCount();
|
|
|
|
|
|
|
|
for( int i = 0; i < count; ++i )
|
|
|
|
{
|
|
|
|
if( ( *aCollector )[i]->Type() != PCB_MODULE_T )
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// All are modules, now find smallest MODULE
|
|
|
|
int minDim = 0x7FFFFFFF;
|
|
|
|
int minNdx = 0;
|
|
|
|
|
|
|
|
for( int i = 0; i < count; ++i )
|
|
|
|
{
|
|
|
|
MODULE* module = (MODULE*) ( *aCollector )[i];
|
|
|
|
|
|
|
|
int lx = module->GetBoundingBox().GetWidth();
|
|
|
|
int ly = module->GetBoundingBox().GetHeight();
|
|
|
|
|
|
|
|
int lmin = std::min( lx, ly );
|
|
|
|
|
|
|
|
if( lmin < minDim )
|
|
|
|
{
|
|
|
|
minDim = lmin;
|
|
|
|
minNdx = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (*aCollector)[minNdx];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-26 12:09:56 +00:00
|
|
|
bool SELECTION_TOOL::selectable( const BOARD_ITEM* aItem ) const
|
2013-09-04 08:56:06 +00:00
|
|
|
{
|
2013-09-27 14:23:43 +00:00
|
|
|
// Is high contrast mode enabled?
|
2013-09-17 09:32:47 +00:00
|
|
|
bool highContrast = getView()->GetPainter()->GetSettings()->GetHighContrast();
|
|
|
|
|
|
|
|
if( highContrast )
|
|
|
|
{
|
2013-09-27 14:23:43 +00:00
|
|
|
bool onActive = false; // Is the item on any of active layers?
|
2013-10-14 14:13:35 +00:00
|
|
|
int layers[KIGFX::VIEW::VIEW_MAX_LAYERS], layers_count;
|
2013-09-17 09:32:47 +00:00
|
|
|
|
|
|
|
// Filter out items that do not belong to active layers
|
2014-08-04 08:06:24 +00:00
|
|
|
const std::set<unsigned int>& activeLayers = getView()->GetPainter()->
|
|
|
|
GetSettings()->GetActiveLayers();
|
2013-09-17 09:32:47 +00:00
|
|
|
aItem->ViewGetLayers( layers, layers_count );
|
|
|
|
|
|
|
|
for( int i = 0; i < layers_count; ++i )
|
|
|
|
{
|
2014-08-04 08:06:24 +00:00
|
|
|
if( activeLayers.count( layers[i] ) > 0 ) // Item is on at least one of the active layers
|
2013-09-17 09:32:47 +00:00
|
|
|
{
|
|
|
|
onActive = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-14 14:13:35 +00:00
|
|
|
if( !onActive ) // We do not want to select items that are in the background
|
2013-09-17 09:32:47 +00:00
|
|
|
return false;
|
|
|
|
}
|
2013-09-06 14:04:12 +00:00
|
|
|
|
2013-09-04 08:56:06 +00:00
|
|
|
switch( aItem->Type() )
|
|
|
|
{
|
|
|
|
case PCB_VIA_T:
|
2014-06-24 16:17:18 +00:00
|
|
|
{
|
|
|
|
// For vias it is enough if only one of layers is visible
|
|
|
|
LAYER_ID top, bottom;
|
2013-09-04 08:56:06 +00:00
|
|
|
|
2014-06-24 16:17:18 +00:00
|
|
|
static_cast<const VIA*>( aItem )->LayerPair( &top, &bottom );
|
|
|
|
|
2016-11-04 21:29:47 +00:00
|
|
|
return board()->IsLayerVisible( top ) || board()->IsLayerVisible( bottom );
|
2014-06-24 16:17:18 +00:00
|
|
|
}
|
|
|
|
break;
|
2013-09-04 08:56:06 +00:00
|
|
|
|
2013-12-05 13:52:08 +00:00
|
|
|
case PCB_MODULE_T:
|
2016-11-04 21:29:47 +00:00
|
|
|
if( aItem->IsOnLayer( F_Cu ) && board()->IsElementVisible( MOD_FR_VISIBLE ) )
|
2014-07-09 10:10:27 +00:00
|
|
|
return !m_editModules;
|
2013-09-04 08:56:06 +00:00
|
|
|
|
2016-11-04 21:29:47 +00:00
|
|
|
if( aItem->IsOnLayer( B_Cu ) && board()->IsElementVisible( MOD_BK_VISIBLE ) )
|
2014-07-09 10:10:27 +00:00
|
|
|
return !m_editModules;
|
2013-09-04 08:56:06 +00:00
|
|
|
|
|
|
|
return false;
|
2013-12-05 13:52:08 +00:00
|
|
|
|
|
|
|
break;
|
2013-09-09 08:10:02 +00:00
|
|
|
|
2013-12-19 09:10:42 +00:00
|
|
|
case PCB_MODULE_TEXT_T:
|
2014-07-09 10:10:27 +00:00
|
|
|
if( m_multiple && !m_editModules )
|
2013-12-19 09:10:42 +00:00
|
|
|
return false;
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2016-12-02 17:58:12 +00:00
|
|
|
return view()->IsVisible( aItem ) && board()->IsLayerVisible( aItem->GetLayer() );
|
2013-12-19 09:10:42 +00:00
|
|
|
|
2013-09-04 08:56:06 +00:00
|
|
|
case PCB_MODULE_EDGE_T:
|
2013-12-18 15:26:21 +00:00
|
|
|
case PCB_PAD_T:
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2017-01-21 17:32:45 +00:00
|
|
|
// Multiple selection is only allowed in modedit mode
|
|
|
|
// In pcbnew, you have to select subparts of modules
|
|
|
|
// one-by-one, rather than with a drag selection.
|
|
|
|
// This is so you can pick up items under an (unlocked)
|
|
|
|
// module without also moving the module's sub-parts.
|
2015-02-18 00:10:20 +00:00
|
|
|
if( m_multiple && !m_editModules )
|
|
|
|
return false;
|
|
|
|
|
2017-01-21 17:32:45 +00:00
|
|
|
// When editing modules, it's allowed to select them, even when
|
|
|
|
// locked, since you already have to explicitly activate the
|
|
|
|
// module editor to get to this stage
|
|
|
|
if ( !m_editModules )
|
|
|
|
{
|
|
|
|
MODULE* mod = static_cast<const D_PAD*>( aItem )->GetParent();
|
|
|
|
if( mod && mod->IsLocked() )
|
|
|
|
return false;
|
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2014-07-09 10:10:27 +00:00
|
|
|
|
2016-01-20 14:22:09 +00:00
|
|
|
// These are not selectable
|
2013-10-02 10:02:25 +00:00
|
|
|
case NOT_USED:
|
|
|
|
case TYPE_NOT_INIT:
|
2013-09-04 08:56:06 +00:00
|
|
|
return false;
|
2013-09-26 12:09:56 +00:00
|
|
|
|
|
|
|
default: // Suppress warnings
|
|
|
|
break;
|
2013-09-04 08:56:06 +00:00
|
|
|
}
|
|
|
|
|
2013-09-06 14:04:12 +00:00
|
|
|
// All other items are selected only if the layer on which they exist is visible
|
2016-11-04 21:29:47 +00:00
|
|
|
return board()->IsLayerVisible( aItem->GetLayer() );
|
2013-09-06 14:04:12 +00:00
|
|
|
}
|
2013-09-19 15:02:57 +00:00
|
|
|
|
|
|
|
|
2016-01-20 14:22:09 +00:00
|
|
|
bool SELECTION_TOOL::modifiable( const BOARD_ITEM* aItem ) const
|
|
|
|
{
|
|
|
|
if( aItem->Type() == PCB_MARKER_T )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-12-18 13:33:34 +00:00
|
|
|
void SELECTION_TOOL::select( BOARD_ITEM* aItem )
|
2013-10-02 08:21:05 +00:00
|
|
|
{
|
2016-01-20 14:22:09 +00:00
|
|
|
if( aItem->IsSelected() )
|
2013-10-02 08:21:05 +00:00
|
|
|
{
|
2016-11-04 21:29:47 +00:00
|
|
|
return;
|
2013-10-02 08:21:05 +00:00
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
if( aItem->Type() == PCB_PAD_T )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
|
|
|
MODULE* module = static_cast<MODULE*>( aItem->GetParent() );
|
2015-03-10 08:36:04 +00:00
|
|
|
|
2016-11-04 21:29:47 +00:00
|
|
|
if( m_selection.Contains( module ) )
|
2015-02-18 00:10:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-12-18 13:33:34 +00:00
|
|
|
selectVisually( aItem );
|
2016-12-09 11:04:32 +00:00
|
|
|
m_selection.Add( aItem );
|
2013-12-04 09:58:51 +00:00
|
|
|
|
2013-12-18 13:33:34 +00:00
|
|
|
if( m_selection.Size() == 1 )
|
2013-12-04 09:58:51 +00:00
|
|
|
{
|
|
|
|
// Set as the current item, so the information about selection is displayed
|
2014-07-09 11:50:27 +00:00
|
|
|
m_frame->SetCurItem( aItem, true );
|
2013-12-04 09:58:51 +00:00
|
|
|
}
|
2013-12-18 14:09:09 +00:00
|
|
|
else if( m_selection.Size() == 2 ) // Check only for 2, so it will not be
|
|
|
|
{ // called for every next selected item
|
2013-12-18 13:33:34 +00:00
|
|
|
// If multiple items are selected, do not show the information about the selected item
|
2014-07-09 11:50:27 +00:00
|
|
|
m_frame->SetCurItem( NULL, true );
|
2013-12-18 13:33:34 +00:00
|
|
|
}
|
2013-10-02 08:21:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-21 10:49:54 +00:00
|
|
|
void SELECTION_TOOL::unselect( BOARD_ITEM* aItem )
|
2013-10-02 08:21:05 +00:00
|
|
|
{
|
2016-01-20 14:22:09 +00:00
|
|
|
if( !aItem->IsSelected() )
|
|
|
|
return;
|
|
|
|
|
2014-11-21 10:49:54 +00:00
|
|
|
unselectVisually( aItem );
|
2016-11-04 21:29:47 +00:00
|
|
|
m_selection.Remove( aItem );
|
2013-12-04 09:58:51 +00:00
|
|
|
|
|
|
|
if( m_selection.Empty() )
|
2014-07-09 14:50:31 +00:00
|
|
|
{
|
2014-07-09 11:50:27 +00:00
|
|
|
m_frame->SetCurItem( NULL );
|
2014-07-09 14:50:31 +00:00
|
|
|
m_locked = true;
|
|
|
|
}
|
2013-10-02 08:21:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-12-18 13:33:34 +00:00
|
|
|
void SELECTION_TOOL::selectVisually( BOARD_ITEM* aItem ) const
|
|
|
|
{
|
|
|
|
// Hide the original item, so it is shown only on overlay
|
|
|
|
aItem->SetSelected();
|
2016-12-02 17:58:12 +00:00
|
|
|
view()->Hide( aItem, true );
|
|
|
|
view()->Update( aItem, KIGFX::GEOMETRY );
|
2016-11-04 21:29:47 +00:00
|
|
|
|
|
|
|
// Modules are treated in a special way - when they are selected, we have to
|
|
|
|
// unselect all the parts that make the module, not the module itself
|
|
|
|
|
|
|
|
if( aItem->Type() == PCB_MODULE_T )
|
|
|
|
{
|
2016-12-09 11:04:32 +00:00
|
|
|
static_cast<MODULE*>( aItem )->RunOnChildren( [&] ( BOARD_ITEM* item )
|
|
|
|
{
|
2016-11-04 21:29:47 +00:00
|
|
|
item->SetSelected();
|
2016-12-02 17:58:12 +00:00
|
|
|
view()->Hide( item, true );
|
|
|
|
view()->Update( item, KIGFX::GEOMETRY );
|
2016-12-09 11:04:32 +00:00
|
|
|
} );
|
2016-11-04 21:29:47 +00:00
|
|
|
}
|
2013-12-18 13:33:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-21 10:49:54 +00:00
|
|
|
void SELECTION_TOOL::unselectVisually( BOARD_ITEM* aItem ) const
|
2013-12-18 13:33:34 +00:00
|
|
|
{
|
|
|
|
// Restore original item visibility
|
|
|
|
aItem->ClearSelected();
|
2016-12-02 17:58:12 +00:00
|
|
|
view()->Hide( aItem, false );
|
|
|
|
view()->Update( aItem, KIGFX::ALL );
|
|
|
|
|
2016-11-04 21:29:47 +00:00
|
|
|
// Modules are treated in a special way - when they are selected, we have to
|
|
|
|
// unselect all the parts that make the module, not the module itself
|
|
|
|
|
|
|
|
if( aItem->Type() == PCB_MODULE_T )
|
2016-12-09 11:04:32 +00:00
|
|
|
{
|
|
|
|
static_cast<MODULE*>( aItem )->RunOnChildren( [&] ( BOARD_ITEM* item )
|
2016-11-04 21:29:47 +00:00
|
|
|
{
|
2016-12-09 11:04:32 +00:00
|
|
|
item->ClearSelected();
|
|
|
|
view()->Hide( item, false );
|
|
|
|
view()->Update( item, KIGFX::ALL );
|
|
|
|
});
|
|
|
|
}
|
2013-12-18 13:33:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-27 15:13:27 +00:00
|
|
|
bool SELECTION_TOOL::selectionContains( const VECTOR2I& aPoint ) const
|
2013-09-19 15:02:57 +00:00
|
|
|
{
|
2014-02-05 09:17:14 +00:00
|
|
|
const unsigned GRIP_MARGIN = 20;
|
|
|
|
VECTOR2D margin = getView()->ToWorld( VECTOR2D( GRIP_MARGIN, GRIP_MARGIN ), false );
|
2013-09-27 16:51:21 +00:00
|
|
|
|
2013-09-19 15:02:57 +00:00
|
|
|
// Check if the point is located within any of the currently selected items bounding boxes
|
2016-11-04 21:29:47 +00:00
|
|
|
for( auto item : m_selection )
|
2013-09-19 15:02:57 +00:00
|
|
|
{
|
2013-12-18 13:33:34 +00:00
|
|
|
BOX2I itemBox = item->ViewBBox();
|
2014-02-05 09:17:14 +00:00
|
|
|
itemBox.Inflate( margin.x, margin.y ); // Give some margin for gripping an item
|
2013-09-19 15:02:57 +00:00
|
|
|
|
|
|
|
if( itemBox.Contains( aPoint ) )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2013-12-03 16:11:22 +00:00
|
|
|
|
|
|
|
|
2015-07-30 11:49:35 +00:00
|
|
|
static EDA_RECT getRect( const BOARD_ITEM* aItem )
|
2014-05-13 09:22:51 +00:00
|
|
|
{
|
2015-07-30 11:49:35 +00:00
|
|
|
if( aItem->Type() == PCB_MODULE_T )
|
|
|
|
return static_cast<const MODULE*>( aItem )->GetFootprintRect();
|
|
|
|
|
|
|
|
return aItem->GetBoundingBox();
|
|
|
|
}
|
2014-05-13 09:22:51 +00:00
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-07-30 11:49:35 +00:00
|
|
|
static double calcArea( const BOARD_ITEM* aItem )
|
|
|
|
{
|
|
|
|
if( aItem->Type() == PCB_TRACE_T )
|
|
|
|
{
|
|
|
|
const TRACK* t = static_cast<const TRACK*>( aItem );
|
|
|
|
return ( t->GetWidth() + t->GetLength() ) * t->GetWidth();
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
2015-07-30 11:49:35 +00:00
|
|
|
|
|
|
|
return getRect( aItem ).GetArea();
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
|
|
|
static double calcMinArea( GENERAL_COLLECTOR& aCollector, KICAD_T aType )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
|
|
|
double best = std::numeric_limits<double>::max();
|
2015-03-10 08:36:04 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
if( !aCollector.GetCount() )
|
2015-02-18 00:10:20 +00:00
|
|
|
return 0.0;
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
for( int i = 0; i < aCollector.GetCount(); i++ )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
BOARD_ITEM* item = aCollector[i];
|
|
|
|
if( item->Type() == aType )
|
|
|
|
best = std::min( best, calcArea( item ) );
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return best;
|
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
|
|
|
static double calcMaxArea( GENERAL_COLLECTOR& aCollector, KICAD_T aType )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
|
|
|
double best = 0.0;
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
for( int i = 0; i < aCollector.GetCount(); i++ )
|
2014-05-13 09:22:51 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
BOARD_ITEM* item = aCollector[i];
|
|
|
|
if( item->Type() == aType )
|
2015-07-30 11:49:35 +00:00
|
|
|
best = std::max( best, calcArea( item ) );
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return best;
|
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2015-07-30 11:49:35 +00:00
|
|
|
static inline double calcCommonArea( const BOARD_ITEM* aItem, const BOARD_ITEM* aOther )
|
|
|
|
{
|
|
|
|
return getRect( aItem ).Common( getRect( aOther ) ).GetArea();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
double calcRatio( double a, double b )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2015-07-30 11:49:35 +00:00
|
|
|
if( a == 0.0 && b == 0.0 )
|
2015-02-18 00:10:20 +00:00
|
|
|
return 1.0;
|
2016-09-15 11:24:58 +00:00
|
|
|
|
2015-07-30 11:49:35 +00:00
|
|
|
if( b == 0.0 )
|
2016-09-15 11:24:58 +00:00
|
|
|
return std::numeric_limits<double>::max();
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
|
|
return a / b;
|
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
// todo: explain the selection heuristics
|
|
|
|
void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) const
|
2014-05-13 09:22:51 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
std::set<BOARD_ITEM*> rejected;
|
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
const double footprintAreaRatio = 0.2;
|
|
|
|
const double modulePadMinCoverRatio = 0.45;
|
|
|
|
const double padViaAreaRatio = 0.5;
|
|
|
|
const double trackViaLengthRatio = 2.0;
|
|
|
|
const double trackTrackLengthRatio = 0.3;
|
|
|
|
const double textToFeatureMinRatio = 0.2;
|
|
|
|
const double textToFootprintMinRatio = 0.4;
|
2015-07-30 11:49:35 +00:00
|
|
|
// If the common area of two compared items is above the following threshold, they cannot
|
|
|
|
// be rejected (it means they overlap and it might be hard to pick one by selecting
|
|
|
|
// its unique area).
|
|
|
|
const double commonAreaRatio = 0.6;
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
LAYER_ID actLayer = m_frame->GetActiveLayer();
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
LSET silkLayers( 2, B_SilkS, F_SilkS );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
if( silkLayers[actLayer] )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
std::set<BOARD_ITEM*> preferred;
|
2014-05-13 09:22:51 +00:00
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
for( int i = 0; i < aCollector.GetCount(); ++i )
|
2014-05-13 09:22:51 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
BOARD_ITEM* item = aCollector[i];
|
2015-07-24 07:42:46 +00:00
|
|
|
KICAD_T type = item->Type();
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2015-07-24 07:42:46 +00:00
|
|
|
if( ( type == PCB_MODULE_TEXT_T || type == PCB_TEXT_T || type == PCB_LINE_T )
|
|
|
|
&& silkLayers[item->GetLayer()] )
|
|
|
|
{
|
|
|
|
preferred.insert( item );
|
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
2014-05-13 09:22:51 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
if( preferred.size() != 0 )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
|
|
|
aCollector.Empty();
|
|
|
|
|
2016-06-29 20:07:55 +00:00
|
|
|
for( BOARD_ITEM* item : preferred )
|
2015-02-18 00:10:20 +00:00
|
|
|
aCollector.Append( item );
|
|
|
|
return;
|
|
|
|
}
|
2015-03-10 08:36:04 +00:00
|
|
|
}
|
2015-02-18 16:53:46 +00:00
|
|
|
|
|
|
|
if( aCollector.CountType( PCB_MODULE_TEXT_T ) > 0 )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
|
|
|
for( int i = 0; i < aCollector.GetCount(); ++i )
|
2015-07-24 07:42:46 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
if( TEXTE_MODULE* txt = dyn_cast<TEXTE_MODULE*>( aCollector[i] ) )
|
2014-05-13 09:22:51 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
double textArea = calcArea( txt );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
|
|
for( int j = 0; j < aCollector.GetCount(); ++j )
|
2014-05-13 09:22:51 +00:00
|
|
|
{
|
2015-07-30 11:49:35 +00:00
|
|
|
if( i == j )
|
|
|
|
continue;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-07-30 11:49:35 +00:00
|
|
|
BOARD_ITEM* item = aCollector[j];
|
|
|
|
double itemArea = calcArea( item );
|
|
|
|
double areaRatio = calcRatio( textArea, itemArea );
|
|
|
|
double commonArea = calcCommonArea( txt, item );
|
|
|
|
double itemCommonRatio = calcRatio( commonArea, itemArea );
|
|
|
|
double txtCommonRatio = calcRatio( commonArea, textArea );
|
|
|
|
|
|
|
|
if( item->Type() == PCB_MODULE_T && areaRatio < textToFootprintMinRatio &&
|
|
|
|
itemCommonRatio < commonAreaRatio )
|
2015-02-18 16:53:46 +00:00
|
|
|
rejected.insert( item );
|
|
|
|
|
|
|
|
switch( item->Type() )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
|
|
|
case PCB_TRACE_T:
|
|
|
|
case PCB_PAD_T:
|
|
|
|
case PCB_LINE_T:
|
|
|
|
case PCB_VIA_T:
|
|
|
|
case PCB_MODULE_T:
|
2015-07-30 11:49:35 +00:00
|
|
|
if( areaRatio > textToFeatureMinRatio && txtCommonRatio < commonAreaRatio )
|
2015-02-18 16:53:46 +00:00
|
|
|
rejected.insert( txt );
|
2015-02-18 00:10:20 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2014-05-13 09:22:51 +00:00
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
2015-07-24 07:42:46 +00:00
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
2014-05-13 09:22:51 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
if( aCollector.CountType( PCB_MODULE_T ) > 0 )
|
|
|
|
{
|
|
|
|
double minArea = calcMinArea( aCollector, PCB_MODULE_T );
|
|
|
|
double maxArea = calcMaxArea( aCollector, PCB_MODULE_T );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
if( calcRatio( minArea, maxArea ) <= footprintAreaRatio )
|
2014-05-13 09:22:51 +00:00
|
|
|
{
|
2015-02-18 00:10:20 +00:00
|
|
|
for( int i = 0; i < aCollector.GetCount(); ++i )
|
2015-07-15 12:08:52 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
if( MODULE* mod = dyn_cast<MODULE*>( aCollector[i] ) )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2015-07-24 07:42:46 +00:00
|
|
|
double normalizedArea = calcRatio( calcArea( mod ), maxArea );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
if( normalizedArea > footprintAreaRatio )
|
2015-02-18 00:10:20 +00:00
|
|
|
rejected.insert( mod );
|
|
|
|
}
|
2015-07-15 12:08:52 +00:00
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
}
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2015-07-15 12:08:52 +00:00
|
|
|
if( aCollector.CountType( PCB_PAD_T ) > 0 )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
|
|
|
for( int i = 0; i < aCollector.GetCount(); ++i )
|
2015-02-18 16:53:46 +00:00
|
|
|
{
|
2015-07-24 07:42:46 +00:00
|
|
|
if( D_PAD* pad = dyn_cast<D_PAD*>( aCollector[i] ) )
|
2014-05-13 09:22:51 +00:00
|
|
|
{
|
2015-02-18 00:10:20 +00:00
|
|
|
double ratio = pad->GetParent()->PadCoverageRatio();
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
if( ratio < modulePadMinCoverRatio )
|
2015-02-18 00:10:20 +00:00
|
|
|
rejected.insert( pad->GetParent() );
|
|
|
|
}
|
2015-02-18 16:53:46 +00:00
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
if( aCollector.CountType( PCB_VIA_T ) > 0 )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
|
|
|
for( int i = 0; i < aCollector.GetCount(); ++i )
|
2015-02-18 16:53:46 +00:00
|
|
|
{
|
|
|
|
if( VIA* via = dyn_cast<VIA*>( aCollector[i] ) )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2015-03-10 08:36:04 +00:00
|
|
|
double viaArea = calcArea( via );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
|
|
for( int j = 0; j < aCollector.GetCount(); ++j )
|
2014-05-13 09:22:51 +00:00
|
|
|
{
|
2015-07-30 11:49:35 +00:00
|
|
|
if( i == j )
|
|
|
|
continue;
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
BOARD_ITEM* item = aCollector[j];
|
2015-07-24 07:42:46 +00:00
|
|
|
double areaRatio = calcRatio( viaArea, calcArea( item ) );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
|
|
if( item->Type() == PCB_MODULE_T && areaRatio < modulePadMinCoverRatio )
|
|
|
|
rejected.insert( item );
|
|
|
|
|
|
|
|
if( item->Type() == PCB_PAD_T && areaRatio < padViaAreaRatio )
|
|
|
|
rejected.insert( item );
|
|
|
|
|
2015-03-10 08:36:04 +00:00
|
|
|
if( TRACK* track = dyn_cast<TRACK*>( item ) )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
|
|
|
if( track->GetNetCode() != via->GetNetCode() )
|
|
|
|
continue;
|
|
|
|
|
2015-07-30 11:49:35 +00:00
|
|
|
double lenRatio = (double) ( track->GetLength() + track->GetWidth() ) /
|
|
|
|
(double) via->GetWidth();
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
|
|
if( lenRatio > trackViaLengthRatio )
|
|
|
|
rejected.insert( track );
|
|
|
|
}
|
2014-05-13 09:22:51 +00:00
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
2015-02-18 16:53:46 +00:00
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
2015-07-24 07:42:46 +00:00
|
|
|
int nTracks = aCollector.CountType( PCB_TRACE_T );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
|
|
if( nTracks > 0 )
|
|
|
|
{
|
|
|
|
double maxLength = 0.0;
|
|
|
|
double minLength = std::numeric_limits<double>::max();
|
|
|
|
double maxArea = 0.0;
|
2016-09-15 11:24:58 +00:00
|
|
|
const TRACK* maxTrack = nullptr;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
|
|
for( int i = 0; i < aCollector.GetCount(); ++i )
|
2015-07-30 11:49:35 +00:00
|
|
|
{
|
|
|
|
if( TRACK* track = dyn_cast<TRACK*> ( aCollector[i] ) )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
|
|
|
maxLength = std::max( track->GetLength(), maxLength );
|
2015-07-30 11:49:35 +00:00
|
|
|
maxLength = std::max( (double) track->GetWidth(), maxLength );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2016-09-15 11:24:58 +00:00
|
|
|
minLength = std::min( std::max( track->GetLength(), (double) track->GetWidth() ), minLength );
|
|
|
|
|
|
|
|
double area = track->GetLength() * track->GetWidth();
|
2015-03-10 08:36:04 +00:00
|
|
|
|
2016-09-15 11:24:58 +00:00
|
|
|
if( area > maxArea )
|
|
|
|
{
|
|
|
|
maxArea = area;
|
|
|
|
maxTrack = track;
|
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
2015-07-30 11:49:35 +00:00
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-07-30 11:49:35 +00:00
|
|
|
if( maxLength > 0.0 && minLength / maxLength < trackTrackLengthRatio && nTracks > 1 )
|
2015-02-18 16:53:46 +00:00
|
|
|
{
|
2015-02-18 00:10:20 +00:00
|
|
|
for( int i = 0; i < aCollector.GetCount(); ++i )
|
2015-02-18 16:53:46 +00:00
|
|
|
{
|
|
|
|
if( TRACK* track = dyn_cast<TRACK*>( aCollector[i] ) )
|
2014-05-13 09:22:51 +00:00
|
|
|
{
|
2015-07-30 11:49:35 +00:00
|
|
|
double ratio = std::max( (double) track->GetWidth(), track->GetLength() ) / maxLength;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
if( ratio > trackTrackLengthRatio )
|
2015-07-30 11:49:35 +00:00
|
|
|
rejected.insert( track );
|
2015-02-18 16:53:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
|
|
for( int j = 0; j < aCollector.GetCount(); ++j )
|
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
if( MODULE* mod = dyn_cast<MODULE*>( aCollector[j] ) )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2016-09-15 11:24:58 +00:00
|
|
|
double ratio = calcRatio( maxArea, mod->GetFootprintRect().GetArea() );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2016-09-15 11:24:58 +00:00
|
|
|
if( ratio < modulePadMinCoverRatio && calcCommonArea( maxTrack, mod ) < commonAreaRatio )
|
2015-02-18 00:10:20 +00:00
|
|
|
rejected.insert( mod );
|
2014-05-13 09:22:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-15 12:08:52 +00:00
|
|
|
if( (unsigned) aCollector.GetCount() > rejected.size() ) // do not remove everything
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2016-06-29 20:07:55 +00:00
|
|
|
for( BOARD_ITEM* item : rejected )
|
2015-07-15 12:08:52 +00:00
|
|
|
{
|
|
|
|
aCollector.Remove( item );
|
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
2014-05-13 09:22:51 +00:00
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
bool SELECTION_TOOL::SanitizeSelection()
|
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
std::set<BOARD_ITEM*> rejected;
|
2015-07-15 12:08:52 +00:00
|
|
|
std::set<BOARD_ITEM*> added;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
if( !m_editModules )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2016-11-04 21:29:47 +00:00
|
|
|
for( auto item : m_selection )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
|
|
|
if( item->Type() == PCB_PAD_T )
|
|
|
|
{
|
2015-07-15 12:08:52 +00:00
|
|
|
MODULE* mod = static_cast<MODULE*>( item->GetParent() );
|
2015-03-10 08:36:04 +00:00
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
// case 1: module (or its pads) are locked
|
2015-02-18 16:53:46 +00:00
|
|
|
if( mod && ( mod->PadsLocked() || mod->IsLocked() ) )
|
2015-07-15 12:08:52 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
rejected.insert( item );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-07-15 12:08:52 +00:00
|
|
|
if( !mod->IsLocked() && !mod->IsSelected() )
|
|
|
|
added.insert( mod );
|
|
|
|
}
|
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
// case 2: multi-item selection contains both the module and its pads - remove the pads
|
2016-11-04 21:29:47 +00:00
|
|
|
if( mod && m_selection.Contains( mod ) )
|
2015-02-18 16:53:46 +00:00
|
|
|
rejected.insert( item );
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-20 14:22:09 +00:00
|
|
|
if( !rejected.empty() )
|
|
|
|
{
|
2016-06-29 20:07:55 +00:00
|
|
|
for( BOARD_ITEM* item : rejected )
|
2016-01-20 14:22:09 +00:00
|
|
|
unselect( item );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2016-01-20 14:22:09 +00:00
|
|
|
// Inform other potentially interested tools
|
|
|
|
m_toolMgr->ProcessEvent( UnselectedEvent );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !added.empty() )
|
|
|
|
{
|
2016-06-29 20:07:55 +00:00
|
|
|
for( BOARD_ITEM* item : added )
|
2016-01-20 14:22:09 +00:00
|
|
|
select( item );
|
|
|
|
|
|
|
|
// Inform other potentially interested tools
|
|
|
|
m_toolMgr->ProcessEvent( UnselectedEvent );
|
|
|
|
}
|
2015-07-15 12:08:52 +00:00
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
return true;
|
2014-05-13 09:22:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-20 10:40:36 +00:00
|
|
|
VECTOR2I SELECTION::GetCenter() const
|
2015-02-12 03:22:24 +00:00
|
|
|
{
|
2015-02-20 10:40:36 +00:00
|
|
|
VECTOR2I centre;
|
2015-02-12 03:22:24 +00:00
|
|
|
|
|
|
|
if( Size() == 1 )
|
|
|
|
{
|
2016-11-04 21:29:47 +00:00
|
|
|
centre = Front()->GetCenter();
|
2015-02-12 03:22:24 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-12-09 11:04:32 +00:00
|
|
|
EDA_RECT bbox = Front()->GetBoundingBox();
|
2016-11-04 21:29:47 +00:00
|
|
|
auto i = m_items.begin();
|
|
|
|
++i;
|
|
|
|
|
|
|
|
for( ; i != m_items.end(); ++i )
|
2015-02-12 03:22:24 +00:00
|
|
|
{
|
2016-11-04 21:29:47 +00:00
|
|
|
bbox.Merge( (*i)->GetBoundingBox() );
|
2015-02-12 03:22:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
centre = bbox.Centre();
|
|
|
|
}
|
|
|
|
|
|
|
|
return centre;
|
|
|
|
}
|
2015-02-18 19:27:00 +00:00
|
|
|
|
|
|
|
|
2017-01-18 21:04:35 +00:00
|
|
|
const KIGFX::VIEW_GROUP::ITEMS SELECTION::updateDrawList() const
|
2016-11-04 21:29:47 +00:00
|
|
|
{
|
2017-01-18 21:04:35 +00:00
|
|
|
std::vector<VIEW_ITEM*> items;
|
2016-11-04 21:29:47 +00:00
|
|
|
|
2016-12-09 11:04:32 +00:00
|
|
|
for( auto item : m_items )
|
2016-11-04 21:29:47 +00:00
|
|
|
{
|
|
|
|
items.push_back( item );
|
2016-12-09 11:04:32 +00:00
|
|
|
|
2016-11-04 21:29:47 +00:00
|
|
|
if( item->Type() == PCB_MODULE_T )
|
|
|
|
{
|
|
|
|
MODULE* module = static_cast<MODULE*>( item );
|
2016-12-09 11:04:32 +00:00
|
|
|
module->RunOnChildren( [&] ( BOARD_ITEM* bitem ) { items.push_back( bitem ); } );
|
2016-11-04 21:29:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return items;
|
|
|
|
}
|
2016-12-09 11:04:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
const TOOL_EVENT SELECTION_TOOL::SelectedEvent( TC_MESSAGE, TA_ACTION, "pcbnew.InteractiveSelection.selected" );
|
|
|
|
const TOOL_EVENT SELECTION_TOOL::UnselectedEvent( TC_MESSAGE, TA_ACTION, "pcbnew.InteractiveSelection.unselected" );
|
|
|
|
const TOOL_EVENT SELECTION_TOOL::ClearedEvent( TC_MESSAGE, TA_ACTION, "pcbnew.InteractiveSelection.cleared" );
|