Commit e5deafb4 authored by Tomasz Włostowski's avatar Tomasz Włostowski

bulk improvements for selection and edit tools (GAL) disambiguatin heuristics...

bulk improvements for selection and edit tools (GAL) disambiguatin heuristics and smarter grid alignment
parent a46a92f9
...@@ -58,8 +58,27 @@ TOOL_ACTION COMMON_ACTIONS::findDummy( "pcbnew.Find.Dummy", // only block the ho ...@@ -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: ...@@ -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 @@ ...@@ -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 @@ ...@@ -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() ...@@ -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() ...@@ -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 ) ...@@ -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 ) ...@@ -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 ) ...@@ -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 ) ...@@ -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 ) ...@@ -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 ) ...@@ -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 ) ...@@ -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 ) ...@@ -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 ) ...@@ -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 ) ...@@ -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 ) ...@@ -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 ) ...@@ -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 ) ...@@ -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 ) ...@@ -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() ...@@ -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 ) ...@@ -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 ) ...@@ -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 ) ...@@ -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: ...@@ -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
/*
* 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;
}
\ No newline at end of file
/*
* 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
/* /*
* 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 @@ ...@@ -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() ...@@ -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 ) ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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() ...@@ -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() ...@@ -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() ...@@ -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 ) ...@@ -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() ...@@ -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 ...@@ -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 ) ...@@ -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 ...@@ -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 ...@@ -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 ) ...@@ -869,38 +897,304 @@ void SELECTION_TOOL::highlightNet( const VECTOR2I& aPoint )
} }
} }
static double calcArea ( BOARD_ITEM *aItem )
{
switch (aItem -> Type() )
{
case PCB_MODULE_T:
return static_cast <MODULE *> (aItem)->GetFootprintRect().GetArea();
case PCB_TRACE_T:
{
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 ));
BOARD_ITEM* SELECTION_TOOL::prefer( GENERAL_COLLECTOR& aCollector, const KICAD_T aTypes[] ) const }
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
{ {
BOARD_ITEM* preferred = NULL; 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;
int typesNr = 0; for( int i = 0; i < aCollector.GetCount(); ++i )
while( aTypes[typesNr++] != EOT ); // count number of types, excluding the sentinel (EOT) {
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 );
}
for( int i = 0; i < aCollector.GetCount(); ++i ) 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] ) )
{
double textArea = calcArea ( txt );
for( int j = 0; j < aCollector.GetCount(); ++j )
{
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;
}
}
}
}
if( aCollector.CountType ( PCB_MODULE_T ) > 0 )
{ {
KICAD_T type = aCollector[i]->Type(); double minArea = calcMinArea ( aCollector, PCB_MODULE_T );
double maxArea = calcMaxArea ( aCollector, PCB_MODULE_T );
for( int j = 0; j < typesNr - 1; ++j ) // Check if the item's type is in our list
if( calcRatio(minArea, maxArea) <= footprintAreaRatio )
{ {
if( aTypes[j] == type ) for( int i = 0; i < aCollector.GetCount(); ++i )
if ( MODULE *mod = dyn_cast<MODULE*> ( aCollector[i] ) )
{
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] ) )
{ {
if( preferred == NULL ) 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 )
{ {
preferred = aCollector[i]; // save the first matching item BOARD_ITEM *item = aCollector[j];
break; 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 );
}
} }
else }
}
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] ) )
{ {
return NULL; // there is more than one preferred item, so there is no clear choice 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 );
} }
} }
} }
} }
return preferred; 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 );
}
}
}
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() ...@@ -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" );
/* /*
* 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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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()
......
#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 );
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment