bulk improvements for selection and edit tools (GAL) disambiguatin heuristics and smarter grid alignment
This commit is contained in:
parent
a46a92f918
commit
e5deafb4bb
|
@ -58,8 +58,27 @@ TOOL_ACTION COMMON_ACTIONS::findDummy( "pcbnew.Find.Dummy", // only block the ho
|
||||||
TOOL_ACTION COMMON_ACTIONS::findMove( "pcbnew.InteractiveSelection.FindMove",
|
TOOL_ACTION COMMON_ACTIONS::findMove( "pcbnew.InteractiveSelection.FindMove",
|
||||||
AS_GLOBAL, 'T');
|
AS_GLOBAL, 'T');
|
||||||
|
|
||||||
|
|
||||||
// Edit tool actions
|
// 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",
|
TOOL_ACTION COMMON_ACTIONS::editActivate( "pcbnew.InteractiveEdit",
|
||||||
AS_GLOBAL, 'M',
|
AS_GLOBAL, 'M',
|
||||||
"Move", "Moves the selected item(s)", AF_ACTIVATE );
|
"Move", "Moves the selected item(s)", AF_ACTIVATE );
|
||||||
|
|
|
@ -236,6 +236,13 @@ public:
|
||||||
|
|
||||||
/// Blocks CTRL+F, it is handled by wxWidgets
|
/// Blocks CTRL+F, it is handled by wxWidgets
|
||||||
static TOOL_ACTION findDummy;
|
static TOOL_ACTION findDummy;
|
||||||
|
|
||||||
|
static TOOL_ACTION editFootprintInFpEditor;
|
||||||
|
static TOOL_ACTION copyPadToSettings;
|
||||||
|
static TOOL_ACTION copySettingsToPads;
|
||||||
|
static TOOL_ACTION globalEditPads;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function TranslateLegacyId()
|
* Function TranslateLegacyId()
|
||||||
* Translates legacy tool ids to the corresponding TOOL_ACTION name.
|
* Translates legacy tool ids to the corresponding TOOL_ACTION name.
|
||||||
|
|
|
@ -22,11 +22,15 @@
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#include <class_board.h>
|
#include <class_board.h>
|
||||||
#include <class_module.h>
|
#include <class_module.h>
|
||||||
#include <class_edge_mod.h>
|
#include <class_edge_mod.h>
|
||||||
#include <class_zone.h>
|
#include <class_zone.h>
|
||||||
#include <wxPcbStruct.h>
|
#include <wxPcbStruct.h>
|
||||||
|
#include <kiway.h>
|
||||||
|
#include <module_editor_frame.h>
|
||||||
|
|
||||||
#include <tool/tool_manager.h>
|
#include <tool/tool_manager.h>
|
||||||
#include <view/view_controls.h>
|
#include <view/view_controls.h>
|
||||||
|
@ -41,6 +45,7 @@
|
||||||
#include "common_actions.h"
|
#include "common_actions.h"
|
||||||
#include "selection_tool.h"
|
#include "selection_tool.h"
|
||||||
#include "edit_tool.h"
|
#include "edit_tool.h"
|
||||||
|
#include "grid_helper.h"
|
||||||
|
|
||||||
EDIT_TOOL::EDIT_TOOL() :
|
EDIT_TOOL::EDIT_TOOL() :
|
||||||
TOOL_INTERACTIVE( "pcbnew.InteractiveEdit" ), m_selectionTool( NULL ),
|
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::remove, SELECTION_CONDITIONS::NotEmpty );
|
||||||
m_selectionTool->AddMenuItem( COMMON_ACTIONS::properties, 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.x = 0;
|
||||||
m_offset.y = 0;
|
m_offset.y = 0;
|
||||||
|
|
||||||
|
@ -82,6 +92,20 @@ bool EDIT_TOOL::Init()
|
||||||
return true;
|
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 )
|
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?
|
// Shall the selection be cleared at the end?
|
||||||
bool unselect = selection.Empty();
|
bool unselect = selection.Empty();
|
||||||
|
|
||||||
// Be sure that there is at least one item that we can modify
|
// Be sure that there is at least one item that we can modify. If nothing was selected before,
|
||||||
if( !makeSelection( selection ) )
|
// try looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection)
|
||||||
|
if( !hoverSelection( selection ) )
|
||||||
{
|
{
|
||||||
setTransitions();
|
setTransitions();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,6 +126,8 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
|
||||||
|
|
||||||
m_dragging = false; // Are selected items being dragged?
|
m_dragging = false; // Are selected items being dragged?
|
||||||
bool restore = false; // Should items' state be restored when finishing the tool?
|
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
|
// By default, modified items need to update their geometry
|
||||||
m_updateFlag = KIGFX::VIEW_ITEM::GEOMETRY;
|
m_updateFlag = KIGFX::VIEW_ITEM::GEOMETRY;
|
||||||
|
@ -109,9 +135,11 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
|
||||||
KIGFX::VIEW_CONTROLS* controls = getViewControls();
|
KIGFX::VIEW_CONTROLS* controls = getViewControls();
|
||||||
PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
|
PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
|
||||||
controls->ShowCursor( true );
|
controls->ShowCursor( true );
|
||||||
controls->SetSnapping( true );
|
//controls->SetSnapping( true );
|
||||||
controls->ForceCursorPosition( false );
|
controls->ForceCursorPosition( false );
|
||||||
|
|
||||||
|
GRID_HELPER grid ( editFrame );
|
||||||
|
|
||||||
// Main loop: keep receiving events
|
// Main loop: keep receiving events
|
||||||
while( OPT_TOOL_EVENT evt = Wait() )
|
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 ) )
|
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 )
|
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 ) -
|
wxPoint movement = wxPoint( m_cursor.x, m_cursor.y ) -
|
||||||
selection.Item<BOARD_ITEM>( 0 )->GetPosition();
|
selection.Item<BOARD_ITEM>( 0 )->GetPosition();
|
||||||
|
|
||||||
|
@ -166,33 +206,52 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
|
||||||
}
|
}
|
||||||
else // Prepare to start dragging
|
else // Prepare to start dragging
|
||||||
{
|
{
|
||||||
if( m_selectionTool->CheckLock() || selection.Empty() )
|
m_selectionTool->SanitizeSelection( );
|
||||||
|
|
||||||
|
if ( selection.Empty() )
|
||||||
break;
|
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
|
// Save items, so changes can be undone
|
||||||
editFrame->OnModify();
|
editFrame->OnModify();
|
||||||
editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
|
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 )
|
if( selection.Size() == 1 )
|
||||||
{
|
{
|
||||||
// Set the current cursor position to the first dragged item origin, so the
|
// Set the current cursor position to the first dragged item origin, so the
|
||||||
// movement vector could be computed later
|
// movement vector could be computed later
|
||||||
m_cursor = VECTOR2I( selection.Item<BOARD_ITEM>( 0 )->GetPosition() );
|
m_cursor = grid.BestDragOrigin ( mousePos, selection.Item<BOARD_ITEM>( 0 ) );
|
||||||
m_offset.x = 0;
|
getViewControls()->ForceCursorPosition ( true, m_cursor );
|
||||||
m_offset.y = 0;
|
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
|
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() -
|
m_offset = static_cast<BOARD_ITEM*>( selection.items.GetPickedItem( 0 ) )->GetPosition() -
|
||||||
wxPoint( origin.x, origin.y );
|
wxPoint( origin.x, origin.y );
|
||||||
|
|
||||||
|
getViewControls()->ForceCursorPosition ( true, origin );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
controls->SetAutoPan( true );
|
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 ) )
|
else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) )
|
||||||
break; // Finish
|
{
|
||||||
|
if (!isDragAndDrop || !lockOverride )
|
||||||
|
break; // Finish
|
||||||
|
|
||||||
|
lockOverride = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_dragging = false;
|
m_dragging = false;
|
||||||
|
@ -231,7 +295,7 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
|
||||||
ratsnest->Recalculate();
|
ratsnest->Recalculate();
|
||||||
|
|
||||||
controls->ShowCursor( false );
|
controls->ShowCursor( false );
|
||||||
controls->SetSnapping( false );
|
//controls->SetSnapping( false );
|
||||||
controls->SetAutoPan( false );
|
controls->SetAutoPan( false );
|
||||||
|
|
||||||
setTransitions();
|
setTransitions();
|
||||||
|
@ -245,7 +309,7 @@ int EDIT_TOOL::Properties( const TOOL_EVENT& aEvent )
|
||||||
const SELECTION& selection = m_selectionTool->GetSelection();
|
const SELECTION& selection = m_selectionTool->GetSelection();
|
||||||
PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
|
PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
|
||||||
|
|
||||||
if( !makeSelection( selection ) )
|
if( !hoverSelection( selection, false ) )
|
||||||
{
|
{
|
||||||
setTransitions();
|
setTransitions();
|
||||||
|
|
||||||
|
@ -258,22 +322,6 @@ int EDIT_TOOL::Properties( const TOOL_EVENT& aEvent )
|
||||||
// Display properties dialog
|
// Display properties dialog
|
||||||
BOARD_ITEM* item = selection.Item<BOARD_ITEM>( 0 );
|
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;
|
std::vector<PICKED_ITEMS_LIST*>& undoList = editFrame->GetScreen()->m_UndoList.m_CommandsList;
|
||||||
|
|
||||||
// Some of properties dialogs alter pointers, so we should deselect them
|
// 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?
|
// Shall the selection be cleared at the end?
|
||||||
bool unselect = selection.Empty();
|
bool unselect = selection.Empty();
|
||||||
|
|
||||||
if( !makeSelection( selection ) || m_selectionTool->CheckLock() )
|
if( !hoverSelection( selection ) )
|
||||||
{
|
{
|
||||||
setTransitions();
|
setTransitions();
|
||||||
|
|
||||||
|
@ -371,7 +419,7 @@ int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent )
|
||||||
// Shall the selection be cleared at the end?
|
// Shall the selection be cleared at the end?
|
||||||
bool unselect = selection.Empty();
|
bool unselect = selection.Empty();
|
||||||
|
|
||||||
if( !makeSelection( selection ) || m_selectionTool->CheckLock() )
|
if( !hoverSelection( selection ) )
|
||||||
{
|
{
|
||||||
setTransitions();
|
setTransitions();
|
||||||
|
|
||||||
|
@ -421,7 +469,7 @@ int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent )
|
||||||
{
|
{
|
||||||
const SELECTION& selection = m_selectionTool->GetSelection();
|
const SELECTION& selection = m_selectionTool->GetSelection();
|
||||||
|
|
||||||
if( !makeSelection( selection ) || m_selectionTool->CheckLock() )
|
if( !hoverSelection( selection ) )
|
||||||
{
|
{
|
||||||
setTransitions();
|
setTransitions();
|
||||||
|
|
||||||
|
@ -477,40 +525,58 @@ void EDIT_TOOL::remove( BOARD_ITEM* aItem )
|
||||||
// Default removal procedure
|
// Default removal procedure
|
||||||
case PCB_MODULE_TEXT_T:
|
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:
|
case TEXTE_MODULE::TEXT_is_REFERENCE:
|
||||||
DisplayError( getEditFrame<PCB_BASE_FRAME>(), _( "Cannot delete REFERENCE!" ) );
|
DisplayError( getEditFrame<PCB_BASE_FRAME>(), _( "Cannot delete component reference." ) );
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case TEXTE_MODULE::TEXT_is_VALUE:
|
case TEXTE_MODULE::TEXT_is_VALUE:
|
||||||
DisplayError( getEditFrame<PCB_BASE_FRAME>(), _( "Cannot delete VALUE!" ) );
|
DisplayError( getEditFrame<PCB_BASE_FRAME>(), _( "Cannot delete component value." ) );
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case TEXTE_MODULE::TEXT_is_DIVERS: // suppress warnings
|
case TEXTE_MODULE::TEXT_is_DIVERS: // suppress warnings
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/* no break */
|
|
||||||
|
|
||||||
case PCB_PAD_T:
|
|
||||||
case PCB_MODULE_EDGE_T:
|
|
||||||
if( m_editModules )
|
if( m_editModules )
|
||||||
{
|
{
|
||||||
|
|
||||||
MODULE* module = static_cast<MODULE*>( aItem->GetParent() );
|
MODULE* module = static_cast<MODULE*>( aItem->GetParent() );
|
||||||
module->SetLastEditTime();
|
module->SetLastEditTime();
|
||||||
|
|
||||||
board->m_Status_Pcb = 0; // it is done in the legacy view
|
board->m_Status_Pcb = 0; // it is done in the legacy view
|
||||||
aItem->DeleteStructure();
|
aItem->DeleteStructure();
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
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_LINE_T: // a segment not on copper layers
|
||||||
case PCB_TEXT_T: // a text on a layer
|
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
|
default: // other types do not need to (or should not) be handled
|
||||||
assert( false );
|
assert( false );
|
||||||
return;
|
return;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getView()->Remove( aItem );
|
getView()->Remove( aItem );
|
||||||
|
@ -541,6 +606,8 @@ void EDIT_TOOL::setTransitions()
|
||||||
Go( &EDIT_TOOL::Flip, COMMON_ACTIONS::flip.MakeEvent() );
|
Go( &EDIT_TOOL::Flip, COMMON_ACTIONS::flip.MakeEvent() );
|
||||||
Go( &EDIT_TOOL::Remove, COMMON_ACTIONS::remove.MakeEvent() );
|
Go( &EDIT_TOOL::Remove, COMMON_ACTIONS::remove.MakeEvent() );
|
||||||
Go( &EDIT_TOOL::Properties, COMMON_ACTIONS::properties.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();
|
RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
|
||||||
|
|
||||||
ratsnest->ClearSimple();
|
ratsnest->ClearSimple();
|
||||||
|
|
||||||
for( unsigned int i = 0; i < selection.items.GetCount(); ++i )
|
for( unsigned int i = 0; i < selection.items.GetCount(); ++i )
|
||||||
{
|
{
|
||||||
BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
|
BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
|
||||||
|
@ -579,16 +647,28 @@ wxPoint EDIT_TOOL::getModificationPoint( const SELECTION& aSelection )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EDIT_TOOL::hoverSelection( const SELECTION& aSelection, bool aSanitize )
|
||||||
bool EDIT_TOOL::makeSelection( const SELECTION& aSelection )
|
|
||||||
{
|
{
|
||||||
if( aSelection.Empty() ) // Try to find an item that could be modified
|
if( aSelection.Empty() ) // Try to find an item that could be modified
|
||||||
|
{
|
||||||
m_toolMgr->RunAction( COMMON_ACTIONS::selectionCursor, true );
|
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();
|
return !aSelection.Empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EDIT_TOOL::processChanges( const PICKED_ITEMS_LIST* aList )
|
void EDIT_TOOL::processChanges( const PICKED_ITEMS_LIST* aList )
|
||||||
{
|
{
|
||||||
for( unsigned int i = 0; i < aList->GetCount(); ++i )
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -147,10 +147,25 @@ private:
|
||||||
|
|
||||||
///> If there are no items currently selected, it tries to choose the item that is under
|
///> 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.
|
///> 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.
|
///> Updates view with the changes in the list.
|
||||||
void processChanges( const PICKED_ITEMS_LIST* aList );
|
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
|
#endif
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
* 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 Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||||
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
||||||
*
|
*
|
||||||
|
@ -22,14 +22,20 @@
|
||||||
* or you may write to the Free Software Foundation, Inc.,
|
* or you may write to the Free Software Foundation, Inc.,
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
*/
|
*/
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
|
||||||
|
|
||||||
#include <class_board.h>
|
#include <class_board.h>
|
||||||
#include <class_board_item.h>
|
#include <class_board_item.h>
|
||||||
#include <class_track.h>
|
#include <class_track.h>
|
||||||
#include <class_module.h>
|
#include <class_module.h>
|
||||||
|
#include <class_pcb_text.h>
|
||||||
|
#include <class_drawsegment.h>
|
||||||
|
|
||||||
|
|
||||||
#include <wxPcbStruct.h>
|
#include <wxPcbStruct.h>
|
||||||
#include <collectors.h>
|
#include <collectors.h>
|
||||||
|
@ -69,9 +75,12 @@ SELECTION_TOOL::~SELECTION_TOOL()
|
||||||
void SELECTION_TOOL::Reset( RESET_REASON aReason )
|
void SELECTION_TOOL::Reset( RESET_REASON aReason )
|
||||||
{
|
{
|
||||||
if( aReason == TOOL_BASE::MODEL_RELOAD )
|
if( aReason == TOOL_BASE::MODEL_RELOAD )
|
||||||
|
{
|
||||||
// Remove pointers to the selected items from containers
|
// Remove pointers to the selected items from containers
|
||||||
// without changing their properties (as they are already deleted)
|
// without changing their properties (as they are already deleted)
|
||||||
|
m_selection.group->Clear();
|
||||||
m_selection.clear();
|
m_selection.clear();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
// Restore previous properties of selected items and remove them from containers
|
// Restore previous properties of selected items and remove them from containers
|
||||||
clearSelection();
|
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;
|
BOARD_ITEM* item;
|
||||||
GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
|
GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
|
||||||
GENERAL_COLLECTOR collector;
|
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 )
|
if( m_editModules )
|
||||||
collector.Collect( getModel<BOARD>(), GENERAL_COLLECTOR::ModuleItems,
|
collector.Collect( getModel<BOARD>(), GENERAL_COLLECTOR::ModuleItems,
|
||||||
wxPoint( aWhere.x, aWhere.y ), guide );
|
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,
|
collector.Collect( getModel<BOARD>(), GENERAL_COLLECTOR::AllBoardItems,
|
||||||
wxPoint( aWhere.x, aWhere.y ), guide );
|
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() )
|
switch( collector.GetCount() )
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
if( !m_additive )
|
if( !m_additive && anyCollected )
|
||||||
clearSelection();
|
clearSelection();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -281,21 +295,9 @@ bool SELECTION_TOOL::selectCursor( const VECTOR2I& aWhere, bool aAllowDisambigua
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Remove unselectable items
|
|
||||||
for( int i = collector.GetCount() - 1; i >= 0; --i )
|
// Apply some ugly heuristics to avoid disambiguation menus whenever possible
|
||||||
{
|
guessSelectionCandidates( collector );
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let's see if there is still disambiguation in selection..
|
// Let's see if there is still disambiguation in selection..
|
||||||
if( collector.GetCount() == 1 )
|
if( collector.GetCount() == 1 )
|
||||||
|
@ -304,9 +306,11 @@ bool SELECTION_TOOL::selectCursor( const VECTOR2I& aWhere, bool aAllowDisambigua
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if( collector.GetCount() > 1 )
|
||||||
else if( aAllowDisambiguation && collector.GetCount() > 1 )
|
|
||||||
{
|
{
|
||||||
|
if( aOnDrag )
|
||||||
|
Wait ( TOOL_EVENT( TC_ANY, TA_MOUSE_UP, BUT_LEFT ) );
|
||||||
|
|
||||||
item = disambiguationMenu( &collector );
|
item = disambiguationMenu( &collector );
|
||||||
|
|
||||||
if( item )
|
if( item )
|
||||||
|
@ -354,6 +358,9 @@ bool SELECTION_TOOL::selectMultiple()
|
||||||
|
|
||||||
if( evt->IsMouseUp( BUT_LEFT ) )
|
if( evt->IsMouseUp( BUT_LEFT ) )
|
||||||
{
|
{
|
||||||
|
// End drawing the selection box
|
||||||
|
m_selArea->ViewSetVisible( false );
|
||||||
|
|
||||||
// Mark items within the selection box as selected
|
// Mark items within the selection box as selected
|
||||||
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
|
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
|
||||||
BOX2I selectionBox = m_selArea->ViewBBox();
|
BOX2I selectionBox = m_selArea->ViewBBox();
|
||||||
|
@ -407,11 +414,10 @@ void SELECTION_TOOL::setTransitions()
|
||||||
Go( &SELECTION_TOOL::findMove, COMMON_ACTIONS::findMove.MakeEvent() );
|
Go( &SELECTION_TOOL::findMove, COMMON_ACTIONS::findMove.MakeEvent() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SELECTION_LOCK_FLAGS SELECTION_TOOL::CheckLock()
|
||||||
bool SELECTION_TOOL::CheckLock()
|
|
||||||
{
|
{
|
||||||
if( !m_locked || m_editModules )
|
if( !m_locked || m_editModules )
|
||||||
return false;
|
return SELECTION_UNLOCKED;
|
||||||
|
|
||||||
bool containsLocked = false;
|
bool containsLocked = false;
|
||||||
|
|
||||||
|
@ -438,18 +444,22 @@ bool SELECTION_TOOL::CheckLock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( containsLocked &&
|
if( containsLocked )
|
||||||
!IsOK( m_frame, _( "Selection contains locked items. Do you want to continue?" ) ) )
|
|
||||||
{
|
{
|
||||||
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;
|
m_locked = false;
|
||||||
|
|
||||||
return false;
|
return SELECTION_UNLOCKED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int SELECTION_TOOL::CursorSelection( const TOOL_EVENT& aEvent )
|
int SELECTION_TOOL::CursorSelection( const TOOL_EVENT& aEvent )
|
||||||
{
|
{
|
||||||
selectCursor( getView()->ToWorld( getViewControls()->GetMousePosition() ) );
|
selectCursor( getView()->ToWorld( getViewControls()->GetMousePosition() ) );
|
||||||
|
@ -511,7 +521,6 @@ void SELECTION_TOOL::findCallback( BOARD_ITEM* aItem )
|
||||||
{
|
{
|
||||||
clearSelection();
|
clearSelection();
|
||||||
select( aItem );
|
select( aItem );
|
||||||
getView()->SetCenter( VECTOR2D( aItem->GetPosition() ) );
|
|
||||||
|
|
||||||
// Inform other potentially interested tools
|
// Inform other potentially interested tools
|
||||||
m_toolMgr->ProcessEvent( SelectedEvent );
|
m_toolMgr->ProcessEvent( SelectedEvent );
|
||||||
|
@ -562,8 +571,9 @@ void SELECTION_TOOL::clearSelection()
|
||||||
{
|
{
|
||||||
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( *it );
|
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( *it );
|
||||||
|
|
||||||
item->ViewSetVisible( true );
|
item->ViewHide ( false );
|
||||||
item->ClearSelected();
|
item->ClearSelected();
|
||||||
|
item->ViewUpdate ( KIGFX::VIEW_ITEM::GEOMETRY ) ;
|
||||||
}
|
}
|
||||||
m_selection.clear();
|
m_selection.clear();
|
||||||
|
|
||||||
|
@ -732,12 +742,21 @@ bool SELECTION_TOOL::selectable( const BOARD_ITEM* aItem ) const
|
||||||
case PCB_MODULE_TEXT_T:
|
case PCB_MODULE_TEXT_T:
|
||||||
if( m_multiple && !m_editModules )
|
if( m_multiple && !m_editModules )
|
||||||
return false;
|
return false;
|
||||||
break;
|
return aItem->ViewIsVisible() && board->IsLayerVisible( aItem->GetLayer() );
|
||||||
|
|
||||||
// These are not selectable
|
// These are not selectable
|
||||||
case PCB_MODULE_EDGE_T:
|
case PCB_MODULE_EDGE_T:
|
||||||
case PCB_PAD_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 NOT_USED:
|
||||||
case TYPE_NOT_INIT:
|
case TYPE_NOT_INIT:
|
||||||
|
@ -762,6 +781,14 @@ void SELECTION_TOOL::select( BOARD_ITEM* aItem )
|
||||||
module->RunOnChildren( boost::bind( &SELECTION_TOOL::selectVisually, this, _1 ) );
|
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 );
|
selectVisually( aItem );
|
||||||
ITEM_PICKER picker( aItem );
|
ITEM_PICKER picker( aItem );
|
||||||
m_selection.items.PushItem( picker );
|
m_selection.items.PushItem( picker );
|
||||||
|
@ -811,7 +838,7 @@ void SELECTION_TOOL::selectVisually( BOARD_ITEM* aItem ) const
|
||||||
m_selection.group->Add( aItem );
|
m_selection.group->Add( aItem );
|
||||||
|
|
||||||
// Hide the original item, so it is shown only on overlay
|
// Hide the original item, so it is shown only on overlay
|
||||||
aItem->ViewSetVisible( false );
|
aItem->ViewHide (true);
|
||||||
aItem->SetSelected();
|
aItem->SetSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -821,8 +848,9 @@ void SELECTION_TOOL::unselectVisually( BOARD_ITEM* aItem ) const
|
||||||
m_selection.group->Remove( aItem );
|
m_selection.group->Remove( aItem );
|
||||||
|
|
||||||
// Restore original item visibility
|
// Restore original item visibility
|
||||||
aItem->ViewSetVisible( true );
|
aItem->ViewHide (false);
|
||||||
aItem->ClearSelected();
|
aItem->ClearSelected();
|
||||||
|
aItem->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -869,38 +897,304 @@ void SELECTION_TOOL::highlightNet( const VECTOR2I& aPoint )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double calcArea ( BOARD_ITEM *aItem )
|
||||||
BOARD_ITEM* SELECTION_TOOL::prefer( GENERAL_COLLECTOR& aCollector, const KICAD_T aTypes[] ) const
|
|
||||||
{
|
{
|
||||||
BOARD_ITEM* preferred = NULL;
|
switch (aItem -> Type() )
|
||||||
|
|
||||||
int typesNr = 0;
|
|
||||||
while( aTypes[typesNr++] != EOT ); // count number of types, excluding the sentinel (EOT)
|
|
||||||
|
|
||||||
for( int i = 0; i < aCollector.GetCount(); ++i )
|
|
||||||
{
|
{
|
||||||
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
|
BOARD_ITEM *item = aCollector[j];
|
||||||
break;
|
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()
|
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::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::UnselectedEvent( TC_MESSAGE, TA_ACTION, "pcbnew.InteractiveSelection.unselected" );
|
||||||
const TOOL_EVENT SELECTION_TOOL::ClearedEvent( TC_MESSAGE, TA_ACTION, "pcbnew.InteractiveSelection.cleared" );
|
const TOOL_EVENT SELECTION_TOOL::ClearedEvent( TC_MESSAGE, TA_ACTION, "pcbnew.InteractiveSelection.cleared" );
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
* 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 Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||||
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
||||||
*
|
*
|
||||||
|
@ -78,6 +78,13 @@ private:
|
||||||
friend class SELECTION_TOOL;
|
friend class SELECTION_TOOL;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum SELECTION_LOCK_FLAGS
|
||||||
|
{
|
||||||
|
SELECTION_UNLOCKED = 0,
|
||||||
|
SELECTION_LOCK_OVERRIDE = 1,
|
||||||
|
SELECTION_LOCKED = 2
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class SELECTION_TOOL
|
* Class SELECTION_TOOL
|
||||||
*
|
*
|
||||||
|
@ -149,7 +156,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
///> Checks if the user has agreed to modify locked items for the given selection.
|
///> 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.
|
///> Select a single item under cursor event handler.
|
||||||
int CursorSelection( const TOOL_EVENT& aEvent );
|
int CursorSelection( const TOOL_EVENT& aEvent );
|
||||||
|
@ -157,6 +164,10 @@ public:
|
||||||
///> Clear current selection event handler.
|
///> Clear current selection event handler.
|
||||||
int ClearSelection( const TOOL_EVENT& aEvent );
|
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.
|
///> Item selection event handler.
|
||||||
int SelectItem( const TOOL_EVENT& aEvent );
|
int SelectItem( const TOOL_EVENT& aEvent );
|
||||||
|
|
||||||
|
@ -183,7 +194,7 @@ private:
|
||||||
* a menu is shown, otherise function finishes without selecting anything.
|
* a menu is shown, otherise function finishes without selecting anything.
|
||||||
* @return True if an item was selected, false otherwise.
|
* @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()
|
* Function selectMultiple()
|
||||||
|
@ -291,14 +302,12 @@ private:
|
||||||
void highlightNet( const VECTOR2I& aPoint );
|
void highlightNet( const VECTOR2I& aPoint );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function prefer()
|
* Function guessSelectionCandidates()
|
||||||
* Checks if collector's list contains only single entry of asked types. If so, it returns it.
|
* 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 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()
|
* Function generateMenu()
|
||||||
|
|
|
@ -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 );
|
||||||
|
}
|
Loading…
Reference in New Issue