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>
|
2020-02-20 12:11:04 +00:00
|
|
|
|
* Copyright (C) 2018-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
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
|
|
|
|
|
*/
|
|
|
|
|
|
2019-07-09 19:50:40 +00:00
|
|
|
|
#include <limits>
|
2016-06-29 10:23:11 +00:00
|
|
|
|
#include <functional>
|
|
|
|
|
using namespace std::placeholders;
|
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>
|
2020-02-03 02:23:27 +00:00
|
|
|
|
#include <class_edge_mod.h>
|
2015-02-18 00:10:20 +00:00
|
|
|
|
#include <class_drawsegment.h>
|
2017-09-22 05:18:05 +00:00
|
|
|
|
#include <class_zone.h>
|
2013-08-02 14:53:50 +00:00
|
|
|
|
#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>
|
2020-01-12 20:30:58 +00:00
|
|
|
|
#include <dialog_filter_selection.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>
|
2017-03-10 13:34:06 +00:00
|
|
|
|
#include <preview_items/selection_area.h>
|
2013-09-17 09:32:47 +00:00
|
|
|
|
#include <painter.h>
|
2017-02-20 18:10:20 +00:00
|
|
|
|
#include <bitmaps.h>
|
2020-06-21 19:39:27 +00:00
|
|
|
|
#include <pcbnew_settings.h>
|
2013-09-16 07:52:47 +00:00
|
|
|
|
#include <tool/tool_event.h>
|
|
|
|
|
#include <tool/tool_manager.h>
|
2018-01-05 23:44:37 +00:00
|
|
|
|
#include <router/router_tool.h>
|
2018-10-12 06:17:15 +00:00
|
|
|
|
#include <connectivity/connectivity_data.h>
|
2018-06-11 10:37:05 +00:00
|
|
|
|
#include <footprint_viewer_frame.h>
|
2019-06-15 19:42:17 +00:00
|
|
|
|
#include <id.h>
|
2018-01-26 14:09:01 +00:00
|
|
|
|
#include "tool_event_utils.h"
|
2013-08-02 14:53:50 +00:00
|
|
|
|
#include "selection_tool.h"
|
2017-03-11 05:46:03 +00:00
|
|
|
|
#include "pcb_bright_box.h"
|
2017-02-21 12:42:08 +00:00
|
|
|
|
#include "pcb_actions.h"
|
2013-08-02 14:53:50 +00:00
|
|
|
|
|
2017-04-15 16:08:23 +00:00
|
|
|
|
#include "kicad_plugin.h"
|
|
|
|
|
|
2017-02-07 06:40:38 +00:00
|
|
|
|
|
2019-06-15 00:29:42 +00:00
|
|
|
|
class SELECT_MENU : public ACTION_MENU
|
2015-03-10 08:36:04 +00:00
|
|
|
|
{
|
|
|
|
|
public:
|
2019-06-15 00:29:42 +00:00
|
|
|
|
SELECT_MENU() :
|
|
|
|
|
ACTION_MENU( true )
|
2015-03-10 08:36:04 +00:00
|
|
|
|
{
|
2018-02-09 16:28:33 +00:00
|
|
|
|
SetTitle( _( "Select" ) );
|
2018-05-02 19:42:41 +00:00
|
|
|
|
SetIcon( options_generic_xpm );
|
2017-02-07 06:40:38 +00:00
|
|
|
|
|
|
|
|
|
Add( PCB_ACTIONS::filterSelection );
|
|
|
|
|
|
|
|
|
|
AppendSeparator();
|
|
|
|
|
|
2017-02-21 12:42:08 +00:00
|
|
|
|
Add( PCB_ACTIONS::selectConnection );
|
|
|
|
|
Add( PCB_ACTIONS::selectNet );
|
|
|
|
|
Add( PCB_ACTIONS::selectSameSheet );
|
2015-03-10 08:36:04 +00:00
|
|
|
|
}
|
2017-01-23 13:47:31 +00:00
|
|
|
|
|
|
|
|
|
private:
|
2019-05-14 11:14:00 +00:00
|
|
|
|
ACTION_MENU* create() const override
|
2017-01-23 13:47:31 +00:00
|
|
|
|
{
|
|
|
|
|
return new SELECT_MENU();
|
|
|
|
|
}
|
2015-03-10 08:36:04 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-03-10 16:05:38 +00:00
|
|
|
|
|
2017-02-07 06:40:38 +00:00
|
|
|
|
/**
|
|
|
|
|
* Private implementation of firewalled private data
|
|
|
|
|
*/
|
|
|
|
|
class SELECTION_TOOL::PRIV
|
|
|
|
|
{
|
|
|
|
|
public:
|
2020-01-12 20:30:58 +00:00
|
|
|
|
DIALOG_FILTER_SELECTION::OPTIONS m_filterOpts;
|
2017-02-07 06:40:38 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2013-08-02 14:53:50 +00:00
|
|
|
|
SELECTION_TOOL::SELECTION_TOOL() :
|
2019-05-12 11:49:58 +00:00
|
|
|
|
PCB_TOOL_BASE( "pcbnew.InteractiveSelection" ),
|
2018-01-05 23:44:37 +00:00
|
|
|
|
m_frame( NULL ),
|
|
|
|
|
m_additive( false ),
|
|
|
|
|
m_subtractive( false ),
|
2019-07-09 19:50:40 +00:00
|
|
|
|
m_exclusive_or( false ),
|
2017-05-07 00:09:03 +00:00
|
|
|
|
m_multiple( false ),
|
2018-09-16 20:32:36 +00:00
|
|
|
|
m_skip_heuristics( false ),
|
2018-01-05 23:44:37 +00:00
|
|
|
|
m_locked( true ),
|
2020-08-11 19:37:07 +00:00
|
|
|
|
m_enteredGroup( NULL ),
|
2017-02-07 06:40:38 +00:00
|
|
|
|
m_priv( std::make_unique<PRIV>() )
|
2015-04-30 08:46:05 +00:00
|
|
|
|
{
|
2020-06-27 22:48:34 +00:00
|
|
|
|
m_filter.lockedItems = true;
|
|
|
|
|
m_filter.footprints = true;
|
|
|
|
|
m_filter.text = true;
|
|
|
|
|
m_filter.tracks = true;
|
|
|
|
|
m_filter.vias = true;
|
|
|
|
|
m_filter.pads = true;
|
|
|
|
|
m_filter.graphics = true;
|
|
|
|
|
m_filter.zones = true;
|
|
|
|
|
m_filter.keepouts = true;
|
|
|
|
|
m_filter.dimensions = true;
|
|
|
|
|
m_filter.otherItems = true;
|
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
|
|
|
|
{
|
2018-06-11 10:37:05 +00:00
|
|
|
|
auto frame = getEditFrame<PCB_BASE_FRAME>();
|
|
|
|
|
|
2019-09-05 22:00:47 +00:00
|
|
|
|
if( frame && ( frame->IsType( FRAME_FOOTPRINT_VIEWER )
|
|
|
|
|
|| frame->IsType( FRAME_FOOTPRINT_VIEWER_MODAL ) ) )
|
2018-06-11 10:37:05 +00:00
|
|
|
|
{
|
2019-06-11 14:38:21 +00:00
|
|
|
|
frame->AddStandardSubMenus( m_menu );
|
2018-06-11 10:37:05 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
2020-07-16 01:22:29 +00:00
|
|
|
|
auto activeToolCondition =
|
|
|
|
|
[ frame ] ( const SELECTION& aSel )
|
|
|
|
|
{
|
|
|
|
|
return !frame->ToolStackIsEmpty();
|
|
|
|
|
};
|
|
|
|
|
|
2020-09-02 01:09:34 +00:00
|
|
|
|
auto inGroupCondition =
|
|
|
|
|
[this] ( const SELECTION& )
|
|
|
|
|
{
|
|
|
|
|
return m_enteredGroup != nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
2019-05-13 20:42:40 +00:00
|
|
|
|
menu.AddMenu( selectMenu.get(), SELECTION_CONDITIONS::NotEmpty );
|
2019-06-15 16:40:14 +00:00
|
|
|
|
menu.AddSeparator( 1000 );
|
2015-08-07 16:20:49 +00:00
|
|
|
|
|
2020-07-16 01:22:29 +00:00
|
|
|
|
// "Cancel" goes at the top of the context menu when a tool is active
|
|
|
|
|
menu.AddItem( ACTIONS::cancelInteractive, activeToolCondition, 1 );
|
2020-09-02 01:09:34 +00:00
|
|
|
|
menu.AddItem( PCB_ACTIONS::groupLeave, inGroupCondition, 1);
|
2020-07-16 01:22:29 +00:00
|
|
|
|
menu.AddSeparator( 1 );
|
|
|
|
|
|
2017-10-31 11:01:59 +00:00
|
|
|
|
if( frame )
|
2019-06-11 14:38:21 +00:00
|
|
|
|
frame->AddStandardSubMenus( m_menu );
|
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;
|
|
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
|
if( m_enteredGroup != NULL )
|
2020-09-02 01:09:34 +00:00
|
|
|
|
ExitGroup();
|
2020-08-11 19:37:07 +00:00
|
|
|
|
|
2015-03-10 17:31:23 +00:00
|
|
|
|
if( aReason == TOOL_BASE::MODEL_RELOAD )
|
|
|
|
|
{
|
2019-12-16 11:44:53 +00:00
|
|
|
|
// Deselect any item being currently in edit, to avoid unexpected behavior
|
|
|
|
|
// and remove pointers to the selected items from containers
|
2015-03-10 17:31:23 +00:00
|
|
|
|
// without changing their properties (as they are already deleted
|
|
|
|
|
// while a new board is loaded)
|
2019-12-16 11:44:53 +00:00
|
|
|
|
ClearSelection( true );
|
|
|
|
|
|
2015-06-04 12:54:08 +00:00
|
|
|
|
getView()->GetPainter()->GetSettings()->SetHighlight( false );
|
2015-03-10 17:31:23 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2019-05-05 10:33:34 +00:00
|
|
|
|
{
|
2015-03-10 17:31:23 +00:00
|
|
|
|
// Restore previous properties of selected items and remove them from containers
|
2019-09-08 18:43:54 +00:00
|
|
|
|
ClearSelection( true );
|
2019-05-05 10:33:34 +00:00
|
|
|
|
}
|
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-10-31 11:01:59 +00:00
|
|
|
|
view()->Remove( &m_selection );
|
|
|
|
|
view()->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
|
2019-06-17 13:43:22 +00:00
|
|
|
|
while( TOOL_EVENT* evt = Wait() )
|
2013-08-06 07:31:08 +00:00
|
|
|
|
{
|
2019-06-27 21:33:48 +00:00
|
|
|
|
if( m_frame->ToolStackIsEmpty() )
|
|
|
|
|
m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_ARROW );
|
|
|
|
|
|
2019-07-25 16:48:11 +00:00
|
|
|
|
bool dragAlwaysSelects = getEditFrame<PCB_BASE_FRAME>()->GetDragSelects();
|
2020-06-21 19:39:27 +00:00
|
|
|
|
TRACK_DRAG_ACTION dragAction = getEditFrame<PCB_BASE_FRAME>()->Settings().m_TrackDragAction;
|
2019-07-09 19:50:40 +00:00
|
|
|
|
m_additive = m_subtractive = m_exclusive_or = false;
|
2013-08-09 08:18:48 +00:00
|
|
|
|
|
2019-12-29 19:40:18 +00:00
|
|
|
|
// OSX uses CTRL for context menu, and SHIFT is exclusive-or
|
|
|
|
|
#ifdef __WXOSX_MAC__
|
|
|
|
|
if( evt->Modifier( MD_SHIFT ) )
|
|
|
|
|
m_exclusive_or = true;
|
|
|
|
|
#else
|
2019-07-09 19:50:40 +00:00
|
|
|
|
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;
|
2019-12-29 19:40:18 +00:00
|
|
|
|
#endif
|
2017-05-05 05:04:46 +00:00
|
|
|
|
|
2018-09-16 20:32:36 +00:00
|
|
|
|
// 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 );
|
|
|
|
|
|
2017-06-12 09:16:30 +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
|
|
|
|
{
|
2020-02-25 12:17:13 +00:00
|
|
|
|
m_frame->FocusOnItem( nullptr );
|
|
|
|
|
|
2019-07-09 19:50:40 +00:00
|
|
|
|
selectPoint( evt->Position() );
|
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 ) )
|
|
|
|
|
{
|
2018-01-01 20:50:50 +00:00
|
|
|
|
bool selectionCancelled = false;
|
|
|
|
|
|
2019-10-29 15:11:06 +00:00
|
|
|
|
if( m_selection.Empty() ||
|
|
|
|
|
!m_selection.GetBoundingBox().Contains( wxPoint( evt->Position() ) ) )
|
2017-08-03 12:28:00 +00:00
|
|
|
|
{
|
2019-10-29 15:11:06 +00:00
|
|
|
|
ClearSelection();
|
2018-01-01 20:50:50 +00:00
|
|
|
|
selectPoint( evt->Position(), false, &selectionCancelled );
|
2017-08-03 12:28:00 +00:00
|
|
|
|
m_selection.SetIsHover( true );
|
|
|
|
|
}
|
2014-02-07 19:44:34 +00:00
|
|
|
|
|
2018-01-01 20:50:50 +00:00
|
|
|
|
if( !selectionCancelled )
|
2019-05-19 21:04:04 +00:00
|
|
|
|
m_menu.ShowContextMenu( m_selection );
|
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 ) )
|
|
|
|
|
{
|
2020-02-25 12:17:13 +00:00
|
|
|
|
m_frame->FocusOnItem( nullptr );
|
|
|
|
|
|
2013-12-03 14:57:09 +00:00
|
|
|
|
if( m_selection.Empty() )
|
2015-07-09 15:11:34 +00:00
|
|
|
|
selectPoint( evt->Position() );
|
2013-12-03 14:57:09 +00:00
|
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
|
if( m_selection.GetSize() == 1 && m_selection[0]->Type() == PCB_GROUP_T )
|
|
|
|
|
{
|
|
|
|
|
EnterGroup();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_toolMgr->RunAction( PCB_ACTIONS::properties, true );
|
|
|
|
|
}
|
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
|
|
|
|
{
|
2020-02-25 12:17:13 +00:00
|
|
|
|
m_frame->FocusOnItem( nullptr );
|
|
|
|
|
|
2019-07-09 19:50:40 +00:00
|
|
|
|
if( m_additive || m_subtractive || m_exclusive_or || dragAlwaysSelects )
|
2013-09-02 14:29:10 +00:00
|
|
|
|
{
|
2014-05-14 07:56:35 +00:00
|
|
|
|
selectMultiple();
|
|
|
|
|
}
|
2019-07-09 19:50:40 +00:00
|
|
|
|
else
|
2014-05-14 07:56:35 +00:00
|
|
|
|
{
|
2019-07-09 19:50:40 +00:00
|
|
|
|
// selection is empty? try to start dragging the item under the point where drag
|
|
|
|
|
// started
|
|
|
|
|
if( m_selection.Empty() && selectCursor() )
|
2017-08-03 12:28:00 +00:00
|
|
|
|
m_selection.SetIsHover( true );
|
2014-03-18 15:25:46 +00:00
|
|
|
|
|
2020-08-22 23:48:19 +00:00
|
|
|
|
// Check if dragging has started within any of selected items bounding box.
|
|
|
|
|
// We verify "HasPosition()" first to protect against edge case involving
|
|
|
|
|
// moving off menus that causes problems (issue #5250)
|
|
|
|
|
if( evt->HasPosition() && 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
|
2020-06-21 19:39:27 +00:00
|
|
|
|
TRACK* track = dynamic_cast<TRACK*>( m_selection.GetItem( 0 ) );
|
|
|
|
|
|
|
|
|
|
if( track && dragAction == TRACK_DRAG_ACTION::DRAG )
|
|
|
|
|
m_toolMgr->RunAction( PCB_ACTIONS::drag45Degree, true );
|
|
|
|
|
else if( track && dragAction == TRACK_DRAG_ACTION::DRAG_FREE_ANGLE )
|
|
|
|
|
m_toolMgr->RunAction( PCB_ACTIONS::dragFreeAngle, true );
|
|
|
|
|
else
|
2020-08-18 14:42:56 +00:00
|
|
|
|
m_toolMgr->RunAction( PCB_ACTIONS::move, true );
|
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
|
|
|
|
{
|
2019-07-09 19:50:40 +00:00
|
|
|
|
// No -> drag a selection box
|
|
|
|
|
selectMultiple();
|
2013-09-19 15:02:57 +00:00
|
|
|
|
}
|
2013-09-02 14:29:10 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-07-09 11:50:27 +00:00
|
|
|
|
|
2019-07-07 23:01:08 +00:00
|
|
|
|
else if( evt->IsCancel() )
|
|
|
|
|
{
|
2020-02-25 12:17:13 +00:00
|
|
|
|
m_frame->FocusOnItem( nullptr );
|
|
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
|
if( m_enteredGroup != NULL )
|
2020-09-02 01:09:34 +00:00
|
|
|
|
ExitGroup();
|
2020-08-11 19:37:07 +00:00
|
|
|
|
|
2019-09-08 18:43:54 +00:00
|
|
|
|
ClearSelection();
|
2019-07-07 23:01:08 +00:00
|
|
|
|
|
|
|
|
|
if( evt->FirstResponder() == this )
|
|
|
|
|
m_toolMgr->RunAction( PCB_ACTIONS::clearHighlight );
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-28 14:39:14 +00:00
|
|
|
|
else
|
2019-06-16 11:06:49 +00:00
|
|
|
|
evt->SetPassEvent();
|
2013-08-06 07:31:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
2013-08-02 14:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-03 19:26:18 +00:00
|
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
|
void SELECTION_TOOL::EnterGroup()
|
|
|
|
|
{
|
|
|
|
|
wxCHECK_RET( m_selection.GetSize() == 1 && m_selection[0]->Type() == PCB_GROUP_T,
|
2020-09-04 18:29:22 +00:00
|
|
|
|
"EnterGroup called when selection is not a single group" );
|
2020-08-12 11:23:30 +00:00
|
|
|
|
PCB_GROUP* aGroup = static_cast<PCB_GROUP*>( m_selection[0] );
|
2020-08-11 19:37:07 +00:00
|
|
|
|
|
|
|
|
|
if( m_enteredGroup != NULL )
|
2020-09-02 01:09:34 +00:00
|
|
|
|
ExitGroup();
|
2020-08-11 19:37:07 +00:00
|
|
|
|
|
|
|
|
|
ClearSelection();
|
|
|
|
|
m_enteredGroup = aGroup;
|
|
|
|
|
m_enteredGroup->RunOnChildren( [&]( BOARD_ITEM* titem ) { select( titem ); } );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-09-02 01:09:34 +00:00
|
|
|
|
void SELECTION_TOOL::ExitGroup( bool aSelectGroup )
|
2020-08-11 19:37:07 +00:00
|
|
|
|
{
|
2020-09-02 01:09:34 +00:00
|
|
|
|
// Only continue if there is a group entered
|
|
|
|
|
if( m_enteredGroup == nullptr )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ClearSelection();
|
|
|
|
|
|
|
|
|
|
if( aSelectGroup )
|
|
|
|
|
select( m_enteredGroup );
|
|
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
|
m_enteredGroup = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-06-08 21:48:22 +00:00
|
|
|
|
PCBNEW_SELECTION& SELECTION_TOOL::GetSelection()
|
2016-01-20 14:22:09 +00:00
|
|
|
|
{
|
2017-03-03 12:42:28 +00:00
|
|
|
|
return m_selection;
|
|
|
|
|
}
|
2016-01-20 14:22:09 +00:00
|
|
|
|
|
2017-03-03 19:26:18 +00:00
|
|
|
|
|
2019-06-08 21:48:22 +00:00
|
|
|
|
PCBNEW_SELECTION& SELECTION_TOOL::RequestSelection( CLIENT_SELECTION_FILTER aClientFilter,
|
2019-07-09 19:50:40 +00:00
|
|
|
|
std::vector<BOARD_ITEM*>* aFiltered,
|
|
|
|
|
bool aConfirmLockedItems )
|
2017-03-03 12:42:28 +00:00
|
|
|
|
{
|
2018-04-19 14:17:40 +00:00
|
|
|
|
bool selectionEmpty = m_selection.Empty();
|
|
|
|
|
m_selection.SetIsHover( selectionEmpty );
|
|
|
|
|
|
|
|
|
|
if( selectionEmpty )
|
2017-03-03 12:42:28 +00:00
|
|
|
|
{
|
2018-01-05 23:44:37 +00:00
|
|
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true, aClientFilter );
|
2017-09-28 16:38:54 +00:00
|
|
|
|
m_selection.ClearReferencePoint();
|
2017-03-03 12:42:28 +00:00
|
|
|
|
}
|
2019-01-16 16:00:38 +00:00
|
|
|
|
|
|
|
|
|
if ( aConfirmLockedItems && CheckLock() == SELECTION_LOCKED )
|
|
|
|
|
{
|
2019-09-08 18:43:54 +00:00
|
|
|
|
ClearSelection();
|
2019-09-27 18:57:14 +00:00
|
|
|
|
return m_selection;
|
2019-01-16 16:00:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( aClientFilter )
|
2016-01-20 14:22:09 +00:00
|
|
|
|
{
|
2020-08-01 21:15:30 +00:00
|
|
|
|
enum DISPOSITION { BEFORE = 1, AFTER, BOTH };
|
2018-03-09 06:25:06 +00:00
|
|
|
|
|
2020-08-01 21:15:30 +00:00
|
|
|
|
std::map<EDA_ITEM*, DISPOSITION> itemDispositions;
|
|
|
|
|
GENERAL_COLLECTOR collector;
|
|
|
|
|
|
|
|
|
|
for( EDA_ITEM* item : m_selection )
|
|
|
|
|
{
|
2018-10-24 04:20:44 +00:00
|
|
|
|
collector.Append( item );
|
2020-08-01 21:15:30 +00:00
|
|
|
|
itemDispositions[ item ] = BEFORE;
|
|
|
|
|
}
|
2017-03-21 15:44:56 +00:00
|
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
|
aClientFilter( VECTOR2I(), collector, this );
|
2018-05-22 23:18:31 +00:00
|
|
|
|
|
2020-08-01 21:15:30 +00:00
|
|
|
|
for( EDA_ITEM* item : collector )
|
2018-12-13 01:22:27 +00:00
|
|
|
|
{
|
2020-08-01 21:15:30 +00:00
|
|
|
|
if( itemDispositions.count( item ) )
|
|
|
|
|
itemDispositions[ item ] = BOTH;
|
|
|
|
|
else
|
|
|
|
|
itemDispositions[ item ] = AFTER;
|
2018-12-13 01:22:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
|
// Unhighlight the BEFORE items before highlighting the AFTER items.
|
|
|
|
|
// This is so that in the case of groups, if aClientFilter replaces a selection
|
|
|
|
|
// with the enclosing group, the unhighlight of the element doesn't undo the
|
|
|
|
|
// recursive highlighting of that elemetn by the group.
|
|
|
|
|
|
2020-08-01 21:15:30 +00:00
|
|
|
|
for( std::pair<EDA_ITEM* const, DISPOSITION> itemDisposition : itemDispositions )
|
|
|
|
|
{
|
|
|
|
|
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( itemDisposition.first );
|
|
|
|
|
DISPOSITION disposition = itemDisposition.second;
|
|
|
|
|
|
|
|
|
|
if( disposition == BEFORE )
|
|
|
|
|
{
|
|
|
|
|
if( aFiltered )
|
|
|
|
|
aFiltered->push_back( item );
|
2018-08-22 14:09:16 +00:00
|
|
|
|
|
2020-08-01 21:15:30 +00:00
|
|
|
|
unhighlight( item, SELECTED, &m_selection );
|
|
|
|
|
}
|
2020-08-11 19:37:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for( std::pair<EDA_ITEM* const, DISPOSITION> itemDisposition : itemDispositions )
|
|
|
|
|
{
|
|
|
|
|
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( itemDisposition.first );
|
|
|
|
|
DISPOSITION disposition = itemDisposition.second;
|
|
|
|
|
|
|
|
|
|
if( disposition == AFTER )
|
2020-08-01 21:15:30 +00:00
|
|
|
|
{
|
|
|
|
|
highlight( item, SELECTED, &m_selection );
|
|
|
|
|
}
|
|
|
|
|
else if( disposition == BOTH )
|
|
|
|
|
{
|
|
|
|
|
// nothing to do
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-25 16:30:51 +00:00
|
|
|
|
|
2019-06-13 17:28:55 +00:00
|
|
|
|
m_frame->GetCanvas()->ForceRefresh();
|
2018-03-09 06:25:06 +00:00
|
|
|
|
}
|
2016-01-20 14:22:09 +00:00
|
|
|
|
|
|
|
|
|
return m_selection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-10-31 15:13:41 +00:00
|
|
|
|
const GENERAL_COLLECTORS_GUIDE SELECTION_TOOL::getCollectorsGuide() const
|
|
|
|
|
{
|
|
|
|
|
GENERAL_COLLECTORS_GUIDE guide( board()->GetVisibleLayers(),
|
2018-11-17 00:34:12 +00:00
|
|
|
|
(PCB_LAYER_ID) view()->GetTopLayer(), view() );
|
2017-10-31 15:13:41 +00:00
|
|
|
|
|
2020-07-11 17:42:00 +00:00
|
|
|
|
bool padsDisabled = !board()->IsElementVisible( LAYER_PADS );
|
|
|
|
|
|
2017-10-31 15:13:41 +00:00
|
|
|
|
// account for the globals
|
|
|
|
|
guide.SetIgnoreMTextsMarkedNoShow( ! board()->IsElementVisible( LAYER_MOD_TEXT_INVISIBLE ) );
|
|
|
|
|
guide.SetIgnoreMTextsOnBack( ! board()->IsElementVisible( LAYER_MOD_TEXT_BK ) );
|
|
|
|
|
guide.SetIgnoreMTextsOnFront( ! board()->IsElementVisible( LAYER_MOD_TEXT_FR ) );
|
|
|
|
|
guide.SetIgnoreModulesOnBack( ! board()->IsElementVisible( LAYER_MOD_BK ) );
|
|
|
|
|
guide.SetIgnoreModulesOnFront( ! board()->IsElementVisible( LAYER_MOD_FR ) );
|
2020-07-11 17:42:00 +00:00
|
|
|
|
guide.SetIgnorePadsOnBack( padsDisabled || ! board()->IsElementVisible( LAYER_PAD_BK ) );
|
|
|
|
|
guide.SetIgnorePadsOnFront( padsDisabled || ! board()->IsElementVisible( LAYER_PAD_FR ) );
|
|
|
|
|
guide.SetIgnoreThroughHolePads( padsDisabled || ! board()->IsElementVisible( LAYER_PADS_TH ) );
|
2017-10-31 15:13:41 +00:00
|
|
|
|
guide.SetIgnoreModulesVals( ! board()->IsElementVisible( LAYER_MOD_VALUES ) );
|
|
|
|
|
guide.SetIgnoreModulesRefs( ! board()->IsElementVisible( LAYER_MOD_REFERENCES ) );
|
2020-08-24 12:59:58 +00:00
|
|
|
|
guide.SetIgnoreThroughVias( ! board()->IsElementVisible( LAYER_VIAS ) );
|
|
|
|
|
guide.SetIgnoreBlindBuriedVias( ! board()->IsElementVisible( LAYER_VIAS ) );
|
|
|
|
|
guide.SetIgnoreMicroVias( ! board()->IsElementVisible( LAYER_VIAS ) );
|
2018-02-22 06:53:16 +00:00
|
|
|
|
guide.SetIgnoreTracks( ! board()->IsElementVisible( LAYER_TRACKS ) );
|
2017-10-31 15:13:41 +00:00
|
|
|
|
|
|
|
|
|
return guide;
|
|
|
|
|
}
|
2014-07-09 11:50:27 +00:00
|
|
|
|
|
2018-01-01 20:50:50 +00:00
|
|
|
|
|
|
|
|
|
bool SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag,
|
2018-01-05 23:44:37 +00:00
|
|
|
|
bool* aSelectionCancelledFlag,
|
|
|
|
|
CLIENT_SELECTION_FILTER aClientFilter )
|
2013-08-02 14:53:50 +00:00
|
|
|
|
{
|
2019-09-27 18:57:14 +00:00
|
|
|
|
GENERAL_COLLECTORS_GUIDE guide = getCollectorsGuide();
|
|
|
|
|
GENERAL_COLLECTOR collector;
|
2019-11-07 14:23:09 +00:00
|
|
|
|
auto& displayOpts = m_frame->GetDisplayOptions();
|
2018-11-02 23:50:33 +00:00
|
|
|
|
|
2020-07-11 17:42:00 +00:00
|
|
|
|
guide.SetIgnoreZoneFills( displayOpts.m_ZoneDisplayMode != ZONE_DISPLAY_MODE::SHOW_FILLED );
|
2014-07-09 14:50:31 +00:00
|
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
|
if( m_enteredGroup &&
|
|
|
|
|
!m_enteredGroup->GetBoundingBox().Contains( wxPoint( aWhere.x, aWhere.y ) ) )
|
2020-09-02 01:09:34 +00:00
|
|
|
|
{
|
|
|
|
|
ExitGroup();
|
|
|
|
|
}
|
2020-08-11 19:37:07 +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
|
|
|
|
// Remove unselectable items
|
|
|
|
|
for( int i = collector.GetCount() - 1; i >= 0; --i )
|
|
|
|
|
{
|
2019-07-28 18:30:56 +00:00
|
|
|
|
if( !Selectable( collector[ i ] ) || ( aOnDrag && collector[i]->IsLocked() ) )
|
2015-02-18 00:10:20 +00:00
|
|
|
|
collector.Remove( i );
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-28 16:38:54 +00:00
|
|
|
|
m_selection.ClearReferencePoint();
|
|
|
|
|
|
2018-01-05 23:44:37 +00:00
|
|
|
|
// Allow the client to do tool- or action-specific filtering to see if we
|
|
|
|
|
// can get down to a single item
|
|
|
|
|
if( aClientFilter )
|
2020-08-11 19:37:07 +00:00
|
|
|
|
aClientFilter( aWhere, collector, this );
|
2018-01-05 23:44:37 +00:00
|
|
|
|
|
2020-06-27 22:48:34 +00:00
|
|
|
|
// Apply the stateful filter
|
|
|
|
|
filterCollectedItems( collector );
|
|
|
|
|
|
2019-04-21 23:45:34 +00:00
|
|
|
|
// Apply some ugly heuristics to avoid disambiguation menus whenever possible
|
|
|
|
|
if( collector.GetCount() > 1 && !m_skip_heuristics )
|
2018-01-05 23:44:37 +00:00
|
|
|
|
{
|
2019-07-28 18:30:56 +00:00
|
|
|
|
GuessSelectionCandidates( collector, aWhere );
|
2018-01-05 23:44:37 +00:00
|
|
|
|
}
|
2013-08-06 07:31:08 +00:00
|
|
|
|
|
2019-04-21 23:45:34 +00:00
|
|
|
|
// If still more than one item we're going to have to ask the user.
|
|
|
|
|
if( collector.GetCount() > 1 )
|
2018-01-05 23:44:37 +00:00
|
|
|
|
{
|
2019-04-21 23:45:34 +00:00
|
|
|
|
if( aOnDrag )
|
|
|
|
|
Wait( TOOL_EVENT( TC_ANY, TA_MOUSE_UP, BUT_LEFT ) );
|
2018-09-16 20:32:36 +00:00
|
|
|
|
|
2020-07-07 04:51:12 +00:00
|
|
|
|
if( !doSelectionMenu( &collector, wxEmptyString ) )
|
2018-09-16 20:32:36 +00:00
|
|
|
|
{
|
2019-04-21 23:45:34 +00:00
|
|
|
|
if( aSelectionCancelledFlag )
|
|
|
|
|
*aSelectionCancelledFlag = true;
|
|
|
|
|
|
|
|
|
|
return false;
|
2018-09-16 20:32:36 +00:00
|
|
|
|
}
|
2018-01-05 23:44:37 +00:00
|
|
|
|
}
|
2015-03-10 08:36:04 +00:00
|
|
|
|
|
2020-06-27 01:57:16 +00:00
|
|
|
|
bool anyAdded = false;
|
|
|
|
|
bool anySubtracted = false;
|
|
|
|
|
|
2019-07-09 19:50:40 +00:00
|
|
|
|
if( !m_additive && !m_subtractive && !m_exclusive_or )
|
2019-09-27 18:57:14 +00:00
|
|
|
|
{
|
|
|
|
|
if( m_selection.GetSize() > 0 )
|
|
|
|
|
{
|
2020-05-24 23:35:04 +00:00
|
|
|
|
ClearSelection( true /*quiet mode*/ );
|
2020-06-27 01:57:16 +00:00
|
|
|
|
anySubtracted = true;
|
2019-09-27 18:57:14 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-07-09 19:50:40 +00:00
|
|
|
|
|
2020-05-24 23:35:04 +00:00
|
|
|
|
if( collector.GetCount() > 0 )
|
|
|
|
|
{
|
|
|
|
|
for( int i = 0; i < collector.GetCount(); ++i )
|
2019-07-09 19:50:40 +00:00
|
|
|
|
{
|
2020-05-24 23:35:04 +00:00
|
|
|
|
if( m_subtractive || ( m_exclusive_or && collector[i]->IsSelected() ) )
|
|
|
|
|
{
|
|
|
|
|
unselect( collector[i] );
|
|
|
|
|
anySubtracted = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
select( collector[i] );
|
|
|
|
|
anyAdded = true;
|
|
|
|
|
}
|
2019-07-09 19:50:40 +00:00
|
|
|
|
}
|
2018-01-05 23:44:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-24 23:35:04 +00:00
|
|
|
|
if( anyAdded )
|
|
|
|
|
{
|
|
|
|
|
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else if( anySubtracted )
|
|
|
|
|
{
|
|
|
|
|
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2019-09-27 18:57:14 +00:00
|
|
|
|
|
2014-03-18 15:25:46 +00:00
|
|
|
|
return false;
|
2013-08-02 14:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-04-21 23:45:34 +00:00
|
|
|
|
bool SELECTION_TOOL::selectCursor( bool aForceSelect, CLIENT_SELECTION_FILTER aClientFilter )
|
2015-07-09 15:11:34 +00:00
|
|
|
|
{
|
2019-04-21 23:45:34 +00:00
|
|
|
|
if( aForceSelect || m_selection.Empty() )
|
2015-07-09 15:11:34 +00:00
|
|
|
|
{
|
2019-09-27 18:57:14 +00:00
|
|
|
|
ClearSelection( true /*quiet mode*/ );
|
2018-01-05 23:44:37 +00:00
|
|
|
|
selectPoint( getViewControls()->GetCursorPosition( false ), false, NULL, aClientFilter );
|
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-08-06 07:31:08 +00:00
|
|
|
|
|
2017-03-10 13:34:06 +00:00
|
|
|
|
KIGFX::PREVIEW::SELECTION_AREA area;
|
2015-04-30 08:46:08 +00:00
|
|
|
|
view->Add( &area );
|
2013-08-06 07:31:08 +00:00
|
|
|
|
|
2019-09-27 18:57:14 +00:00
|
|
|
|
bool anyAdded = false;
|
|
|
|
|
bool anySubtracted = false;
|
|
|
|
|
|
2019-06-17 13:43:22 +00:00
|
|
|
|
while( TOOL_EVENT* evt = Wait() )
|
2013-08-06 07:31:08 +00:00
|
|
|
|
{
|
2019-07-01 21:01:33 +00:00
|
|
|
|
if( evt->IsCancelInteractive() || evt->IsActivate() )
|
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
|
|
|
|
{
|
2019-07-09 19:50:40 +00:00
|
|
|
|
if( !m_additive && !m_subtractive && !m_exclusive_or )
|
2019-09-27 18:57:14 +00:00
|
|
|
|
{
|
|
|
|
|
if( m_selection.GetSize() > 0 )
|
|
|
|
|
{
|
|
|
|
|
anySubtracted = true;
|
|
|
|
|
ClearSelection( true /*quiet mode*/ );
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-07-09 19:50:40 +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() );
|
2017-05-09 07:28:54 +00:00
|
|
|
|
area.SetAdditive( m_additive );
|
|
|
|
|
area.SetSubtractive( m_subtractive );
|
2019-07-09 19:50:40 +00:00
|
|
|
|
area.SetExclusiveOr( m_exclusive_or );
|
2017-05-09 07:28:54 +00:00
|
|
|
|
|
2016-12-02 17:58:12 +00:00
|
|
|
|
view->SetVisible( &area, true );
|
|
|
|
|
view->Update( &area );
|
2018-02-20 08:48:45 +00:00
|
|
|
|
getViewControls()->SetAutoPan( true );
|
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
|
|
|
|
{
|
2018-02-20 08:48:45 +00:00
|
|
|
|
getViewControls()->SetAutoPan( false );
|
|
|
|
|
|
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;
|
2017-04-20 14:20:56 +00:00
|
|
|
|
|
|
|
|
|
// Filter the view items based on the selection box
|
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
|
|
|
|
|
2017-04-20 14:20:56 +00:00
|
|
|
|
int width = area.GetEnd().x - area.GetOrigin().x;
|
|
|
|
|
int height = area.GetEnd().y - area.GetOrigin().y;
|
|
|
|
|
|
2018-04-28 20:32:39 +00:00
|
|
|
|
/* 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 ? true : false;
|
|
|
|
|
|
|
|
|
|
if( view->IsMirroredX() )
|
|
|
|
|
windowSelection = !windowSelection;
|
|
|
|
|
|
2017-04-20 14:20:56 +00:00
|
|
|
|
// Construct an EDA_RECT to determine BOARD_ITEM selection
|
2019-07-09 19:50:40 +00:00
|
|
|
|
EDA_RECT selectionRect( (wxPoint) area.GetOrigin(), wxSize( width, height ) );
|
2017-04-20 14:20:56 +00:00
|
|
|
|
|
|
|
|
|
selectionRect.Normalize();
|
|
|
|
|
|
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 );
|
|
|
|
|
|
2020-06-27 22:48:34 +00:00
|
|
|
|
if( !item || !Selectable( item ) || !itemPassesFilter( item ) )
|
2017-05-07 00:09:03 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
2019-05-05 10:33:34 +00:00
|
|
|
|
if( item->HitTest( selectionRect, windowSelection ) )
|
2014-03-06 09:43:40 +00:00
|
|
|
|
{
|
2019-07-09 19:50:40 +00:00
|
|
|
|
if( m_subtractive || ( m_exclusive_or && item->IsSelected() ) )
|
|
|
|
|
{
|
2019-05-05 10:33:34 +00:00
|
|
|
|
unselect( item );
|
2019-07-09 19:50:40 +00:00
|
|
|
|
anySubtracted = true;
|
|
|
|
|
}
|
2019-05-05 10:33:34 +00:00
|
|
|
|
else
|
2019-07-09 19:50:40 +00:00
|
|
|
|
{
|
2019-05-05 10:33:34 +00:00
|
|
|
|
select( item );
|
2019-07-09 19:50:40 +00:00
|
|
|
|
anyAdded = true;
|
|
|
|
|
}
|
2017-04-20 14:53:57 +00:00
|
|
|
|
}
|
2013-08-08 17:42:19 +00:00
|
|
|
|
}
|
2013-09-26 16:38:58 +00:00
|
|
|
|
|
2019-07-09 19:50:40 +00:00
|
|
|
|
m_selection.SetIsHover( false );
|
|
|
|
|
|
2016-01-20 14:22:09 +00:00
|
|
|
|
// Inform other potentially interested tools
|
2019-07-09 19:50:40 +00:00
|
|
|
|
if( anyAdded )
|
2019-04-21 23:45:34 +00:00
|
|
|
|
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
|
2019-09-27 18:57:14 +00:00
|
|
|
|
else if( anySubtracted )
|
2019-07-09 19:50:40 +00:00
|
|
|
|
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
|
|
|
|
|
|
2013-12-04 09:58:51 +00:00
|
|
|
|
break; // Stop waiting for events
|
2013-08-06 07:31:08 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-06 12:01:44 +00:00
|
|
|
|
getViewControls()->SetAutoPan( false );
|
|
|
|
|
|
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
|
2013-09-06 14:04:12 +00:00
|
|
|
|
|
2017-09-29 09:40:10 +00:00
|
|
|
|
if( !cancelled )
|
2017-09-28 16:38:54 +00:00
|
|
|
|
m_selection.ClearReferencePoint();
|
|
|
|
|
|
2013-09-06 14:04:12 +00:00
|
|
|
|
return cancelled;
|
2013-08-02 14:53:50 +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:
|
2020-06-14 14:23:10 +00:00
|
|
|
|
case PCB_MODULE_ZONE_AREA_T:
|
2014-07-09 14:50:31 +00:00
|
|
|
|
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
|
|
|
|
|
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
|
|
|
|
{
|
2018-01-05 23:44:37 +00:00
|
|
|
|
CLIENT_SELECTION_FILTER aClientFilter = aEvent.Parameter<CLIENT_SELECTION_FILTER>();
|
2017-02-09 08:47:32 +00:00
|
|
|
|
|
2018-09-25 14:23:38 +00:00
|
|
|
|
selectCursor( false, aClientFilter );
|
2017-02-09 08:47:32 +00:00
|
|
|
|
|
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
|
|
|
|
{
|
2019-09-08 18:43:54 +00:00
|
|
|
|
ClearSelection();
|
2014-07-09 11:50:27 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-12 09:16:30 +00:00
|
|
|
|
|
|
|
|
|
int SELECTION_TOOL::SelectItems( const TOOL_EVENT& aEvent )
|
|
|
|
|
{
|
|
|
|
|
std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
|
|
|
|
|
|
|
|
|
|
if( items )
|
|
|
|
|
{
|
2019-04-21 23:45:34 +00:00
|
|
|
|
// Perform individual selection of each item before processing the event.
|
2017-06-12 09:16:30 +00:00
|
|
|
|
for( auto item : *items )
|
|
|
|
|
select( item );
|
|
|
|
|
|
2019-04-21 23:45:34 +00:00
|
|
|
|
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
|
2017-06-12 09:16:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-02-14 20:28:47 +00:00
|
|
|
|
int SELECTION_TOOL::SelectItem( const TOOL_EVENT& aEvent )
|
2014-11-21 10:50:13 +00:00
|
|
|
|
{
|
2019-06-25 13:01:22 +00:00
|
|
|
|
AddItemToSel( aEvent.Parameter<BOARD_ITEM*>() );
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2014-11-21 10:50:13 +00:00
|
|
|
|
|
2019-06-25 13:01:22 +00:00
|
|
|
|
|
2020-08-19 22:15:44 +00:00
|
|
|
|
int SELECTION_TOOL::SelectAll( const TOOL_EVENT& aEvent )
|
|
|
|
|
{
|
|
|
|
|
KIGFX::VIEW* view = getView();
|
|
|
|
|
|
|
|
|
|
// hold all visible items
|
|
|
|
|
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
|
|
|
|
|
|
|
|
|
|
// Filter the view items based on the selection box
|
|
|
|
|
BOX2I selectionBox;
|
|
|
|
|
|
|
|
|
|
selectionBox.SetMaximum();
|
|
|
|
|
view->Query( selectionBox, selectedItems ); // Get the list of selected items
|
|
|
|
|
|
|
|
|
|
for( auto& item_pair : selectedItems )
|
|
|
|
|
{
|
|
|
|
|
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( item_pair.first );
|
|
|
|
|
|
|
|
|
|
if( !item || !Selectable( item ) || !itemPassesFilter( item ) )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
select( item );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-06-25 13:01:22 +00:00
|
|
|
|
void SELECTION_TOOL::AddItemToSel( BOARD_ITEM* aItem, bool aQuietMode )
|
|
|
|
|
{
|
|
|
|
|
if( aItem )
|
2014-11-21 10:50:13 +00:00
|
|
|
|
{
|
2019-06-25 13:01:22 +00:00
|
|
|
|
select( aItem );
|
2014-11-21 10:50:13 +00:00
|
|
|
|
|
|
|
|
|
// Inform other potentially interested tools
|
2019-06-25 13:01:22 +00:00
|
|
|
|
if( !aQuietMode )
|
|
|
|
|
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
|
2014-11-21 10:50:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-30 08:46:07 +00:00
|
|
|
|
|
2017-06-12 09:16:30 +00:00
|
|
|
|
int SELECTION_TOOL::UnselectItems( const TOOL_EVENT& aEvent )
|
|
|
|
|
{
|
|
|
|
|
std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
|
|
|
|
|
|
|
|
|
|
if( items )
|
|
|
|
|
{
|
2019-04-21 23:45:34 +00:00
|
|
|
|
// Perform individual unselection of each item before processing the event
|
2017-06-12 09:16:30 +00:00
|
|
|
|
for( auto item : *items )
|
|
|
|
|
unselect( item );
|
|
|
|
|
|
2019-04-21 23:45:34 +00:00
|
|
|
|
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
|
2017-06-12 09:16:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-02-14 20:28:47 +00:00
|
|
|
|
int SELECTION_TOOL::UnselectItem( const TOOL_EVENT& aEvent )
|
2014-11-21 10:50:13 +00:00
|
|
|
|
{
|
2019-11-16 22:31:18 +00:00
|
|
|
|
RemoveItemFromSel( aEvent.Parameter<BOARD_ITEM*>() );
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2014-11-21 10:50:13 +00:00
|
|
|
|
|
2019-11-16 22:31:18 +00:00
|
|
|
|
|
|
|
|
|
void SELECTION_TOOL::RemoveItemFromSel( BOARD_ITEM* aItem, bool aQuietMode )
|
|
|
|
|
{
|
|
|
|
|
if( aItem )
|
2014-11-21 10:50:13 +00:00
|
|
|
|
{
|
2019-11-16 22:31:18 +00:00
|
|
|
|
unselect( aItem );
|
2014-11-21 10:50:13 +00:00
|
|
|
|
|
|
|
|
|
// Inform other potentially interested tools
|
2019-04-21 23:45:34 +00:00
|
|
|
|
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
|
2014-11-21 10:50:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-07-09 13:10:32 +00:00
|
|
|
|
|
2015-03-10 08:36:04 +00:00
|
|
|
|
|
2019-06-25 13:01:22 +00:00
|
|
|
|
void SELECTION_TOOL::BrightenItem( BOARD_ITEM* aItem )
|
|
|
|
|
{
|
|
|
|
|
highlight( aItem, BRIGHTENED );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SELECTION_TOOL::UnbrightenItem( BOARD_ITEM* aItem )
|
|
|
|
|
{
|
|
|
|
|
unhighlight( aItem, BRIGHTENED );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
|
void connectedItemFilter( const VECTOR2I&, GENERAL_COLLECTOR& aCollector, SELECTION_TOOL* sTool )
|
2018-01-05 23:44:37 +00:00
|
|
|
|
{
|
2019-09-27 18:57:14 +00:00
|
|
|
|
// Narrow the collection down to a single BOARD_CONNECTED_ITEM for each represented net.
|
|
|
|
|
// All other items types are removed.
|
2018-01-05 23:44:37 +00:00
|
|
|
|
std::set<int> representedNets;
|
|
|
|
|
|
|
|
|
|
for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
|
|
|
|
|
{
|
|
|
|
|
BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( aCollector[i] );
|
|
|
|
|
if( !item )
|
|
|
|
|
aCollector.Remove( i );
|
|
|
|
|
else if ( representedNets.count( item->GetNetCode() ) )
|
|
|
|
|
aCollector.Remove( i );
|
|
|
|
|
else
|
|
|
|
|
representedNets.insert( item->GetNetCode() );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-11-22 19:29:07 +00:00
|
|
|
|
int SELECTION_TOOL::expandConnection( const TOOL_EVENT& aEvent )
|
2015-03-10 08:36:04 +00:00
|
|
|
|
{
|
2019-11-23 17:36:40 +00:00
|
|
|
|
unsigned initialCount = 0;
|
2018-03-08 18:02:55 +00:00
|
|
|
|
|
|
|
|
|
for( auto item : m_selection.GetItems() )
|
|
|
|
|
{
|
|
|
|
|
if( dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
|
2019-11-22 19:29:07 +00:00
|
|
|
|
initialCount++;
|
2018-03-08 18:02:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-22 19:29:07 +00:00
|
|
|
|
if( initialCount == 0 )
|
2018-03-08 18:02:55 +00:00
|
|
|
|
selectCursor( true, connectedItemFilter );
|
2015-07-09 15:11:34 +00:00
|
|
|
|
|
2020-03-27 21:56:39 +00:00
|
|
|
|
for( STOP_CONDITION stopCondition : { STOP_AT_JUNCTION, STOP_AT_PAD, STOP_NEVER } )
|
2019-11-22 19:29:07 +00:00
|
|
|
|
{
|
|
|
|
|
// copy the selection, since we're going to iterate and modify
|
|
|
|
|
std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
|
2019-11-22 17:31:03 +00:00
|
|
|
|
|
2019-11-22 19:29:07 +00:00
|
|
|
|
for( EDA_ITEM* item : selectedItems )
|
2020-06-27 16:06:01 +00:00
|
|
|
|
item->ClearTempFlags();
|
2015-03-10 08:36:04 +00:00
|
|
|
|
|
2019-11-22 19:29:07 +00:00
|
|
|
|
for( EDA_ITEM* item : selectedItems )
|
|
|
|
|
{
|
|
|
|
|
TRACK* trackItem = dynamic_cast<TRACK*>( item );
|
2017-06-23 12:08:22 +00:00
|
|
|
|
|
2020-06-27 16:06:01 +00:00
|
|
|
|
// Track items marked SKIP_STRUCT have already been visited
|
|
|
|
|
if( trackItem && !( trackItem->GetFlags() & SKIP_STRUCT ) )
|
2019-11-22 19:29:07 +00:00
|
|
|
|
selectConnectedTracks( *trackItem, stopCondition );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( m_selection.GetItems().size() > initialCount )
|
|
|
|
|
break;
|
2017-02-04 06:44:17 +00:00
|
|
|
|
}
|
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 )
|
2019-04-21 23:45:34 +00:00
|
|
|
|
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
|
2015-03-10 08:36:04 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-11-22 17:31:03 +00:00
|
|
|
|
void SELECTION_TOOL::selectConnectedTracks( BOARD_CONNECTED_ITEM& aStartItem,
|
2020-03-27 21:56:39 +00:00
|
|
|
|
STOP_CONDITION aStopCondition )
|
2015-03-10 08:36:04 +00:00
|
|
|
|
{
|
2019-05-17 00:13:21 +00:00
|
|
|
|
constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, EOT };
|
2015-07-09 15:11:34 +00:00
|
|
|
|
|
2019-11-22 17:31:03 +00:00
|
|
|
|
auto connectivity = board()->GetConnectivity();
|
|
|
|
|
auto connectedItems = connectivity->GetConnectedItems( &aStartItem, types );
|
2016-11-04 21:29:47 +00:00
|
|
|
|
|
2019-11-22 17:31:03 +00:00
|
|
|
|
std::map<wxPoint, std::vector<TRACK*>> trackMap;
|
|
|
|
|
std::map<wxPoint, VIA*> viaMap;
|
|
|
|
|
std::map<wxPoint, D_PAD*> padMap;
|
2015-07-09 15:11:34 +00:00
|
|
|
|
|
2019-11-22 17:31:03 +00:00
|
|
|
|
// Build maps of connected items
|
|
|
|
|
for( BOARD_CONNECTED_ITEM* item : connectedItems )
|
|
|
|
|
{
|
|
|
|
|
switch( item->Type() )
|
|
|
|
|
{
|
|
|
|
|
case PCB_TRACE_T:
|
|
|
|
|
{
|
|
|
|
|
TRACK* track = static_cast<TRACK*>( item );
|
|
|
|
|
trackMap[ track->GetStart() ].push_back( track );
|
|
|
|
|
trackMap[ track->GetEnd() ].push_back( track );
|
|
|
|
|
}
|
|
|
|
|
break;
|
2020-07-05 09:37:42 +00:00
|
|
|
|
|
2019-11-22 17:31:03 +00:00
|
|
|
|
case PCB_VIA_T:
|
|
|
|
|
{
|
|
|
|
|
VIA* via = static_cast<VIA*>( item );
|
|
|
|
|
viaMap[ via->GetStart() ] = via;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2020-07-05 09:37:42 +00:00
|
|
|
|
|
2019-11-22 17:31:03 +00:00
|
|
|
|
case PCB_PAD_T:
|
|
|
|
|
{
|
|
|
|
|
D_PAD* pad = static_cast<D_PAD*>( item );
|
|
|
|
|
padMap[ pad->GetPosition() ] = pad;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2020-07-05 09:37:42 +00:00
|
|
|
|
|
2019-11-22 17:31:03 +00:00
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-05 09:37:42 +00:00
|
|
|
|
item->SetState( SKIP_STRUCT, false );
|
2019-11-22 17:31:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<wxPoint> activePts;
|
2017-02-04 06:44:17 +00:00
|
|
|
|
|
2019-11-22 17:31:03 +00:00
|
|
|
|
// Set up the initial active points
|
|
|
|
|
switch( aStartItem.Type() )
|
2018-09-03 14:42:18 +00:00
|
|
|
|
{
|
2019-11-22 17:31:03 +00:00
|
|
|
|
case PCB_TRACE_T:
|
|
|
|
|
activePts.push_back( static_cast<TRACK*>( &aStartItem )->GetStart() );
|
|
|
|
|
activePts.push_back( static_cast<TRACK*>( &aStartItem )->GetEnd() );
|
|
|
|
|
break;
|
2020-07-05 09:37:42 +00:00
|
|
|
|
|
2019-11-22 17:31:03 +00:00
|
|
|
|
case PCB_VIA_T:
|
|
|
|
|
activePts.push_back( static_cast<TRACK*>( &aStartItem )->GetStart() );
|
|
|
|
|
break;
|
2020-07-05 09:37:42 +00:00
|
|
|
|
|
2019-11-22 17:31:03 +00:00
|
|
|
|
case PCB_PAD_T:
|
|
|
|
|
activePts.push_back( aStartItem.GetPosition() );
|
|
|
|
|
break;
|
2020-07-05 09:37:42 +00:00
|
|
|
|
|
2019-11-22 17:31:03 +00:00
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool expand = true;
|
|
|
|
|
|
|
|
|
|
// Iterative push from all active points
|
|
|
|
|
while( expand )
|
|
|
|
|
{
|
|
|
|
|
expand = false;
|
|
|
|
|
|
|
|
|
|
for( int i = activePts.size() - 1; i >= 0; --i )
|
|
|
|
|
{
|
|
|
|
|
wxPoint pt = activePts[i];
|
|
|
|
|
|
2020-03-27 21:56:39 +00:00
|
|
|
|
if( trackMap[ pt ].size() > 2 && aStopCondition == STOP_AT_JUNCTION )
|
2019-11-22 17:31:03 +00:00
|
|
|
|
{
|
|
|
|
|
activePts.erase( activePts.begin() + i );
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-27 21:56:39 +00:00
|
|
|
|
if( padMap.count( pt ) && aStopCondition != STOP_NEVER )
|
2019-11-22 17:31:03 +00:00
|
|
|
|
{
|
|
|
|
|
activePts.erase( activePts.begin() + i );
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for( TRACK* track : trackMap[ pt ] )
|
|
|
|
|
{
|
|
|
|
|
if( track->GetState( SKIP_STRUCT ) )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
track->SetState( SKIP_STRUCT, true );
|
|
|
|
|
select( track );
|
|
|
|
|
|
|
|
|
|
if( track->GetStart() == pt )
|
|
|
|
|
activePts.push_back( track->GetEnd() );
|
|
|
|
|
else
|
|
|
|
|
activePts.push_back( track->GetStart() );
|
|
|
|
|
|
|
|
|
|
expand = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( viaMap.count( pt ) && !viaMap[ pt ]->IsSelected() )
|
|
|
|
|
select( viaMap[ pt ] );
|
|
|
|
|
|
|
|
|
|
activePts.erase( activePts.begin() + i );
|
|
|
|
|
}
|
2018-09-03 14:42:18 +00:00
|
|
|
|
}
|
2017-02-04 06:44:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SELECTION_TOOL::selectAllItemsOnNet( int aNetCode )
|
|
|
|
|
{
|
2017-03-22 13:43:10 +00:00
|
|
|
|
constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_VIA_T, EOT };
|
|
|
|
|
auto connectivity = board()->GetConnectivity();
|
2015-03-10 08:36:04 +00:00
|
|
|
|
|
2019-11-22 17:31:03 +00:00
|
|
|
|
for( BOARD_CONNECTED_ITEM* item : connectivity->GetNetItems( aNetCode, types ) )
|
2020-07-11 17:42:00 +00:00
|
|
|
|
if( itemPassesFilter( item ) )
|
|
|
|
|
select( item );
|
2017-02-04 06:44:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int SELECTION_TOOL::selectNet( const TOOL_EVENT& aEvent )
|
|
|
|
|
{
|
2020-07-11 17:42:00 +00:00
|
|
|
|
// If we've been passed an argument, just select that netcode
|
|
|
|
|
int netcode = aEvent.Parameter<intptr_t>();
|
|
|
|
|
|
|
|
|
|
if( netcode > 0 )
|
|
|
|
|
{
|
|
|
|
|
selectAllItemsOnNet( netcode );
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-04 06:44:17 +00:00
|
|
|
|
if( !selectCursor() )
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
// copy the selection, since we're going to iterate and modify
|
|
|
|
|
auto selection = m_selection.GetItems();
|
|
|
|
|
|
2019-11-22 17:31:03 +00:00
|
|
|
|
for( EDA_ITEM* i : selection )
|
2017-02-04 06:44:17 +00:00
|
|
|
|
{
|
2019-11-22 17:31:03 +00:00
|
|
|
|
BOARD_CONNECTED_ITEM* connItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( i );
|
2017-02-04 06:44:17 +00:00
|
|
|
|
|
2019-11-22 17:31:03 +00:00
|
|
|
|
if( connItem )
|
|
|
|
|
selectAllItemsOnNet( connItem->GetNetCode() );
|
2017-02-04 06:44:17 +00:00
|
|
|
|
}
|
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 )
|
2019-04-21 23:45:34 +00:00
|
|
|
|
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
|
2017-02-08 16:32:41 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2017-03-04 15:28:26 +00:00
|
|
|
|
|
|
|
|
|
|
2020-04-02 12:20:28 +00:00
|
|
|
|
void SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetPath )
|
2017-02-08 16:32:41 +00:00
|
|
|
|
{
|
|
|
|
|
std::list<MODULE*> modList;
|
|
|
|
|
|
2020-04-02 12:20:28 +00:00
|
|
|
|
// store all modules that are on that sheet path
|
2019-11-22 17:31:03 +00:00
|
|
|
|
for( MODULE* module : board()->Modules() )
|
2017-02-08 16:32:41 +00:00
|
|
|
|
{
|
2020-02-20 12:11:04 +00:00
|
|
|
|
if( module == nullptr )
|
|
|
|
|
continue;
|
|
|
|
|
|
2020-04-02 12:20:28 +00:00
|
|
|
|
wxString footprint_path = module->GetPath().AsString().BeforeLast('/');
|
|
|
|
|
|
|
|
|
|
if( aSheetPath.IsEmpty() )
|
|
|
|
|
aSheetPath += '/';
|
|
|
|
|
|
|
|
|
|
if( footprint_path == aSheetPath )
|
|
|
|
|
modList.push_back( module );
|
2017-02-08 16:32:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Generate a list of all pads, and of all nets they belong to.
|
|
|
|
|
std::list<int> netcodeList;
|
2019-11-22 17:31:03 +00:00
|
|
|
|
std::list<D_PAD*> padList;
|
2017-02-28 03:04:44 +00:00
|
|
|
|
for( MODULE* mmod : modList )
|
2017-02-08 16:32:41 +00:00
|
|
|
|
{
|
2019-11-22 17:31:03 +00:00
|
|
|
|
for( D_PAD* pad : mmod->Pads() )
|
2017-02-08 16:32:41 +00:00
|
|
|
|
{
|
|
|
|
|
if( pad->IsConnected() )
|
|
|
|
|
{
|
|
|
|
|
netcodeList.push_back( pad->GetNetCode() );
|
2017-07-17 12:55:31 +00:00
|
|
|
|
padList.push_back( pad );
|
2017-02-08 16:32:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// remove all duplicates
|
|
|
|
|
netcodeList.sort();
|
|
|
|
|
netcodeList.unique();
|
|
|
|
|
|
2017-07-17 12:55:31 +00:00
|
|
|
|
// auto select trivial connections segments which are launched from the pads
|
|
|
|
|
std::list<TRACK*> launchTracks;
|
2017-09-18 09:25:32 +00:00
|
|
|
|
|
2019-11-22 17:31:03 +00:00
|
|
|
|
for( D_PAD* pad : padList )
|
2020-03-27 21:56:39 +00:00
|
|
|
|
selectConnectedTracks( *pad, STOP_NEVER );
|
2017-07-17 12:55:31 +00:00
|
|
|
|
|
2017-02-08 16:32:41 +00:00
|
|
|
|
// 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 )
|
|
|
|
|
std::list<int> removeCodeList;
|
2017-07-17 08:21:36 +00:00
|
|
|
|
constexpr KICAD_T padType[] = { PCB_PAD_T, EOT };
|
2017-07-16 11:39:30 +00:00
|
|
|
|
|
2017-02-08 16:32:41 +00:00
|
|
|
|
for( int netCode : netcodeList )
|
|
|
|
|
{
|
2017-07-16 11:39:30 +00:00
|
|
|
|
for( BOARD_CONNECTED_ITEM* mitem : board()->GetConnectivity()->GetNetItems( netCode, padType ) )
|
2017-02-08 16:32:41 +00:00
|
|
|
|
{
|
2017-07-16 11:39:30 +00:00
|
|
|
|
if( mitem->Type() == PCB_PAD_T)
|
2017-02-08 16:32:41 +00:00
|
|
|
|
{
|
2019-11-22 17:31:03 +00:00
|
|
|
|
bool found = std::find( modList.begin(), modList.end(), mitem->GetParent() ) != modList.end();
|
2017-07-16 11:39:30 +00:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2017-02-08 16:32:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// remove all duplicates
|
|
|
|
|
removeCodeList.sort();
|
|
|
|
|
removeCodeList.unique();
|
|
|
|
|
|
|
|
|
|
for( int removeCode : removeCodeList )
|
|
|
|
|
{
|
|
|
|
|
netcodeList.remove( removeCode );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::list<BOARD_CONNECTED_ITEM*> localConnectionList;
|
2017-07-17 08:21:36 +00:00
|
|
|
|
constexpr KICAD_T trackViaType[] = { PCB_TRACE_T, PCB_VIA_T, EOT };
|
|
|
|
|
|
2017-02-08 16:32:41 +00:00
|
|
|
|
for( int netCode : netcodeList )
|
|
|
|
|
{
|
2017-07-17 08:21:36 +00:00
|
|
|
|
for( BOARD_CONNECTED_ITEM* item : board()->GetConnectivity()->GetNetItems( netCode, trackViaType ) )
|
2017-03-22 13:59:30 +00:00
|
|
|
|
localConnectionList.push_back( item );
|
2017-02-08 16:32:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for( BOARD_ITEM* i : modList )
|
|
|
|
|
{
|
|
|
|
|
if( i != NULL )
|
|
|
|
|
select( i );
|
|
|
|
|
}
|
2017-07-17 08:21:36 +00:00
|
|
|
|
|
2017-02-08 16:32:41 +00:00
|
|
|
|
for( BOARD_CONNECTED_ITEM* i : localConnectionList )
|
|
|
|
|
{
|
|
|
|
|
if( i != NULL )
|
|
|
|
|
select( i );
|
|
|
|
|
}
|
2017-03-02 21:07:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-07-17 08:21:36 +00:00
|
|
|
|
|
2018-11-11 20:06:23 +00:00
|
|
|
|
void SELECTION_TOOL::zoomFitSelection()
|
2017-03-02 23:42:23 +00:00
|
|
|
|
{
|
2017-03-04 10:11:33 +00:00
|
|
|
|
//Should recalculate the view to zoom in on the selection
|
2020-05-29 19:24:41 +00:00
|
|
|
|
auto selectionBox = m_selection.GetBoundingBox();
|
2017-03-04 10:11:33 +00:00
|
|
|
|
auto view = getView();
|
2017-03-02 23:42:23 +00:00
|
|
|
|
|
2019-06-13 17:28:55 +00:00
|
|
|
|
VECTOR2D screenSize = view->ToWorld( m_frame->GetCanvas()->GetClientSize(), false );
|
2020-03-04 20:33:42 +00:00
|
|
|
|
screenSize.x = std::max( 10.0, screenSize.x );
|
|
|
|
|
screenSize.y = std::max( 10.0, screenSize.y );
|
2017-03-02 23:42:23 +00:00
|
|
|
|
|
2019-06-13 17:28:55 +00:00
|
|
|
|
if( selectionBox.GetWidth() != 0 || selectionBox.GetHeight() != 0 )
|
2017-03-04 10:11:33 +00:00
|
|
|
|
{
|
|
|
|
|
VECTOR2D vsize = selectionBox.GetSize();
|
|
|
|
|
double scale = view->GetScale() / std::max( fabs( vsize.x / screenSize.x ),
|
|
|
|
|
fabs( vsize.y / screenSize.y ) );
|
|
|
|
|
view->SetScale( scale );
|
|
|
|
|
view->SetCenter( selectionBox.Centre() );
|
|
|
|
|
view->Add( &m_selection );
|
|
|
|
|
}
|
2017-03-02 23:42:23 +00:00
|
|
|
|
|
2019-06-13 17:28:55 +00:00
|
|
|
|
m_frame->GetCanvas()->ForceRefresh();
|
2017-03-02 23:42:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-04 16:30:44 +00:00
|
|
|
|
|
2019-05-14 19:21:10 +00:00
|
|
|
|
int SELECTION_TOOL::selectSheetContents( const TOOL_EVENT& aEvent )
|
2017-03-02 23:42:23 +00:00
|
|
|
|
{
|
2019-09-27 18:57:14 +00:00
|
|
|
|
ClearSelection( true /*quiet mode*/ );
|
2020-04-02 12:20:28 +00:00
|
|
|
|
wxString sheetPath = *aEvent.Parameter<wxString*>();
|
2017-03-04 16:30:44 +00:00
|
|
|
|
|
2020-04-02 12:20:28 +00:00
|
|
|
|
selectAllItemsOnSheet( sheetPath );
|
2017-03-02 23:42:23 +00:00
|
|
|
|
|
2017-03-04 10:11:33 +00:00
|
|
|
|
zoomFitSelection();
|
2017-03-02 23:42:23 +00:00
|
|
|
|
|
2017-03-04 10:11:33 +00:00
|
|
|
|
if( m_selection.Size() > 0 )
|
2019-04-21 23:45:34 +00:00
|
|
|
|
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
|
2017-03-02 23:42:23 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-04 16:30:44 +00:00
|
|
|
|
|
2017-03-02 21:07:45 +00:00
|
|
|
|
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();
|
2017-03-04 15:28:26 +00:00
|
|
|
|
|
2017-03-11 21:18:45 +00:00
|
|
|
|
if( !item )
|
2017-03-02 21:07:45 +00:00
|
|
|
|
return 0;
|
2017-03-04 15:28:26 +00:00
|
|
|
|
|
2017-03-11 21:18:45 +00:00
|
|
|
|
if( item->Type() != PCB_MODULE_T )
|
2017-03-02 21:07:45 +00:00
|
|
|
|
return 0;
|
2017-03-04 15:28:26 +00:00
|
|
|
|
|
2017-03-02 21:07:45 +00:00
|
|
|
|
auto mod = dynamic_cast<MODULE*>( item );
|
|
|
|
|
|
2020-02-20 12:11:04 +00:00
|
|
|
|
if( mod->GetPath().empty() )
|
|
|
|
|
return 0;
|
|
|
|
|
|
2019-09-27 18:57:14 +00:00
|
|
|
|
ClearSelection( true /*quiet mode*/ );
|
2017-03-02 21:07:45 +00:00
|
|
|
|
|
2020-04-02 12:20:28 +00:00
|
|
|
|
// get the sheet path only.
|
|
|
|
|
wxString sheetPath = mod->GetPath().AsString().BeforeLast( '/' );
|
|
|
|
|
|
|
|
|
|
if( sheetPath.IsEmpty() )
|
|
|
|
|
sheetPath += '/';
|
2017-03-02 21:07:45 +00:00
|
|
|
|
|
2020-04-02 12:20:28 +00:00
|
|
|
|
selectAllItemsOnSheet( sheetPath );
|
2017-02-08 16:32:41 +00:00
|
|
|
|
|
|
|
|
|
// Inform other potentially interested tools
|
2017-02-04 06:44:17 +00:00
|
|
|
|
if( m_selection.Size() > 0 )
|
2019-04-21 23:45:34 +00:00
|
|
|
|
m_toolMgr->ProcessEvent( EVENTS::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 )
|
|
|
|
|
{
|
2019-09-27 18:57:14 +00:00
|
|
|
|
bool cleared = false;
|
|
|
|
|
|
|
|
|
|
if( m_selection.GetSize() > 0 )
|
|
|
|
|
{
|
|
|
|
|
// Don't fire an event now; most of the time it will be redundant as we're about to
|
|
|
|
|
// fire a SelectedEvent.
|
|
|
|
|
cleared = true;
|
2020-05-24 23:35:04 +00:00
|
|
|
|
ClearSelection( true /*quiet mode*/ );
|
2019-09-27 18:57:14 +00:00
|
|
|
|
}
|
2014-10-13 13:25:16 +00:00
|
|
|
|
|
|
|
|
|
if( aItem )
|
2015-01-29 17:23:54 +00:00
|
|
|
|
{
|
|
|
|
|
select( aItem );
|
2019-07-26 19:19:17 +00:00
|
|
|
|
m_frame->FocusOnLocation( aItem->GetPosition() );
|
2015-01-29 17:23:54 +00:00
|
|
|
|
|
|
|
|
|
// Inform other potentially interested tools
|
2019-04-21 23:45:34 +00:00
|
|
|
|
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
|
2015-01-29 17:23:54 +00:00
|
|
|
|
}
|
2019-09-27 18:57:14 +00:00
|
|
|
|
else if( cleared )
|
|
|
|
|
{
|
|
|
|
|
m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
|
|
|
|
|
}
|
2014-10-13 13:25:16 +00:00
|
|
|
|
|
2019-06-13 17:28:55 +00:00
|
|
|
|
m_frame->GetCanvas()->ForceRefresh();
|
2014-10-13 13:25:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
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 );
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-02-07 06:40:38 +00:00
|
|
|
|
/**
|
|
|
|
|
* Function itemIsIncludedByFilter()
|
|
|
|
|
*
|
|
|
|
|
* Determine if an item is included by the filter specified
|
|
|
|
|
*
|
2020-01-12 20:52:19 +00:00
|
|
|
|
* @return true if aItem should be selected by this filter (i..e not filtered out)
|
2017-02-07 06:40:38 +00:00
|
|
|
|
*/
|
2020-01-12 20:52:19 +00:00
|
|
|
|
static bool itemIsIncludedByFilter( const BOARD_ITEM& aItem, const BOARD& aBoard,
|
|
|
|
|
const DIALOG_FILTER_SELECTION::OPTIONS& aFilterOptions )
|
2017-02-07 06:40:38 +00:00
|
|
|
|
{
|
|
|
|
|
bool include = true;
|
2017-03-13 03:19:33 +00:00
|
|
|
|
const PCB_LAYER_ID layer = aItem.GetLayer();
|
2017-02-07 06:40:38 +00:00
|
|
|
|
|
2018-03-17 11:07:50 +00:00
|
|
|
|
// if the item needs to be checked against the options
|
2017-02-07 06:40:38 +00:00
|
|
|
|
if( include )
|
|
|
|
|
{
|
|
|
|
|
switch( aItem.Type() )
|
|
|
|
|
{
|
|
|
|
|
case PCB_MODULE_T:
|
|
|
|
|
{
|
|
|
|
|
const auto& module = static_cast<const MODULE&>( aItem );
|
|
|
|
|
|
2020-01-12 20:52:19 +00:00
|
|
|
|
include = aFilterOptions.includeModules;
|
2017-02-07 06:40:38 +00:00
|
|
|
|
|
2020-01-12 20:52:19 +00:00
|
|
|
|
if( include && !aFilterOptions.includeLockedModules )
|
2017-02-07 06:40:38 +00:00
|
|
|
|
{
|
|
|
|
|
include = !module.IsLocked();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case PCB_TRACE_T:
|
2019-05-17 00:13:21 +00:00
|
|
|
|
case PCB_ARC_T:
|
2017-02-07 06:40:38 +00:00
|
|
|
|
{
|
2020-01-12 20:52:19 +00:00
|
|
|
|
include = aFilterOptions.includeTracks;
|
2017-02-07 06:40:38 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2018-03-17 13:51:16 +00:00
|
|
|
|
case PCB_VIA_T:
|
|
|
|
|
{
|
2020-01-12 20:52:19 +00:00
|
|
|
|
include = aFilterOptions.includeVias;
|
2018-03-17 13:51:16 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2017-02-07 06:40:38 +00:00
|
|
|
|
case PCB_ZONE_AREA_T:
|
|
|
|
|
{
|
2020-01-12 20:52:19 +00:00
|
|
|
|
include = aFilterOptions.includeZones;
|
2017-02-07 06:40:38 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case PCB_LINE_T:
|
|
|
|
|
case PCB_TARGET_T:
|
|
|
|
|
case PCB_DIMENSION_T:
|
|
|
|
|
{
|
2018-03-17 11:07:50 +00:00
|
|
|
|
if( layer == Edge_Cuts )
|
2020-01-12 20:52:19 +00:00
|
|
|
|
include = aFilterOptions.includeBoardOutlineLayer;
|
2018-03-17 11:07:50 +00:00
|
|
|
|
else
|
2020-01-12 20:52:19 +00:00
|
|
|
|
include = aFilterOptions.includeItemsOnTechLayers;
|
2017-02-07 06:40:38 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case PCB_TEXT_T:
|
|
|
|
|
{
|
2020-01-12 20:52:19 +00:00
|
|
|
|
include = aFilterOptions.includePcbTexts;
|
2017-02-07 06:40:38 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
2018-03-17 11:07:50 +00:00
|
|
|
|
// no filtering, just select it
|
2017-02-07 06:40:38 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return include;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int SELECTION_TOOL::filterSelection( const TOOL_EVENT& aEvent )
|
|
|
|
|
{
|
2020-01-12 20:52:19 +00:00
|
|
|
|
const BOARD& board = *getModel<BOARD>();
|
2020-01-12 20:30:58 +00:00
|
|
|
|
DIALOG_FILTER_SELECTION::OPTIONS& opts = m_priv->m_filterOpts;
|
2020-01-12 20:52:19 +00:00
|
|
|
|
DIALOG_FILTER_SELECTION dlg( m_frame, opts );
|
2017-02-07 06:40:38 +00:00
|
|
|
|
|
|
|
|
|
const int cmd = dlg.ShowModal();
|
|
|
|
|
|
|
|
|
|
if( cmd != wxID_OK )
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
// copy current selection
|
2019-09-27 18:57:14 +00:00
|
|
|
|
std::deque<EDA_ITEM*> selection = m_selection.GetItems();
|
2017-02-07 06:40:38 +00:00
|
|
|
|
|
2019-09-27 18:57:14 +00:00
|
|
|
|
ClearSelection( true /*quiet mode*/ );
|
2017-02-07 06:40:38 +00:00
|
|
|
|
|
2019-09-27 18:57:14 +00:00
|
|
|
|
// re-select items from the saved selection according to the dialog options
|
|
|
|
|
for( EDA_ITEM* i : selection )
|
2017-02-07 06:40:38 +00:00
|
|
|
|
{
|
2019-09-27 18:57:14 +00:00
|
|
|
|
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
|
|
|
|
|
bool include = itemIsIncludedByFilter( *item, board, opts );
|
2017-02-07 06:40:38 +00:00
|
|
|
|
|
|
|
|
|
if( include )
|
|
|
|
|
select( item );
|
|
|
|
|
}
|
2019-09-27 18:57:14 +00:00
|
|
|
|
|
|
|
|
|
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
|
|
|
|
|
|
2017-02-07 06:40:38 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-06-27 22:48:34 +00:00
|
|
|
|
void SELECTION_TOOL::filterCollectedItems( GENERAL_COLLECTOR& aCollector )
|
|
|
|
|
{
|
|
|
|
|
if( aCollector.GetCount() == 0 )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
std::set<BOARD_ITEM*> rejected;
|
|
|
|
|
|
|
|
|
|
for( EDA_ITEM* i : aCollector )
|
|
|
|
|
{
|
|
|
|
|
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
|
|
|
|
|
|
|
|
|
|
if( !itemPassesFilter( item ) )
|
|
|
|
|
rejected.insert( item );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for( BOARD_ITEM* item : rejected )
|
|
|
|
|
aCollector.Remove( item );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool SELECTION_TOOL::itemPassesFilter( BOARD_ITEM* aItem )
|
|
|
|
|
{
|
|
|
|
|
if( aItem->IsLocked() && !m_filter.lockedItems )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
switch( aItem->Type() )
|
|
|
|
|
{
|
|
|
|
|
case PCB_MODULE_T:
|
|
|
|
|
if( !m_filter.footprints )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PCB_PAD_T:
|
|
|
|
|
if( !m_filter.pads )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PCB_TRACE_T:
|
|
|
|
|
case PCB_ARC_T:
|
|
|
|
|
if( !m_filter.tracks )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PCB_VIA_T:
|
|
|
|
|
if( !m_filter.vias )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PCB_ZONE_AREA_T:
|
|
|
|
|
{
|
|
|
|
|
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( aItem );
|
|
|
|
|
|
|
|
|
|
if( ( !m_filter.zones && !zone->GetIsKeepout() )
|
|
|
|
|
|| ( !m_filter.keepouts && zone->GetIsKeepout() ) )
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case PCB_LINE_T:
|
|
|
|
|
case PCB_TARGET_T:
|
|
|
|
|
if( !m_filter.graphics )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PCB_MODULE_TEXT_T:
|
|
|
|
|
case PCB_TEXT_T:
|
|
|
|
|
if( !m_filter.text )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PCB_DIMENSION_T:
|
|
|
|
|
if( !m_filter.dimensions )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
if( !m_filter.otherItems )
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
|
if( m_enteredGroup != NULL )
|
|
|
|
|
{
|
|
|
|
|
return m_enteredGroup->GetItems().find( aItem ) != m_enteredGroup->GetItems().end();
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-27 22:48:34 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-09-08 18:43:54 +00:00
|
|
|
|
void SELECTION_TOOL::ClearSelection( bool aQuietMode )
|
2014-07-09 11:50:27 +00:00
|
|
|
|
{
|
2016-02-08 14:12:59 +00:00
|
|
|
|
if( m_selection.Empty() )
|
|
|
|
|
return;
|
|
|
|
|
|
2018-01-28 09:35:33 +00:00
|
|
|
|
while( m_selection.GetSize() )
|
2019-06-25 13:01:22 +00:00
|
|
|
|
unhighlight( static_cast<BOARD_ITEM*>( m_selection.Front() ), SELECTED, &m_selection );
|
2018-01-28 09:35:33 +00:00
|
|
|
|
|
|
|
|
|
view()->Update( &m_selection );
|
2014-07-09 11:50:27 +00:00
|
|
|
|
|
2017-08-03 12:28:00 +00:00
|
|
|
|
m_selection.SetIsHover( false );
|
2017-10-19 21:57:21 +00:00
|
|
|
|
m_selection.ClearReferencePoint();
|
2017-10-31 15:13:41 +00:00
|
|
|
|
|
2014-07-09 14:50:31 +00:00
|
|
|
|
m_locked = true;
|
2014-07-09 11:50:27 +00:00
|
|
|
|
|
|
|
|
|
// Inform other potentially interested tools
|
2019-09-08 18:43:54 +00:00
|
|
|
|
if( !aQuietMode )
|
|
|
|
|
{
|
|
|
|
|
m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
|
|
|
|
|
m_toolMgr->RunAction( PCB_ACTIONS::hideDynamicRatsnest, true );
|
|
|
|
|
}
|
2014-07-09 11:50:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-05-11 10:06:28 +00:00
|
|
|
|
void SELECTION_TOOL::RebuildSelection()
|
|
|
|
|
{
|
|
|
|
|
m_selection.Clear();
|
|
|
|
|
|
|
|
|
|
INSPECTOR_FUNC inspector = [&] ( EDA_ITEM* item, void* testData )
|
|
|
|
|
{
|
|
|
|
|
if( item->IsSelected() )
|
|
|
|
|
{
|
|
|
|
|
EDA_ITEM* parent = item->GetParent();
|
|
|
|
|
|
|
|
|
|
// Flags on module children might be set only because the parent is selected.
|
|
|
|
|
if( parent && parent->Type() == PCB_MODULE_T && parent->IsSelected() )
|
2019-12-28 00:55:11 +00:00
|
|
|
|
return SEARCH_RESULT::CONTINUE;
|
2019-05-11 10:06:28 +00:00
|
|
|
|
|
2019-06-25 13:01:22 +00:00
|
|
|
|
highlight( (BOARD_ITEM*) item, SELECTED, &m_selection );
|
2019-05-11 10:06:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-28 00:55:11 +00:00
|
|
|
|
return SEARCH_RESULT::CONTINUE;
|
2019-05-11 10:06:28 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
board()->Visit( inspector, nullptr, m_editModules ? GENERAL_COLLECTOR::ModuleItems
|
|
|
|
|
: GENERAL_COLLECTOR::AllBoardItems );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-01-28 09:35:33 +00:00
|
|
|
|
int SELECTION_TOOL::SelectionMenu( const TOOL_EVENT& aEvent )
|
|
|
|
|
{
|
|
|
|
|
GENERAL_COLLECTOR* collector = aEvent.Parameter<GENERAL_COLLECTOR*>();
|
2019-04-23 12:51:48 +00:00
|
|
|
|
|
2018-01-28 09:35:33 +00:00
|
|
|
|
doSelectionMenu( collector, wxEmptyString );
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-04-21 23:45:34 +00:00
|
|
|
|
bool SELECTION_TOOL::doSelectionMenu( GENERAL_COLLECTOR* aCollector, const wxString& aTitle )
|
2013-08-02 14:53:50 +00:00
|
|
|
|
{
|
2019-06-08 21:48:22 +00:00
|
|
|
|
BOARD_ITEM* current = nullptr;
|
|
|
|
|
PCBNEW_SELECTION highlightGroup;
|
2020-05-24 23:35:04 +00:00
|
|
|
|
bool selectAll = false;
|
2020-07-07 04:51:12 +00:00
|
|
|
|
bool expandSelection = false;
|
2013-08-06 07:31:08 +00:00
|
|
|
|
|
2018-08-28 21:57:31 +00:00
|
|
|
|
highlightGroup.SetLayer( LAYER_SELECT_OVERLAY );
|
2017-09-20 19:56:22 +00:00
|
|
|
|
getView()->Add( &highlightGroup );
|
2016-12-02 17:58:12 +00:00
|
|
|
|
|
2020-07-07 04:51:12 +00:00
|
|
|
|
do
|
2013-08-06 07:31:08 +00:00
|
|
|
|
{
|
2020-07-07 04:51:12 +00:00
|
|
|
|
/// The user has requested the full, non-limited list of selection items
|
|
|
|
|
if( expandSelection )
|
|
|
|
|
aCollector->Combine();
|
2020-05-24 23:35:04 +00:00
|
|
|
|
|
2020-07-07 04:51:12 +00:00
|
|
|
|
expandSelection = false;
|
2019-04-21 23:45:34 +00:00
|
|
|
|
|
2020-07-07 04:51:12 +00:00
|
|
|
|
int limit = std::min( 9, aCollector->GetCount() );
|
|
|
|
|
ACTION_MENU menu( true );
|
2013-08-06 07:31:08 +00:00
|
|
|
|
|
2020-07-07 04:51:12 +00:00
|
|
|
|
for( int i = 0; i < limit; ++i )
|
2013-08-06 07:31:08 +00:00
|
|
|
|
{
|
2020-07-07 04:51:12 +00:00
|
|
|
|
wxString text;
|
|
|
|
|
BOARD_ITEM* item = ( *aCollector )[i];
|
|
|
|
|
text = item->GetSelectMenuText( m_frame->GetUserUnits() );
|
2013-08-06 07:31:08 +00:00
|
|
|
|
|
2020-07-07 04:51:12 +00:00
|
|
|
|
wxString menuText = wxString::Format( "&%d. %s\t%d", i + 1, text, i + 1 );
|
|
|
|
|
menu.Add( menuText, i + 1, item->GetMenuImage() );
|
|
|
|
|
}
|
2013-08-08 10:30:00 +00:00
|
|
|
|
|
2020-07-07 04:51:12 +00:00
|
|
|
|
menu.AppendSeparator();
|
|
|
|
|
menu.Add( _( "Select &All\tA" ), limit + 1, net_highlight_xpm );
|
2020-05-24 23:35:04 +00:00
|
|
|
|
|
2020-07-07 04:51:12 +00:00
|
|
|
|
if( !expandSelection && aCollector->HasAdditionalItems() )
|
|
|
|
|
menu.Add( _( "&Expand Selection\tE" ), limit + 2, nullptr );
|
|
|
|
|
|
|
|
|
|
if( aTitle.Length() )
|
|
|
|
|
{
|
|
|
|
|
menu.SetTitle( aTitle );
|
|
|
|
|
menu.SetIcon( info_xpm );
|
|
|
|
|
menu.DisplayTitle( true );
|
2013-08-08 10:30:00 +00:00
|
|
|
|
}
|
2020-07-07 04:51:12 +00:00
|
|
|
|
else
|
|
|
|
|
menu.DisplayTitle( false );
|
|
|
|
|
|
|
|
|
|
SetContextMenu( &menu, CMENU_NOW );
|
|
|
|
|
|
|
|
|
|
while( TOOL_EVENT* evt = Wait() )
|
2013-08-06 07:31:08 +00:00
|
|
|
|
{
|
2020-07-07 04:51:12 +00:00
|
|
|
|
if( evt->Action() == TA_CHOICE_MENU_UPDATE )
|
2020-05-24 23:35:04 +00:00
|
|
|
|
{
|
2020-07-07 04:51:12 +00:00
|
|
|
|
if( selectAll )
|
|
|
|
|
{
|
|
|
|
|
for( int i = 0; i < aCollector->GetCount(); ++i )
|
|
|
|
|
unhighlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
|
|
|
|
|
}
|
|
|
|
|
else if( current )
|
|
|
|
|
unhighlight( current, BRIGHTENED, &highlightGroup );
|
2017-12-12 11:01:19 +00:00
|
|
|
|
|
2020-07-07 04:51:12 +00:00
|
|
|
|
int id = *evt->GetCommandId();
|
2013-08-06 07:31:08 +00:00
|
|
|
|
|
2020-07-07 04:51:12 +00:00
|
|
|
|
// User has pointed an item, so show it in a different way
|
|
|
|
|
if( id > 0 && id <= limit )
|
|
|
|
|
{
|
|
|
|
|
current = ( *aCollector )[id - 1];
|
|
|
|
|
highlight( current, BRIGHTENED, &highlightGroup );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
current = nullptr;
|
|
|
|
|
|
|
|
|
|
// User has pointed on the "Select All" option
|
|
|
|
|
if( id == limit + 1 )
|
|
|
|
|
{
|
|
|
|
|
for( int i = 0; i < aCollector->GetCount(); ++i )
|
|
|
|
|
highlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
|
|
|
|
|
selectAll = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
selectAll = false;
|
2020-05-24 23:35:04 +00:00
|
|
|
|
}
|
2020-07-07 04:51:12 +00:00
|
|
|
|
else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
|
2020-05-24 23:35:04 +00:00
|
|
|
|
{
|
2020-07-07 04:51:12 +00:00
|
|
|
|
if( selectAll )
|
|
|
|
|
{
|
|
|
|
|
for( int i = 0; i < aCollector->GetCount(); ++i )
|
|
|
|
|
unhighlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
|
|
|
|
|
}
|
|
|
|
|
else if( current )
|
|
|
|
|
unhighlight( current, BRIGHTENED, &highlightGroup );
|
|
|
|
|
|
|
|
|
|
OPT<int> id = evt->GetCommandId();
|
|
|
|
|
|
|
|
|
|
// User has selected the "Select All" option
|
|
|
|
|
if( id == limit + 1 )
|
|
|
|
|
{
|
|
|
|
|
selectAll = true;
|
|
|
|
|
current = nullptr;
|
|
|
|
|
}
|
|
|
|
|
else if( id == limit + 2 )
|
|
|
|
|
{
|
|
|
|
|
expandSelection = true;
|
|
|
|
|
selectAll = false;
|
|
|
|
|
current = nullptr;
|
|
|
|
|
}
|
|
|
|
|
// User has selected an item, so this one will be returned
|
|
|
|
|
else if( id && ( *id > 0 ) && ( *id <= limit ) )
|
|
|
|
|
{
|
|
|
|
|
selectAll = false;
|
|
|
|
|
current = ( *aCollector )[*id - 1];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
selectAll = false;
|
|
|
|
|
current = nullptr;
|
|
|
|
|
}
|
2020-05-24 23:35:04 +00:00
|
|
|
|
}
|
2020-07-07 04:51:12 +00:00
|
|
|
|
else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
|
2020-05-24 23:35:04 +00:00
|
|
|
|
{
|
2020-07-07 04:51:12 +00:00
|
|
|
|
break;
|
2020-05-24 23:35:04 +00:00
|
|
|
|
}
|
2013-09-16 09:00:59 +00:00
|
|
|
|
}
|
2020-07-07 04:51:12 +00:00
|
|
|
|
} while( expandSelection );
|
|
|
|
|
|
2017-09-20 19:56:22 +00:00
|
|
|
|
getView()->Remove( &highlightGroup );
|
2016-12-02 17:58:12 +00:00
|
|
|
|
|
2020-05-24 23:35:04 +00:00
|
|
|
|
if( selectAll )
|
|
|
|
|
return true;
|
|
|
|
|
else if( current )
|
2019-04-21 23:45:34 +00:00
|
|
|
|
{
|
|
|
|
|
aCollector->Empty();
|
|
|
|
|
aCollector->Append( current );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2019-03-26 02:38:18 +00:00
|
|
|
|
|
2019-04-21 23:45:34 +00:00
|
|
|
|
return false;
|
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];
|
|
|
|
|
|
2018-08-09 00:11:47 +00:00
|
|
|
|
int lx = module->GetFootprintRect().GetWidth();
|
|
|
|
|
int ly = module->GetFootprintRect().GetHeight();
|
2014-02-04 15:03:56 +00:00
|
|
|
|
|
|
|
|
|
int lmin = std::min( lx, ly );
|
|
|
|
|
|
|
|
|
|
if( lmin < minDim )
|
|
|
|
|
{
|
|
|
|
|
minDim = lmin;
|
|
|
|
|
minNdx = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (*aCollector)[minNdx];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-07-28 18:30:56 +00:00
|
|
|
|
bool SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibilityOnly ) const
|
2013-09-04 08:56:06 +00:00
|
|
|
|
{
|
2020-06-27 11:57:40 +00:00
|
|
|
|
const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
|
2013-09-17 09:32:47 +00:00
|
|
|
|
|
2020-06-27 11:57:40 +00:00
|
|
|
|
if( settings->GetHighContrast() )
|
|
|
|
|
{
|
|
|
|
|
std::set<unsigned int> activeLayers = settings->GetActiveLayers();
|
2020-08-24 19:18:33 +00:00
|
|
|
|
bool onActiveLayer = false;
|
2018-09-20 20:54:06 +00:00
|
|
|
|
|
2020-08-24 19:18:33 +00:00
|
|
|
|
for( unsigned int layer : activeLayers )
|
2013-09-17 09:32:47 +00:00
|
|
|
|
{
|
2020-08-24 20:59:43 +00:00
|
|
|
|
// NOTE: Only checking the regular layers (not GAL meta-layers)
|
|
|
|
|
if( layer < PCB_LAYER_ID_COUNT && aItem->IsOnLayer( ToLAYER_ID( layer ) ) )
|
2020-08-24 19:18:33 +00:00
|
|
|
|
{
|
|
|
|
|
onActiveLayer = true;
|
2013-09-17 09:32:47 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-24 19:18:33 +00:00
|
|
|
|
if( !onActiveLayer ) // 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() )
|
|
|
|
|
{
|
2017-09-22 05:18:05 +00:00
|
|
|
|
case PCB_ZONE_AREA_T:
|
2020-06-14 14:23:10 +00:00
|
|
|
|
case PCB_MODULE_ZONE_AREA_T:
|
2019-11-16 22:31:18 +00:00
|
|
|
|
{
|
2020-08-08 23:23:11 +00:00
|
|
|
|
const ZONE_CONTAINER* zone = static_cast<const ZONE_CONTAINER*>( aItem );
|
|
|
|
|
|
2019-11-16 22:31:18 +00:00
|
|
|
|
// Check to see if this keepout is part of a footprint
|
|
|
|
|
// If it is, and we are not editing the footprint, it should not be selectable
|
2020-08-08 23:23:11 +00:00
|
|
|
|
bool zoneInFootprint = zone->GetParent() && zone->GetParent()->Type() == PCB_MODULE_T;
|
|
|
|
|
|
2019-11-16 22:31:18 +00:00
|
|
|
|
if( zoneInFootprint && !m_editModules && !checkVisibilityOnly )
|
|
|
|
|
return false;
|
|
|
|
|
|
2020-06-24 02:19:08 +00:00
|
|
|
|
// zones can exist on multiple layers!
|
2020-08-08 23:23:11 +00:00
|
|
|
|
return ( zone->GetLayerSet() & board()->GetVisibleLayers() ).any();
|
2019-11-16 22:31:18 +00:00
|
|
|
|
}
|
2017-09-22 05:18:05 +00:00
|
|
|
|
break;
|
2018-01-18 08:55:06 +00:00
|
|
|
|
|
2018-02-22 06:53:16 +00:00
|
|
|
|
case PCB_TRACE_T:
|
2019-05-17 00:13:21 +00:00
|
|
|
|
case PCB_ARC_T:
|
2020-08-24 12:59:58 +00:00
|
|
|
|
if( !board()->IsElementVisible( LAYER_TRACKS ) )
|
|
|
|
|
return false;
|
2018-02-22 06:53:16 +00:00
|
|
|
|
break;
|
|
|
|
|
|
2013-09-04 08:56:06 +00:00
|
|
|
|
case PCB_VIA_T:
|
2020-08-24 12:59:58 +00:00
|
|
|
|
{
|
|
|
|
|
if( !board()->IsElementVisible( LAYER_VIAS ) )
|
|
|
|
|
return false;
|
2018-01-18 08:55:06 +00:00
|
|
|
|
|
2020-08-24 12:59:58 +00:00
|
|
|
|
const VIA* via = static_cast<const VIA*>( aItem );
|
2019-12-28 00:55:11 +00:00
|
|
|
|
|
2020-08-24 12:59:58 +00:00
|
|
|
|
// For vias it is enough if only one of its layers is visible
|
|
|
|
|
return ( board()->GetVisibleLayers() & via->GetLayerSet() ).any();
|
|
|
|
|
}
|
2013-09-04 08:56:06 +00:00
|
|
|
|
|
2013-12-05 13:52:08 +00:00
|
|
|
|
case PCB_MODULE_T:
|
2017-12-20 15:16:46 +00:00
|
|
|
|
{
|
2018-11-11 20:06:23 +00:00
|
|
|
|
// In modedit, we do not want to select the module itself.
|
2017-05-07 00:09:03 +00:00
|
|
|
|
if( m_editModules )
|
|
|
|
|
return false;
|
2017-12-20 15:16:46 +00:00
|
|
|
|
|
2018-11-11 20:06:23 +00:00
|
|
|
|
// Allow selection of footprints if some part of the footprint is visible.
|
2018-03-08 01:40:50 +00:00
|
|
|
|
|
2018-11-11 20:06:23 +00:00
|
|
|
|
MODULE* module = const_cast<MODULE*>( static_cast<const MODULE*>( aItem ) );
|
2018-03-08 01:40:50 +00:00
|
|
|
|
|
2020-08-24 12:59:58 +00:00
|
|
|
|
for( BOARD_ITEM* item : module->GraphicalItems() )
|
2018-03-08 01:40:50 +00:00
|
|
|
|
{
|
2019-07-28 18:30:56 +00:00
|
|
|
|
if( Selectable( item, true ) )
|
2018-11-11 20:06:23 +00:00
|
|
|
|
return true;
|
2018-03-08 01:40:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-24 12:59:58 +00:00
|
|
|
|
for( D_PAD* pad : module->Pads() )
|
2018-11-11 20:06:23 +00:00
|
|
|
|
{
|
2019-07-28 18:30:56 +00:00
|
|
|
|
if( Selectable( pad, true ) )
|
2018-11-11 20:06:23 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2013-12-05 13:52:08 +00:00
|
|
|
|
|
2020-08-24 12:59:58 +00:00
|
|
|
|
for( ZONE_CONTAINER* zone : module->Zones() )
|
2019-11-16 22:31:18 +00:00
|
|
|
|
{
|
|
|
|
|
if( Selectable( zone, true ) )
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-11 20:06:23 +00:00
|
|
|
|
return false;
|
2017-12-20 15:16:46 +00:00
|
|
|
|
}
|
2013-09-09 08:10:02 +00:00
|
|
|
|
|
2013-12-19 09:10:42 +00:00
|
|
|
|
case PCB_MODULE_TEXT_T:
|
2018-11-11 20:06:23 +00:00
|
|
|
|
// Multiple selection is only allowed in modedit mode. In pcbnew, you have to select
|
|
|
|
|
// module subparts 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.
|
2018-11-12 21:59:02 +00:00
|
|
|
|
if( !m_editModules && !checkVisibilityOnly )
|
|
|
|
|
{
|
2020-06-27 11:57:40 +00:00
|
|
|
|
if( m_multiple && !settings->GetHighContrast() )
|
2018-11-12 21:59:02 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2015-02-18 16:53:46 +00:00
|
|
|
|
|
2018-08-07 17:37:41 +00:00
|
|
|
|
if( !m_editModules && !view()->IsVisible( aItem ) )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
break;
|
2013-12-19 09:10:42 +00:00
|
|
|
|
|
2013-09-04 08:56:06 +00:00
|
|
|
|
case PCB_MODULE_EDGE_T:
|
2020-09-03 12:15:08 +00:00
|
|
|
|
// Module edge selections are only allowed in modedit mode.
|
|
|
|
|
if( !m_editModules )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2013-12-18 15:26:21 +00:00
|
|
|
|
case PCB_PAD_T:
|
2015-02-18 00:10:20 +00:00
|
|
|
|
{
|
2018-11-11 20:06:23 +00:00
|
|
|
|
// Multiple selection is only allowed in modedit mode. In pcbnew, you have to select
|
|
|
|
|
// module subparts 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.
|
2018-11-12 21:59:02 +00:00
|
|
|
|
if( !m_editModules && !checkVisibilityOnly )
|
2017-01-21 17:32:45 +00:00
|
|
|
|
{
|
2018-11-12 21:59:02 +00:00
|
|
|
|
if( m_multiple )
|
2017-01-21 17:32:45 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-09-09 00:23:09 +00:00
|
|
|
|
|
|
|
|
|
if( aItem->Type() == PCB_PAD_T )
|
2018-03-21 01:14:57 +00:00
|
|
|
|
{
|
|
|
|
|
auto pad = static_cast<const D_PAD*>( aItem );
|
|
|
|
|
|
2018-11-12 21:59:02 +00:00
|
|
|
|
// In pcbnew, locked modules prevent individual pad selection.
|
|
|
|
|
// In modedit, we don't enforce this as the module is assumed to be edited by design.
|
|
|
|
|
if( !m_editModules && !checkVisibilityOnly )
|
|
|
|
|
{
|
|
|
|
|
if( pad->GetParent() && pad->GetParent()->IsLocked() )
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-25 14:23:38 +00:00
|
|
|
|
// Check render mode (from the Items tab) first
|
|
|
|
|
switch( pad->GetAttribute() )
|
|
|
|
|
{
|
|
|
|
|
case PAD_ATTRIB_STANDARD:
|
|
|
|
|
case PAD_ATTRIB_HOLE_NOT_PLATED:
|
|
|
|
|
if( !board()->IsElementVisible( LAYER_PADS_TH ) )
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PAD_ATTRIB_CONN:
|
|
|
|
|
case PAD_ATTRIB_SMD:
|
|
|
|
|
if( pad->IsOnLayer( F_Cu ) && !board()->IsElementVisible( LAYER_PAD_FR ) )
|
|
|
|
|
return false;
|
|
|
|
|
else if( pad->IsOnLayer( B_Cu ) && !board()->IsElementVisible( LAYER_PAD_BK ) )
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise, pads are selectable if any draw layer is visible
|
|
|
|
|
|
2018-03-21 01:14:57 +00:00
|
|
|
|
// Shortcut: check copper layer visibility
|
|
|
|
|
if( board()->IsLayerVisible( F_Cu ) && pad->IsOnLayer( F_Cu ) )
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if( board()->IsLayerVisible( B_Cu ) && pad->IsOnLayer( B_Cu ) )
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
// Now check the non-copper layers
|
|
|
|
|
|
|
|
|
|
bool draw_layer_visible = false;
|
|
|
|
|
|
|
|
|
|
int pad_layers[KIGFX::VIEW::VIEW_MAX_LAYERS], pad_layers_count;
|
|
|
|
|
pad->ViewGetLayers( pad_layers, pad_layers_count );
|
|
|
|
|
|
|
|
|
|
for( int i = 0; i < pad_layers_count; ++i )
|
|
|
|
|
{
|
|
|
|
|
// NOTE: Only checking the regular layers (not GAL meta-layers)
|
|
|
|
|
if( ( ( pad_layers[i] < PCB_LAYER_ID_COUNT ) &&
|
|
|
|
|
board()->IsLayerVisible( static_cast<PCB_LAYER_ID>( pad_layers[i] ) ) ) )
|
|
|
|
|
{
|
|
|
|
|
draw_layer_visible = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return draw_layer_visible;
|
|
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2014-07-09 10:10:27 +00:00
|
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
|
case PCB_GROUP_T:
|
|
|
|
|
{
|
2020-08-12 11:23:30 +00:00
|
|
|
|
PCB_GROUP* group = const_cast<PCB_GROUP*>( static_cast<const PCB_GROUP*>( aItem ) );
|
2020-08-11 19:37:07 +00:00
|
|
|
|
|
|
|
|
|
// Similar to logic for module, a group is selectable if any of its
|
|
|
|
|
// members are. (This recurses)
|
|
|
|
|
for( auto item : group->GetItems() )
|
|
|
|
|
{
|
|
|
|
|
if( Selectable( item, true ) )
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-12-13 08:06:38 +00:00
|
|
|
|
|
|
|
|
|
case PCB_MARKER_T: // Always selectable
|
|
|
|
|
return true;
|
|
|
|
|
|
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
|
2019-11-05 14:20:50 +00:00
|
|
|
|
return board()->IsLayerVisible( aItem->GetLayer() )
|
|
|
|
|
&& aItem->ViewGetLOD( aItem->GetLayer(), view() ) < view()->GetScale();
|
2013-09-06 14:04:12 +00:00
|
|
|
|
}
|
2013-09-19 15:02:57 +00:00
|
|
|
|
|
2017-06-12 09:16:30 +00:00
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-25 13:01:22 +00:00
|
|
|
|
highlight( aItem, SELECTED, &m_selection );
|
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
|
|
|
|
{
|
2019-06-25 13:01:22 +00:00
|
|
|
|
unhighlight( aItem, SELECTED, &m_selection );
|
2013-12-04 09:58:51 +00:00
|
|
|
|
|
|
|
|
|
if( m_selection.Empty() )
|
2014-07-09 14:50:31 +00:00
|
|
|
|
m_locked = true;
|
2013-10-02 08:21:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-25 13:01:22 +00:00
|
|
|
|
void SELECTION_TOOL::highlight( BOARD_ITEM* aItem, int aMode, PCBNEW_SELECTION* aGroup )
|
2013-12-18 13:33:34 +00:00
|
|
|
|
{
|
2020-08-11 19:37:07 +00:00
|
|
|
|
highlightInternal( aItem, aMode, aGroup, false );
|
|
|
|
|
|
|
|
|
|
view()->Update( aItem );
|
|
|
|
|
|
|
|
|
|
// Many selections are very temporal and updating the display each time just
|
|
|
|
|
// creates noise.
|
|
|
|
|
if( aMode == BRIGHTENED )
|
|
|
|
|
getView()->MarkTargetDirty( KIGFX::TARGET_OVERLAY );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SELECTION_TOOL::highlightInternal( BOARD_ITEM* aItem, int aMode, PCBNEW_SELECTION* aGroup, bool isChild )
|
|
|
|
|
{
|
2020-08-15 15:46:52 +00:00
|
|
|
|
wxLogTrace( "GRP", wxString::Format( "highlight() of %s %p",
|
2020-08-11 19:37:07 +00:00
|
|
|
|
aItem->GetSelectMenuText( m_frame->GetUserUnits() ) ), aItem );
|
2018-01-28 09:35:33 +00:00
|
|
|
|
if( aMode == SELECTED )
|
|
|
|
|
aItem->SetSelected();
|
|
|
|
|
else if( aMode == BRIGHTENED )
|
|
|
|
|
aItem->SetBrightened();
|
|
|
|
|
|
2019-06-25 13:01:22 +00:00
|
|
|
|
if( aGroup )
|
|
|
|
|
{
|
|
|
|
|
// Hide the original item, so it is shown only on overlay
|
|
|
|
|
view()->Hide( aItem, true );
|
2016-11-04 21:29:47 +00:00
|
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
|
if( !isChild || aMode == BRIGHTENED )
|
|
|
|
|
aGroup->Add( aItem );
|
2019-06-25 13:01:22 +00:00
|
|
|
|
}
|
2018-01-28 09:35:33 +00:00
|
|
|
|
|
2019-04-21 23:45:34 +00:00
|
|
|
|
// Modules are treated in a special way - when they are highlighted, we have to
|
|
|
|
|
// highlight all the parts that make the module, not the module itself
|
2016-11-04 21:29:47 +00:00
|
|
|
|
if( aItem->Type() == PCB_MODULE_T )
|
|
|
|
|
{
|
2020-08-11 19:37:07 +00:00
|
|
|
|
static_cast<MODULE*>( aItem )->RunOnChildren( [&]( BOARD_ITEM* titem ) {
|
|
|
|
|
highlightInternal( titem, aMode, aGroup, true ); } );
|
|
|
|
|
}
|
|
|
|
|
else if( aItem->Type() == PCB_GROUP_T )
|
|
|
|
|
{
|
2020-08-12 11:23:30 +00:00
|
|
|
|
static_cast<PCB_GROUP*>( aItem )->RunOnChildren( [&]( BOARD_ITEM* titem ) {
|
2020-08-11 19:37:07 +00:00
|
|
|
|
highlightInternal( titem, aMode, aGroup, true ); } );
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-06-25 13:01:22 +00:00
|
|
|
|
|
|
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
|
void SELECTION_TOOL::unhighlight( BOARD_ITEM* aItem, int aMode, PCBNEW_SELECTION* aGroup )
|
|
|
|
|
{
|
|
|
|
|
unhighlightInternal( aItem, aMode, aGroup, false );
|
2017-03-03 12:42:28 +00:00
|
|
|
|
|
2019-07-01 21:14:36 +00:00
|
|
|
|
view()->Update( aItem );
|
|
|
|
|
|
|
|
|
|
// Many selections are very temporal and updating the display each time just
|
|
|
|
|
// creates noise.
|
|
|
|
|
if( aMode == BRIGHTENED )
|
|
|
|
|
getView()->MarkTargetDirty( KIGFX::TARGET_OVERLAY );
|
2013-12-18 13:33:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
|
void SELECTION_TOOL::unhighlightInternal( BOARD_ITEM* aItem, int aMode, PCBNEW_SELECTION* aGroup, bool isChild )
|
2013-12-18 13:33:34 +00:00
|
|
|
|
{
|
2020-08-15 15:46:52 +00:00
|
|
|
|
wxLogTrace( "GRP", wxString::Format( "unhighlight() of %s %p",
|
2020-08-11 19:37:07 +00:00
|
|
|
|
aItem->GetSelectMenuText( m_frame->GetUserUnits() ) ), aItem );
|
2018-01-28 09:35:33 +00:00
|
|
|
|
if( aMode == SELECTED )
|
|
|
|
|
aItem->ClearSelected();
|
|
|
|
|
else if( aMode == BRIGHTENED )
|
|
|
|
|
aItem->ClearBrightened();
|
|
|
|
|
|
2019-06-25 13:01:22 +00:00
|
|
|
|
if( aGroup )
|
|
|
|
|
{
|
|
|
|
|
aGroup->Remove( aItem );
|
2018-01-28 09:35:33 +00:00
|
|
|
|
|
2019-06-25 13:01:22 +00:00
|
|
|
|
// Restore original item visibility
|
|
|
|
|
view()->Hide( aItem, false );
|
2020-08-11 19:37:07 +00:00
|
|
|
|
|
|
|
|
|
// N.B. if we clear the selection flag for sub-elements, we need to also
|
|
|
|
|
// remove the element from the selection group (if it exists)
|
|
|
|
|
if( isChild )
|
|
|
|
|
view()->Update( aItem );
|
2019-06-25 13:01:22 +00:00
|
|
|
|
}
|
2016-12-02 17:58:12 +00:00
|
|
|
|
|
2019-04-21 23:45:34 +00:00
|
|
|
|
// Modules are treated in a special way - when they are highlighted, we have to
|
|
|
|
|
// highlight all the parts that make the module, not the module itself
|
2016-11-04 21:29:47 +00:00
|
|
|
|
if( aItem->Type() == PCB_MODULE_T )
|
2016-12-09 11:04:32 +00:00
|
|
|
|
{
|
2020-08-11 19:37:07 +00:00
|
|
|
|
static_cast<MODULE*>( aItem )->RunOnChildren( [&]( BOARD_ITEM* titem ) {
|
|
|
|
|
unhighlightInternal( titem, aMode, aGroup, true ); } );
|
|
|
|
|
}
|
|
|
|
|
else if( aItem->Type() == PCB_GROUP_T )
|
|
|
|
|
{
|
2020-08-12 11:23:30 +00:00
|
|
|
|
static_cast<PCB_GROUP*>( aItem )->RunOnChildren( [&]( BOARD_ITEM* titem ) {
|
2020-08-11 19:37:07 +00:00
|
|
|
|
unhighlightInternal( titem, aMode, aGroup, true ); } );
|
2016-12-09 11:04:32 +00:00
|
|
|
|
}
|
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;
|
2018-11-11 20:06:23 +00:00
|
|
|
|
VECTOR2I margin = getView()->ToWorld( VECTOR2I( 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
|
|
|
|
|
|
|
|
|
|
2019-05-05 10:33:34 +00:00
|
|
|
|
static EDA_RECT getRect( const BOARD_ITEM* aItem )
|
|
|
|
|
{
|
|
|
|
|
if( aItem->Type() == PCB_MODULE_T )
|
|
|
|
|
return static_cast<const MODULE*>( aItem )->GetFootprintRect();
|
|
|
|
|
|
|
|
|
|
return aItem->GetBoundingBox();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2017-08-01 16:00:30 +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;
|
2017-08-01 16:00:30 +00:00
|
|
|
|
}*/
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
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 )
|
|
|
|
|
{
|
2017-03-11 21:18:45 +00:00
|
|
|
|
if( !aItem || !aOther )
|
|
|
|
|
return 0;
|
|
|
|
|
|
2015-07-30 11:49:35 +00:00
|
|
|
|
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
|
|
|
|
|
2018-03-18 21:44:28 +00:00
|
|
|
|
// The general idea here is that if the user clicks directly on a small item inside a larger
|
|
|
|
|
// one, then they want the small item. The quintessential case of this is clicking on a pad
|
|
|
|
|
// within a footprint, but we also apply it for text within a footprint, footprints within
|
|
|
|
|
// larger footprints, and vias within either larger pads or longer tracks.
|
|
|
|
|
//
|
|
|
|
|
// These "guesses" presume there is area within the larger item to click in to select it. If
|
|
|
|
|
// an item is mostly covered by smaller items within it, then the guesses are inappropriate as
|
|
|
|
|
// there might not be any area left to click to select the larger item. In this case we must
|
|
|
|
|
// leave the items in the collector and bring up a Selection Clarification menu.
|
|
|
|
|
//
|
|
|
|
|
// We currently check for pads and text mostly covering a footprint, but we don’t check for
|
|
|
|
|
// smaller footprints mostly covering a larger footprint.
|
|
|
|
|
//
|
2019-07-28 18:30:56 +00:00
|
|
|
|
void SELECTION_TOOL::GuessSelectionCandidates( GENERAL_COLLECTOR& aCollector,
|
|
|
|
|
const VECTOR2I& aWhere ) const
|
2014-05-13 09:22:51 +00:00
|
|
|
|
{
|
2018-11-02 23:50:33 +00:00
|
|
|
|
std::set<BOARD_ITEM*> preferred;
|
2015-02-18 16:53:46 +00:00
|
|
|
|
std::set<BOARD_ITEM*> rejected;
|
2017-12-21 09:28:13 +00:00
|
|
|
|
std::set<BOARD_ITEM*> forced;
|
2018-11-02 23:50:33 +00:00
|
|
|
|
wxPoint where( aWhere.x, aWhere.y );
|
2017-12-21 09:28:13 +00:00
|
|
|
|
|
2017-12-21 11:51:47 +00:00
|
|
|
|
// footprints which are below this percentage of the largest footprint will be considered
|
|
|
|
|
// for selection; all others will not
|
2017-12-22 00:49:35 +00:00
|
|
|
|
constexpr double footprintToFootprintMinRatio = 0.20;
|
|
|
|
|
// pads which are below this percentage of their parent's area will exclude their parent
|
|
|
|
|
constexpr double padToFootprintMinRatio = 0.45;
|
|
|
|
|
// footprints containing items with items-to-footprint area ratio higher than this will be
|
2017-12-21 09:28:13 +00:00
|
|
|
|
// forced to stay on the list
|
2020-02-03 16:05:56 +00:00
|
|
|
|
constexpr double footprintMaxCoverRatio = 0.90;
|
2017-12-22 00:49:35 +00:00
|
|
|
|
constexpr double viaToPadMinRatio = 0.50;
|
2017-12-21 09:28:13 +00:00
|
|
|
|
constexpr double trackViaLengthRatio = 2.0;
|
|
|
|
|
constexpr double trackTrackLengthRatio = 0.3;
|
|
|
|
|
constexpr double textToFeatureMinRatio = 0.2;
|
|
|
|
|
constexpr 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).
|
2017-12-21 09:28:13 +00:00
|
|
|
|
constexpr double commonAreaRatio = 0.6;
|
2015-02-18 16:53:46 +00:00
|
|
|
|
|
2018-11-02 23:50:33 +00:00
|
|
|
|
PCB_LAYER_ID activeLayer = (PCB_LAYER_ID) view()->GetTopLayer();
|
|
|
|
|
LSET silkLayers( 2, B_SilkS, F_SilkS );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
2018-11-02 23:50:33 +00:00
|
|
|
|
if( silkLayers[activeLayer] )
|
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
|
|
|
|
|
2018-11-02 23:50:33 +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
|
|
|
|
|
2018-11-02 23:50:33 +00:00
|
|
|
|
// Zone edges are very specific; zone fills much less so.
|
2020-02-03 15:44:41 +00:00
|
|
|
|
if( aCollector.CountType( PCB_ZONE_AREA_T ) > 0 )
|
2018-09-16 20:32:36 +00:00
|
|
|
|
{
|
|
|
|
|
for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
|
|
|
|
|
{
|
2018-11-02 23:50:33 +00:00
|
|
|
|
if( aCollector[i]->Type() == PCB_ZONE_AREA_T )
|
2018-10-08 13:27:41 +00:00
|
|
|
|
{
|
2018-11-02 23:50:33 +00:00
|
|
|
|
auto zone = static_cast<ZONE_CONTAINER*>( aCollector[i] );
|
|
|
|
|
|
2018-11-17 00:34:12 +00:00
|
|
|
|
if( zone->HitTestForEdge( where, 5 * aCollector.GetGuide()->OnePixelInIU() ) )
|
2018-11-02 23:50:33 +00:00
|
|
|
|
preferred.insert( zone );
|
|
|
|
|
else
|
|
|
|
|
rejected.insert( zone );
|
2018-10-08 13:27:41 +00:00
|
|
|
|
}
|
2018-09-16 20:32:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-02 23:50:33 +00:00
|
|
|
|
if( preferred.size() > 0 )
|
2018-09-16 20:32:36 +00:00
|
|
|
|
{
|
2018-11-02 23:50:33 +00:00
|
|
|
|
aCollector.Empty();
|
|
|
|
|
|
|
|
|
|
for( BOARD_ITEM* item : preferred )
|
|
|
|
|
aCollector.Append( item );
|
|
|
|
|
return;
|
2018-09-16 20:32:36 +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 );
|
|
|
|
|
|
2017-12-22 00:49:35 +00:00
|
|
|
|
if( item->Type() == PCB_MODULE_T )
|
|
|
|
|
{
|
|
|
|
|
// when text area is small compared to an overlapping footprint,
|
|
|
|
|
// then it's a clear sign the text is the selection target
|
|
|
|
|
if( areaRatio < textToFootprintMinRatio && itemCommonRatio < commonAreaRatio )
|
|
|
|
|
rejected.insert( item );
|
|
|
|
|
}
|
2015-02-18 16:53:46 +00:00
|
|
|
|
|
|
|
|
|
switch( item->Type() )
|
2015-02-18 00:10:20 +00:00
|
|
|
|
{
|
|
|
|
|
case PCB_TRACE_T:
|
2019-05-17 00:13:21 +00:00
|
|
|
|
case PCB_ARC_T:
|
2015-02-18 00:10:20 +00:00
|
|
|
|
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
|
|
|
|
|
2020-02-03 02:23:27 +00:00
|
|
|
|
if( aCollector.CountType( PCB_MODULE_EDGE_T ) + aCollector.CountType( PCB_LINE_T ) > 1 )
|
|
|
|
|
{
|
|
|
|
|
// Prefer exact hits to sloppy ones
|
|
|
|
|
int accuracy = KiROUND( 5 * aCollector.GetGuide()->OnePixelInIU() );
|
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
|
|
for( int dist = 0; dist < accuracy; ++dist )
|
|
|
|
|
{
|
|
|
|
|
for( int i = 0; i < aCollector.GetCount(); ++i )
|
|
|
|
|
{
|
|
|
|
|
if( DRAWSEGMENT* drawSegment = dynamic_cast<DRAWSEGMENT*>( aCollector[i] ) )
|
|
|
|
|
{
|
|
|
|
|
if( drawSegment->HitTest( where, dist ) )
|
|
|
|
|
{
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( found )
|
|
|
|
|
{
|
|
|
|
|
// throw out everything that is more sloppy than what we found
|
|
|
|
|
for( int i = 0; i < aCollector.GetCount(); ++i )
|
|
|
|
|
{
|
|
|
|
|
if( DRAWSEGMENT* drawSegment = dynamic_cast<DRAWSEGMENT*>( aCollector[i] ) )
|
|
|
|
|
{
|
|
|
|
|
if( !drawSegment->HitTest( where, dist ) )
|
|
|
|
|
rejected.insert( drawSegment );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we're done now
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-21 09:28:13 +00:00
|
|
|
|
if( aCollector.CountType( PCB_PAD_T ) > 0 )
|
|
|
|
|
{
|
|
|
|
|
for( int i = 0; i < aCollector.GetCount(); ++i )
|
|
|
|
|
{
|
|
|
|
|
if( D_PAD* pad = dyn_cast<D_PAD*>( aCollector[i] ) )
|
|
|
|
|
{
|
2017-12-22 00:49:35 +00:00
|
|
|
|
MODULE* parent = pad->GetParent();
|
|
|
|
|
double ratio = calcRatio( calcArea( pad ), calcArea( parent ) );
|
2017-12-21 09:28:13 +00:00
|
|
|
|
|
|
|
|
|
// when pad area is small compared to the parent footprint,
|
|
|
|
|
// then it is a clear sign the pad is the selection target
|
2017-12-22 00:49:35 +00:00
|
|
|
|
if( ratio < padToFootprintMinRatio )
|
2017-12-21 09:28:13 +00:00
|
|
|
|
rejected.insert( pad->GetParent() );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-08 23:05:25 +00:00
|
|
|
|
bool hasNonModules = false;
|
|
|
|
|
|
|
|
|
|
for( int i = 0; i < aCollector.GetCount(); ++i )
|
|
|
|
|
{
|
|
|
|
|
if( aCollector[i]->Type() != PCB_MODULE_T )
|
|
|
|
|
{
|
|
|
|
|
hasNonModules = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-03 15:44:41 +00:00
|
|
|
|
if( aCollector.CountType( PCB_MODULE_T ) > 0 )
|
2015-02-18 16:53:46 +00:00
|
|
|
|
{
|
|
|
|
|
double maxArea = calcMaxArea( aCollector, PCB_MODULE_T );
|
2017-08-01 16:00:30 +00:00
|
|
|
|
BOX2D viewportD = getView()->GetViewport();
|
|
|
|
|
BOX2I viewport( VECTOR2I( viewportD.GetPosition() ), VECTOR2I( viewportD.GetSize() ) );
|
2018-05-25 11:58:43 +00:00
|
|
|
|
double maxCoverRatio = footprintMaxCoverRatio;
|
|
|
|
|
|
|
|
|
|
// MODULE::CoverageRatio() doesn't take zone handles & borders into account so just
|
|
|
|
|
// use a more aggressive cutoff point if zones are involved.
|
|
|
|
|
if( aCollector.CountType( PCB_ZONE_AREA_T ) )
|
|
|
|
|
maxCoverRatio /= 2;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
2017-08-01 16:00:30 +00:00
|
|
|
|
for( int i = 0; i < aCollector.GetCount(); ++i )
|
2014-05-13 09:22:51 +00:00
|
|
|
|
{
|
2017-08-01 16:00:30 +00:00
|
|
|
|
if( MODULE* mod = dyn_cast<MODULE*>( aCollector[i] ) )
|
2015-07-15 12:08:52 +00:00
|
|
|
|
{
|
2017-12-21 11:51:47 +00:00
|
|
|
|
// filter out components larger than the viewport
|
|
|
|
|
if( mod->ViewBBox().Contains( viewport ) )
|
|
|
|
|
rejected.insert( mod );
|
2017-12-22 00:49:35 +00:00
|
|
|
|
// footprints completely covered with other features have no other
|
|
|
|
|
// means of selection, so must be kept
|
2018-05-25 11:58:43 +00:00
|
|
|
|
else if( mod->CoverageRatio( aCollector ) > maxCoverRatio )
|
2017-12-22 00:49:35 +00:00
|
|
|
|
rejected.erase( mod );
|
|
|
|
|
// if a footprint is much smaller than the largest overlapping
|
2018-02-22 10:10:48 +00:00
|
|
|
|
// footprint then it should be considered for selection
|
|
|
|
|
else if( calcRatio( calcArea( mod ), maxArea ) <= footprintToFootprintMinRatio )
|
|
|
|
|
continue;
|
2018-11-02 23:50:33 +00:00
|
|
|
|
// reject ALL OTHER footprints if there's still something else left
|
|
|
|
|
// to select
|
2020-04-08 23:05:25 +00:00
|
|
|
|
else if( hasNonModules )
|
2017-08-01 16:00:30 +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
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2017-12-22 00:49:35 +00:00
|
|
|
|
if( item->Type() == PCB_MODULE_T && areaRatio < padToFootprintMinRatio )
|
2015-02-18 00:10:20 +00:00
|
|
|
|
rejected.insert( item );
|
|
|
|
|
|
2017-12-22 00:49:35 +00:00
|
|
|
|
if( item->Type() == PCB_PAD_T && areaRatio < viaToPadMinRatio )
|
2015-02-18 00:10:20 +00:00
|
|
|
|
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
|
|
|
|
{
|
2017-03-11 21:18:45 +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
|
|
|
|
|
2017-12-22 00:49:35 +00:00
|
|
|
|
if( ratio < padToFootprintMinRatio && 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
|
|
|
|
{
|
2020-07-07 04:51:12 +00:00
|
|
|
|
aCollector.Transfer( item );
|
2015-07-15 12:08:52 +00:00
|
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
|
}
|
2020-08-11 19:37:07 +00:00
|
|
|
|
|
|
|
|
|
FilterCollectorForGroups( aCollector );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SELECTION_TOOL::FilterCollectorForGroups( GENERAL_COLLECTOR& aCollector ) const
|
|
|
|
|
{
|
|
|
|
|
std::unordered_set<BOARD_ITEM*> toAdd;
|
|
|
|
|
|
|
|
|
|
// If any element is a member of a group, replace those elements with the top containing group.
|
|
|
|
|
for( int j = 0; j < aCollector.GetCount(); ++j )
|
|
|
|
|
{
|
2020-08-12 11:23:30 +00:00
|
|
|
|
PCB_GROUP* aTop = board()->TopLevelGroup( aCollector[j], m_enteredGroup );
|
2020-08-11 19:37:07 +00:00
|
|
|
|
|
|
|
|
|
if( aTop != NULL )
|
|
|
|
|
{
|
|
|
|
|
if( aTop != aCollector[j] )
|
|
|
|
|
{
|
|
|
|
|
toAdd.insert( aTop );
|
|
|
|
|
aCollector.Remove( aCollector[j] );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if( m_enteredGroup != NULL &&
|
|
|
|
|
m_enteredGroup->GetItems().find( aCollector[j] ) == m_enteredGroup->GetItems().end() )
|
|
|
|
|
{
|
|
|
|
|
// If a group is entered, no selections of objects not in the group.
|
|
|
|
|
aCollector.Remove( aCollector[j] );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for( BOARD_ITEM* item : toAdd )
|
|
|
|
|
{
|
|
|
|
|
if( !aCollector.HasItem( item ) )
|
|
|
|
|
{
|
|
|
|
|
aCollector.Append( item );
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-05-13 09:22:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
|
2017-04-20 10:06:58 +00:00
|
|
|
|
int SELECTION_TOOL::updateSelection( const TOOL_EVENT& aEvent )
|
|
|
|
|
{
|
|
|
|
|
getView()->Update( &m_selection );
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-05-14 19:21:10 +00:00
|
|
|
|
int SELECTION_TOOL::UpdateMenu( const TOOL_EVENT& aEvent )
|
|
|
|
|
{
|
|
|
|
|
ACTION_MENU* actionMenu = aEvent.Parameter<ACTION_MENU*>();
|
|
|
|
|
CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( actionMenu );
|
|
|
|
|
|
|
|
|
|
if( conditionalMenu )
|
|
|
|
|
conditionalMenu->Evaluate( m_selection );
|
|
|
|
|
|
|
|
|
|
if( actionMenu )
|
|
|
|
|
actionMenu->UpdateAll();
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SELECTION_TOOL::setTransitions()
|
|
|
|
|
{
|
|
|
|
|
Go( &SELECTION_TOOL::UpdateMenu, ACTIONS::updateMenu.MakeEvent() );
|
|
|
|
|
|
|
|
|
|
Go( &SELECTION_TOOL::Main, PCB_ACTIONS::selectionActivate.MakeEvent() );
|
|
|
|
|
Go( &SELECTION_TOOL::CursorSelection, PCB_ACTIONS::selectionCursor.MakeEvent() );
|
|
|
|
|
Go( &SELECTION_TOOL::ClearSelection, PCB_ACTIONS::selectionClear.MakeEvent() );
|
|
|
|
|
|
|
|
|
|
Go( &SELECTION_TOOL::SelectItem, PCB_ACTIONS::selectItem.MakeEvent() );
|
|
|
|
|
Go( &SELECTION_TOOL::SelectItems, PCB_ACTIONS::selectItems.MakeEvent() );
|
|
|
|
|
Go( &SELECTION_TOOL::UnselectItem, PCB_ACTIONS::unselectItem.MakeEvent() );
|
|
|
|
|
Go( &SELECTION_TOOL::UnselectItems, PCB_ACTIONS::unselectItems.MakeEvent() );
|
|
|
|
|
Go( &SELECTION_TOOL::SelectionMenu, PCB_ACTIONS::selectionMenu.MakeEvent() );
|
|
|
|
|
|
2019-05-15 22:49:48 +00:00
|
|
|
|
Go( &SELECTION_TOOL::find, ACTIONS::find.MakeEvent() );
|
2019-05-14 19:21:10 +00:00
|
|
|
|
|
|
|
|
|
Go( &SELECTION_TOOL::filterSelection, PCB_ACTIONS::filterSelection.MakeEvent() );
|
2019-11-22 19:29:07 +00:00
|
|
|
|
Go( &SELECTION_TOOL::expandConnection, PCB_ACTIONS::selectConnection.MakeEvent() );
|
2019-05-14 19:21:10 +00:00
|
|
|
|
Go( &SELECTION_TOOL::selectNet, PCB_ACTIONS::selectNet.MakeEvent() );
|
|
|
|
|
Go( &SELECTION_TOOL::selectSameSheet, PCB_ACTIONS::selectSameSheet.MakeEvent() );
|
|
|
|
|
Go( &SELECTION_TOOL::selectSheetContents, PCB_ACTIONS::selectOnSheetFromEeschema.MakeEvent() );
|
|
|
|
|
Go( &SELECTION_TOOL::updateSelection, EVENTS::SelectedItemsModified );
|
2020-08-15 18:18:04 +00:00
|
|
|
|
Go( &SELECTION_TOOL::updateSelection, EVENTS::SelectedItemsMoved );
|
2020-08-19 22:15:44 +00:00
|
|
|
|
|
|
|
|
|
Go( &SELECTION_TOOL::SelectAll, ACTIONS::selectAll.MakeEvent() );
|
2019-05-14 19:21:10 +00:00
|
|
|
|
}
|