diff --git a/common/tool/group_tool.cpp b/common/tool/group_tool.cpp index 25e781c198..dec521b695 100644 --- a/common/tool/group_tool.cpp +++ b/common/tool/group_tool.cpp @@ -22,6 +22,8 @@ */ #include "tool/group_tool.h" +#include + #include #include #include @@ -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 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() ); } } diff --git a/eeschema/sch_design_block_utils.cpp b/eeschema/sch_design_block_utils.cpp index daf9bf272b..6a99d87eae 100644 --- a/eeschema/sch_design_block_utils.cpp +++ b/eeschema/sch_design_block_utils.cpp @@ -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; } } diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp index 2c0f45228d..92ec97a725 100644 --- a/eeschema/sch_edit_frame.cpp +++ b/eeschema/sch_edit_frame.cpp @@ -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 ) ); diff --git a/eeschema/tools/sch_group_tool.cpp b/eeschema/tools/sch_group_tool.cpp index e50beb341c..349d64ec2c 100644 --- a/eeschema/tools/sch_group_tool.cpp +++ b/eeschema/tools/sch_group_tool.cpp @@ -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; diff --git a/pcbnew/footprint_edit_frame.cpp b/pcbnew/footprint_edit_frame.cpp index 811145cf86..6badcb8ab1 100644 --- a/pcbnew/footprint_edit_frame.cpp +++ b/pcbnew/footprint_edit_frame.cpp @@ -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() ) ); diff --git a/pcbnew/netlist_reader/board_netlist_updater.cpp b/pcbnew/netlist_reader/board_netlist_updater.cpp index 1203afb2e2..07251d4448 100644 --- a/pcbnew/netlist_reader/board_netlist_updater.cpp +++ b/pcbnew/netlist_reader/board_netlist_updater.cpp @@ -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 ); diff --git a/pcbnew/pcb_design_block_utils.cpp b/pcbnew/pcb_design_block_utils.cpp index edb7bbb7fa..b173cc3321 100644 --- a/pcbnew/pcb_design_block_utils.cpp +++ b/pcbnew/pcb_design_block_utils.cpp @@ -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; } } diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index 826eac8c3d..aed3abd89d 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -1014,7 +1014,7 @@ void PCB_EDIT_FRAME::setupUIConditions() static const std::vector 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 ) ); diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp index 2c553158c8..508101db6e 100644 --- a/pcbnew/tools/drawing_tool.cpp +++ b/pcbnew/tools/drawing_tool.cpp @@ -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& 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() ) diff --git a/pcbnew/tools/multichannel_tool.cpp b/pcbnew/tools/multichannel_tool.cpp index 0d6a309800..b1de65d94a 100644 --- a/pcbnew/tools/multichannel_tool.cpp +++ b/pcbnew/tools/multichannel_tool.cpp @@ -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 toPrune; std::copy( ra.m_components.begin(), ra.m_components.end(), std::inserter( toPrune, toPrune.begin() ) ); diff --git a/pcbnew/tools/pcb_control.cpp b/pcbnew/tools/pcb_control.cpp index 7cef94eabb..3c7348dd6e 100644 --- a/pcbnew/tools/pcb_control.cpp +++ b/pcbnew/tools/pcb_control.cpp @@ -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 selection = selTool->GetSelection(); - for( EDA_ITEM* eda_item : selection ) - { - if( eda_item->IsBOARD_ITEM() ) - { - if( static_cast( 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( eda_item )->GetParentFootprint() ) + if( eda_item->IsBOARD_ITEM() + && !static_cast( 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( eda_item )->IsLocked() ) + group->SetLocked( true ); + } + } + + commit->Add( group ); + + for( EDA_ITEM* eda_item : selection ) + { + if( eda_item->IsBOARD_ITEM() + && !static_cast( 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 diff --git a/pcbnew/tools/pcb_group_tool.cpp b/pcbnew/tools/pcb_group_tool.cpp index 3030c9312b..ed575f3234 100644 --- a/pcbnew/tools/pcb_group_tool.cpp +++ b/pcbnew/tools/pcb_group_tool.cpp @@ -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();