Prevent single-element groups
Groups with fewer than two items serve no purpose. All user-facing group creation paths now require at least two items before creating a group. The Group menu action is greyed out when fewer than two items are selected. When items are removed from a group via RemoveFromGroup, pruneExistingGroups, or the netlist updater, the group is dissolved if it drops below two members. Importers are left unchanged since they reflect external file structure.
This commit is contained in:
@@ -22,6 +22,8 @@
|
||||
*/
|
||||
#include "tool/group_tool.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <eda_draw_frame.h>
|
||||
#include <kiplatform/ui.h>
|
||||
#include <tool/actions.h>
|
||||
@@ -61,7 +63,7 @@ public:
|
||||
private:
|
||||
void update() override
|
||||
{
|
||||
bool canGroup = false;
|
||||
int selectionCount = 0;
|
||||
bool hasGroup = false;
|
||||
bool hasMember = false;
|
||||
bool onlyOneGroup = false;
|
||||
@@ -71,7 +73,7 @@ private:
|
||||
{
|
||||
for( EDA_ITEM* item : m_selectionTool->GetSelection() )
|
||||
{
|
||||
canGroup = true;
|
||||
selectionCount++;
|
||||
|
||||
if( item->Type() == PCB_GROUP_T || item->Type() == SCH_GROUP_T )
|
||||
{
|
||||
@@ -92,7 +94,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
Enable( ACTIONS::group.GetUIId(), canGroup );
|
||||
Enable( ACTIONS::group.GetUIId(), selectionCount >= 2 );
|
||||
Enable( ACTIONS::ungroup.GetUIId(), hasGroup );
|
||||
Enable( ACTIONS::addToGroup.GetUIId(), onlyOneGroup && hasUngroupedItems );
|
||||
Enable( ACTIONS::removeFromGroup.GetUIId(), hasMember );
|
||||
@@ -253,6 +255,8 @@ int GROUP_TOOL::RemoveFromGroup( const TOOL_EVENT& aEvent )
|
||||
if( selection.Empty() )
|
||||
m_toolMgr->RunAction( ACTIONS::selectionCursor );
|
||||
|
||||
std::set<EDA_GROUP*> affectedGroups;
|
||||
|
||||
for( EDA_ITEM* item : selection )
|
||||
{
|
||||
if( EDA_GROUP* group = item->GetParentGroup() )
|
||||
@@ -260,6 +264,16 @@ int GROUP_TOOL::RemoveFromGroup( const TOOL_EVENT& aEvent )
|
||||
m_commit->Modify( group->AsEdaItem(), m_frame->GetScreen(), RECURSE_MODE::NO_RECURSE );
|
||||
m_commit->Modify( item, m_frame->GetScreen() );
|
||||
group->RemoveItem( item );
|
||||
affectedGroups.insert( group );
|
||||
}
|
||||
}
|
||||
|
||||
for( EDA_GROUP* group : affectedGroups )
|
||||
{
|
||||
if( group->GetItems().size() < 2 )
|
||||
{
|
||||
group->RemoveAll();
|
||||
m_commit->Remove( group->AsEdaItem(), m_frame->GetScreen() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -394,7 +394,7 @@ bool SCH_EDIT_FRAME::SaveSelectionAsDesignBlock( const wxString& aLibraryName )
|
||||
newGroup->SetName( blk.GetLibId().GetUniStringLibItemName() );
|
||||
newGroup->SetDesignBlockLibId( blk.GetLibId() );
|
||||
|
||||
bool added = false;
|
||||
int addedCount = 0;
|
||||
|
||||
for( EDA_ITEM* edaItem : selection )
|
||||
{
|
||||
@@ -414,10 +414,10 @@ bool SCH_EDIT_FRAME::SaveSelectionAsDesignBlock( const wxString& aLibraryName )
|
||||
|
||||
commit.Modify( item, screen, RECURSE_MODE::NO_RECURSE );
|
||||
newGroup->AddItem( item );
|
||||
added = true;
|
||||
addedCount++;
|
||||
}
|
||||
|
||||
if( added )
|
||||
if( addedCount >= 2 )
|
||||
{
|
||||
commit.Add( newGroup, screen );
|
||||
commit.Push( _( "Group Items" ) );
|
||||
@@ -427,6 +427,7 @@ bool SCH_EDIT_FRAME::SaveSelectionAsDesignBlock( const wxString& aLibraryName )
|
||||
}
|
||||
else
|
||||
{
|
||||
newGroup->RemoveAll();
|
||||
delete newGroup;
|
||||
}
|
||||
}
|
||||
@@ -619,7 +620,7 @@ bool SCH_EDIT_FRAME::UpdateDesignBlockFromSelection( const LIB_ID& aLibId )
|
||||
newGroup->SetName( aLibId.GetUniStringLibItemName() );
|
||||
newGroup->SetDesignBlockLibId( aLibId );
|
||||
|
||||
bool added = false;
|
||||
int addedCount = 0;
|
||||
|
||||
for( EDA_ITEM* edaItem : selection )
|
||||
{
|
||||
@@ -639,10 +640,10 @@ bool SCH_EDIT_FRAME::UpdateDesignBlockFromSelection( const LIB_ID& aLibId )
|
||||
|
||||
commit.Modify( item, screen, RECURSE_MODE::NO_RECURSE );
|
||||
newGroup->AddItem( item );
|
||||
added = true;
|
||||
addedCount++;
|
||||
}
|
||||
|
||||
if( added )
|
||||
if( addedCount >= 2 )
|
||||
{
|
||||
commit.Add( newGroup, screen );
|
||||
commit.Push( _( "Group Items" ) );
|
||||
@@ -652,6 +653,7 @@ bool SCH_EDIT_FRAME::UpdateDesignBlockFromSelection( const LIB_ID& aLibId )
|
||||
}
|
||||
else
|
||||
{
|
||||
newGroup->RemoveAll();
|
||||
delete newGroup;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,7 +813,7 @@ void SCH_EDIT_FRAME::setupUIConditions()
|
||||
mgr->SetConditions( SCH_ACTIONS::rotateCCW, ENABLE( hasElements ) );
|
||||
mgr->SetConditions( SCH_ACTIONS::mirrorH, ENABLE( hasElements ) );
|
||||
mgr->SetConditions( SCH_ACTIONS::mirrorV, ENABLE( hasElements ) );
|
||||
mgr->SetConditions( ACTIONS::group, ENABLE( SELECTION_CONDITIONS::NotEmpty ) );
|
||||
mgr->SetConditions( ACTIONS::group, ENABLE( SELECTION_CONDITIONS::MoreThan( 1 ) ) );
|
||||
mgr->SetConditions( ACTIONS::ungroup, ENABLE( SELECTION_CONDITIONS::HasType( SCH_GROUP_T ) ) );
|
||||
|
||||
mgr->SetConditions( SCH_ACTIONS::placeLinkedDesignBlock, ENABLE( groupWithDesignBlockLink ) );
|
||||
|
||||
@@ -150,7 +150,7 @@ int SCH_GROUP_TOOL::Group( const TOOL_EVENT& aEvent )
|
||||
selection.Remove( schItem );
|
||||
}
|
||||
|
||||
if( selection.Empty() )
|
||||
if( selection.GetSize() < 2 )
|
||||
return 0;
|
||||
|
||||
SCH_GROUP* group = new SCH_GROUP;
|
||||
|
||||
@@ -1368,7 +1368,7 @@ void FOOTPRINT_EDIT_FRAME::setupUIConditions()
|
||||
mgr->SetConditions( PCB_ACTIONS::rotateCcw, ENABLE( cond.HasItems() ) );
|
||||
mgr->SetConditions( PCB_ACTIONS::mirrorH, ENABLE( cond.HasItems() ) );
|
||||
mgr->SetConditions( PCB_ACTIONS::mirrorV, ENABLE( cond.HasItems() ) );
|
||||
mgr->SetConditions( ACTIONS::group, ENABLE( SELECTION_CONDITIONS::NotEmpty ) );
|
||||
mgr->SetConditions( ACTIONS::group, ENABLE( SELECTION_CONDITIONS::MoreThan( 1 ) ) );
|
||||
mgr->SetConditions( ACTIONS::ungroup, ENABLE( SELECTION_CONDITIONS::HasType( PCB_GROUP_T ) ) );
|
||||
|
||||
mgr->SetConditions( PCB_ACTIONS::padDisplayMode, CHECK( !cond.PadFillDisplay() ) );
|
||||
|
||||
@@ -947,6 +947,12 @@ bool BOARD_NETLIST_UPDATER::updateFootprintGroup( FOOTPRINT* aPcbFootprint,
|
||||
changed = true;
|
||||
m_commit.Modify( existingGroup, nullptr, RECURSE_MODE::NO_RECURSE );
|
||||
existingGroup->RemoveItem( aPcbFootprint );
|
||||
|
||||
if( existingGroup->GetItems().size() < 2 )
|
||||
{
|
||||
existingGroup->RemoveAll();
|
||||
m_commit.Remove( existingGroup );
|
||||
}
|
||||
}
|
||||
|
||||
m_reporter->Report( msg, RPT_SEVERITY_ACTION );
|
||||
|
||||
@@ -423,7 +423,7 @@ bool PCB_EDIT_FRAME::SaveSelectionAsDesignBlock( const wxString& aLibraryName )
|
||||
newGroup->SetName( blk.GetLibId().GetUniStringLibItemName() );
|
||||
newGroup->SetDesignBlockLibId( blk.GetLibId() );
|
||||
|
||||
bool added = false;
|
||||
int addedCount = 0;
|
||||
|
||||
for( EDA_ITEM* edaItem : selection )
|
||||
{
|
||||
@@ -443,10 +443,10 @@ bool PCB_EDIT_FRAME::SaveSelectionAsDesignBlock( const wxString& aLibraryName )
|
||||
|
||||
commit.Modify( item, nullptr, RECURSE_MODE::NO_RECURSE );
|
||||
newGroup->AddItem( item );
|
||||
added = true;
|
||||
addedCount++;
|
||||
}
|
||||
|
||||
if( added )
|
||||
if( addedCount >= 2 )
|
||||
{
|
||||
commit.Add( newGroup );
|
||||
commit.Push( _( "Group Items" ) );
|
||||
@@ -456,6 +456,7 @@ bool PCB_EDIT_FRAME::SaveSelectionAsDesignBlock( const wxString& aLibraryName )
|
||||
}
|
||||
else
|
||||
{
|
||||
newGroup->RemoveAll();
|
||||
delete newGroup;
|
||||
}
|
||||
}
|
||||
@@ -560,7 +561,7 @@ bool PCB_EDIT_FRAME::UpdateDesignBlockFromSelection( const LIB_ID& aLibId )
|
||||
newGroup->SetName( aLibId.GetUniStringLibItemName() );
|
||||
newGroup->SetDesignBlockLibId( aLibId );
|
||||
|
||||
bool added = false;
|
||||
int addedCount = 0;
|
||||
|
||||
for( EDA_ITEM* edaItem : selection )
|
||||
{
|
||||
@@ -580,10 +581,10 @@ bool PCB_EDIT_FRAME::UpdateDesignBlockFromSelection( const LIB_ID& aLibId )
|
||||
|
||||
commit.Modify( item, nullptr, RECURSE_MODE::NO_RECURSE );
|
||||
newGroup->AddItem( item );
|
||||
added = true;
|
||||
addedCount++;
|
||||
}
|
||||
|
||||
if( added )
|
||||
if( addedCount >= 2 )
|
||||
{
|
||||
commit.Add( newGroup );
|
||||
commit.Push( _( "Group Items" ) );
|
||||
@@ -593,6 +594,7 @@ bool PCB_EDIT_FRAME::UpdateDesignBlockFromSelection( const LIB_ID& aLibId )
|
||||
}
|
||||
else
|
||||
{
|
||||
newGroup->RemoveAll();
|
||||
delete newGroup;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1014,7 +1014,7 @@ void PCB_EDIT_FRAME::setupUIConditions()
|
||||
|
||||
static const std::vector<KICAD_T> groupTypes = { PCB_GROUP_T, PCB_GENERATOR_T };
|
||||
|
||||
mgr->SetConditions( ACTIONS::group, ENABLE( SELECTION_CONDITIONS::NotEmpty ) );
|
||||
mgr->SetConditions( ACTIONS::group, ENABLE( SELECTION_CONDITIONS::MoreThan( 1 ) ) );
|
||||
mgr->SetConditions( ACTIONS::ungroup, ENABLE( SELECTION_CONDITIONS::HasTypes( groupTypes ) ) );
|
||||
mgr->SetConditions( PCB_ACTIONS::lock, ENABLE( PCB_SELECTION_CONDITIONS::HasUnlockedItems ) );
|
||||
mgr->SetConditions( PCB_ACTIONS::unlock, ENABLE( PCB_SELECTION_CONDITIONS::HasLockedItems ) );
|
||||
|
||||
@@ -2076,11 +2076,20 @@ int DRAWING_TOOL::PlaceImportedGraphics( const TOOL_EVENT& aEvent )
|
||||
|
||||
if( dlg.ShouldGroupItems() )
|
||||
{
|
||||
group = new PCB_GROUP( m_frame->GetModel() );
|
||||
size_t boardItemCount = std::count_if( list.begin(), list.end(),
|
||||
[]( const std::unique_ptr<EDA_ITEM>& ptr )
|
||||
{
|
||||
return ptr->IsBOARD_ITEM();
|
||||
} );
|
||||
|
||||
newItems.push_back( group );
|
||||
selectedItems.push_back( group );
|
||||
preview.Add( group );
|
||||
if( boardItemCount >= 2 )
|
||||
{
|
||||
group = new PCB_GROUP( m_frame->GetModel() );
|
||||
|
||||
newItems.push_back( group );
|
||||
selectedItems.push_back( group );
|
||||
preview.Add( group );
|
||||
}
|
||||
}
|
||||
|
||||
if( dlg.ShouldFixDiscontinuities() )
|
||||
|
||||
@@ -885,6 +885,9 @@ int MULTICHANNEL_TOOL::RepeatLayout( const TOOL_EVENT& aEvent, ZONE* aRefZone )
|
||||
{
|
||||
for( const auto& [targetArea, compatData] : m_areas.m_compatMap )
|
||||
{
|
||||
if( compatData.m_groupableItems.size() < 2 )
|
||||
continue;
|
||||
|
||||
pruneExistingGroups( commit, compatData.m_affectedItems );
|
||||
|
||||
PCB_GROUP* group = new PCB_GROUP( board() );
|
||||
@@ -1533,7 +1536,7 @@ bool MULTICHANNEL_TOOL::pruneExistingGroups( COMMIT& aCommit,
|
||||
for( EDA_ITEM* item : pruneList )
|
||||
group->RemoveItem( item );
|
||||
|
||||
if( group->GetItems().empty() )
|
||||
if( group->GetItems().size() < 2 )
|
||||
aCommit.Remove( group );
|
||||
}
|
||||
}
|
||||
@@ -1712,6 +1715,10 @@ int MULTICHANNEL_TOOL::AutogenerateRuleAreas( const TOOL_EVENT& aEvent )
|
||||
if( ra.m_existsAlready && !m_areas.m_replaceExisting )
|
||||
continue;
|
||||
|
||||
// A group needs at least 2 items (zone + at least 1 component)
|
||||
if( ra.m_components.empty() )
|
||||
continue;
|
||||
|
||||
std::unordered_set<BOARD_ITEM*> toPrune;
|
||||
|
||||
std::copy( ra.m_components.begin(), ra.m_components.end(), std::inserter( toPrune, toPrune.begin() ) );
|
||||
|
||||
@@ -2064,48 +2064,63 @@ int PCB_CONTROL::AppendBoard( PCB_IO& pi, const wxString& fileName, DESIGN_BLOCK
|
||||
{
|
||||
if( placeAsGroup )
|
||||
{
|
||||
PCB_GROUP* group = new PCB_GROUP( brd );
|
||||
|
||||
if( aDesignBlock )
|
||||
{
|
||||
group->SetName( aDesignBlock->GetLibId().GetLibItemName() );
|
||||
group->SetDesignBlockLibId( aDesignBlock->GetLibId() );
|
||||
}
|
||||
else
|
||||
{
|
||||
group->SetName( wxFileName( fileName ).GetName() );
|
||||
}
|
||||
|
||||
// Get the selection tool selection
|
||||
PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
|
||||
PCB_SELECTION selection = selTool->GetSelection();
|
||||
|
||||
for( EDA_ITEM* eda_item : selection )
|
||||
{
|
||||
if( eda_item->IsBOARD_ITEM() )
|
||||
{
|
||||
if( static_cast<BOARD_ITEM*>( eda_item )->IsLocked() )
|
||||
group->SetLocked( true );
|
||||
}
|
||||
}
|
||||
|
||||
commit->Add( group );
|
||||
// Count items that would be added to the group
|
||||
int groupableCount = 0;
|
||||
|
||||
for( EDA_ITEM* eda_item : selection )
|
||||
{
|
||||
if( eda_item->IsBOARD_ITEM() && !static_cast<BOARD_ITEM*>( eda_item )->GetParentFootprint() )
|
||||
if( eda_item->IsBOARD_ITEM()
|
||||
&& !static_cast<BOARD_ITEM*>( eda_item )->GetParentFootprint() )
|
||||
{
|
||||
commit->Modify( eda_item );
|
||||
group->AddItem( eda_item );
|
||||
groupableCount++;
|
||||
}
|
||||
}
|
||||
|
||||
selTool->ClearSelection();
|
||||
selTool->select( group );
|
||||
if( groupableCount >= 2 )
|
||||
{
|
||||
PCB_GROUP* group = new PCB_GROUP( brd );
|
||||
|
||||
m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
|
||||
m_frame->OnModify();
|
||||
m_frame->Refresh();
|
||||
if( aDesignBlock )
|
||||
{
|
||||
group->SetName( aDesignBlock->GetLibId().GetLibItemName() );
|
||||
group->SetDesignBlockLibId( aDesignBlock->GetLibId() );
|
||||
}
|
||||
else
|
||||
{
|
||||
group->SetName( wxFileName( fileName ).GetName() );
|
||||
}
|
||||
|
||||
for( EDA_ITEM* eda_item : selection )
|
||||
{
|
||||
if( eda_item->IsBOARD_ITEM() )
|
||||
{
|
||||
if( static_cast<BOARD_ITEM*>( eda_item )->IsLocked() )
|
||||
group->SetLocked( true );
|
||||
}
|
||||
}
|
||||
|
||||
commit->Add( group );
|
||||
|
||||
for( EDA_ITEM* eda_item : selection )
|
||||
{
|
||||
if( eda_item->IsBOARD_ITEM()
|
||||
&& !static_cast<BOARD_ITEM*>( eda_item )->GetParentFootprint() )
|
||||
{
|
||||
commit->Modify( eda_item );
|
||||
group->AddItem( eda_item );
|
||||
}
|
||||
}
|
||||
|
||||
selTool->ClearSelection();
|
||||
selTool->select( group );
|
||||
|
||||
m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
|
||||
m_frame->OnModify();
|
||||
m_frame->Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
// If we were provided a commit, let the caller control when to push it
|
||||
|
||||
@@ -176,7 +176,7 @@ int PCB_GROUP_TOOL::Group( const TOOL_EVENT& aEvent )
|
||||
} );
|
||||
}
|
||||
|
||||
if( selection.Empty() )
|
||||
if( selection.GetSize() < 2 )
|
||||
return 0;
|
||||
|
||||
BOARD* board = getModel<BOARD>();
|
||||
|
||||
Reference in New Issue
Block a user