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",
|
||||
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 );
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
* 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" );
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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