diff --git a/pcbnew/ratsnest_data.cpp b/pcbnew/ratsnest_data.cpp index b543b61d26..5c3859f836 100644 --- a/pcbnew/ratsnest_data.cpp +++ b/pcbnew/ratsnest_data.cpp @@ -730,6 +730,34 @@ std::list RN_NET::GetNodes( const BOARD_CONNECTED_ITEM* aItem ) con } +void RN_NET::GetAllItems( std::list& aOutput, RN_ITEM_TYPE aType ) const +{ + if( aType & RN_PADS ) + { + BOOST_FOREACH( const BOARD_CONNECTED_ITEM* aItem, m_pads | boost::adaptors::map_keys ) + aOutput.push_back( const_cast( aItem ) ); + } + + if( aType & RN_VIAS ) + { + BOOST_FOREACH( const BOARD_CONNECTED_ITEM* aItem, m_vias | boost::adaptors::map_keys ) + aOutput.push_back( const_cast( aItem ) ); + } + + if( aType & RN_TRACKS ) + { + BOOST_FOREACH( const BOARD_CONNECTED_ITEM* aItem, m_tracks | boost::adaptors::map_keys ) + aOutput.push_back( const_cast( aItem ) ); + } + + if( aType & RN_ZONES ) + { + BOOST_FOREACH( const BOARD_CONNECTED_ITEM* aItem, m_zoneConnections | boost::adaptors::map_keys ) + aOutput.push_back( const_cast( aItem ) ); + } +} + + void RN_NET::ClearSimple() { BOOST_FOREACH( const RN_NODE_PTR& node, m_simpleNodes ) @@ -745,7 +773,7 @@ void RN_NET::ClearSimple() void RN_NET::GetConnectedItems( const BOARD_CONNECTED_ITEM* aItem, std::list& aOutput, - RN_ITEM_TYPES aTypes ) const + RN_ITEM_TYPE aTypes ) const { std::list nodes = GetNodes( aItem ); assert( !nodes.empty() ); @@ -872,7 +900,7 @@ void RN_DATA::AddSimple( const VECTOR2I& aPosition, int aNetCode ) void RN_DATA::GetConnectedItems( const BOARD_CONNECTED_ITEM* aItem, std::list& aOutput, - RN_ITEM_TYPES aTypes ) const + RN_ITEM_TYPE aTypes ) const { int net = aItem->GetNetCode(); @@ -885,6 +913,18 @@ void RN_DATA::GetConnectedItems( const BOARD_CONNECTED_ITEM* aItem, } +void RN_DATA::GetNetItems( int aNetCode, std::list& aOutput, + RN_ITEM_TYPE aTypes ) const +{ + if( aNetCode < 1 ) + return; + + assert( aNetCode < (int) m_nets.size() ); + + m_nets[aNetCode].GetAllItems( aOutput, aTypes ); +} + + bool RN_DATA::AreConnected( const BOARD_CONNECTED_ITEM* aItem, const BOARD_CONNECTED_ITEM* aOther ) { int net1 = aItem->GetNetCode(); diff --git a/pcbnew/ratsnest_data.h b/pcbnew/ratsnest_data.h index ce2ef4fa9b..ac48852ab3 100644 --- a/pcbnew/ratsnest_data.h +++ b/pcbnew/ratsnest_data.h @@ -50,7 +50,7 @@ class ZONE_CONTAINER; class CPolyPt; ///> Types of items that are handled by the class -enum RN_ITEM_TYPES +enum RN_ITEM_TYPE { RN_PADS = 0x01, RN_VIAS = 0x02, @@ -399,6 +399,14 @@ public: */ std::list GetNodes( const BOARD_CONNECTED_ITEM* aItem ) const; + /** + * Function GetAllNodes() + * Adds all stored items to a list. + * @param aOutput is the list that will have items added. + * @param aType determines the type of added items. + */ + void GetAllItems( std::list& aOutput, RN_ITEM_TYPE aType = RN_ALL ) const; + /** * Function GetClosestNode() * Returns a single node that lies in the shortest distance from a specific node. @@ -487,7 +495,7 @@ public: */ void GetConnectedItems( const BOARD_CONNECTED_ITEM* aItem, std::list& aOutput, - RN_ITEM_TYPES aTypes = RN_ALL) const; + RN_ITEM_TYPE aTypes = RN_ALL ) const; protected: ///> Validates edge, i.e. modifies source and target nodes for an edge @@ -524,6 +532,7 @@ protected: typedef boost::unordered_map TRACK_EDGE_MAP; typedef boost::unordered_map > ZONE_POLY_MAP; typedef boost::unordered_map > ZONE_EDGE_MAP; + ///> Map that associates nodes in the ratsnest model to respective nodes. PAD_NODE_MAP m_pads; @@ -661,7 +670,17 @@ public: */ void GetConnectedItems( const BOARD_CONNECTED_ITEM* aItem, std::list& aOutput, - RN_ITEM_TYPES aTypes = RN_ALL ) const; + RN_ITEM_TYPE aTypes = RN_ALL ) const; + + /** + * Function GetNetItems() + * Adds all items that belong to a certain net to a list. + * @param aNetCode is the net code. + * @param aOutput is the list that will have items added. + * @param aTypes allows to filter by item types. + */ + void GetNetItems( int aNetCode, std::list& aOutput, + RN_ITEM_TYPE aTypes = RN_ALL ) const; /** * Function AreConnected() diff --git a/pcbnew/tools/common_actions.cpp b/pcbnew/tools/common_actions.cpp index 10038316ca..d41b644450 100644 --- a/pcbnew/tools/common_actions.cpp +++ b/pcbnew/tools/common_actions.cpp @@ -48,6 +48,14 @@ TOOL_ACTION COMMON_ACTIONS::selectionClear( "pcbnew.InteractiveSelection.Clear", AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere +TOOL_ACTION COMMON_ACTIONS::selectConnection( "pcbnew.InteractiveSelection.SelectConnection", + AS_GLOBAL, 0, + "copper connection", "Selects whole copper connection." ); + +TOOL_ACTION COMMON_ACTIONS::selectNet( "pcbnew.InteractiveSelection.SelectNet", + AS_GLOBAL, 0, + "whole net", "Selects all tracks & vias belonging to the same net." ); + TOOL_ACTION COMMON_ACTIONS::find( "pcbnew.InteractiveSelection.Find", AS_GLOBAL, 0, // it is handled by wxWidgets hotkey system "Find an item", "Searches the document for an item" ); diff --git a/pcbnew/tools/common_actions.h b/pcbnew/tools/common_actions.h index 6d07eaeac0..2e51f81d30 100644 --- a/pcbnew/tools/common_actions.h +++ b/pcbnew/tools/common_actions.h @@ -56,6 +56,12 @@ public: /// Unselects an item (specified as the event parameter). static TOOL_ACTION unselectItem; + /// Selects whole copper connection. + static TOOL_ACTION selectConnection; + + /// Selects all connections belonging to a single net. + static TOOL_ACTION selectNet; + // Edit Tool /// Activation of the edit tool static TOOL_ACTION editActivate; diff --git a/pcbnew/tools/selection_conditions.cpp b/pcbnew/tools/selection_conditions.cpp index fe130b9db4..9b600899d1 100644 --- a/pcbnew/tools/selection_conditions.cpp +++ b/pcbnew/tools/selection_conditions.cpp @@ -34,6 +34,23 @@ bool SELECTION_CONDITIONS::NotEmpty( const SELECTION& aSelection ) } +bool SELECTION_CONDITIONS::OnlyConnectedItems( const SELECTION& aSelection ) +{ + if( aSelection.Empty() ) + return false; + + for( int i = 0; i < aSelection.Size(); ++i ) + { + KICAD_T type = aSelection.Item( i )->Type(); + + if( type != PCB_PAD_T && type != PCB_VIA_T && type != PCB_TRACE_T && type != PCB_ZONE_T ) + return false; + } + + return true; +} + + SELECTION_CONDITION SELECTION_CONDITIONS::HasType( KICAD_T aType ) { return boost::bind( &SELECTION_CONDITIONS::hasTypeFunc, _1, aType ); diff --git a/pcbnew/tools/selection_conditions.h b/pcbnew/tools/selection_conditions.h index 99364b5f6e..b001dd37d6 100644 --- a/pcbnew/tools/selection_conditions.h +++ b/pcbnew/tools/selection_conditions.h @@ -65,6 +65,14 @@ public: */ static bool NotEmpty( const SELECTION& aSelection ); + /** + * Function OnlyConnectedItems + * Tests if selection contains exclusively connected items (pads, tracks, vias, zones). + * @param aSelection is the selection to be tested. + * @return True if there are only connected items connected. + */ + static bool OnlyConnectedItems( const SELECTION& aSelection ); + /** * Function HasType * Creates functor that tests if among the selected items there is at least one of a given type. diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp index c400d521bf..45330c3d18 100644 --- a/pcbnew/tools/selection_tool.cpp +++ b/pcbnew/tools/selection_tool.cpp @@ -28,7 +28,6 @@ #include #include - #include #include #include @@ -48,12 +47,23 @@ #include #include +#include #include "selection_tool.h" #include "selection_area.h" #include "bright_box.h" #include "common_actions.h" +class SELECT_MENU: public CONTEXT_MENU +{ +public: + SELECT_MENU() + { + Add( COMMON_ACTIONS::selectConnection ); + Add( COMMON_ACTIONS::selectNet ); + } +}; + SELECTION_TOOL::SELECTION_TOOL() : TOOL_INTERACTIVE( "pcbnew.InteractiveSelection" ), m_frame( NULL ), m_additive( false ), m_multiple( false ), @@ -61,6 +71,10 @@ SELECTION_TOOL::SELECTION_TOOL() : { m_selArea = new SELECTION_AREA; m_selection.group = new KIGFX::VIEW_GROUP; + + AddSubMenu( new SELECT_MENU, "Select...", + (SELECTION_CONDITION) SELECTION_CONDITIONS::OnlyConnectedItems && + SELECTION_CONDITIONS::Count( 1 ) ); } @@ -207,6 +221,16 @@ int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent ) { clearSelection(); } + + else if( evt->IsAction( &COMMON_ACTIONS::selectConnection ) ) + { + selectConnection( *evt ); + } + + else if( evt->IsAction( &COMMON_ACTIONS::selectNet ) ) + { + selectNet( *evt ); + } } // This tool is supposed to be active forever @@ -293,7 +317,7 @@ bool SELECTION_TOOL::selectCursor( const VECTOR2I& aWhere, bool aOnDrag ) return true; - default: + default: // Apply some ugly heuristics to avoid disambiguation menus whenever possible guessSelectionCandidates( collector ); @@ -308,7 +332,7 @@ bool SELECTION_TOOL::selectCursor( const VECTOR2I& aWhere, bool aOnDrag ) { if( aOnDrag ) Wait ( TOOL_EVENT( TC_ANY, TA_MOUSE_UP, BUT_LEFT ) ); - + item = disambiguationMenu( &collector ); if( item ) @@ -408,8 +432,11 @@ void SELECTION_TOOL::setTransitions() Go( &SELECTION_TOOL::ClearSelection, COMMON_ACTIONS::selectionClear.MakeEvent() ); Go( &SELECTION_TOOL::SelectItem, COMMON_ACTIONS::selectItem.MakeEvent() ); Go( &SELECTION_TOOL::UnselectItem, COMMON_ACTIONS::unselectItem.MakeEvent() ); + Go( &SELECTION_TOOL::SelectItem, COMMON_ACTIONS::selectItem.MakeEvent() ); Go( &SELECTION_TOOL::find, COMMON_ACTIONS::find.MakeEvent() ); Go( &SELECTION_TOOL::findMove, COMMON_ACTIONS::findMove.MakeEvent() ); + Go( &SELECTION_TOOL::selectConnection, COMMON_ACTIONS::selectConnection.MakeEvent() ); + Go( &SELECTION_TOOL::selectNet, COMMON_ACTIONS::selectNet.MakeEvent() ); } @@ -453,7 +480,7 @@ SELECTION_LOCK_FLAGS SELECTION_TOOL::CheckLock() else return SELECTION_LOCKED; } - + m_locked = false; return SELECTION_UNLOCKED; @@ -512,6 +539,58 @@ int SELECTION_TOOL::UnselectItem( const TOOL_EVENT& aEvent ) return 0; } + +int SELECTION_TOOL::selectConnection( const TOOL_EVENT& aEvent ) +{ + std::list itemsList; + RN_DATA* ratsnest = getModel()->GetRatsnest(); + BOARD_CONNECTED_ITEM* item = m_selection.Item( 0 ); + + clearSelection(); + ratsnest->GetConnectedItems( item, itemsList, (RN_ITEM_TYPE)( RN_TRACKS | RN_VIAS ) ); + + BOOST_FOREACH( BOARD_CONNECTED_ITEM* i, itemsList ) + select( i ); + + // Inform other potentially interested tools + if( itemsList.size() > 0 ) + { + TOOL_EVENT selectEvent( SelectedEvent ); + m_toolMgr->ProcessEvent( selectEvent ); + } + + setTransitions(); + + return 0; +} + + +int SELECTION_TOOL::selectNet( const TOOL_EVENT& aEvent ) +{ + std::list itemsList; + RN_DATA* ratsnest = getModel()->GetRatsnest(); + BOARD_CONNECTED_ITEM* item = m_selection.Item( 0 ); + int netCode = item->GetNetCode(); + + clearSelection(); + ratsnest->GetNetItems( netCode, itemsList, (RN_ITEM_TYPE)( RN_TRACKS | RN_VIAS ) ); + + BOOST_FOREACH( BOARD_CONNECTED_ITEM* i, itemsList ) + select( i ); + + // Inform other potentially interested tools + if( itemsList.size() > 0 ) + { + TOOL_EVENT selectEvent( SelectedEvent ); + m_toolMgr->ProcessEvent( selectEvent ); + } + + setTransitions(); + + return 0; +} + + void SELECTION_TOOL::findCallback( BOARD_ITEM* aItem ) { clearSelection(); @@ -785,8 +864,8 @@ void SELECTION_TOOL::select( BOARD_ITEM* aItem ) if( aItem->Type() == PCB_PAD_T ) { MODULE* module = static_cast( aItem->GetParent() ); - - if( m_selection.items.FindItem( module ) >= 0 ) + + if( m_selection.items.FindItem( module ) >= 0 ) return; } @@ -921,7 +1000,7 @@ static double calcArea( BOARD_ITEM* aItem ) static double calcMinArea( GENERAL_COLLECTOR& aCollector, KICAD_T aType ) { double best = std::numeric_limits::max(); - + if( !aCollector.GetCount() ) return 0.0; @@ -998,11 +1077,11 @@ void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) c { aCollector.Empty(); - BOOST_FOREACH( BOARD_ITEM* item, preferred ) + BOOST_FOREACH( BOARD_ITEM* item, preferred ) aCollector.Append( item ); return; } - } + } if( aCollector.CountType( PCB_MODULE_TEXT_T ) > 0 ) { @@ -1085,7 +1164,7 @@ void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) c { if( VIA* via = dyn_cast( aCollector[i] ) ) { - double viaArea = calcArea( via ); + double viaArea = calcArea( via ); for( int j = 0; j < aCollector.GetCount(); ++j ) { @@ -1098,7 +1177,7 @@ void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) c if( item->Type() == PCB_PAD_T && areaRatio < padViaAreaRatio ) rejected.insert( item ); - if( TRACK* track = dyn_cast( item ) ) + if( TRACK* track = dyn_cast( item ) ) { if( track->GetNetCode() != via->GetNetCode() ) continue; @@ -1128,7 +1207,7 @@ void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) c 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); } @@ -1180,11 +1259,11 @@ bool SELECTION_TOOL::SanitizeSelection() for( unsigned int i = 0; i < m_selection.items.GetCount(); ++i ) { BOARD_ITEM* item = m_selection.Item( i ); - + if( item->Type() == PCB_PAD_T ) { MODULE* mod = static_cast ( item->GetParent() ); - + // case 1: module (or its pads) are locked if( mod && ( mod->PadsLocked() || mod->IsLocked() ) ) rejected.insert( item ); @@ -1196,9 +1275,9 @@ bool SELECTION_TOOL::SanitizeSelection() } } - while( !rejected.empty () ) + while( !rejected.empty () ) { - BOARD_ITEM* item = *rejected.begin(); + BOARD_ITEM* item = *rejected.begin(); int itemIdx = m_selection.items.FindItem( item ); if( itemIdx >= 0 ) diff --git a/pcbnew/tools/selection_tool.h b/pcbnew/tools/selection_tool.h index 149262c1ee..1481bd7fab 100644 --- a/pcbnew/tools/selection_tool.h +++ b/pcbnew/tools/selection_tool.h @@ -81,7 +81,7 @@ private: }; enum SELECTION_LOCK_FLAGS -{ +{ SELECTION_UNLOCKED = 0, SELECTION_LOCK_OVERRIDE = 1, SELECTION_LOCKED = 2 @@ -206,6 +206,12 @@ private: */ bool selectMultiple(); + ///> Selects a continuous copper connection. + int selectConnection( const TOOL_EVENT& aEvent ); + + ///> Selects all copper connections belonging to a single net. + int selectNet( const TOOL_EVENT& aEvent ); + ///> Find dialog callback. void findCallback( BOARD_ITEM* aItem );