Only delete grouped / generated BOARD_ITEMS from the root item

Fixes https://gitlab.com/kicad/code/kicad/-/issues/17527

This is required as during an undo / redo operation, rebuilding
the previous selection can result in a group / generator being
selected as well as all of their contained items. This prunes
the contained items to remove the possibility of a double-delete.
This commit is contained in:
JamesJ 2024-03-27 12:47:05 +00:00 committed by Jon Evans
parent fd501b4bc8
commit 79a3dd71e4
2 changed files with 62 additions and 2 deletions

View File

@ -2217,6 +2217,51 @@ int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent )
}
void EDIT_TOOL::getChildItemsOfGroupsAndGenerators( EDA_ITEM* item,
std::unordered_set<BOARD_ITEM*>& children )
{
wxASSERT( item->Type() == PCB_GROUP_T || item->Type() == PCB_GENERATOR_T );
std::unordered_set<BOARD_ITEM*>& childItems = static_cast<PCB_GROUP*>( item )->GetItems();
for( BOARD_ITEM* childItem : childItems )
{
children.insert( childItem );
if( childItem->Type() == PCB_GROUP_T || childItem->Type() == PCB_GENERATOR_T )
getChildItemsOfGroupsAndGenerators( childItem, children );
}
}
void EDIT_TOOL::removeNonRootItems( std::unordered_set<EDA_ITEM*>& items )
{
auto itr = items.begin();
while( itr != items.end() )
{
BOARD_ITEM* curItem = dynamic_cast<BOARD_ITEM*>( *itr );
if( curItem )
{
if( curItem->Type() == PCB_GROUP_T || curItem->Type() == PCB_GENERATOR_T )
{
std::unordered_set<BOARD_ITEM*> childItems;
getChildItemsOfGroupsAndGenerators( curItem, childItems );
std::for_each( childItems.begin(), childItems.end(),
[&]( auto eraseItem )
{
items.erase( eraseItem );
} );
}
}
++itr;
}
}
void EDIT_TOOL::DeleteItems( const PCB_SELECTION& aItems, bool aIsCut )
{
BOARD_COMMIT commit( this );
@ -2224,7 +2269,12 @@ void EDIT_TOOL::DeleteItems( const PCB_SELECTION& aItems, bool aIsCut )
// As we are about to remove items, they have to be removed from the selection first
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
for( EDA_ITEM* item : aItems )
// Only delete items that are the root of a selected set (e.g. only delete grouped / generated
// items from the parent item, not individually) - issue #17527
std::unordered_set<EDA_ITEM*> rootItems( aItems.begin(), aItems.end() );
removeNonRootItems( rootItems );
for( EDA_ITEM* item : rootItems )
{
BOARD_ITEM* board_item = dynamic_cast<BOARD_ITEM*>( item );
wxCHECK2( board_item, continue );
@ -2325,7 +2375,7 @@ void EDIT_TOOL::DeleteItems( const PCB_SELECTION& aItems, bool aIsCut )
break;
case PCB_GENERATOR_T:
if( aItems.Size() == 1 )
if( rootItems.size() == 1 )
{
PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( board_item );

View File

@ -32,6 +32,7 @@
#include <tools/pcb_tool_base.h>
#include <tools/pcb_selection_tool.h>
#include <status_popup.h>
#include <unordered_set>
class BOARD_COMMIT;
@ -210,6 +211,15 @@ private:
///< Rebuilds the ratsnest for operations that require it outside the commit rebuild
void rebuildConnectivity();
///< Removes all items from the set which are children of other PCB_GROUP or PCB_GENERATOR
///< items in the set
void removeNonRootItems( std::unordered_set<EDA_ITEM*>& items );
///< Recursively adds any child items of the given item to the set
void getChildItemsOfGroupsAndGenerators( EDA_ITEM* item,
std::unordered_set<BOARD_ITEM*>& children );
private:
PCB_SELECTION_TOOL* m_selectionTool;
bool m_dragging; // Indicates objects are currently being dragged