First round of cleanup for the PCB groups

ADDED: Leave group action

Fixes https://gitlab.com/kicad/code/kicad/issues/5299
This commit is contained in:
Ian McInerney 2020-09-02 02:09:34 +01:00
parent 6c164310ec
commit cb36006406
13 changed files with 207 additions and 117 deletions

View File

@ -32,6 +32,7 @@
#include <base_struct.h> #include <base_struct.h>
#include <common.h>
#include <convert_to_biu.h> #include <convert_to_biu.h>
#include <gr_basic.h> #include <gr_basic.h>
#include <layers_id_colors_and_visibility.h> #include <layers_id_colors_and_visibility.h>
@ -88,13 +89,37 @@ class BOARD_ITEM : public EDA_ITEM
{ {
protected: protected:
PCB_LAYER_ID m_Layer; PCB_LAYER_ID m_Layer;
KIID m_groupUuid;
public: public:
BOARD_ITEM( BOARD_ITEM* aParent, KICAD_T idtype ) : BOARD_ITEM( BOARD_ITEM* aParent, KICAD_T idtype )
EDA_ITEM( aParent, idtype ), m_Layer( F_Cu ) : EDA_ITEM( aParent, idtype ),
m_Layer( F_Cu ),
m_groupUuid( 0 )
{ {
} }
/**
* Set the group that this item belongs to.
*
* @param aGroup is the group this belongs to
*/
void SetGroup( const KIID& aGroup ) { m_groupUuid = aGroup; }
/**
* Get the group this item belongs to.
*
* @return the group this item is in
*/
const KIID& GetGroup() const { return m_groupUuid; }
/**
* Test if this item is inside a group.
*
* @return true if inside a group
*/
bool IsInGroup() { return m_groupUuid != niluuid; }
// Do not create a copy constructor & operator=. // Do not create a copy constructor & operator=.
// The ones generated by the compiler are adequate. // The ones generated by the compiler are adequate.
int GetX() const int GetX() const
@ -159,13 +184,13 @@ public:
/** /**
* Function GetEffectiveShape * Function GetEffectiveShape
* Some pad shapes can be complex (rounded/chamfered rectangle), even without considering * Some pad shapes can be complex (rounded/chamfered rectangle), even without considering
* custom shapes. This routine returns a COMPOUND shape (set of simple shapes which make * custom shapes. This routine returns a COMPOUND shape (set of simple shapes which make
* up the pad fod use with routing, collision determiniation, etc). * up the pad fod use with routing, collision determiniation, etc).
* *
* Note that this list can contain a SHAPE_SIMPLE (a simple single-outline non-intersecting * Note that this list can contain a SHAPE_SIMPLE (a simple single-outline non-intersecting
* polygon), but should never contain a SHAPE_POLY_SET (a complex polygon consisting of * polygon), but should never contain a SHAPE_POLY_SET (a complex polygon consisting of
* multiple outlines and/or holes). * multiple outlines and/or holes).
* *
* @param aLayer in case of items spanning multiple layers, only the shapes belonging to aLayer * @param aLayer in case of items spanning multiple layers, only the shapes belonging to aLayer
* will be returned. Pass UNDEFINED_LAYER to return shapes for all layers. * will be returned. Pass UNDEFINED_LAYER to return shapes for all layers.
*/ */

View File

@ -53,7 +53,7 @@ typedef std::unordered_set<BOARD_ITEM*> ITEM_SET;
class PCB_GROUP : public BOARD_ITEM class PCB_GROUP : public BOARD_ITEM
{ {
public: public:
PCB_GROUP( BOARD* parent ); PCB_GROUP( BOARD* aParent );
static inline bool ClassOf( const EDA_ITEM* aItem ) static inline bool ClassOf( const EDA_ITEM* aItem )
{ {
@ -70,22 +70,24 @@ public:
return m_items; return m_items;
} }
void SetName( wxString name ) void SetName( wxString aName )
{ {
m_name = name; m_name = aName;
} }
/** /**
* Adds item to group. Does not take ownership of item. * Adds item to group. Does not take ownership of item.
* @return true if item was added (false if item was already in set). *
* @return true if item was added (false if item belongs to a different group).
*/ */
bool AddItem( BOARD_ITEM* item ); bool AddItem( BOARD_ITEM* aItem );
/** /**
* Removes item from group. * Removes item from group.
*
* @return true if item was removed (false if item was not in the group). * @return true if item was removed (false if item was not in the group).
*/ */
bool RemoveItem( const BOARD_ITEM* item ); bool RemoveItem( BOARD_ITEM* aItem );
wxString GetClass() const override wxString GetClass() const override
{ {
@ -103,7 +105,7 @@ public:
wxPoint GetPosition() const override; wxPoint GetPosition() const override;
///> @copydoc EDA_ITEM::SetPosition ///> @copydoc EDA_ITEM::SetPosition
void SetPosition( const wxPoint& ) override; void SetPosition( const wxPoint& aNewpos ) override;
///> @copydoc BOARD_ITEM::GetLayerSet ///> @copydoc BOARD_ITEM::GetLayerSet
LSET GetLayerSet() const override; LSET GetLayerSet() const override;
@ -131,11 +133,7 @@ public:
void SwapData( BOARD_ITEM* aImage ) override; void SwapData( BOARD_ITEM* aImage ) override;
///> @copydoc BOARD_ITEM::IsOnLayer ///> @copydoc BOARD_ITEM::IsOnLayer
bool IsOnLayer( PCB_LAYER_ID aLayer ) const override bool IsOnLayer( PCB_LAYER_ID aLayer ) const override;
{
wxFAIL_MSG( "groups don't support layer IsOnLayer" );
return false;
}
///> @copydoc EDA_ITEM::HitTest ///> @copydoc EDA_ITEM::HitTest
bool HitTest( const wxPoint& aPosition, int aAccuracy = 0 ) const override; bool HitTest( const wxPoint& aPosition, int aAccuracy = 0 ) const override;
@ -147,7 +145,7 @@ public:
const EDA_RECT GetBoundingBox() const override; const EDA_RECT GetBoundingBox() const override;
///> @copydoc EDA_ITEM::Visit ///> @copydoc EDA_ITEM::Visit
SEARCH_RESULT Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] ) override; SEARCH_RESULT Visit( INSPECTOR aInspector, void* aTestData, const KICAD_T aScanTypes[] ) override;
///> @copydoc VIEW_ITEM::ViewGetLayers ///> @copydoc VIEW_ITEM::ViewGetLayers
void ViewGetLayers( int aLayers[], int& aCount ) const override; void ViewGetLayers( int aLayers[], int& aCount ) const override;

View File

@ -1943,6 +1943,7 @@ wxString BOARD::GroupsSanityCheckInternal( bool repair )
{ {
if( repair ) if( repair )
board.Groups().erase( board.Groups().begin() + idx ); board.Groups().erase( board.Groups().begin() + idx );
return wxString::Format( _( "Group Uuid %s maps to 2 different BOARD_ITEMS: %p and %p" ), return wxString::Format( _( "Group Uuid %s maps to 2 different BOARD_ITEMS: %p and %p" ),
group.m_Uuid.AsString(), group.m_Uuid.AsString(),
testItem, groups[idx] ); testItem, groups[idx] );
@ -1955,13 +1956,15 @@ wxString BOARD::GroupsSanityCheckInternal( bool repair )
{ {
if( repair ) if( repair )
group.SetName( group.GetName() + "-" + group.m_Uuid.AsString() ); group.SetName( group.GetName() + "-" + group.m_Uuid.AsString() );
return wxString::Format( _( "Two groups of identical name: %s" ), group.GetName() ); return wxString::Format( _( "Two groups of identical name: %s" ), group.GetName() );
} }
wxCHECK( groupNames.insert( group.GetName() ).second == true, wxCHECK( groupNames.insert( group.GetName() ).second == true,
"Insert failed of new group" ); "Insert failed of new group" );
} }
for( const BOARD_ITEM* member : group.GetItems() ) for( BOARD_ITEM* member : group.GetItems() )
{ {
BOARD_ITEM* item = board.GetItem( member->m_Uuid ); BOARD_ITEM* item = board.GetItem( member->m_Uuid );
@ -1969,6 +1972,7 @@ wxString BOARD::GroupsSanityCheckInternal( bool repair )
{ {
if( repair ) if( repair )
group.RemoveItem( member ); group.RemoveItem( member );
return wxString::Format( _( "Group %s contains deleted item %s" ), return wxString::Format( _( "Group %s contains deleted item %s" ),
group.m_Uuid.AsString(), group.m_Uuid.AsString(),
member->m_Uuid.AsString() ); member->m_Uuid.AsString() );
@ -1978,6 +1982,7 @@ wxString BOARD::GroupsSanityCheckInternal( bool repair )
{ {
if( repair ) if( repair )
group.RemoveItem( member ); group.RemoveItem( member );
return wxString::Format( _( "Uuid %s maps to 2 different BOARD_ITEMS: %s %p %s and %p %s" ), return wxString::Format( _( "Uuid %s maps to 2 different BOARD_ITEMS: %s %p %s and %p %s" ),
member->m_Uuid.AsString(), member->m_Uuid.AsString(),
item->m_Uuid.AsString(), item->m_Uuid.AsString(),
@ -1992,11 +1997,13 @@ wxString BOARD::GroupsSanityCheckInternal( bool repair )
{ {
if( repair ) if( repair )
group.RemoveItem( member ); group.RemoveItem( member );
return wxString::Format( return wxString::Format(
_( "BOARD_ITEM %s appears multiple times in groups (either in the " _( "BOARD_ITEM %s appears multiple times in groups (either in the "
"same group or in multiple groups) " ), "same group or in multiple groups) " ),
item->m_Uuid.AsString() ); item->m_Uuid.AsString() );
} }
wxCHECK( allMembers.insert( member->m_Uuid.AsString() ).second == true, wxCHECK( allMembers.insert( member->m_Uuid.AsString() ).second == true,
"Insert failed of new member" ); "Insert failed of new member" );
@ -2022,6 +2029,7 @@ wxString BOARD::GroupsSanityCheckInternal( bool repair )
{ {
if( repair ) if( repair )
board.Groups().erase( board.Groups().begin() + idx ); board.Groups().erase( board.Groups().begin() + idx );
return wxString::Format( _( "Group must have at least one member: %s" ), group.m_Uuid.AsString() ); return wxString::Format( _( "Group must have at least one member: %s" ), group.m_Uuid.AsString() );
} }
} }
@ -2053,12 +2061,14 @@ wxString BOARD::GroupsSanityCheckInternal( bool repair )
{ {
currentChainGroups.clear(); currentChainGroups.clear();
int currIdx = *toCheckGroups.begin(); int currIdx = *toCheckGroups.begin();
while( true ) while( true )
{ {
if( currentChainGroups.find( currIdx ) != currentChainGroups.end() ) if( currentChainGroups.find( currIdx ) != currentChainGroups.end() )
{ {
if( repair ) if( repair )
board.Groups().erase( board.Groups().begin() + currIdx ); board.Groups().erase( board.Groups().begin() + currIdx );
return "Cycle detected in group membership"; return "Cycle detected in group membership";
} }
else if( knownCycleFreeGroups.find( currIdx ) != knownCycleFreeGroups.end() ) else if( knownCycleFreeGroups.find( currIdx ) != knownCycleFreeGroups.end() )
@ -2066,21 +2076,25 @@ wxString BOARD::GroupsSanityCheckInternal( bool repair )
// Parent is a group we know does not lead to a cycle // Parent is a group we know does not lead to a cycle
break; break;
} }
wxCHECK( currentChainGroups.insert( currIdx ).second == true, wxCHECK( currentChainGroups.insert( currIdx ).second == true,
"Insert of new group to check failed" ); "Insert of new group to check failed" );
// We haven't visited currIdx yet, so it must be in toCheckGroups // We haven't visited currIdx yet, so it must be in toCheckGroups
wxCHECK( toCheckGroups.erase( currIdx ) == 1, wxCHECK( toCheckGroups.erase( currIdx ) == 1,
"Erase of idx for group just checked failed" ); "Erase of idx for group just checked failed" );
currIdx = parentGroupIdx[currIdx]; currIdx = parentGroupIdx[currIdx];
if( currIdx == -1 ) if( currIdx == -1 )
{ {
// end of chain and no cycles found in this chain // end of chain and no cycles found in this chain
break; break;
} }
} }
// No cycles found in chain, so add it to set of groups we know don't participate in a cycle. // No cycles found in chain, so add it to set of groups we know don't participate in a cycle.
knownCycleFreeGroups.insert( currentChainGroups.begin(), currentChainGroups.end() ); knownCycleFreeGroups.insert( currentChainGroups.begin(), currentChainGroups.end() );
} }
// Success // Success
return ""; return "";
} }

View File

@ -22,25 +22,43 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/ */
#include <bitmaps.h> #include <bitmaps.h>
#include <class_board.h>
#include <class_board_item.h>
#include <class_pcb_group.h> #include <class_pcb_group.h>
#include <confirm.h> #include <confirm.h>
#include <msgpanel.h> #include <msgpanel.h>
#include <view/view.h> #include <view/view.h>
PCB_GROUP::PCB_GROUP( BOARD* parent ) : BOARD_ITEM( (BOARD_ITEM*) parent, PCB_GROUP_T ) PCB_GROUP::PCB_GROUP( BOARD*aParent ) : BOARD_ITEM( aParent, PCB_GROUP_T )
{ {
} }
bool PCB_GROUP::AddItem( BOARD_ITEM* item ) bool PCB_GROUP::AddItem( BOARD_ITEM* aItem )
{ {
return m_items.insert( item ).second; // Items can only be in one group at a time
if( aItem->IsInGroup() )
return false;
m_items.insert( aItem );
aItem->SetGroup( m_Uuid );
return true;
} }
bool PCB_GROUP::RemoveItem( const BOARD_ITEM* item ) bool PCB_GROUP::RemoveItem( BOARD_ITEM* aItem )
{ {
return m_items.erase( const_cast<BOARD_ITEM*>( item ) ) == 1; if( !aItem->IsInGroup() )
return false;
// Only clear the item's group field if it was inside this group
if( m_items.erase( aItem ) == 1 )
{
aItem->SetGroup( niluuid );
return true;
}
return false;
} }
@ -50,9 +68,9 @@ wxPoint PCB_GROUP::GetPosition() const
} }
void PCB_GROUP::SetPosition( const wxPoint& newpos ) void PCB_GROUP::SetPosition( const wxPoint& aNewpos )
{ {
wxPoint delta = newpos - GetPosition(); wxPoint delta = aNewpos - GetPosition();
Move( delta ); Move( delta );
} }
@ -72,16 +90,12 @@ PCB_GROUP* PCB_GROUP::DeepClone() const
PCB_GROUP* newGroup = new PCB_GROUP( *this ); PCB_GROUP* newGroup = new PCB_GROUP( *this );
newGroup->m_items.clear(); newGroup->m_items.clear();
for( auto member : m_items ) for( BOARD_ITEM* member : m_items )
{ {
if( member->Type() == PCB_GROUP_T ) if( member->Type() == PCB_GROUP_T )
{
newGroup->AddItem( static_cast<PCB_GROUP*>( member )->DeepClone() ); newGroup->AddItem( static_cast<PCB_GROUP*>( member )->DeepClone() );
}
else else
{
newGroup->AddItem( static_cast<BOARD_ITEM*>( member->Clone() ) ); newGroup->AddItem( static_cast<BOARD_ITEM*>( member->Clone() ) );
}
} }
return newGroup; return newGroup;
@ -93,16 +107,12 @@ PCB_GROUP* PCB_GROUP::DeepDuplicate() const
PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( this->Duplicate() ); PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( this->Duplicate() );
newGroup->m_items.clear(); newGroup->m_items.clear();
for( auto member : m_items ) for( BOARD_ITEM* member : m_items )
{ {
if( member->Type() == PCB_GROUP_T ) if( member->Type() == PCB_GROUP_T )
{
newGroup->AddItem( static_cast<PCB_GROUP*>( member )->DeepDuplicate() ); newGroup->AddItem( static_cast<PCB_GROUP*>( member )->DeepDuplicate() );
}
else else
{
newGroup->AddItem( static_cast<BOARD_ITEM*>( member->Duplicate() ) ); newGroup->AddItem( static_cast<BOARD_ITEM*>( member->Duplicate() ) );
}
} }
return newGroup; return newGroup;
@ -132,7 +142,9 @@ bool PCB_GROUP::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy )
EDA_RECT bbox = GetBoundingBox(); EDA_RECT bbox = GetBoundingBox();
if( aContained ) if( aContained )
{
return arect.Contains( bbox ); return arect.Contains( bbox );
}
else else
{ {
// If the rect does not intersect the bounding box, skip any tests // If the rect does not intersect the bounding box, skip any tests
@ -144,44 +156,34 @@ bool PCB_GROUP::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy )
if( member->HitTest( arect, false, 0 ) ) if( member->HitTest( arect, false, 0 ) )
return true; return true;
} }
// No items were hit
return false;
} }
// No items were hit
return false;
} }
const EDA_RECT PCB_GROUP::GetBoundingBox() const const EDA_RECT PCB_GROUP::GetBoundingBox() const
{ {
EDA_RECT area; EDA_RECT area;
bool isFirst = true;
for( BOARD_ITEM* item : m_items ) for( BOARD_ITEM* item : m_items )
{ area.Merge( item->GetBoundingBox() );
if( isFirst )
{
area = item->GetBoundingBox();
isFirst = false;
}
else
{
area.Merge( item->GetBoundingBox() );
}
}
area.Inflate( Millimeter2iu( 0.25 ) ); // Give a min size to the area area.Inflate( Millimeter2iu( 0.25 ) ); // Give a min size to the area
return area; return area;
} }
SEARCH_RESULT PCB_GROUP::Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] ) SEARCH_RESULT PCB_GROUP::Visit( INSPECTOR aInspector, void* aTestData, const KICAD_T aScanTypes[] )
{ {
for( const KICAD_T* stype = scanTypes; *stype != EOT; ++stype ) for( const KICAD_T* stype = aScanTypes; *stype != EOT; ++stype )
{ {
// If caller wants to inspect my type // If caller wants to inspect my type
if( *stype == Type() ) if( *stype == Type() )
{ {
if( SEARCH_RESULT::QUIT == inspector( this, testData ) ) if( SEARCH_RESULT::QUIT == aInspector( this, aTestData ) )
return SEARCH_RESULT::QUIT; return SEARCH_RESULT::QUIT;
} }
} }
@ -195,13 +197,25 @@ LSET PCB_GROUP::GetLayerSet() const
LSET aSet; LSET aSet;
for( BOARD_ITEM* item : m_items ) for( BOARD_ITEM* item : m_items )
{
aSet |= item->GetLayerSet(); aSet |= item->GetLayerSet();
}
return aSet; return aSet;
} }
bool PCB_GROUP::IsOnLayer( PCB_LAYER_ID aLayer ) const
{
// A group is on a layer if any item is on the layer
for( BOARD_ITEM* item : m_items )
{
if( item->IsOnLayer( aLayer ) )
return true;
}
return false;
}
void PCB_GROUP::ViewGetLayers( int aLayers[], int& aCount ) const void PCB_GROUP::ViewGetLayers( int aLayers[], int& aCount ) const
{ {
// What layer to put bounding box on? change in class_pcb_group.cpp // What layer to put bounding box on? change in class_pcb_group.cpp
@ -235,28 +249,22 @@ unsigned int PCB_GROUP::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
void PCB_GROUP::Move( const wxPoint& aMoveVector ) void PCB_GROUP::Move( const wxPoint& aMoveVector )
{ {
for( auto member : m_items ) for( BOARD_ITEM* member : m_items )
{
member->Move( aMoveVector ); member->Move( aMoveVector );
}
} }
void PCB_GROUP::Rotate( const wxPoint& aRotCentre, double aAngle ) void PCB_GROUP::Rotate( const wxPoint& aRotCentre, double aAngle )
{ {
for( BOARD_ITEM* item : m_items ) for( BOARD_ITEM* item : m_items )
{
item->Rotate( aRotCentre, aAngle ); item->Rotate( aRotCentre, aAngle );
}
} }
void PCB_GROUP::Flip( const wxPoint& aCentre, bool aFlipLeftRight ) void PCB_GROUP::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
{ {
for( BOARD_ITEM* item : m_items ) for( BOARD_ITEM* item : m_items )
{
item->Flip( aCentre, aFlipLeftRight ); item->Flip( aCentre, aFlipLeftRight );
}
} }
@ -264,10 +272,11 @@ wxString PCB_GROUP::GetSelectMenuText( EDA_UNITS aUnits ) const
{ {
if( m_name.empty() ) if( m_name.empty() )
{ {
return wxString::Format( _( "Anonymous group %s with %ld members" ), return wxString::Format( _( "Anonymous group %s with %zu members" ),
m_Uuid.AsString(), m_items.size() ); m_Uuid.AsString(), m_items.size() );
} }
return wxString::Format( _( "Group \"%s\" with %ld members" ), m_name, m_items.size() );
return wxString::Format( _( "Group \"%s\" with %zu members" ), m_name, m_items.size() );
} }
@ -281,7 +290,7 @@ void PCB_GROUP::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_I
{ {
aList.emplace_back( _( "Group" ), m_name.empty() ? _( "Anonymous" ) : aList.emplace_back( _( "Group" ), m_name.empty() ? _( "Anonymous" ) :
wxString::Format( "\"%s\"", m_name ), DARKCYAN ); wxString::Format( "\"%s\"", m_name ), DARKCYAN );
aList.emplace_back( _( "Members" ), wxString::Format( "%ld", m_items.size() ), BROWN ); aList.emplace_back( _( "Members" ), wxString::Format( "%zu", m_items.size() ), BROWN );
} }
@ -294,7 +303,7 @@ void PCB_GROUP::RunOnChildren( const std::function<void( BOARD_ITEM* )>& aFuncti
} }
catch( std::bad_function_call& ) catch( std::bad_function_call& )
{ {
wxFAIL_MSG( "Error running PCB_GROUP::RunOnChildren" ); wxFAIL_MSG( wxT( "Error calling function in PCB_GROUP::RunOnChildren" ) );
} }
} }
@ -306,12 +315,13 @@ void PCB_GROUP::RunOnDescendants( const std::function<void( BOARD_ITEM* )>& aFun
for( BOARD_ITEM* item : m_items ) for( BOARD_ITEM* item : m_items )
{ {
aFunction( item ); aFunction( item );
if( item->Type() == PCB_GROUP_T ) if( item->Type() == PCB_GROUP_T )
static_cast<PCB_GROUP*>( item )->RunOnDescendants( aFunction ); static_cast<PCB_GROUP*>( item )->RunOnDescendants( aFunction );
} }
} }
catch( std::bad_function_call& ) catch( std::bad_function_call& )
{ {
wxFAIL_MSG( "Error running PCB_GROUP::RunOnDescendants" ); wxFAIL_MSG( wxT( "Error calling function in PCB_GROUP::RunOnDescendants" ) );
} }
} }

View File

@ -737,6 +737,7 @@ BOARD* PCB_PARSER::parseBOARD_unchecked()
} }
wxString error; wxString error;
for( size_t idx = 0; idx < m_groupInfos.size(); idx++ ) for( size_t idx = 0; idx < m_groupInfos.size(); idx++ )
{ {
auto& aGrp = m_groupInfos[idx]; auto& aGrp = m_groupInfos[idx];
@ -753,26 +754,33 @@ BOARD* PCB_PARSER::parseBOARD_unchecked()
for( const auto& aUuid : aGrp.memberUuids ) for( const auto& aUuid : aGrp.memberUuids )
{ {
KIID tUuid = aUuid; KIID tUuid = aUuid;
if( m_resetKIIDs ) if( m_resetKIIDs )
{ {
if( m_resetKIIDMap.find( aUuid.AsString() ) == m_resetKIIDMap.end() ) if( m_resetKIIDMap.find( aUuid.AsString() ) == m_resetKIIDMap.end() )
{ {
if( error == wxEmptyString ) if( error == wxEmptyString )
{
error = wxString::Format( _( "Group %s references missing item %s" ), error = wxString::Format( _( "Group %s references missing item %s" ),
aGrp.uuid.AsString(), aUuid.AsString() ); aGrp.uuid.AsString(), aUuid.AsString() );
}
} }
else else
{ {
tUuid = m_resetKIIDMap[ aUuid.AsString() ]; tUuid = m_resetKIIDMap[ aUuid.AsString() ];
} }
} }
BOARD_ITEM* item = m_board->GetItem( tUuid ); BOARD_ITEM* item = m_board->GetItem( tUuid );
if( ( item == nullptr ) || ( item->Type() == NOT_USED ) ) if( ( item == nullptr ) || ( item->Type() == NOT_USED ) )
{ {
if( error == wxEmptyString ) if( error == wxEmptyString )
{
error = wxString::Format( _( "Group %s references missing item %s" ), error = wxString::Format( _( "Group %s references missing item %s" ),
aGrp.uuid.AsString(), tUuid.AsString() ); aGrp.uuid.AsString(), tUuid.AsString() );
}
} }
else else
{ {
@ -782,12 +790,13 @@ BOARD* PCB_PARSER::parseBOARD_unchecked()
} }
wxString sanityResult = m_board->GroupsSanityCheck(); wxString sanityResult = m_board->GroupsSanityCheck();
if( error != wxEmptyString || sanityResult != wxEmptyString ) if( error != wxEmptyString || sanityResult != wxEmptyString )
{ {
wxString errMsg = ( error != wxEmptyString ) ? error : sanityResult; wxString errMsg = ( error != wxEmptyString ) ? error : sanityResult;
KIDIALOG dlg( nullptr, wxString::Format( KIDIALOG dlg( nullptr, wxString::Format(
_( "Error in group structure in file: %s\n\nAttempt repair?" ), errMsg ), _( "Error in group structure in file: %s\n\nAttempt repair?" ), errMsg ),
_( "File data error" ), wxOK | wxCANCEL | wxICON_ERROR ); _( "File data error" ), wxOK | wxCANCEL | wxICON_ERROR );
dlg.SetOKLabel( _( "Attempt repair" ) ); dlg.SetOKLabel( _( "Attempt repair" ) );
if( dlg.ShowModal() == wxID_CANCEL ) if( dlg.ShowModal() == wxID_CANCEL )

View File

@ -1214,7 +1214,7 @@ int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent )
board()->GroupRemoveItems( removed, m_commit.get() ); board()->GroupRemoveItems( removed, m_commit.get() );
if( m_commit->HasRemoveEntry( enteredGroup ) ) if( m_commit->HasRemoveEntry( enteredGroup ) )
m_selectionTool->exitGroup(); m_selectionTool->ExitGroup();
} }
if( isCut ) if( isCut )

View File

@ -629,7 +629,7 @@ TOOL_ACTION PCB_ACTIONS::unlock( "pcbnew.EditorControl.unlock",
TOOL_ACTION PCB_ACTIONS::groupCreate( "pcbnew.EditorControl.groupCreate", TOOL_ACTION PCB_ACTIONS::groupCreate( "pcbnew.EditorControl.groupCreate",
AS_GLOBAL, 0, "", AS_GLOBAL, 0, "",
_( "Group" ), "", _( "Group" ), _( "Add the selected items to a new group" ),
locked_xpm ); locked_xpm );
TOOL_ACTION PCB_ACTIONS::groupMerge( "pcbnew.EditorControl.groupMerge", TOOL_ACTION PCB_ACTIONS::groupMerge( "pcbnew.EditorControl.groupMerge",
@ -644,19 +644,24 @@ TOOL_ACTION PCB_ACTIONS::groupUngroup( "pcbnew.EditorControl.groupUngroup",
TOOL_ACTION PCB_ACTIONS::groupRemoveItems( "pcbnew.EditorControl.groupRemoveItems", TOOL_ACTION PCB_ACTIONS::groupRemoveItems( "pcbnew.EditorControl.groupRemoveItems",
AS_GLOBAL, 0, "", AS_GLOBAL, 0, "",
_( "Remove Items" ), "", _( "Remove Items" ), _( "Remove items from group" ),
unlocked_xpm ); unlocked_xpm );
TOOL_ACTION PCB_ACTIONS::groupFlatten( "pcbnew.EditorControl.groupFlatten", TOOL_ACTION PCB_ACTIONS::groupFlatten( "pcbnew.EditorControl.groupFlatten",
AS_GLOBAL, 0, "", AS_GLOBAL, 0, "",
_( "Flatten" ), "", _( "Flatten Group" ), "",
unlocked_xpm ); unlocked_xpm );
TOOL_ACTION PCB_ACTIONS::groupEnter( "pcbnew.EditorControl.groupEnter", TOOL_ACTION PCB_ACTIONS::groupEnter( "pcbnew.EditorControl.groupEnter",
AS_GLOBAL, 0, "", AS_GLOBAL, 0, "",
_( "Enter" ), "", _( "Enter Group" ), _( "Enter the group to edit items" ),
unlocked_xpm ); unlocked_xpm );
TOOL_ACTION PCB_ACTIONS::groupLeave( "pcbnew.EditorControl.groupLeave",
AS_GLOBAL, 0, "",
_( "Leave Group" ), _( "Leave the current group" ),
cancel_xpm );
TOOL_ACTION PCB_ACTIONS::appendBoard( "pcbnew.EditorControl.appendBoard", TOOL_ACTION PCB_ACTIONS::appendBoard( "pcbnew.EditorControl.appendBoard",
AS_GLOBAL, 0, "", AS_GLOBAL, 0, "",
_( "Append Board..." ), "", _( "Append Board..." ), "",

View File

@ -403,6 +403,7 @@ public:
static TOOL_ACTION groupRemoveItems; static TOOL_ACTION groupRemoveItems;
static TOOL_ACTION groupFlatten; static TOOL_ACTION groupFlatten;
static TOOL_ACTION groupEnter; static TOOL_ACTION groupEnter;
static TOOL_ACTION groupLeave;
// Miscellaneous // Miscellaneous
static TOOL_ACTION selectionTool; static TOOL_ACTION selectionTool;

View File

@ -28,6 +28,7 @@
#include "pcbnew_picker_tool.h" #include "pcbnew_picker_tool.h"
#include "selection_tool.h" #include "selection_tool.h"
#include "edit_tool.h" #include "edit_tool.h"
#include "tool/tool_event.h"
#include <bitmaps.h> #include <bitmaps.h>
#include <board_commit.h> #include <board_commit.h>
#include <class_board.h> #include <class_board.h>
@ -1019,14 +1020,7 @@ int PCB_EDITOR_CONTROL::GroupSelected( const TOOL_EVENT& aEvent )
PCB_GROUP* group = new PCB_GROUP( board ); PCB_GROUP* group = new PCB_GROUP( board );
for( EDA_ITEM* item : selection ) for( EDA_ITEM* item : selection )
{ group->AddItem( static_cast<BOARD_ITEM*>( item ) );
BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
// Should be impossible to generate a selection with duplicates
wxCHECK_MSG( group->AddItem( board_item ), 0,
wxString::Format( _( "Item %s appears in selection multiple times" ),
board_item->m_Uuid.AsString() ) );
}
commit.Add( group ); commit.Add( group );
@ -1079,12 +1073,7 @@ int PCB_EDITOR_CONTROL::GroupMergeSelected( const TOOL_EVENT& aEvent )
BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item ); BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
if( board_item != firstGroup ) if( board_item != firstGroup )
{ firstGroup->AddItem( board_item );
// Should be impossible to generate a selection with duplicates
wxCHECK_MSG( firstGroup->AddItem( board_item ), 0,
wxString::Format( _( "Item %s is already in group for merge"),
board_item->m_Uuid.AsString() ) );
}
} }
commit.Push( "GroupMerge" ); commit.Push( "GroupMerge" );
@ -1257,12 +1246,16 @@ int PCB_EDITOR_CONTROL::GroupEnterSelected( const TOOL_EVENT& aEvent )
SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>(); SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
const PCBNEW_SELECTION& selection = selTool->GetSelection(); const PCBNEW_SELECTION& selection = selTool->GetSelection();
wxCHECK( selection.GetSize() == 1, 0 ); if( selection.GetSize() == 1 && selection[0]->Type() == PCB_GROUP_T )
BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( selection[0] ); selTool->EnterGroup();
wxCHECK( board_item->Type() == PCB_GROUP_T, 0 );
selTool->EnterGroup(); return 0;
}
int PCB_EDITOR_CONTROL::GroupLeave( const TOOL_EVENT& aEvent )
{
m_toolMgr->GetTool<SELECTION_TOOL>()->ExitGroup( true /* Select the group */ );
return 0; return 0;
} }
@ -1631,24 +1624,25 @@ void PCB_EDITOR_CONTROL::setTransitions()
Go( &PCB_EDITOR_CONTROL::EditFpInFpEditor, PCB_ACTIONS::editFpInFpEditor.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::EditFpInFpEditor, PCB_ACTIONS::editFpInFpEditor.MakeEvent() );
// Other // Other
Go( &PCB_EDITOR_CONTROL::ToggleLockSelected, PCB_ACTIONS::toggleLock.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::ToggleLockSelected, PCB_ACTIONS::toggleLock.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::LockSelected, PCB_ACTIONS::lock.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::LockSelected, PCB_ACTIONS::lock.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::UnlockSelected, PCB_ACTIONS::unlock.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::UnlockSelected, PCB_ACTIONS::unlock.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::GroupSelected, PCB_ACTIONS::groupCreate.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::GroupSelected, PCB_ACTIONS::groupCreate.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::GroupMergeSelected, PCB_ACTIONS::groupMerge.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::GroupMergeSelected, PCB_ACTIONS::groupMerge.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::UngroupSelected, PCB_ACTIONS::groupUngroup.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::UngroupSelected, PCB_ACTIONS::groupUngroup.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::GroupRemoveItemsSelected, PCB_ACTIONS::groupRemoveItems.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::GroupRemoveItemsSelected, PCB_ACTIONS::groupRemoveItems.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::GroupFlattenSelected, PCB_ACTIONS::groupFlatten.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::GroupFlattenSelected, PCB_ACTIONS::groupFlatten.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::GroupEnterSelected, PCB_ACTIONS::groupEnter.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::GroupEnterSelected, PCB_ACTIONS::groupEnter.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::GroupLeave, PCB_ACTIONS::groupLeave.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::UpdatePCBFromSchematic, ACTIONS::updatePcbFromSchematic.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::UpdatePCBFromSchematic, ACTIONS::updatePcbFromSchematic.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::UpdateSchematicFromPCB, ACTIONS::updateSchematicFromPcb.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::UpdateSchematicFromPCB, ACTIONS::updateSchematicFromPcb.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::ShowEeschema, PCB_ACTIONS::showEeschema.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::ShowEeschema, PCB_ACTIONS::showEeschema.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::ToggleLayersManager, PCB_ACTIONS::showLayersManager.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::ToggleLayersManager, PCB_ACTIONS::showLayersManager.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::ToggleMicrowaveToolbar, PCB_ACTIONS::showMicrowaveToolbar.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::ToggleMicrowaveToolbar, PCB_ACTIONS::showMicrowaveToolbar.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::TogglePythonConsole, PCB_ACTIONS::showPythonConsole.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::TogglePythonConsole, PCB_ACTIONS::showPythonConsole.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::FlipPcbView, PCB_ACTIONS::flipBoard.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::FlipPcbView, PCB_ACTIONS::flipBoard.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::RepairBoard, PCB_ACTIONS::repairBoard.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::RepairBoard, PCB_ACTIONS::repairBoard.MakeEvent() );
} }

View File

@ -58,7 +58,7 @@ public:
int SaveCopyAs( const TOOL_EVENT& aEvent ); int SaveCopyAs( const TOOL_EVENT& aEvent );
int PageSettings( const TOOL_EVENT& aEvent ); int PageSettings( const TOOL_EVENT& aEvent );
int Plot( const TOOL_EVENT& aEvent ); int Plot( const TOOL_EVENT& aEvent );
int BoardSetup( const TOOL_EVENT& aEvent ); int BoardSetup( const TOOL_EVENT& aEvent );
int ImportNetlist( const TOOL_EVENT& aEvent ); int ImportNetlist( const TOOL_EVENT& aEvent );
int ImportSpecctraSession( const TOOL_EVENT& aEvent ); int ImportSpecctraSession( const TOOL_EVENT& aEvent );
@ -125,9 +125,12 @@ public:
///> Collaps subgroups to single group. ///> Collaps subgroups to single group.
int GroupFlattenSelected( const TOOL_EVENT& aEvent ); int GroupFlattenSelected( const TOOL_EVENT& aEvent );
///> Restrit seletion to only member of the group. ///> Restrict seletion to only member of the group.
int GroupEnterSelected( const TOOL_EVENT& aEvent ); int GroupEnterSelected( const TOOL_EVENT& aEvent );
///> Leave the current group (deselect its members and select the group as a whole)
int GroupLeave( const TOOL_EVENT& aEvent );
///> Runs the drill origin tool for setting the origin for drill and pick-and-place files. ///> Runs the drill origin tool for setting the origin for drill and pick-and-place files.
int DrillOrigin( const TOOL_EVENT& aEvent ); int DrillOrigin( const TOOL_EVENT& aEvent );

View File

@ -843,7 +843,17 @@ int PCBNEW_CONTROL::placeBoardItems( std::vector<BOARD_ITEM*>& aItems, bool aIsN
const_cast<KIID&>( item->m_Uuid ) = KIID(); const_cast<KIID&>( item->m_Uuid ) = KIID();
if( item->Type() == PCB_MODULE_T ) if( item->Type() == PCB_MODULE_T )
{
static_cast<MODULE*>( item )->SetPath( KIID_PATH() ); static_cast<MODULE*>( item )->SetPath( KIID_PATH() );
}
else if( item->Type() == PCB_GROUP_T )
{
// If pasting a group, its immediate children must be updated to have its new KIID
static_cast<PCB_GROUP*>( item )->RunOnChildren( [item] ( BOARD_ITEM* aBrdItem )
{
aBrdItem->SetGroup( item->m_Uuid );
} );
}
} }
// Add or just select items for the move/place command // Add or just select items for the move/place command

View File

@ -149,11 +149,18 @@ bool SELECTION_TOOL::Init()
return !frame->ToolStackIsEmpty(); return !frame->ToolStackIsEmpty();
}; };
auto inGroupCondition =
[this] ( const SELECTION& )
{
return m_enteredGroup != nullptr;
};
menu.AddMenu( selectMenu.get(), SELECTION_CONDITIONS::NotEmpty ); menu.AddMenu( selectMenu.get(), SELECTION_CONDITIONS::NotEmpty );
menu.AddSeparator( 1000 ); menu.AddSeparator( 1000 );
// "Cancel" goes at the top of the context menu when a tool is active // "Cancel" goes at the top of the context menu when a tool is active
menu.AddItem( ACTIONS::cancelInteractive, activeToolCondition, 1 ); menu.AddItem( ACTIONS::cancelInteractive, activeToolCondition, 1 );
menu.AddItem( PCB_ACTIONS::groupLeave, inGroupCondition, 1);
menu.AddSeparator( 1 ); menu.AddSeparator( 1 );
if( frame ) if( frame )
@ -169,7 +176,7 @@ void SELECTION_TOOL::Reset( RESET_REASON aReason )
m_locked = true; m_locked = true;
if( m_enteredGroup != NULL ) if( m_enteredGroup != NULL )
exitGroup(); ExitGroup();
if( aReason == TOOL_BASE::MODEL_RELOAD ) if( aReason == TOOL_BASE::MODEL_RELOAD )
{ {
@ -309,7 +316,7 @@ int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
m_frame->FocusOnItem( nullptr ); m_frame->FocusOnItem( nullptr );
if( m_enteredGroup != NULL ) if( m_enteredGroup != NULL )
exitGroup(); ExitGroup();
ClearSelection(); ClearSelection();
@ -332,9 +339,7 @@ void SELECTION_TOOL::EnterGroup()
PCB_GROUP* aGroup = static_cast<PCB_GROUP*>( m_selection[0] ); PCB_GROUP* aGroup = static_cast<PCB_GROUP*>( m_selection[0] );
if( m_enteredGroup != NULL ) if( m_enteredGroup != NULL )
{ ExitGroup();
exitGroup();
}
ClearSelection(); ClearSelection();
m_enteredGroup = aGroup; m_enteredGroup = aGroup;
@ -342,8 +347,17 @@ void SELECTION_TOOL::EnterGroup()
} }
void SELECTION_TOOL::exitGroup() void SELECTION_TOOL::ExitGroup( bool aSelectGroup )
{ {
// Only continue if there is a group entered
if( m_enteredGroup == nullptr )
return;
ClearSelection();
if( aSelectGroup )
select( m_enteredGroup );
m_enteredGroup = NULL; m_enteredGroup = NULL;
} }
@ -354,9 +368,6 @@ PCBNEW_SELECTION& SELECTION_TOOL::GetSelection()
} }
PCBNEW_SELECTION& SELECTION_TOOL::RequestSelection( CLIENT_SELECTION_FILTER aClientFilter, PCBNEW_SELECTION& SELECTION_TOOL::RequestSelection( CLIENT_SELECTION_FILTER aClientFilter,
std::vector<BOARD_ITEM*>* aFiltered, std::vector<BOARD_ITEM*>* aFiltered,
bool aConfirmLockedItems ) bool aConfirmLockedItems )
@ -479,7 +490,9 @@ bool SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag,
if( m_enteredGroup && if( m_enteredGroup &&
!m_enteredGroup->GetBoundingBox().Contains( wxPoint( aWhere.x, aWhere.y ) ) ) !m_enteredGroup->GetBoundingBox().Contains( wxPoint( aWhere.x, aWhere.y ) ) )
exitGroup(); {
ExitGroup();
}
collector.Collect( board(), collector.Collect( board(),
m_editModules ? GENERAL_COLLECTOR::ModuleItems : GENERAL_COLLECTOR::AllBoardItems, m_editModules ? GENERAL_COLLECTOR::ModuleItems : GENERAL_COLLECTOR::AllBoardItems,

View File

@ -185,12 +185,20 @@ public:
} }
void EnterGroup(); void EnterGroup();
void exitGroup();
/**
* Leave the currently entered group.
*
* @param aSelectGroup when true will select the group after leaving
*/
void ExitGroup( bool aSelectGroup = false );
void FilterCollectorForGroups( GENERAL_COLLECTOR& aCollector ) const; void FilterCollectorForGroups( GENERAL_COLLECTOR& aCollector ) const;
PCB_GROUP* GetEnteredGroup() { return m_enteredGroup; } PCB_GROUP* GetEnteredGroup() { return m_enteredGroup; }
private: private:
/** /**
* Function selectPoint() * Function selectPoint()
* Selects an item pointed by the parameter aWhere. If there is more than one item at that * Selects an item pointed by the parameter aWhere. If there is more than one item at that