bulk improvements for selection and edit tools (GAL) disambiguatin heuristics and smarter grid alignment

This commit is contained in:
Tomasz Włostowski 2015-02-18 01:10:20 +01:00
parent a46a92f918
commit e5deafb4bb
9 changed files with 1046 additions and 125 deletions

View File

@ -58,8 +58,27 @@ TOOL_ACTION COMMON_ACTIONS::findDummy( "pcbnew.Find.Dummy", // only block the ho
TOOL_ACTION COMMON_ACTIONS::findMove( "pcbnew.InteractiveSelection.FindMove",
AS_GLOBAL, 'T');
// Edit tool actions
TOOL_ACTION COMMON_ACTIONS::editFootprintInFpEditor( "pcbnew.InteractiveEdit.editFootprintInFpEditor",
AS_CONTEXT, MD_CTRL + 'E',
"Open in Footprint Editor",
"Opens the selected footprint in the Footprint Editor" );
TOOL_ACTION COMMON_ACTIONS::copyPadToSettings ( "pcbnew.InteractiveEdit.copyPadToSettings",
AS_CONTEXT, 0,
"Copy pad settings to Current Settings",
"Copies the properties of selected pad to the current template pad settings." );
TOOL_ACTION COMMON_ACTIONS::copySettingsToPads ( "pcbnew.InteractiveEdit.copySettingsToPads",
AS_CONTEXT, 0,
"Copy Current Settings to pads",
"Copies the current template pad settings to the selected pad(s)." );
TOOL_ACTION COMMON_ACTIONS::globalEditPads ( "pcbnew.InteractiveEdit.globalPadEdit",
AS_CONTEXT, 0,
"Global Pad Edition",
"Changes pad properties globally." );
TOOL_ACTION COMMON_ACTIONS::editActivate( "pcbnew.InteractiveEdit",
AS_GLOBAL, 'M',
"Move", "Moves the selected item(s)", AF_ACTIVATE );

View File

@ -236,6 +236,13 @@ public:
/// Blocks CTRL+F, it is handled by wxWidgets
static TOOL_ACTION findDummy;
static TOOL_ACTION editFootprintInFpEditor;
static TOOL_ACTION copyPadToSettings;
static TOOL_ACTION copySettingsToPads;
static TOOL_ACTION globalEditPads;
/**
* Function TranslateLegacyId()
* Translates legacy tool ids to the corresponding TOOL_ACTION name.

View File

@ -22,11 +22,15 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <limits>
#include <class_board.h>
#include <class_module.h>
#include <class_edge_mod.h>
#include <class_zone.h>
#include <wxPcbStruct.h>
#include <kiway.h>
#include <module_editor_frame.h>
#include <tool/tool_manager.h>
#include <view/view_controls.h>
@ -41,6 +45,7 @@
#include "common_actions.h"
#include "selection_tool.h"
#include "edit_tool.h"
#include "grid_helper.h"
EDIT_TOOL::EDIT_TOOL() :
TOOL_INTERACTIVE( "pcbnew.InteractiveEdit" ), m_selectionTool( NULL ),
@ -74,6 +79,11 @@ bool EDIT_TOOL::Init()
m_selectionTool->AddMenuItem( COMMON_ACTIONS::remove, SELECTION_CONDITIONS::NotEmpty );
m_selectionTool->AddMenuItem( COMMON_ACTIONS::properties, SELECTION_CONDITIONS::NotEmpty );
// Footprint actions
m_selectionTool->AddMenuItem( COMMON_ACTIONS::editFootprintInFpEditor,
SELECTION_CONDITIONS::OnlyType ( PCB_MODULE_T ) &&
SELECTION_CONDITIONS::Count ( 1 ) );
m_offset.x = 0;
m_offset.y = 0;
@ -82,6 +92,20 @@ bool EDIT_TOOL::Init()
return true;
}
bool EDIT_TOOL::invokeInlineRouter()
{
TRACK *track = uniqueSelected<TRACK> ();
VIA *via = uniqueSelected<VIA> ();
if( track || via )
{
printf("Calling interactive drag\n");
m_toolMgr->RunAction( COMMON_ACTIONS::routerInlineDrag, true );
return true;
}
return false;
}
int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
{
@ -90,11 +114,11 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
// Shall the selection be cleared at the end?
bool unselect = selection.Empty();
// Be sure that there is at least one item that we can modify
if( !makeSelection( selection ) )
// Be sure that there is at least one item that we can modify. If nothing was selected before,
// try looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection)
if( !hoverSelection( selection ) )
{
setTransitions();
return 0;
}
@ -102,6 +126,8 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
m_dragging = false; // Are selected items being dragged?
bool restore = false; // Should items' state be restored when finishing the tool?
bool lockOverride = false;
bool isDragAndDrop = false;
// By default, modified items need to update their geometry
m_updateFlag = KIGFX::VIEW_ITEM::GEOMETRY;
@ -109,9 +135,11 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
KIGFX::VIEW_CONTROLS* controls = getViewControls();
PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
controls->ShowCursor( true );
controls->SetSnapping( true );
//controls->SetSnapping( true );
controls->ForceCursorPosition( false );
GRID_HELPER grid ( editFrame );
// Main loop: keep receiving events
while( OPT_TOOL_EVENT evt = Wait() )
{
@ -151,10 +179,22 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
else if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
{
m_cursor = controls->GetCursorPosition();
//if ( invokeInlineRouter ( ) )
// break;
VECTOR2I mousePos = evt->Position();
m_cursor = grid.Align ( evt->Position() );
isDragAndDrop = evt->IsDrag( BUT_LEFT );
if( m_dragging )
{
m_cursor = grid.BestSnapAnchor ( evt->Position(), selection.Item<BOARD_ITEM>( 0 ) );
getViewControls()->ForceCursorPosition ( true, m_cursor );
wxPoint movement = wxPoint( m_cursor.x, m_cursor.y ) -
selection.Item<BOARD_ITEM>( 0 )->GetPosition();
@ -166,33 +206,52 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
}
else // Prepare to start dragging
{
if( m_selectionTool->CheckLock() || selection.Empty() )
m_selectionTool->SanitizeSelection( );
if ( selection.Empty() )
break;
// deal with locked items (override lock or abort the operation)
SELECTION_LOCK_FLAGS lockFlags = m_selectionTool->CheckLock();
if ( lockFlags == SELECTION_LOCKED )
break;
else if ( lockFlags == SELECTION_LOCK_OVERRIDE )
lockOverride = true;
// Save items, so changes can be undone
editFrame->OnModify();
editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
VECTOR2I origin;
if( evt->IsDrag( BUT_LEFT ) )
mousePos = evt->DragOrigin();
// origin = grid.Align ( evt->DragOrigin() );
//else
origin = grid.Align ( mousePos );
if( selection.Size() == 1 )
{
// Set the current cursor position to the first dragged item origin, so the
// movement vector could be computed later
m_cursor = VECTOR2I( selection.Item<BOARD_ITEM>( 0 )->GetPosition() );
m_offset.x = 0;
m_offset.y = 0;
m_cursor = grid.BestDragOrigin ( mousePos, selection.Item<BOARD_ITEM>( 0 ) );
getViewControls()->ForceCursorPosition ( true, m_cursor );
grid.SetAuxAxes ( true, m_cursor );
VECTOR2I o = VECTOR2I( selection.Item<BOARD_ITEM>( 0 )->GetPosition() );
m_offset.x = o.x - m_cursor.x;
m_offset.y = o.y - m_cursor.y;
}
else
{
VECTOR2D origin;
if( evt->IsDrag( BUT_LEFT ) )
origin = getView()->GetGAL()->GetGridPoint( evt->DragOrigin() );
else
origin = getViewControls()->GetCursorPosition();
// Update dragging offset (distance between cursor and the first dragged item)
m_offset = static_cast<BOARD_ITEM*>( selection.items.GetPickedItem( 0 ) )->GetPosition() -
wxPoint( origin.x, origin.y );
getViewControls()->ForceCursorPosition ( true, origin );
}
controls->SetAutoPan( true );
@ -204,7 +263,12 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
}
else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) )
break; // Finish
{
if (!isDragAndDrop || !lockOverride )
break; // Finish
lockOverride = false;
}
}
m_dragging = false;
@ -231,7 +295,7 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
ratsnest->Recalculate();
controls->ShowCursor( false );
controls->SetSnapping( false );
//controls->SetSnapping( false );
controls->SetAutoPan( false );
setTransitions();
@ -245,7 +309,7 @@ int EDIT_TOOL::Properties( const TOOL_EVENT& aEvent )
const SELECTION& selection = m_selectionTool->GetSelection();
PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
if( !makeSelection( selection ) )
if( !hoverSelection( selection, false ) )
{
setTransitions();
@ -258,22 +322,6 @@ int EDIT_TOOL::Properties( const TOOL_EVENT& aEvent )
// Display properties dialog
BOARD_ITEM* item = selection.Item<BOARD_ITEM>( 0 );
// Check if user wants to edit pad or module properties
if( item->Type() == PCB_MODULE_T )
{
VECTOR2D cursor = getViewControls()->GetCursorPosition();
for( D_PAD* pad = static_cast<MODULE*>( item )->Pads(); pad; pad = pad->Next() )
{
if( pad->ViewBBox().Contains( cursor ) )
{
// Turns out that user wants to edit a pad properties
item = pad;
break;
}
}
}
std::vector<PICKED_ITEMS_LIST*>& undoList = editFrame->GetScreen()->m_UndoList.m_CommandsList;
// Some of properties dialogs alter pointers, so we should deselect them
@ -317,7 +365,7 @@ int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
// Shall the selection be cleared at the end?
bool unselect = selection.Empty();
if( !makeSelection( selection ) || m_selectionTool->CheckLock() )
if( !hoverSelection( selection ) )
{
setTransitions();
@ -371,7 +419,7 @@ int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent )
// Shall the selection be cleared at the end?
bool unselect = selection.Empty();
if( !makeSelection( selection ) || m_selectionTool->CheckLock() )
if( !hoverSelection( selection ) )
{
setTransitions();
@ -421,7 +469,7 @@ int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent )
{
const SELECTION& selection = m_selectionTool->GetSelection();
if( !makeSelection( selection ) || m_selectionTool->CheckLock() )
if( !hoverSelection( selection ) )
{
setTransitions();
@ -477,40 +525,58 @@ void EDIT_TOOL::remove( BOARD_ITEM* aItem )
// Default removal procedure
case PCB_MODULE_TEXT_T:
{
if( m_editModules )
{
TEXTE_MODULE* text = static_cast<TEXTE_MODULE*>( aItem );
TEXTE_MODULE* text = static_cast<TEXTE_MODULE*>( aItem );
switch( text->GetType() )
{
switch( text->GetType() )
{
case TEXTE_MODULE::TEXT_is_REFERENCE:
DisplayError( getEditFrame<PCB_BASE_FRAME>(), _( "Cannot delete REFERENCE!" ) );
DisplayError( getEditFrame<PCB_BASE_FRAME>(), _( "Cannot delete component reference." ) );
return;
case TEXTE_MODULE::TEXT_is_VALUE:
DisplayError( getEditFrame<PCB_BASE_FRAME>(), _( "Cannot delete VALUE!" ) );
DisplayError( getEditFrame<PCB_BASE_FRAME>(), _( "Cannot delete component value." ) );
return;
case TEXTE_MODULE::TEXT_is_DIVERS: // suppress warnings
break;
}
}
}
/* no break */
case PCB_PAD_T:
case PCB_MODULE_EDGE_T:
if( m_editModules )
{
MODULE* module = static_cast<MODULE*>( aItem->GetParent() );
module->SetLastEditTime();
board->m_Status_Pcb = 0; // it is done in the legacy view
aItem->DeleteStructure();
}
return;
break;
}
case PCB_PAD_T:
case PCB_MODULE_EDGE_T:
{
MODULE* module = static_cast<MODULE*>( aItem->GetParent() );
module->SetLastEditTime();
board->m_Status_Pcb = 0; // it is done in the legacy view
if(!m_editModules)
{
if(aItem->Type() == PCB_PAD_T && module->GetPadCount() == 1)
{
DisplayError( getEditFrame<PCB_BASE_FRAME>(), _( "Cannot delete the only remaining pad of the module (modules on PCB must have at least one pad)." ) );
return;
}
getView()->Remove( aItem );
board->Remove( aItem );
}
aItem->DeleteStructure();
return;
}
case PCB_LINE_T: // a segment not on copper layers
case PCB_TEXT_T: // a text on a layer
@ -526,7 +592,6 @@ void EDIT_TOOL::remove( BOARD_ITEM* aItem )
default: // other types do not need to (or should not) be handled
assert( false );
return;
break;
}
getView()->Remove( aItem );
@ -541,6 +606,8 @@ void EDIT_TOOL::setTransitions()
Go( &EDIT_TOOL::Flip, COMMON_ACTIONS::flip.MakeEvent() );
Go( &EDIT_TOOL::Remove, COMMON_ACTIONS::remove.MakeEvent() );
Go( &EDIT_TOOL::Properties, COMMON_ACTIONS::properties.MakeEvent() );
Go( &EDIT_TOOL::editFootprintInFpEditor, COMMON_ACTIONS::editFootprintInFpEditor.MakeEvent() );
}
@ -550,6 +617,7 @@ void EDIT_TOOL::updateRatsnest( bool aRedraw )
RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
ratsnest->ClearSimple();
for( unsigned int i = 0; i < selection.items.GetCount(); ++i )
{
BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
@ -579,16 +647,28 @@ wxPoint EDIT_TOOL::getModificationPoint( const SELECTION& aSelection )
}
}
bool EDIT_TOOL::makeSelection( const SELECTION& aSelection )
bool EDIT_TOOL::hoverSelection( const SELECTION& aSelection, bool aSanitize )
{
if( aSelection.Empty() ) // Try to find an item that could be modified
{
m_toolMgr->RunAction( COMMON_ACTIONS::selectionCursor, true );
if ( m_selectionTool->CheckLock() == SELECTION_LOCKED )
{
m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
return false;
}
}
if ( aSanitize )
m_selectionTool->SanitizeSelection();
if ( aSelection.Empty() )
m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
return !aSelection.Empty();
}
void EDIT_TOOL::processChanges( const PICKED_ITEMS_LIST* aList )
{
for( unsigned int i = 0; i < aList->GetCount(); ++i )
@ -626,3 +706,32 @@ void EDIT_TOOL::processChanges( const PICKED_ITEMS_LIST* aList )
}
}
}
int EDIT_TOOL::editFootprintInFpEditor( const TOOL_EVENT& aEvent )
{
MODULE *mod = uniqueSelected <MODULE> ();
if( !mod )
return 0;
PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
editFrame-> SetCurItem( mod );
if( editFrame->GetCurItem()->GetTimeStamp() == 0 ) // Module Editor needs a non null timestamp
{
editFrame->GetCurItem()->SetTimeStamp( GetNewTimeStamp() );
editFrame->OnModify();
}
FOOTPRINT_EDIT_FRAME* editor = (FOOTPRINT_EDIT_FRAME*) editFrame->Kiway().Player( FRAME_PCB_MODULE_EDITOR, true );
editor->Load_Module_From_BOARD( (MODULE*)editFrame->GetCurItem() );
editFrame->SetCurItem( NULL ); // the current module could be deleted by
editor->Show( true );
editor->Raise(); // Iconize( false );
setTransitions();
return 0;
}

View File

@ -147,10 +147,25 @@ private:
///> If there are no items currently selected, it tries to choose the item that is under
///> the cursor or displays a disambiguation menu if there are multpile items.
bool makeSelection( const SELECTION& aSelection );
bool hoverSelection( const SELECTION& aSelection, bool aSanitize = true );
///> Updates view with the changes in the list.
void processChanges( const PICKED_ITEMS_LIST* aList );
int editFootprintInFpEditor( const TOOL_EVENT& aEvent );
bool invokeInlineRouter();
template <class T> T* uniqueSelected()
{
const SELECTION& selection = m_selectionTool->GetSelection();
if(selection.items.GetCount() > 1)
return NULL;
BOARD_ITEM *item = selection.Item<BOARD_ITEM>( 0 );
return dyn_cast<T*> (item);
}
};
#endif

View File

@ -0,0 +1,334 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2014 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <boost/foreach.hpp>
#include <boost/bind.hpp>
#include <wxPcbStruct.h>
#include <class_board.h>
#include <class_module.h>
#include <class_edge_mod.h>
#include <class_zone.h>
#include <class_draw_panel_gal.h>
#include <view/view_controls.h>
#include <gal/graphics_abstraction_layer.h>
#include <geometry/shape_line_chain.h>
#include "grid_helper.h"
GRID_HELPER::GRID_HELPER ( PCB_BASE_FRAME *aFrame ) :
m_frame ( aFrame )
{
}
GRID_HELPER::~GRID_HELPER ()
{
}
void GRID_HELPER::SetGrid ( int aSize )
{
assert ( false );
}
void GRID_HELPER::SetOrigin ( const VECTOR2I& aOrigin )
{
}
VECTOR2I GRID_HELPER::GetGrid ()
{
PCB_SCREEN *screen = m_frame->GetScreen();
const wxRealPoint& size = screen->GetGridSize();
return VECTOR2I ( KiROUND ( size.x ), KiROUND ( size.y ) );
}
VECTOR2I GRID_HELPER::GetOrigin ()
{
return VECTOR2I ( 0, 0 );
}
void GRID_HELPER::SetAuxAxes ( bool aEnable, const VECTOR2I aOrigin, bool aEnableDiagonal)
{
if( aEnable )
m_auxAxis = aOrigin;
else
m_auxAxis = boost::optional <VECTOR2I> ();
m_diagonalAuxAxesEnable = aEnable;
}
VECTOR2I GRID_HELPER::Align ( const VECTOR2I& aPoint )
{
const VECTOR2D gridOffset ( GetOrigin () );
const VECTOR2D gridSize ( GetGrid() );
VECTOR2I nearest ( round( ( aPoint.x - gridOffset.x ) / gridSize.x ) * gridSize.x + gridOffset.x,
round( ( aPoint.y - gridOffset.y ) / gridSize.y ) * gridSize.y + gridOffset.y );
if ( !m_auxAxis )
return nearest;
if ( std::abs ( m_auxAxis->x - aPoint.x) < std::abs ( nearest.x - aPoint.x ) )
nearest.x = m_auxAxis->x;
if ( std::abs ( m_auxAxis->y - aPoint.y) < std::abs ( nearest.y - aPoint.y ) )
nearest.y = m_auxAxis->y;
return nearest;
}
VECTOR2I GRID_HELPER::BestDragOrigin ( const VECTOR2I &aMousePos, BOARD_ITEM *aItem )
{
clearAnchors();
computeAnchors( aItem, aMousePos );
double worldScale = m_frame->GetGalCanvas()->GetGAL()->GetWorldScale();
double lineSnapMinCornerDistance = 50.0 / worldScale;
ANCHOR* nearestOutline = nearestAnchor ( aMousePos, OUTLINE, LSET::AllLayersMask() );
ANCHOR* nearestCorner = nearestAnchor ( aMousePos, CORNER, LSET::AllLayersMask() );
ANCHOR* nearestOrigin = nearestAnchor ( aMousePos, ORIGIN, LSET::AllLayersMask() );
ANCHOR* best = NULL;
double minDist = std::numeric_limits<double>::max();
if (nearestOrigin)
{
minDist = nearestOrigin->Distance(aMousePos);
best = nearestOrigin;
}
if (nearestCorner)
{
double dist = nearestCorner->Distance(aMousePos);
if (dist < minDist)
{
minDist = dist;
best = nearestCorner;
}
}
if (nearestOutline)
{
double dist = nearestOutline->Distance(aMousePos);
if (minDist > lineSnapMinCornerDistance && dist < minDist)
best = nearestOutline;
}
return best ? best->pos : aMousePos;
}
std::set<BOARD_ITEM *> GRID_HELPER::queryVisible ( const BOX2I& aArea )
{
std::set <BOARD_ITEM *> items;
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR>::iterator it, it_end;
m_frame->GetGalCanvas()->GetView()->Query( aArea, selectedItems ); // Get the list of selected items
for( it = selectedItems.begin(), it_end = selectedItems.end(); it != it_end; ++it )
{
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it->first );
if( item->ViewIsVisible() )
items.insert ( item );
}
return items;
}
VECTOR2I GRID_HELPER::BestSnapAnchor ( const VECTOR2I &aOrigin, BOARD_ITEM *aDraggedItem )
{
double worldScale = m_frame->GetGalCanvas()->GetGAL()->GetWorldScale();
int snapRange = (int) (100.0 / worldScale);
BOX2I bb ( VECTOR2I ( aOrigin.x - snapRange / 2, aOrigin.y - snapRange/2) , VECTOR2I (snapRange, snapRange) );
clearAnchors();
BOOST_FOREACH ( BOARD_ITEM *item, queryVisible ( bb ) )
{
computeAnchors(item, aOrigin);
}
LSET layers ( aDraggedItem->GetLayer() );
ANCHOR *nearest = nearestAnchor ( aOrigin, CORNER | SNAPPABLE, layers );
VECTOR2I nearestGrid = Align ( aOrigin );
double gridDist = (nearestGrid - aOrigin).EuclideanNorm();
if (nearest)
{
double snapDist = nearest->Distance ( aOrigin );
if(nearest && snapDist < gridDist)
return nearest->pos;
}
return nearestGrid;
}
void GRID_HELPER::computeAnchors ( BOARD_ITEM *aItem, const VECTOR2I& aRefPos )
{
VECTOR2I origin;
switch ( aItem->Type() )
{
case PCB_MODULE_T:
{
MODULE *mod = static_cast <MODULE *> (aItem);
addAnchor ( mod->GetPosition(), ORIGIN | SNAPPABLE, mod );
for (D_PAD *pad = mod->Pads(); pad; pad = pad->Next() )
addAnchor ( pad->GetPosition(), CORNER | SNAPPABLE, pad );
break;
}
case PCB_MODULE_EDGE_T:
case PCB_LINE_T:
{
DRAWSEGMENT *dseg = static_cast <DRAWSEGMENT*> (aItem);
VECTOR2I start = dseg->GetStart();
VECTOR2I end = dseg->GetEnd();
//LAYER_ID layer = dseg->GetLayer();
switch( dseg->GetShape() )
{
case S_CIRCLE:
{
int r = (start - end).EuclideanNorm();
addAnchor ( start, ORIGIN | SNAPPABLE, dseg );
addAnchor ( start + VECTOR2I ( -r, 0 ) , OUTLINE | SNAPPABLE, dseg );
addAnchor ( start + VECTOR2I ( r, 0 ) , OUTLINE | SNAPPABLE, dseg );
addAnchor ( start + VECTOR2I ( 0, -r ) , OUTLINE | SNAPPABLE, dseg);
addAnchor ( start + VECTOR2I ( 0, r ) , OUTLINE | SNAPPABLE, dseg );
break;
}
case S_ARC:
{
origin = dseg->GetCenter();
addAnchor ( dseg->GetArcStart(), CORNER | SNAPPABLE, dseg );
addAnchor ( dseg->GetArcEnd(), CORNER | SNAPPABLE, dseg );
addAnchor ( origin, ORIGIN | SNAPPABLE, dseg );
break;
}
case S_SEGMENT:
{
origin.x = start.x + ( start.x - end.x ) / 2;
origin.y = start.y + ( start.y - end.y ) / 2;
addAnchor ( start, CORNER | SNAPPABLE, dseg );
addAnchor ( end, CORNER | SNAPPABLE, dseg );
addAnchor ( origin, ORIGIN, dseg );
break;
}
default:
{
origin = dseg->GetStart();
addAnchor ( origin, ORIGIN | SNAPPABLE, dseg );
break;
}
}
break;
}
case PCB_TRACE_T:
{
TRACK *track = static_cast <TRACK*> (aItem);
VECTOR2I start = track->GetStart();
VECTOR2I end = track->GetEnd();
origin.x = start.x + ( start.x - end.x ) / 2;
origin.y = start.y + ( start.y - end.y ) / 2;
addAnchor ( start, CORNER | SNAPPABLE, track );
addAnchor ( end, CORNER | SNAPPABLE, track );
addAnchor ( origin, ORIGIN, track);
break;
}
case PCB_ZONE_AREA_T:
{
const CPolyLine* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline();
int cornersCount = outline->GetCornersCount();
SHAPE_LINE_CHAIN lc;
lc.SetClosed ( true );
for( int i = 0; i < cornersCount; ++i )
{
const VECTOR2I p ( outline->GetPos( i ) );
addAnchor ( p, CORNER, aItem );
lc.Append ( p );
}
addAnchor( lc.NearestPoint ( aRefPos ), OUTLINE, aItem );
break;
}
case PCB_MODULE_TEXT_T:
case PCB_TEXT_T:
addAnchor ( aItem->GetPosition(), ORIGIN, aItem );
default:
break;
}
}
GRID_HELPER::ANCHOR* GRID_HELPER::nearestAnchor ( VECTOR2I aPos, int aFlags, LSET aMatchLayers )
{
double minDist = std::numeric_limits<double>::max();
ANCHOR *best = NULL;
BOOST_FOREACH( ANCHOR& a, m_anchors )
{
if ( !aMatchLayers [ a.item->GetLayer() ] )
continue;
if ( ( aFlags & a.flags ) != aFlags )
continue;
double dist = a.Distance(aPos);
if(dist < minDist)
{
minDist = dist;
best = &a;
}
}
return best;
}

105
pcbnew/tools/grid_helper.h Normal file
View File

@ -0,0 +1,105 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2014 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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
*/
#ifndef __GRID_HELPER_H
#define __GRID_HELPER_H
#include <vector>
#include <math/vector2d.h>
#include <boost/optional.hpp>
#include <layers_id_colors_and_visibility.h>
class PCB_BASE_FRAME;
class GRID_HELPER {
public:
GRID_HELPER ( PCB_BASE_FRAME *aFrame );
~GRID_HELPER ();
void SetGrid ( int aSize );
void SetOrigin ( const VECTOR2I& aOrigin );
VECTOR2I GetGrid ();
VECTOR2I GetOrigin ();
void SetAuxAxes ( bool aEnable, const VECTOR2I aOrigin = VECTOR2I(0, 0), bool aEnableDiagonal = false );
VECTOR2I Align ( const VECTOR2I& aPoint );
VECTOR2I BestDragOrigin ( const VECTOR2I &aMousePos, BOARD_ITEM *aItem );
VECTOR2I BestSnapAnchor ( const VECTOR2I &aOrigin, BOARD_ITEM *aDraggedItem );
private:
enum ANCHOR_FLAGS {
CORNER = 0x1,
OUTLINE = 0x2,
SNAPPABLE = 0x4,
ORIGIN = 0x8
};
struct ANCHOR
{
ANCHOR ( VECTOR2I aPos, int aFlags = CORNER | SNAPPABLE, BOARD_ITEM *aItem = NULL ):
pos (aPos), flags (aFlags), item (aItem) {} ;
VECTOR2I pos;
int flags;
BOARD_ITEM *item;
double Distance ( const VECTOR2I& aP )
{
return (aP - pos).EuclideanNorm();
}
bool CanSnapItem ( const BOARD_ITEM *aItem );
};
std::vector<ANCHOR> m_anchors;
std::set<BOARD_ITEM *> queryVisible ( const BOX2I& aArea );
void addAnchor( VECTOR2I aPos, int aFlags = CORNER | SNAPPABLE, BOARD_ITEM *aItem = NULL )
{
m_anchors.push_back( ANCHOR( aPos, aFlags, aItem ) );
}
ANCHOR* nearestAnchor ( VECTOR2I aPos, int aFlags, LSET aMatchLayers );
void computeAnchors ( BOARD_ITEM *aItem, const VECTOR2I& aRefPos );
void clearAnchors ()
{
m_anchors.clear();
}
PCB_BASE_FRAME* m_frame;
boost::optional<VECTOR2I> m_auxAxis;
bool m_diagonalAuxAxesEnable;
};
#endif

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* Copyright (C) 2013-2015 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
@ -22,14 +22,20 @@
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <limits>
#include <boost/foreach.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <class_board.h>
#include <class_board_item.h>
#include <class_track.h>
#include <class_module.h>
#include <class_pcb_text.h>
#include <class_drawsegment.h>
#include <wxPcbStruct.h>
#include <collectors.h>
@ -69,9 +75,12 @@ SELECTION_TOOL::~SELECTION_TOOL()
void SELECTION_TOOL::Reset( RESET_REASON aReason )
{
if( aReason == TOOL_BASE::MODEL_RELOAD )
{
// Remove pointers to the selected items from containers
// without changing their properties (as they are already deleted)
m_selection.group->Clear();
m_selection.clear();
}
else
// Restore previous properties of selected items and remove them from containers
clearSelection();
@ -250,16 +259,12 @@ void SELECTION_TOOL::toggleSelection( BOARD_ITEM* aItem )
}
bool SELECTION_TOOL::selectCursor( const VECTOR2I& aWhere, bool aAllowDisambiguation )
bool SELECTION_TOOL::selectCursor( const VECTOR2I& aWhere, bool aOnDrag )
{
BOARD_ITEM* item;
GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
GENERAL_COLLECTOR collector;
// Preferred types (they have the priority when if they are covered by a bigger item)
const KICAD_T types[] = { PCB_TRACE_T, PCB_VIA_T, PCB_LINE_T,
PCB_MODULE_EDGE_T, PCB_MODULE_TEXT_T, EOT };
if( m_editModules )
collector.Collect( getModel<BOARD>(), GENERAL_COLLECTOR::ModuleItems,
wxPoint( aWhere.x, aWhere.y ), guide );
@ -267,10 +272,19 @@ bool SELECTION_TOOL::selectCursor( const VECTOR2I& aWhere, bool aAllowDisambigua
collector.Collect( getModel<BOARD>(), GENERAL_COLLECTOR::AllBoardItems,
wxPoint( aWhere.x, aWhere.y ), guide );
bool anyCollected = collector.GetCount() != 0;
// Remove unselectable items
for( int i = collector.GetCount() - 1; i >= 0; --i )
{
if( !selectable( collector[i] ) || ( aOnDrag && collector[i]->IsLocked() ))
collector.Remove( i );
}
switch( collector.GetCount() )
{
case 0:
if( !m_additive )
if( !m_additive && anyCollected )
clearSelection();
return false;
@ -281,21 +295,9 @@ bool SELECTION_TOOL::selectCursor( const VECTOR2I& aWhere, bool aAllowDisambigua
return true;
default:
// Remove unselectable items
for( int i = collector.GetCount() - 1; i >= 0; --i )
{
if( !selectable( collector[i] ) )
collector.Remove( i );
}
// Check if among the selection candidates there is only one instance of preferred type
item = prefer( collector, types );
if( item )
{
toggleSelection( item );
return true;
}
// Apply some ugly heuristics to avoid disambiguation menus whenever possible
guessSelectionCandidates( collector );
// Let's see if there is still disambiguation in selection..
if( collector.GetCount() == 1 )
@ -304,9 +306,11 @@ bool SELECTION_TOOL::selectCursor( const VECTOR2I& aWhere, bool aAllowDisambigua
return true;
}
else if( aAllowDisambiguation && collector.GetCount() > 1 )
else if( collector.GetCount() > 1 )
{
if( aOnDrag )
Wait ( TOOL_EVENT( TC_ANY, TA_MOUSE_UP, BUT_LEFT ) );
item = disambiguationMenu( &collector );
if( item )
@ -354,6 +358,9 @@ bool SELECTION_TOOL::selectMultiple()
if( evt->IsMouseUp( BUT_LEFT ) )
{
// End drawing the selection box
m_selArea->ViewSetVisible( false );
// Mark items within the selection box as selected
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
BOX2I selectionBox = m_selArea->ViewBBox();
@ -407,11 +414,10 @@ void SELECTION_TOOL::setTransitions()
Go( &SELECTION_TOOL::findMove, COMMON_ACTIONS::findMove.MakeEvent() );
}
bool SELECTION_TOOL::CheckLock()
SELECTION_LOCK_FLAGS SELECTION_TOOL::CheckLock()
{
if( !m_locked || m_editModules )
return false;
return SELECTION_UNLOCKED;
bool containsLocked = false;
@ -438,18 +444,22 @@ bool SELECTION_TOOL::CheckLock()
}
}
if( containsLocked &&
!IsOK( m_frame, _( "Selection contains locked items. Do you want to continue?" ) ) )
if( containsLocked )
{
return true;
if ( IsOK( m_frame, _( "Selection contains locked items. Do you want to continue?" ) ) )
{
m_locked = false;
return SELECTION_LOCK_OVERRIDE;
}
else
return SELECTION_LOCKED;
}
m_locked = false;
return false;
return SELECTION_UNLOCKED;
}
int SELECTION_TOOL::CursorSelection( const TOOL_EVENT& aEvent )
{
selectCursor( getView()->ToWorld( getViewControls()->GetMousePosition() ) );
@ -511,7 +521,6 @@ void SELECTION_TOOL::findCallback( BOARD_ITEM* aItem )
{
clearSelection();
select( aItem );
getView()->SetCenter( VECTOR2D( aItem->GetPosition() ) );
// Inform other potentially interested tools
m_toolMgr->ProcessEvent( SelectedEvent );
@ -562,8 +571,9 @@ void SELECTION_TOOL::clearSelection()
{
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( *it );
item->ViewSetVisible( true );
item->ViewHide ( false );
item->ClearSelected();
item->ViewUpdate ( KIGFX::VIEW_ITEM::GEOMETRY ) ;
}
m_selection.clear();
@ -732,12 +742,21 @@ bool SELECTION_TOOL::selectable( const BOARD_ITEM* aItem ) const
case PCB_MODULE_TEXT_T:
if( m_multiple && !m_editModules )
return false;
break;
return aItem->ViewIsVisible() && board->IsLayerVisible( aItem->GetLayer() );
// These are not selectable
case PCB_MODULE_EDGE_T:
case PCB_PAD_T:
return m_editModules;
{
if( m_multiple && !m_editModules )
return false;
MODULE *mod = static_cast<const D_PAD *> (aItem) -> GetParent();
if( mod && mod->IsLocked() )
return false;
break;
}
case NOT_USED:
case TYPE_NOT_INIT:
@ -762,6 +781,14 @@ void SELECTION_TOOL::select( BOARD_ITEM* aItem )
module->RunOnChildren( boost::bind( &SELECTION_TOOL::selectVisually, this, _1 ) );
}
if ( aItem->Type() == PCB_PAD_T )
{
MODULE* module = static_cast<MODULE*>( aItem->GetParent() );
if( m_selection.items.FindItem( module ) >= 0 )
return;
}
selectVisually( aItem );
ITEM_PICKER picker( aItem );
m_selection.items.PushItem( picker );
@ -811,7 +838,7 @@ void SELECTION_TOOL::selectVisually( BOARD_ITEM* aItem ) const
m_selection.group->Add( aItem );
// Hide the original item, so it is shown only on overlay
aItem->ViewSetVisible( false );
aItem->ViewHide (true);
aItem->SetSelected();
}
@ -821,8 +848,9 @@ void SELECTION_TOOL::unselectVisually( BOARD_ITEM* aItem ) const
m_selection.group->Remove( aItem );
// Restore original item visibility
aItem->ViewSetVisible( true );
aItem->ViewHide (false);
aItem->ClearSelected();
aItem->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
}
@ -869,38 +897,304 @@ void SELECTION_TOOL::highlightNet( const VECTOR2I& aPoint )
}
}
BOARD_ITEM* SELECTION_TOOL::prefer( GENERAL_COLLECTOR& aCollector, const KICAD_T aTypes[] ) const
static double calcArea ( BOARD_ITEM *aItem )
{
BOARD_ITEM* preferred = NULL;
int typesNr = 0;
while( aTypes[typesNr++] != EOT ); // count number of types, excluding the sentinel (EOT)
for( int i = 0; i < aCollector.GetCount(); ++i )
switch (aItem -> Type() )
{
KICAD_T type = aCollector[i]->Type();
case PCB_MODULE_T:
return static_cast <MODULE *> (aItem)->GetFootprintRect().GetArea();
for( int j = 0; j < typesNr - 1; ++j ) // Check if the item's type is in our list
case PCB_TRACE_T:
{
if( aTypes[j] == type )
TRACK *t = static_cast<TRACK *> (aItem);
return ( t->GetWidth() + t->GetLength() ) * t->GetWidth();
}
default:
return aItem->GetBoundingBox().GetArea();
}
}
static double calcMinArea ( GENERAL_COLLECTOR& aCollector, KICAD_T aType )
{
double best = std::numeric_limits<double>::max();
if(!aCollector.GetCount())
return 0.0;
for(int i = 0; i < aCollector.GetCount(); i++)
{
BOARD_ITEM *item = aCollector[i];
if(item->Type() == aType)
best = std::min(best, calcArea ( item ));
}
return best;
}
static double calcMaxArea ( GENERAL_COLLECTOR& aCollector, KICAD_T aType )
{
double best = 0.0;
for(int i = 0; i < aCollector.GetCount(); i++)
{
BOARD_ITEM *item = aCollector[i];
if(item->Type() == aType)
best = std::max(best, calcArea ( item ));
}
return best;
}
double calcRatio ( double a, double b )
{
if ( a == 0.0 && b == 0.0 )
return 1.0;
if ( b == 0.0 )
return 10000000.0; // something arbitrarily big for the moment
return a / b;
}
// todo: explain the selection heuristics
void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) const
{
std::set<BOARD_ITEM *> rejected;
const double footprintAreaRatio = 0.2;
const double modulePadMinCoverRatio = 0.45;
const double padViaAreaRatio = 0.5;
const double trackViaLengthRatio = 2.0;
const double trackTrackLengthRatio = 0.3;
const double textToFeatureMinRatio = 0.2;
const double textToFootprintMinRatio = 0.4;
LAYER_ID actLayer = m_frame->GetActiveLayer();
LSET silkLayers(2, B_SilkS, F_SilkS );
if( silkLayers[ actLayer ] )
{
std::set<BOARD_ITEM *> preferred;
for( int i = 0; i < aCollector.GetCount(); ++i )
{
BOARD_ITEM *item = aCollector[i];
if ( item->Type() == PCB_MODULE_TEXT_T || item->Type() == PCB_TEXT_T || item->Type() == PCB_LINE_T )
if ( silkLayers[item->GetLayer() ] )
preferred.insert ( item );
}
if( preferred.size() != 0)
{
aCollector.Empty();
BOOST_FOREACH( BOARD_ITEM *item, preferred )
aCollector.Append( item );
return;
}
}
if (aCollector.CountType ( PCB_MODULE_TEXT_T ) > 0 )
{
for( int i = 0; i < aCollector.GetCount(); ++i )
if ( TEXTE_MODULE *txt = dyn_cast<TEXTE_MODULE *> ( aCollector[i] ) )
{
if( preferred == NULL )
double textArea = calcArea ( txt );
for( int j = 0; j < aCollector.GetCount(); ++j )
{
preferred = aCollector[i]; // save the first matching item
break;
BOARD_ITEM *item = aCollector[j];
double areaRatio = calcRatio ( textArea, calcArea ( item ) );
if (item->Type () == PCB_MODULE_T && areaRatio < textToFootprintMinRatio )
{
printf("rejectModuleN\n");
rejected.insert ( item );
}
switch (item->Type())
{
case PCB_TRACE_T:
case PCB_PAD_T:
case PCB_LINE_T:
case PCB_VIA_T:
case PCB_MODULE_T:
if ( areaRatio > textToFeatureMinRatio )
{
printf("t after moduleRejected\n");
rejected.insert ( txt );
}
break;
default:
break;
}
}
else
}
}
if( aCollector.CountType ( PCB_MODULE_T ) > 0 )
{
double minArea = calcMinArea ( aCollector, PCB_MODULE_T );
double maxArea = calcMaxArea ( aCollector, PCB_MODULE_T );
if( calcRatio(minArea, maxArea) <= footprintAreaRatio )
{
for( int i = 0; i < aCollector.GetCount(); ++i )
if ( MODULE *mod = dyn_cast<MODULE*> ( aCollector[i] ) )
{
return NULL; // there is more than one preferred item, so there is no clear choice
double normalizedArea = calcRatio ( calcArea(mod), maxArea );
if(normalizedArea > footprintAreaRatio)
{
printf("rejectModule1\n");
rejected.insert( mod );
}
}
}
}
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] ) )
{
double ratio = pad->GetParent()->PadCoverageRatio();
if(ratio < modulePadMinCoverRatio)
rejected.insert( pad->GetParent() );
}
}
if( aCollector.CountType ( PCB_VIA_T ) > 0 )
{
for( int i = 0; i < aCollector.GetCount(); ++i )
if ( VIA *via = dyn_cast<VIA*> ( aCollector[i] ) )
{
double viaArea = calcArea ( via );
for( int j = 0; j < aCollector.GetCount(); ++j )
{
BOARD_ITEM *item = aCollector[j];
double areaRatio = calcRatio ( viaArea, calcArea ( item ) );
if( item->Type() == PCB_MODULE_T && areaRatio < modulePadMinCoverRatio )
rejected.insert( item );
if( item->Type() == PCB_PAD_T && areaRatio < padViaAreaRatio )
rejected.insert( item );
if ( TRACK *track = dyn_cast<TRACK*> ( item ) )
{
if( track->GetNetCode() != via->GetNetCode() )
continue;
double lenRatio = (double) ( track->GetLength() + track->GetWidth()) / (double) via->GetWidth();
if( lenRatio > trackViaLengthRatio )
rejected.insert( track );
}
}
}
}
int nTracks = aCollector.CountType ( PCB_TRACE_T );
if( nTracks > 0 )
{
double maxLength = 0.0;
double minLength = std::numeric_limits<double>::max();
double maxArea = 0.0;
for( int i = 0; i < aCollector.GetCount(); ++i )
if ( TRACK *track = dyn_cast<TRACK*> ( aCollector[i] ) )
{
maxLength = std::max( track->GetLength(), maxLength );
maxLength = std::max( (double)track->GetWidth(), maxLength );
minLength = std::min( std::max ( track->GetLength(), (double)track->GetWidth() ), minLength );
double area = ( track->GetLength() + track->GetWidth() * track->GetWidth() );
maxArea = std::max(area, maxArea);
}
if(maxLength > 0.0 && minLength/maxLength < trackTrackLengthRatio && nTracks > 1 )
for( int i = 0; i < aCollector.GetCount(); ++i )
if ( TRACK *track = dyn_cast<TRACK*> ( aCollector[i] ) )
{
double ratio = std::max( (double) track->GetWidth(), track->GetLength()) / maxLength;
if( ratio > trackTrackLengthRatio)
rejected.insert(track);
}
for( int j = 0; j < aCollector.GetCount(); ++j )
{
if ( MODULE *mod = dyn_cast<MODULE*> ( aCollector[j] ) )
{
double ratio = maxArea / mod->GetFootprintRect().GetArea();
if( ratio < modulePadMinCoverRatio )
{
printf("rejectModule\n");
rejected.insert( mod );
}
}
}
}
BOOST_FOREACH(BOARD_ITEM *item, rejected)
{
aCollector.Remove(item);
}
printf("Post-selection: %d\n", aCollector.GetCount() );
}
bool SELECTION_TOOL::SanitizeSelection()
{
std::set <BOARD_ITEM *> rejected;
if ( !m_editModules )
{
for( unsigned int i = 0; i < m_selection.items.GetCount(); ++i )
{
BOARD_ITEM* item = m_selection.Item<BOARD_ITEM>( i );
if( item->Type() == PCB_PAD_T )
{
MODULE *mod = static_cast <MODULE*> ( item->GetParent( ) );
// case 1: module (or its pads) are locked
if( mod && ( mod->PadsLocked( ) || mod->IsLocked( ) ) )
rejected.insert ( item );
// case 2: multi-item selection contains both the module and its pads - remove the pads
if (mod && m_selection.items.FindItem ( mod ) >= 0 )
rejected.insert ( item );
}
}
}
return preferred;
}
while ( !rejected.empty () )
{
BOARD_ITEM *item = *rejected.begin();
int itemIdx = m_selection.items.FindItem( item );
if( itemIdx >= 0 )
m_selection.items.RemovePicker( itemIdx );
rejected.erase(item);
}
return true;
}
void SELECTION_TOOL::generateMenu()
{
@ -931,6 +1225,7 @@ void SELECTION::clear()
}
const TOOL_EVENT SELECTION_TOOL::SelectedEvent( TC_MESSAGE, TA_ACTION, "pcbnew.InteractiveSelection.selected" );
const TOOL_EVENT SELECTION_TOOL::UnselectedEvent( TC_MESSAGE, TA_ACTION, "pcbnew.InteractiveSelection.unselected" );
const TOOL_EVENT SELECTION_TOOL::ClearedEvent( TC_MESSAGE, TA_ACTION, "pcbnew.InteractiveSelection.cleared" );

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* Copyright (C) 2013-2015 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
@ -78,6 +78,13 @@ private:
friend class SELECTION_TOOL;
};
enum SELECTION_LOCK_FLAGS
{
SELECTION_UNLOCKED = 0,
SELECTION_LOCK_OVERRIDE = 1,
SELECTION_LOCKED = 2
};
/**
* Class SELECTION_TOOL
*
@ -149,7 +156,7 @@ public:
}
///> Checks if the user has agreed to modify locked items for the given selection.
bool CheckLock();
SELECTION_LOCK_FLAGS CheckLock();
///> Select a single item under cursor event handler.
int CursorSelection( const TOOL_EVENT& aEvent );
@ -157,6 +164,10 @@ public:
///> Clear current selection event handler.
int ClearSelection( const TOOL_EVENT& aEvent );
///> Makes sure a group selection does not contain items that would cause
///> conflicts when moving/rotating together (e.g. a footprint and one of the same footprint's pads)
bool SanitizeSelection( );
///> Item selection event handler.
int SelectItem( const TOOL_EVENT& aEvent );
@ -183,7 +194,7 @@ private:
* a menu is shown, otherise function finishes without selecting anything.
* @return True if an item was selected, false otherwise.
*/
bool selectCursor( const VECTOR2I& aWhere, bool aAllowDisambiguation = true );
bool selectCursor( const VECTOR2I& aWhere, bool aOnDrag = false);
/**
* Function selectMultiple()
@ -291,14 +302,12 @@ private:
void highlightNet( const VECTOR2I& aPoint );
/**
* Function prefer()
* Checks if collector's list contains only single entry of asked types. If so, it returns it.
* Function guessSelectionCandidates()
* Tries to guess best selection candidates in case multiple items are clicked, by
* doing some braindead heuristics.
* @param aCollector is the collector that has a list of items to be queried.
* @param aTypes is the list of searched/preferred types.
* @return Pointer to the preferred item, if there is only one entry of given type or NULL
* if there are more entries or no entries at all.
*/
BOARD_ITEM* prefer( GENERAL_COLLECTOR& aCollector, const KICAD_T aTypes[] ) const;
void guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) const;
/**
* Function generateMenu()

View File

@ -0,0 +1,28 @@
#include <io_mgr.h>
#include <tool/tool_manager.h>
#include <tools/selection_tool.h>
#include <tools/edit_tool.h>
#include <tools/drawing_tool.h>
#include <tools/point_editor.h>
#include <tools/pcbnew_control.h>
#include <tools/pcb_editor_control.h>
#include <tools/placement_tool.h>
#include <tools/common_actions.h>
#include <router/router_tool.h>
#include <router/length_tuner_tool.h>
void registerAllTools ( TOOL_MANAGER *aToolManager )
{
aToolManager->RegisterTool( new SELECTION_TOOL );
aToolManager->RegisterTool( new ROUTER_TOOL );
aToolManager->RegisterTool( new LENGTH_TUNER_TOOL );
aToolManager->RegisterTool( new EDIT_TOOL );
aToolManager->RegisterTool( new DRAWING_TOOL );
aToolManager->RegisterTool( new POINT_EDITOR );
aToolManager->RegisterTool( new PCBNEW_CONTROL );
aToolManager->RegisterTool( new PCB_EDITOR_CONTROL );
aToolManager->RegisterTool( new PLACEMENT_TOOL );
}