Rewrite GROUP undo based on uuids.

This also removes the GROUP/UNGROUP-specific undo actions.

This also fixes a bunch of undo bugs when duplicating
group members, creating pins, etc.

This also fixes some undo bugs when dividing wires etc.

This also fixes some bugs with new sch items not
being created within an entered group.
This commit is contained in:
Jeff Young 2025-05-19 21:36:46 +01:00
parent 2b5b3ffb8d
commit f58fc0b952
136 changed files with 1058 additions and 1631 deletions

View File

@ -380,9 +380,6 @@ void BOARD_ADAPTER::createPadWithMargin( const PAD* aPad, CONTAINER_2D_BASE* aCo
dummy.SetSize( aLayer, VECTOR2I( dummySize.x, dummySize.y ) );
dummy.TransformShapeToPolygon( poly, aLayer, 0, maxError, ERROR_INSIDE );
clearance = { 0, 0 };
// Remove group membership from dummy item before deleting
dummy.SetParentGroup( nullptr );
}
else if( aPad->GetShape( aLayer ) == PAD_SHAPE::CUSTOM )
{

View File

@ -1179,10 +1179,10 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
// ADD PLATED COPPER
ConvertPolygonToTriangles( *m_frontPlatedCopperPolys, *m_platedPadsFront, m_biuTo3Dunits,
*m_board->GetItem( niluuid ) );
*DELETED_BOARD_ITEM::GetInstance() );
ConvertPolygonToTriangles( *m_backPlatedCopperPolys, *m_platedPadsBack, m_biuTo3Dunits,
*m_board->GetItem( niluuid ) );
*DELETED_BOARD_ITEM::GetInstance() );
m_platedPadsFront->BuildBVH();
m_platedPadsBack->BuildBVH();

View File

@ -39,28 +39,12 @@ COMMIT::COMMIT()
COMMIT::~COMMIT()
{
for( COMMIT_LINE& ent : m_changes )
{
if( ent.m_copy )
{
// If we're deleting changes, we have a commit that is being abandoned,
// and the copies of items with group memberships can have their group membership
// cleared as long as we make sure that group doesn't hold a reference to the copy.
if( ent.m_copy->GetParentGroup() )
{
// This is a copy, so it should not be in the group.
// (RemoveItem() returns false when it doesn't find the item),
// but if it is we need to remove it to prevent a crash.
wxASSERT( ent.m_copy->GetParentGroup()->RemoveItem( ent.m_copy ) == false );
ent.m_copy->SetParentGroup( nullptr );
}
delete ent.m_copy;
}
}
delete ent.m_copy;
}
COMMIT& COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType, BASE_SCREEN* aScreen )
COMMIT& COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType, BASE_SCREEN* aScreen,
RECURSE_MODE aRecurse )
{
// CHT_MODIFY and CHT_DONE are not compatible
wxASSERT( ( aChangeType & ( CHT_MODIFY | CHT_DONE ) ) != ( CHT_MODIFY | CHT_DONE ) );
@ -71,32 +55,25 @@ COMMIT& COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType, BASE_SCREEN* aS
{
case CHT_ADD:
makeEntry( aItem, CHT_ADD | flag, nullptr, aScreen );
return *this;
break;
case CHT_REMOVE:
wxASSERT( m_deletedItems.find( aItem ) == m_deletedItems.end() );
m_deletedItems.insert( aItem );
makeEntry( aItem, CHT_REMOVE | flag, nullptr, aScreen );
return *this;
makeEntry( aItem, CHT_REMOVE | flag, makeImage( aItem ), aScreen );
if( EDA_GROUP* parentGroup = aItem->GetParentGroup() )
Modify( parentGroup->AsEdaItem(), aScreen );
break;
case CHT_MODIFY:
{
EDA_ITEM* parent = parentObject( aItem );
EDA_ITEM* clone = makeImage( parent );
wxASSERT( clone );
if( clone )
return createModified( parent, clone, flag, aScreen );
createModified( parent, makeImage( parent ), flag, aScreen );
break;
}
case CHT_GROUP:
case CHT_UNGROUP:
makeEntry( aItem, aChangeType, nullptr, aScreen );
return *this;
default:
wxFAIL;
}
@ -106,7 +83,7 @@ COMMIT& COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType, BASE_SCREEN* aS
COMMIT& COMMIT::Stage( std::vector<EDA_ITEM*> &container, CHANGE_TYPE aChangeType,
BASE_SCREEN *aScreen )
BASE_SCREEN *aScreen )
{
for( EDA_ITEM* item : container )
Stage( item, aChangeType, aScreen);
@ -157,7 +134,6 @@ COMMIT& COMMIT::createModified( EDA_ITEM* aItem, EDA_ITEM* aCopy, int aExtraFlag
if( m_changedItems.find( parent ) != m_changedItems.end() )
{
aCopy->SetParentGroup( nullptr );
delete aCopy;
return *this; // item has been already modified once
}
@ -170,9 +146,6 @@ COMMIT& COMMIT::createModified( EDA_ITEM* aItem, EDA_ITEM* aCopy, int aExtraFlag
void COMMIT::makeEntry( EDA_ITEM* aItem, CHANGE_TYPE aType, EDA_ITEM* aCopy, BASE_SCREEN *aScreen )
{
// Expect an item copy if it is going to be modified
wxASSERT( !!aCopy == ( ( aType & CHT_TYPE ) == CHT_MODIFY ) );
COMMIT_LINE ent;
ent.m_item = aItem;
@ -206,8 +179,6 @@ CHANGE_TYPE COMMIT::convert( UNDO_REDO aType ) const
{
case UNDO_REDO::NEWITEM: return CHT_ADD;
case UNDO_REDO::DELETED: return CHT_REMOVE;
case UNDO_REDO::REGROUP: return CHT_GROUP;
case UNDO_REDO::UNGROUP: return CHT_UNGROUP;
case UNDO_REDO::CHANGED: return CHT_MODIFY;
default: wxASSERT( false ); return CHT_MODIFY;
}
@ -220,8 +191,6 @@ UNDO_REDO COMMIT::convert( CHANGE_TYPE aType ) const
{
case CHT_ADD: return UNDO_REDO::NEWITEM;
case CHT_REMOVE: return UNDO_REDO::DELETED;
case CHT_GROUP: return UNDO_REDO::REGROUP;
case CHT_UNGROUP: return UNDO_REDO::UNGROUP;
case CHT_MODIFY: return UNDO_REDO::CHANGED;
default: wxASSERT( false ); return UNDO_REDO::CHANGED;
}

View File

@ -83,7 +83,7 @@ bool DIALOG_GROUP_PROPERTIES::TransferDataToWindow()
bool DIALOG_GROUP_PROPERTIES::TransferDataFromWindow()
{
m_commit->Modify( m_group->AsEdaItem(), m_frame->GetScreen() );
m_commit->Modify( m_group->AsEdaItem(), m_frame->GetScreen(), RECURSE_MODE::RECURSE );
for( size_t ii = 0; ii < m_membersList->GetCount(); ++ii )
{
@ -117,7 +117,9 @@ bool DIALOG_GROUP_PROPERTIES::TransferDataFromWindow()
m_group->SetDesignBlockLibId( libId );
}
else
{
m_group->SetDesignBlockLibId( LIB_ID() );
}
m_toolMgr->RunAction( ACTIONS::selectionClear );
m_group->RemoveAll();

View File

@ -1,6 +1,68 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <eda_group.h>
EDA_GROUP::~EDA_GROUP()
void EDA_GROUP::AddItem( EDA_ITEM* aItem )
{
wxCHECK_RET( aItem, wxT( "Nullptr added to group." ) );
// Items can only be in one group at a time
if( EDA_GROUP* parentGroup = aItem->GetParentGroup() )
parentGroup->RemoveItem( aItem );
m_items.insert( aItem );
aItem->SetParentGroup( this );
}
void EDA_GROUP::RemoveItem( EDA_ITEM* aItem )
{
wxCHECK_RET( aItem, wxT( "Nullptr removed from group." ) );
if( m_items.erase( aItem ) == 1 )
aItem->SetParentGroup( nullptr );
}
void EDA_GROUP::RemoveAll()
{
for( EDA_ITEM* item : m_items )
item->SetParentGroup( nullptr );
m_items.clear();
}
KIID_VECT_LIST EDA_GROUP::GetGroupMemberIds() const
{
KIID_VECT_LIST members;
for( EDA_ITEM* item : m_items )
members.push_back( item->m_Uuid );
return members;
}

View File

@ -46,53 +46,36 @@ class EDA_GROUP
{
public:
virtual EDA_ITEM* AsEdaItem() = 0;
virtual ~EDA_GROUP();
virtual ~EDA_GROUP() = default;
wxString GetName() const { return m_name; }
void SetName( const wxString& aName ) { m_name = aName; }
std::unordered_set<EDA_ITEM*>& GetItems() { return m_items; }
const std::unordered_set<EDA_ITEM*>& GetItems() const { return m_items; }
/**
* Add item to group. Does not take ownership of item.
*
* @return true if item was added (false if item belongs to a different group).
*/
virtual bool AddItem( EDA_ITEM* aItem ) = 0;
void AddItem( EDA_ITEM* aItem );
/**
* Remove item from group.
*
* @return true if item was removed (false if item was not in the group).
*/
virtual bool RemoveItem( EDA_ITEM* aItem ) = 0;
void RemoveItem( EDA_ITEM* aItem );
void RemoveAll();
virtual void RemoveAll() = 0;
/*
* Clone() this and all descendants
*/
virtual EDA_GROUP* DeepClone() const = 0;
/*
* Duplicate() this and all descendants
*/
virtual EDA_GROUP* DeepDuplicate() const = 0;
KIID_VECT_LIST GetGroupMemberIds() const;
bool HasDesignBlockLink() const { return m_designBlockLibId.IsValid(); }
void SetDesignBlockLibId( const LIB_ID& aLibId ) { m_designBlockLibId = aLibId; }
const LIB_ID& GetDesignBlockLibId() const { return m_designBlockLibId; }
protected:
std::unordered_set<EDA_ITEM*> m_items; // Members of the group
wxString m_name; // Optional group name
// Optional link to a design block
LIB_ID m_designBlockLibId;
std::unordered_set<EDA_ITEM*> m_items; // Members of the group
wxString m_name; // Optional group name
LIB_ID m_designBlockLibId; // Optional link to a design block
};
#endif // CLASS_PCB_GROUP_H_

View File

@ -26,6 +26,7 @@
#include <bitmaps.h>
#include <eda_item.h>
#include <eda_group.h>
#include <trace_helpers.h>
#include <trigo.h>
#include <i18n_utility.h>
@ -71,6 +72,31 @@ EDA_ITEM::EDA_ITEM( const EDA_ITEM& base ) :
}
EDA_ITEM* EDA_ITEM::findParent( KICAD_T aType ) const
{
EDA_ITEM* parent = GetParent();
while( parent )
{
if( parent->Type() == aType )
return parent;
else
parent = parent->GetParent();
}
return nullptr;
}
KIID EDA_ITEM::GetParentGroupId() const
{
if( EDA_GROUP* group = GetParentGroup() )
return group->AsEdaItem()->m_Uuid;
else
return niluuid;
}
void EDA_ITEM::SetModified()
{
SetFlags( IS_CHANGED );

View File

@ -402,9 +402,8 @@ unsigned int RC_TREE_MODEL::GetChildren( wxDataViewItem const& aItem,
}
void RC_TREE_MODEL::GetValue( wxVariant& aVariant,
wxDataViewItem const& aItem,
unsigned int aCol ) const
void RC_TREE_MODEL::GetValue( wxVariant& aVariant, wxDataViewItem const& aItem,
unsigned int aCol ) const
{
const RC_TREE_NODE* node = ToNode( aItem );
const std::shared_ptr<RC_ITEM> rcItem = node->m_RcItem;
@ -443,20 +442,20 @@ void RC_TREE_MODEL::GetValue( wxVariant& aVariant,
if( marker && marker->GetMarkerType() == MARKER_BASE::MARKER_DRAWING_SHEET )
msg = _( "Drawing Sheet" );
else
item = m_editFrame->GetItem( rcItem->GetMainItemID() );
item = m_editFrame->ResolveItem( rcItem->GetMainItemID() );
break;
case RC_TREE_NODE::AUX_ITEM:
item = m_editFrame->GetItem( rcItem->GetAuxItemID() );
item = m_editFrame->ResolveItem( rcItem->GetAuxItemID() );
break;
case RC_TREE_NODE::AUX_ITEM2:
item = m_editFrame->GetItem( rcItem->GetAuxItem2ID() );
item = m_editFrame->ResolveItem( rcItem->GetAuxItem2ID() );
break;
case RC_TREE_NODE::AUX_ITEM3:
item = m_editFrame->GetItem( rcItem->GetAuxItem3ID() );
item = m_editFrame->ResolveItem( rcItem->GetAuxItem3ID() );
break;
case RC_TREE_NODE::COMMENT:

View File

@ -164,18 +164,18 @@ int GROUP_TOOL::Ungroup( const TOOL_EVENT& aEvent )
for( EDA_ITEM* item : selCopy )
{
EDA_GROUP* group = dynamic_cast<EDA_GROUP*>( item );
if( group )
if( EDA_GROUP* group = dynamic_cast<EDA_GROUP*>( item ) )
{
group->AsEdaItem()->SetSelected();
m_commit->Remove( group->AsEdaItem(), m_frame->GetScreen() );
for( EDA_ITEM* member : group->GetItems() )
{
m_commit->Stage( member, CHT_UNGROUP, m_frame->GetScreen() );
m_commit->Modify( member, m_frame->GetScreen() );
toSelect.push_back( member );
}
group->AsEdaItem()->SetSelected();
m_commit->Remove( group->AsEdaItem(), m_frame->GetScreen() );
group->RemoveAll();
}
}
@ -208,7 +208,9 @@ int GROUP_TOOL::AddToGroup( const TOOL_EVENT& aEvent )
group = item;
}
else if( !item->GetParentGroup() )
{
toAdd.push_back( item );
}
}
if( !group || toAdd.empty() )
@ -219,7 +221,18 @@ int GROUP_TOOL::AddToGroup( const TOOL_EVENT& aEvent )
m_commit->Modify( group, m_frame->GetScreen() );
for( EDA_ITEM* item : toAdd )
m_commit->Stage( item, CHT_GROUP, m_frame->GetScreen() );
{
EDA_GROUP* existingGroup = item->GetParentGroup();
KIID existingGroupId = existingGroup ? existingGroup->AsEdaItem()->m_Uuid : niluuid;
if( existingGroupId != group->m_Uuid )
{
m_commit->Modify( item, m_frame->GetScreen() );
if( existingGroup )
m_commit->Modify( existingGroup->AsEdaItem(), m_frame->GetScreen() );
}
}
m_commit->Push( _( "Add Items to Group" ) );
@ -240,8 +253,12 @@ int GROUP_TOOL::RemoveFromGroup( const TOOL_EVENT& aEvent )
for( EDA_ITEM* item : selection )
{
if( item->GetParentGroup() )
m_commit->Stage( item, CHT_UNGROUP, m_frame->GetScreen() );
if( EDA_GROUP* group = item->GetParentGroup() )
{
m_commit->Modify( group->AsEdaItem(), m_frame->GetScreen() );
m_commit->Modify( item, m_frame->GetScreen() );
group->RemoveItem( item );
}
}
m_commit->Push( _( "Remove Group Items" ) );

View File

@ -24,6 +24,7 @@
*/
#include <eda_item.h>
#include <eda_group.h>
#include <undo_redo_container.h>
@ -47,13 +48,6 @@ ITEM_PICKER::ITEM_PICKER( BASE_SCREEN* aScreen, EDA_ITEM* aItem, UNDO_REDO aUndo
}
void ITEM_PICKER::SetItem( EDA_ITEM* aItem )
{
m_pickedItem = aItem;
m_pickedItemType = aItem ? aItem->Type() : TYPE_NOT_INIT;
}
PICKED_ITEMS_LIST::PICKED_ITEMS_LIST()
{
}
@ -64,6 +58,29 @@ PICKED_ITEMS_LIST::~PICKED_ITEMS_LIST()
}
void ITEM_PICKER::SetItem( EDA_ITEM* aItem )
{
m_pickedItem = aItem;
m_pickedItemType = aItem ? aItem->Type() : TYPE_NOT_INIT;
if( EDA_GROUP* group = dynamic_cast<EDA_GROUP*>( aItem ) )
m_groupMembers = group->GetGroupMemberIds();
m_groupId = aItem->GetParentGroupId();
}
void ITEM_PICKER::SetLink( EDA_ITEM* aItem )
{
m_link = aItem;
if( EDA_GROUP* group = dynamic_cast<EDA_GROUP*>( aItem ) )
m_groupMembers = group->GetGroupMemberIds();
m_groupId = aItem->GetParentGroupId();
}
void PICKED_ITEMS_LIST::PushItem( const ITEM_PICKER& aItem )
{
m_ItemsList.push_back( aItem );
@ -96,22 +113,6 @@ bool PICKED_ITEMS_LIST::ContainsItem( const EDA_ITEM* aItem ) const
}
bool PICKED_ITEMS_LIST::ContainsItemType( KICAD_T aItemType ) const
{
for( const ITEM_PICKER& picker : m_ItemsList )
{
const EDA_ITEM* item = picker.GetItem();
wxCHECK2( item, continue );
if( item->Type() == aItemType )
return true;
}
return false;
}
int PICKED_ITEMS_LIST::FindItem( const EDA_ITEM* aItem ) const
{
for( size_t i = 0; i < m_ItemsList.size(); i++ )
@ -157,14 +158,15 @@ void PICKED_ITEMS_LIST::ClearListAndDeleteItems( std::function<void(EDA_ITEM*)>
}
ITEM_PICKER PICKED_ITEMS_LIST::GetItemWrapper( unsigned int aIdx ) const
const ITEM_PICKER& PICKED_ITEMS_LIST::GetItemWrapper( unsigned int aIdx ) const
{
ITEM_PICKER picker;
return m_ItemsList.at( aIdx );
}
if( aIdx < m_ItemsList.size() )
picker = m_ItemsList[aIdx];
return picker;
ITEM_PICKER& PICKED_ITEMS_LIST::GetItemWrapper( unsigned int aIdx )
{
return m_ItemsList.at( aIdx );
}
@ -213,27 +215,6 @@ EDA_ITEM_FLAGS PICKED_ITEMS_LIST::GetPickerFlags( unsigned aIdx ) const
}
KIID PICKED_ITEMS_LIST::GetPickedItemGroupId( unsigned aIdx ) const
{
if( aIdx < m_ItemsList.size() )
return m_ItemsList[aIdx].GetGroupId();
return KIID();
}
bool PICKED_ITEMS_LIST::SetPickedItemGroupId( KIID aGroupId, unsigned aIdx )
{
if( aIdx < m_ItemsList.size() )
{
m_ItemsList[aIdx].SetGroupId( aGroupId );
return true;
}
return false;
}
bool PICKED_ITEMS_LIST::SetPickedItem( EDA_ITEM* aItem, unsigned aIdx )
{
if( aIdx < m_ItemsList.size() )
@ -258,19 +239,6 @@ bool PICKED_ITEMS_LIST::SetPickedItemLink( EDA_ITEM* aLink, unsigned aIdx )
}
bool PICKED_ITEMS_LIST::SetPickedItem( EDA_ITEM* aItem, UNDO_REDO aStatus, unsigned aIdx )
{
if( aIdx < m_ItemsList.size() )
{
m_ItemsList[aIdx].SetItem( aItem );
m_ItemsList[aIdx].SetStatus( aStatus );
return true;
}
return false;
}
bool PICKED_ITEMS_LIST::SetPickedItemStatus( UNDO_REDO aStatus, unsigned aIdx )
{
if( aIdx < m_ItemsList.size() )

View File

@ -308,10 +308,8 @@ FOOTPRINT* DISPLAY_FOOTPRINTS_FRAME::GetFootprint( const wxString& aFootprintNam
try
{
const FOOTPRINT* fp = fpTable->GetEnumeratedFootprint( libNickname, fpName );
if( fp )
footprint = static_cast<FOOTPRINT*>( fp->Duplicate() );
if( const FOOTPRINT* fp = fpTable->GetEnumeratedFootprint( libNickname, fpName ) )
footprint = static_cast<FOOTPRINT*>( fp->Duplicate( IGNORE_PARENT_GROUP ) );
}
catch( const IO_ERROR& ioe )
{

View File

@ -302,7 +302,7 @@ void SCH_EDIT_FRAME::BreakSegment( SCH_COMMIT* aCommit, SCH_LINE* aSegment, cons
// Save the copy of aSegment before breaking it
aCommit->Modify( aSegment, aScreen );
SCH_LINE* newSegment = aSegment->BreakAt( aPoint );
SCH_LINE* newSegment = aSegment->BreakAt( aCommit, aPoint );
aSegment->SetFlags( IS_CHANGED | IS_BROKEN );
newSegment->SetFlags( IS_NEW | IS_BROKEN );

View File

@ -964,7 +964,7 @@ void SCH_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
KIID uuid( payload );
SCH_SHEET_PATH path;
if( SCH_ITEM* item = m_schematic->GetItem( uuid, &path ) )
if( SCH_ITEM* item = m_schematic->ResolveItem( uuid, &path, true ) )
{
if( item->Type() == SCH_SHEET_T )
payload = static_cast<SCH_SHEET*>( item )->GetShownName( false );

View File

@ -530,7 +530,7 @@ void DIALOG_ERC::OnERCItemSelected( wxDataViewEvent& aEvent )
{
const KIID& itemID = RC_TREE_MODEL::ToUUID( aEvent.GetItem() );
SCH_SHEET_PATH sheet;
SCH_ITEM* item = m_parent->Schematic().GetItem( itemID, &sheet );
SCH_ITEM* item = m_parent->Schematic().ResolveItem( itemID, &sheet, true );
if( m_centerMarkerOnIdle )
{
@ -544,8 +544,8 @@ void DIALOG_ERC::OnERCItemSelected( wxDataViewEvent& aEvent )
if( node )
{
// Determine the owning sheet for sheet-specific items
std::shared_ptr<ERC_ITEM> ercItem =
std::static_pointer_cast<ERC_ITEM>( node->m_RcItem );
std::shared_ptr<ERC_ITEM> ercItem = std::static_pointer_cast<ERC_ITEM>( node->m_RcItem );
switch( node->m_Type )
{
case RC_TREE_NODE::MARKER:
@ -569,8 +569,7 @@ void DIALOG_ERC::OnERCItemSelected( wxDataViewEvent& aEvent )
if( !sheet.empty() && sheet != m_parent->GetCurrentSheet() )
{
m_parent->GetToolManager()->RunAction<SCH_SHEET_PATH*>( SCH_ACTIONS::changeSheet,
&sheet );
m_parent->GetToolManager()->RunAction<SCH_SHEET_PATH*>( SCH_ACTIONS::changeSheet, &sheet );
m_parent->RedrawScreen( m_parent->GetScreen()->m_ScrollCenter, false );
}

View File

@ -425,7 +425,7 @@ void ERC_TREE_MODEL::GetValue( wxVariant& aVariant, wxDataViewItem const& aItem,
}
else
{
msg = getItemDesc( schEditFrame->GetItem( ercItem->GetMainItemID() ),
msg = getItemDesc( schEditFrame->ResolveItem( ercItem->GetMainItemID() ),
ercItem->MainItemHasSheetPath() ? ercItem->GetMainItemSheetPath()
: schEditFrame->GetCurrentSheet() );
}
@ -433,18 +433,18 @@ void ERC_TREE_MODEL::GetValue( wxVariant& aVariant, wxDataViewItem const& aItem,
break;
case RC_TREE_NODE::AUX_ITEM:
msg = getItemDesc( schEditFrame->GetItem( ercItem->GetAuxItemID() ),
msg = getItemDesc( schEditFrame->ResolveItem( ercItem->GetAuxItemID() ),
ercItem->AuxItemHasSheetPath() ? ercItem->GetAuxItemSheetPath()
: schEditFrame->GetCurrentSheet() );
break;
case RC_TREE_NODE::AUX_ITEM2:
msg = getItemDesc( schEditFrame->GetItem( ercItem->GetAuxItem2ID() ),
msg = getItemDesc( schEditFrame->ResolveItem( ercItem->GetAuxItem2ID() ),
schEditFrame->GetCurrentSheet() );
break;
case RC_TREE_NODE::AUX_ITEM3:
msg = getItemDesc( schEditFrame->GetItem( ercItem->GetAuxItem3ID() ),
msg = getItemDesc( schEditFrame->ResolveItem( ercItem->GetAuxItem3ID() ),
schEditFrame->GetCurrentSheet() );
break;

View File

@ -1357,7 +1357,7 @@ void LIB_SYMBOL::SetUnitCount( int aCount, bool aDuplicateDrawItems )
for( int j = prevCount + 1; j <= aCount; j++ )
{
SCH_ITEM* newItem = item.Duplicate();
SCH_ITEM* newItem = item.Duplicate( IGNORE_PARENT_GROUP );
newItem->m_unit = j;
tmp.push_back( newItem );
}
@ -1397,7 +1397,7 @@ void LIB_SYMBOL::SetHasAlternateBodyStyle( bool aHasAlternate, bool aDuplicatePi
{
if( item.m_bodyStyle == 1 )
{
SCH_ITEM* newItem = item.Duplicate();
SCH_ITEM* newItem = item.Duplicate( IGNORE_PARENT_GROUP );
newItem->m_bodyStyle = 2;
tmp.push_back( newItem );
}

View File

@ -85,8 +85,6 @@ EDA_ITEM* SCH_BITMAP::Clone() const
void SCH_BITMAP::swapData( SCH_ITEM* aItem )
{
SCH_ITEM::SwapFlags( aItem );
wxCHECK_RET( aItem->Type() == SCH_BITMAP_T,
wxString::Format( wxT( "SCH_BITMAP object cannot swap data with %s object." ),
aItem->GetClass() ) );

View File

@ -136,8 +136,6 @@ VECTOR2I SCH_BUS_ENTRY_BASE::GetEnd() const
void SCH_BUS_ENTRY_BASE::swapData( SCH_ITEM* aItem )
{
SCH_ITEM::SwapFlags( aItem );
SCH_BUS_ENTRY_BASE* item = dynamic_cast<SCH_BUS_ENTRY_BASE*>( aItem );
wxCHECK_RET( item, wxT( "Cannot swap bus entry data with invalid item." ) );

View File

@ -67,7 +67,8 @@ SCH_COMMIT::~SCH_COMMIT()
}
COMMIT& SCH_COMMIT::Stage( EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen )
COMMIT& SCH_COMMIT::Stage( EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen,
RECURSE_MODE aRecurse )
{
wxCHECK( aItem, *this );
@ -82,18 +83,12 @@ COMMIT& SCH_COMMIT::Stage( EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN
aChangeType = CHT_MODIFY;
}
// Many operations (move, rotate, etc.) are applied directly to a group's children, so they
// must be staged as well.
if( aChangeType == CHT_MODIFY )
if( aRecurse == RECURSE_MODE::RECURSE )
{
if( SCH_GROUP* group = dynamic_cast<SCH_GROUP*>( aItem ) )
{
group->RunOnChildren(
[&]( EDA_ITEM* child )
{
Stage( child, aChangeType, aScreen );
},
RECURSE_MODE::NO_RECURSE );
for( EDA_ITEM* member : group->GetItems() )
Stage( member, aChangeType, aScreen, aRecurse );
}
}
@ -198,12 +193,12 @@ void SCH_COMMIT::pushSchEdit( const wxString& aMessage, int aCommitFlags )
SCH_EDIT_FRAME* frame = static_cast<SCH_EDIT_FRAME*>( m_toolMgr->GetToolHolder() );
SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
SCH_GROUP* enteredGroup = selTool ? selTool->GetEnteredGroup() : nullptr;
bool itemsDeselected = false;
bool selectedModified = false;
bool dirtyConnectivity = false;
bool refreshHierarchy = false;
SCH_CLEANUP_FLAGS connectivityCleanUp = NO_CLEANUP;
SCH_GROUP* addedGroup = nullptr;
if( Empty() )
return;
@ -215,6 +210,37 @@ void SCH_COMMIT::pushSchEdit( const wxString& aMessage, int aCommitFlags )
std::vector<SCH_ITEM*> bulkRemovedItems;
std::vector<SCH_ITEM*> itemsChanged;
auto updateConnectivityFlag =
[&]( SCH_ITEM* schItem )
{
if( schItem->IsConnectable() || ( schItem->Type() == SCH_RULE_AREA_T ) )
{
dirtyConnectivity = true;
// Do a local clean up if there are any connectable objects in the commit.
if( connectivityCleanUp == NO_CLEANUP )
connectivityCleanUp = LOCAL_CLEANUP;
// Do a full rebuild of the connectivity if there is a sheet in the commit.
if( schItem->Type() == SCH_SHEET_T )
connectivityCleanUp = GLOBAL_CLEANUP;
}
};
// We don't know that anything will be added to the entered group, but it does no harm to
// add it to the commit anyway.
if( enteredGroup )
Modify( enteredGroup );
for( COMMIT_LINE& ent : m_changes )
{
SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( ent.m_item );
int changeType = ent.m_type & CHT_TYPE;
if( changeType == CHT_REMOVE && schItem->GetParentGroup() )
Modify( schItem->GetParentGroup()->AsEdaItem() );
}
for( COMMIT_LINE& ent : m_changes )
{
int changeType = ent.m_type & CHT_TYPE;
@ -242,33 +268,14 @@ void SCH_COMMIT::pushSchEdit( const wxString& aMessage, int aCommitFlags )
RECURSE_MODE::NO_RECURSE );
}
auto updateConnectivityFlag = [&]()
{
if( schItem->IsConnectable() || ( schItem->Type() == SCH_RULE_AREA_T ) )
{
dirtyConnectivity = true;
// Do a local clean up if there are any connectable objects in the commit.
if( connectivityCleanUp == NO_CLEANUP )
connectivityCleanUp = LOCAL_CLEANUP;
// Do a full rebuild of the connectivity if there is a sheet in the commit.
if( schItem->Type() == SCH_SHEET_T )
connectivityCleanUp = GLOBAL_CLEANUP;
}
};
switch( changeType )
{
case CHT_ADD:
{
if( selTool && selTool->GetEnteredGroup() && !schItem->GetParentGroup()
&& SCH_GROUP::IsGroupableType( schItem->Type() ) )
{
if( enteredGroup && schItem->IsGroupableType() && !schItem->GetParentGroup() )
selTool->GetEnteredGroup()->AddItem( schItem );
}
updateConnectivityFlag();
updateConnectivityFlag( schItem );
if( !( aCommitFlags & SKIP_UNDO ) )
undoList.PushItem( ITEM_PICKER( screen, schItem, UNDO_REDO::NEWITEM ) );
@ -282,12 +289,6 @@ void SCH_COMMIT::pushSchEdit( const wxString& aMessage, int aCommitFlags )
view->Add( schItem );
}
if( schItem->Type() == SCH_GROUP_T )
{
wxASSERT_MSG( !addedGroup, "Multiple groups in a single commit. This is not supported." );
addedGroup = static_cast<SCH_GROUP*>( schItem );
}
if( frame )
frame->UpdateItem( schItem, true, true );
@ -301,10 +302,15 @@ void SCH_COMMIT::pushSchEdit( const wxString& aMessage, int aCommitFlags )
case CHT_REMOVE:
{
updateConnectivityFlag();
updateConnectivityFlag( schItem );
if( !( aCommitFlags & SKIP_UNDO ) )
undoList.PushItem( ITEM_PICKER( screen, schItem, UNDO_REDO::DELETED ) );
{
ITEM_PICKER itemWrapper( screen, schItem, UNDO_REDO::DELETED );
itemWrapper.SetLink( ent.m_copy );
ent.m_copy = nullptr; // We've transferred ownership to the undo list
undoList.PushItem( itemWrapper );
}
if( schItem->IsSelected() )
{
@ -320,6 +326,9 @@ void SCH_COMMIT::pushSchEdit( const wxString& aMessage, int aCommitFlags )
break;
}
if( EDA_GROUP* group = schItem->GetParentGroup() )
group->RemoveItem( schItem );
if( !( changeFlags & CHT_DONE ) )
{
screen->Remove( schItem );
@ -331,104 +340,61 @@ void SCH_COMMIT::pushSchEdit( const wxString& aMessage, int aCommitFlags )
if( frame )
frame->UpdateItem( schItem, true, true );
bulkRemovedItems.push_back( schItem );
if( schItem->Type() == SCH_SHEET_T )
refreshHierarchy = true;
bulkRemovedItems.push_back( schItem );
break;
}
case CHT_MODIFY:
{
const SCH_ITEM* itemCopy = static_cast<const SCH_ITEM*>( ent.m_copy );
SCH_SHEET_PATH currentSheet;
if( frame )
currentSheet = frame->GetCurrentSheet();
if( itemCopy->HasConnectivityChanges( schItem, &currentSheet )
|| ( itemCopy->Type() == SCH_RULE_AREA_T ) )
{
updateConnectivityFlag( schItem );
}
if( !( aCommitFlags & SKIP_UNDO ) )
{
ITEM_PICKER itemWrapper( screen, schItem, UNDO_REDO::CHANGED );
wxASSERT( ent.m_copy );
itemWrapper.SetLink( ent.m_copy );
const SCH_ITEM* itemCopy = static_cast<const SCH_ITEM*>( ent.m_copy );
wxCHECK2( itemCopy, continue );
SCH_SHEET_PATH currentSheet;
if( frame )
currentSheet = frame->GetCurrentSheet();
if( itemCopy->HasConnectivityChanges( schItem, &currentSheet )
|| ( itemCopy->Type() == SCH_RULE_AREA_T ) )
{
updateConnectivityFlag();
}
if( schItem->Type() == SCH_SHEET_T )
{
const SCH_SHEET* modifiedSheet = static_cast<const SCH_SHEET*>( schItem );
const SCH_SHEET* originalSheet = static_cast<const SCH_SHEET*>( itemCopy );
wxCHECK2( modifiedSheet && originalSheet, continue );
if( originalSheet->HasPageNumberChanges( *modifiedSheet ) )
refreshHierarchy = true;
}
undoList.PushItem( itemWrapper );
ent.m_copy = nullptr; // We've transferred ownership to the undo list
undoList.PushItem( itemWrapper );
}
if( schItem->Type() == SCH_GROUP_T )
if( schItem->Type() == SCH_SHEET_T )
{
wxASSERT_MSG( !addedGroup, "Multiple groups in a single commit. This is not supported." );
addedGroup = static_cast<SCH_GROUP*>( schItem );
const SCH_SHEET* modifiedSheet = static_cast<const SCH_SHEET*>( schItem );
const SCH_SHEET* originalSheet = static_cast<const SCH_SHEET*>( itemCopy );
wxCHECK2( modifiedSheet && originalSheet, continue );
if( originalSheet->HasPageNumberChanges( *modifiedSheet ) )
refreshHierarchy = true;
}
if( frame )
frame->UpdateItem( schItem, false, true );
itemsChanged.push_back( schItem );
if( ent.m_copy )
{
// if no undo entry is needed, the copy would create a memory leak
delete ent.m_copy;
ent.m_copy = nullptr;
}
break;
}
case CHT_UNGROUP:
if( EDA_GROUP* group = schItem->GetParentGroup() )
{
if( !( aCommitFlags & SKIP_UNDO ) )
{
ITEM_PICKER itemWrapper( nullptr, schItem, UNDO_REDO::UNGROUP );
itemWrapper.SetGroupId( group->AsEdaItem()->m_Uuid );
undoList.PushItem( itemWrapper );
}
group->RemoveItem( schItem );
}
break;
case CHT_GROUP:
if( addedGroup )
{
addedGroup->AddItem( schItem );
if( !( aCommitFlags & SKIP_UNDO ) )
undoList.PushItem( ITEM_PICKER( nullptr, schItem, UNDO_REDO::REGROUP ) );
}
break;
default:
wxASSERT( false );
break;
}
// Delete any copies we still have ownership of
delete ent.m_copy;
ent.m_copy = nullptr;
// Clear all flags but SELECTED and others used to move and rotate commands,
// after edition (selected items must keep their selection flag).
const int selected_mask = ( SELECTED | STARTPOINT | ENDPOINT );

View File

@ -55,8 +55,8 @@ public:
int aCommitFlags = 0 ) override;
virtual void Revert() override;
COMMIT& Stage( EDA_ITEM *aItem, CHANGE_TYPE aChangeType,
BASE_SCREEN *aScreen = nullptr ) override;
COMMIT& Stage( EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen = nullptr,
RECURSE_MODE aRecurse = RECURSE_MODE::NO_RECURSE ) override;
COMMIT& Stage( std::vector<EDA_ITEM*> &container, CHANGE_TYPE aChangeType,
BASE_SCREEN *aScreen = nullptr ) override;
COMMIT& Stage( const PICKED_ITEMS_LIST &aItems, UNDO_REDO aModFlag = UNDO_REDO::UNSPECIFIED,

View File

@ -392,10 +392,14 @@ bool SCH_EDIT_FRAME::SaveSelectionToDesignBlock( const LIB_ID& aLibId )
{
group = static_cast<SCH_GROUP*>( item );
selection.Remove( item );
selection.Remove( group );
// Don't recurse; if we have a group of groups the user probably intends the inner groups to be saved
group->RunOnChildren( [&]( EDA_ITEM* aItem ) { selection.Add( aItem ); }, RECURSE_MODE::NO_RECURSE );
group->RunOnChildren( [&]( EDA_ITEM* aItem )
{
selection.Add( aItem );
},
RECURSE_MODE::NO_RECURSE );
}
}
@ -420,15 +424,15 @@ bool SCH_EDIT_FRAME::SaveSelectionToDesignBlock( const LIB_ID& aLibId )
SCH_SCREEN* tempScreen = new SCH_SCREEN( m_schematic );
auto cloneAndAdd =
[&] ( EDA_ITEM* aItem )
{
if( !aItem->IsSCH_ITEM() )
return static_cast<SCH_ITEM*>( nullptr );
[&] ( EDA_ITEM* aItem ) -> SCH_ITEM*
{
if( !aItem->IsSCH_ITEM() )
return nullptr;
SCH_ITEM* copy = static_cast<SCH_ITEM*>( aItem->Clone() );
tempScreen->Append( static_cast<SCH_ITEM*>( copy ) );
return copy;
};
SCH_ITEM* copy = static_cast<SCH_ITEM*>( aItem->Clone() );
tempScreen->Append( static_cast<SCH_ITEM*>( copy ) );
return copy;
};
// Copy the selected items to the temporary board
for( EDA_ITEM* item : selection )

View File

@ -856,7 +856,7 @@ void SCH_EDIT_FRAME::AddCopyForRepeatItem( const SCH_ITEM* aItem )
if( aItem )
{
std::unique_ptr<SCH_ITEM> repeatItem( static_cast<SCH_ITEM*>( aItem->Duplicate() ) );
std::unique_ptr<SCH_ITEM> repeatItem( static_cast<SCH_ITEM*>( aItem->Duplicate( IGNORE_PARENT_GROUP ) ) );
// Clone() preserves the flags & parent, we want 'em cleared.
repeatItem->ClearFlags();
@ -867,9 +867,9 @@ void SCH_EDIT_FRAME::AddCopyForRepeatItem( const SCH_ITEM* aItem )
}
EDA_ITEM* SCH_EDIT_FRAME::GetItem( const KIID& aId ) const
EDA_ITEM* SCH_EDIT_FRAME::ResolveItem( const KIID& aId, bool aAllowNullptrReturn ) const
{
return Schematic().GetItem( aId );
return Schematic().ResolveItem( aId, nullptr, aAllowNullptrReturn );
}
@ -2319,8 +2319,7 @@ void SCH_EDIT_FRAME::FocusOnItem( EDA_ITEM* aItem )
static KIID lastBrightenedItemID( niluuid );
SCH_SHEET_PATH dummy;
SCH_ITEM* lastItem = Schematic().GetItem( lastBrightenedItemID, &dummy );
SCH_ITEM* lastItem = Schematic().ResolveItem( lastBrightenedItemID, nullptr, true );
if( lastItem && lastItem != aItem )
{
@ -2377,7 +2376,7 @@ void SCH_EDIT_FRAME::SaveSymbolToSchematic( const LIB_SYMBOL& aSymbol,
{
SCH_SHEET_PATH principalPath;
SCH_SHEET_LIST sheets = Schematic().Hierarchy();
SCH_ITEM* item = sheets.GetItem( aSchematicSymbolUUID, &principalPath );
SCH_ITEM* item = sheets.ResolveItem( aSchematicSymbolUUID, &principalPath, true );
SCH_SYMBOL* principalSymbol = dynamic_cast<SCH_SYMBOL*>( item );
SCH_COMMIT commit( m_toolManager );

View File

@ -743,7 +743,7 @@ public:
m_items_to_repeat.clear();
}
EDA_ITEM* GetItem( const KIID& aId ) const override;
EDA_ITEM* ResolveItem( const KIID& aId, bool aAllowNullptrReturn = false ) const override;
/**
* Perform an undo of the last edit **without** logging a corresponding redo.

View File

@ -407,8 +407,6 @@ void SCH_FIELD::swapData( SCH_ITEM* aItem )
{
wxCHECK_RET( aItem && aItem->Type() == SCH_FIELD_T, wxT( "Cannot swap with invalid item." ) );
SCH_ITEM::SwapFlags( aItem );
SCH_FIELD* item = static_cast<SCH_FIELD*>( aItem );
std::swap( m_layer, item->m_layer );

View File

@ -47,91 +47,14 @@ SCH_GROUP::SCH_GROUP( SCH_SCREEN* aParent ) : SCH_ITEM( aParent, SCH_GROUP_T )
{
}
bool SCH_GROUP::IsGroupableType( KICAD_T aType )
{
switch( aType )
{
case SCH_SYMBOL_T:
case SCH_PIN_T:
case SCH_SHAPE_T:
case SCH_BITMAP_T:
case SCH_FIELD_T:
case SCH_TEXT_T:
case SCH_TEXTBOX_T:
case SCH_TABLE_T:
case SCH_GROUP_T:
case SCH_LINE_T:
case SCH_JUNCTION_T:
case SCH_NO_CONNECT_T:
case SCH_BUS_WIRE_ENTRY_T:
case SCH_BUS_BUS_ENTRY_T:
case SCH_LABEL_T:
case SCH_GLOBAL_LABEL_T:
case SCH_HIER_LABEL_T:
case SCH_RULE_AREA_T:
case SCH_DIRECTIVE_LABEL_T:
case SCH_SHEET_PIN_T:
case SCH_SHEET_T:
return true;
default:
return false;
}
}
bool SCH_GROUP::AddItem( EDA_ITEM* aItem )
{
wxCHECK_MSG( aItem, false, wxT( "Nullptr added to group." ) );
wxCHECK_MSG( IsGroupableType( aItem->Type() ), false,
wxT( "Invalid item type added to group: " ) + aItem->GetTypeDesc() );
// Items can only be in one group at a time
if( aItem->GetParentGroup() )
aItem->GetParentGroup()->RemoveItem( aItem );
m_items.insert( aItem );
aItem->SetParentGroup( this );
return true;
}
bool SCH_GROUP::RemoveItem( EDA_ITEM* aItem )
{
wxCHECK_MSG( aItem, false, wxT( "Nullptr removed from group." ) );
// Only clear the item's group field if it was inside this group
if( m_items.erase( aItem ) == 1 )
{
aItem->SetParentGroup( nullptr );
return true;
}
return false;
}
void SCH_GROUP::RemoveAll()
{
for( EDA_ITEM* item : m_items )
item->SetParentGroup( nullptr );
m_items.clear();
}
std::unordered_set<SCH_ITEM*> SCH_GROUP::GetSchItems() const
{
std::unordered_set<SCH_ITEM*> items;
for( EDA_ITEM* item : m_items )
{
SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( item );
if( sch_item )
{
items.insert( sch_item );
}
if( item->IsSCH_ITEM() )
items.insert( static_cast<SCH_ITEM*>( item ) );
}
return items;
@ -230,17 +153,17 @@ SCH_GROUP* SCH_GROUP::DeepClone() const
}
SCH_GROUP* SCH_GROUP::DeepDuplicate() const
SCH_GROUP* SCH_GROUP::DeepDuplicate( bool addToParentGroup, SCH_COMMIT* aCommit ) const
{
SCH_GROUP* newGroup = static_cast<SCH_GROUP*>( Duplicate() );
SCH_GROUP* newGroup = static_cast<SCH_GROUP*>( Duplicate( addToParentGroup, aCommit ) );
newGroup->m_items.clear();
for( EDA_ITEM* member : m_items )
{
if( member->Type() == SCH_GROUP_T )
newGroup->AddItem( static_cast<SCH_GROUP*>( member )->DeepDuplicate() );
newGroup->AddItem( static_cast<SCH_GROUP*>( member )->DeepDuplicate( IGNORE_PARENT_GROUP ) );
else
newGroup->AddItem( static_cast<SCH_ITEM*>( member )->Duplicate() );
newGroup->AddItem( static_cast<SCH_ITEM*>( member )->Duplicate( IGNORE_PARENT_GROUP ) );
}
return newGroup;
@ -252,26 +175,9 @@ void SCH_GROUP::swapData( SCH_ITEM* aImage )
assert( aImage->Type() == SCH_GROUP_T );
SCH_GROUP* image = static_cast<SCH_GROUP*>( aImage );
std::swap( *this, *image );
// A group doesn't own its children (they're owned by the schematic), so undo doesn't do a
// deep clone when making an image. However, it's still safest to update the parentGroup
// pointers of the group's children -- we just have to be careful to do it in the right
// order in case any of the children are shared (ie: image first, "this" second so that
// any shared children end up with "this").
image->RunOnChildren(
[&]( SCH_ITEM* child )
{
child->SetParentGroup( image );
},
RECURSE_MODE::NO_RECURSE );
RunOnChildren(
[&]( SCH_ITEM* child )
{
child->SetParentGroup( this );
},
RECURSE_MODE::NO_RECURSE );
std::swap( m_items, image->m_items );
std::swap( m_name, image->m_name );
std::swap( m_designBlockLibId, image->m_designBlockLibId );
}

View File

@ -63,22 +63,6 @@ public:
wxString GetClass() const override { return wxT( "SCH_GROUP" ); }
/**
* Add item to group. Does not take ownership of item.
*
* @return true if item was added (false if item belongs to a different group).
*/
bool AddItem( EDA_ITEM* aItem ) override;
/**
* Remove item from group.
*
* @return true if item was removed (false if item was not in the group).
*/
bool RemoveItem( EDA_ITEM* aItem ) override;
void RemoveAll() override;
std::unordered_set<SCH_ITEM*> GetSchItems() const;
/*
@ -111,14 +95,17 @@ public:
EDA_ITEM* Clone() const override;
/*
* Clone() this and all descendants
* Clone this and all descendants
*/
SCH_GROUP* DeepClone() const override;
SCH_GROUP* DeepClone() const;
/*
* Duplicate() this and all descendants
* Duplicate this and all descendants
*
* @param addToParentGroup if the original is part of a group then the new member will also
* be added to said group
*/
SCH_GROUP* DeepDuplicate() const override;
SCH_GROUP* DeepDuplicate( bool addToParentGroup, SCH_COMMIT* aCommit = nullptr ) const;
/// @copydoc EDA_ITEM::HitTest
bool HitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const override;
@ -166,13 +153,6 @@ public:
///< @copydoc SCH_ITEM::RunOnChildren
void RunOnChildren( const std::function<void( SCH_ITEM* )>& aFunction, RECURSE_MODE aMode ) override;
/**
* Check if the proposed type can be added to a group
* @param aType KICAD_T type to check
* @return true if the type can belong to a group, false otherwise
*/
static bool IsGroupableType( KICAD_T aType );
/// @copydoc SCH_ITEM::swapData
void swapData( SCH_ITEM* aImage ) override;
};

View File

@ -1728,7 +1728,7 @@ const LIB_SYMBOL* CADSTAR_SCH_ARCHIVE_LOADER::loadSymdef( const SYMDEF_ID& aSymd
RotatePoint( linePos, libtext->GetTextPos(), -libtext->GetTextAngle() );
SCH_TEXT* textLine = static_cast<SCH_TEXT*>( libtext->Duplicate() );
SCH_TEXT* textLine = static_cast<SCH_TEXT*>( libtext->Duplicate( IGNORE_PARENT_GROUP ) );
textLine->SetText( strings[ii] );
textLine->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
textLine->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
@ -2655,8 +2655,8 @@ void CADSTAR_SCH_ARCHIVE_LOADER::loadItemOntoKiCadSheet( const LAYER_ID& aCadsta
for( std::pair<LAYER_ID, SHEET_NAME> sheetPair : Sheets.SheetNames )
{
LAYER_ID sheetID = sheetPair.first;
duplicateItem = aItem->Duplicate();
m_sheetMap.at( sheetID )->GetScreen()->Append( aItem->Duplicate() );
duplicateItem = aItem->Duplicate( IGNORE_PARENT_GROUP );
m_sheetMap.at( sheetID )->GetScreen()->Append( aItem->Duplicate( IGNORE_PARENT_GROUP ) );
}
//Get rid of the extra copy:

View File

@ -785,7 +785,7 @@ void SCH_IO_EAGLE::loadSchematic( const ESCHEMATIC& aSchematic )
// Instantiate the missing symbol unit
int unit = unitEntry.first;
const wxString reference = origSymbol->GetField( FIELD_T::REFERENCE )->GetText();
std::unique_ptr<SCH_SYMBOL> symbol( (SCH_SYMBOL*) origSymbol->Duplicate() );
std::unique_ptr<SCH_SYMBOL> symbol( (SCH_SYMBOL*) origSymbol->Duplicate( IGNORE_PARENT_GROUP ) );
symbol->SetUnitSelection( &sheetpath, unit );
symbol->SetUnit( unit );

View File

@ -397,16 +397,14 @@ LIB_SYMBOL* SCH_IO_HTTP_LIB::loadSymbolFromPart( const wxString& aSymbo
}
else if( !symbolId.IsValid() )
{
wxLogTrace( traceHTTPLib,
wxT( "loadSymbolFromPart: source symbol id '%s' is invalid, "
"will create empty symbol" ),
wxLogTrace( traceHTTPLib, wxT( "loadSymbolFromPart: source symbol id '%s' is invalid, "
"will create empty symbol" ),
symbolIdStr );
}
else
{
wxLogTrace( traceHTTPLib,
wxT( "loadSymbolFromPart: source symbol '%s' not found, "
"will create empty symbol" ),
wxLogTrace( traceHTTPLib, wxT( "loadSymbolFromPart: source symbol '%s' not found, "
"will create empty symbol" ),
symbolIdStr );
}
}

View File

@ -3088,9 +3088,9 @@ SCH_SYMBOL* SCH_IO_KICAD_SEXPR_PARSER::parseSchematicSymbol()
{
if( static_cast<int>( name.size() ) > bad_pos )
{
wxString msg = wxString::Format(
_( "Symbol %s contains invalid character '%c'" ), name,
name[bad_pos] );
wxString msg = wxString::Format( _( "Symbol %s contains invalid character '%c'" ),
name,
name[bad_pos] );
THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
}
@ -4865,6 +4865,7 @@ void SCH_IO_KICAD_SEXPR_PARSER::parseGroup()
if( !IsSymbol( token ) && token != T_NUMBER )
Expecting( "symbol|number" );
LIB_ID libId;
wxString name = FromUTF8();
// Some symbol LIB_IDs have the '/' character escaped which can break
// symbol links. The '/' character is no longer an illegal LIB_ID character so
@ -4952,15 +4953,8 @@ void SCH_IO_KICAD_SEXPR_PARSER::resolveGroups( SCH_SCREEN* aParent )
{
for( const KIID& aUuid : groupInfo.memberUuids )
{
SCH_ITEM* gItem = getItem( aUuid );
if( !gItem || gItem->Type() == NOT_USED )
{
// This is the deleted item singleton, which means we didn't find the uuid.
continue;
}
group->AddItem( gItem );
if( SCH_ITEM* gItem = getItem( aUuid ) )
group->AddItem( gItem );
}
}
}

View File

@ -129,16 +129,45 @@ SCH_ITEM::~SCH_ITEM()
}
SCH_ITEM* SCH_ITEM::Duplicate( bool doClone ) const
bool SCH_ITEM::IsGroupableType() const
{
switch( Type() )
{
case SCH_SYMBOL_T:
case SCH_PIN_T:
case SCH_SHAPE_T:
case SCH_BITMAP_T:
case SCH_FIELD_T:
case SCH_TEXT_T:
case SCH_TEXTBOX_T:
case SCH_TABLE_T:
case SCH_GROUP_T:
case SCH_LINE_T:
case SCH_JUNCTION_T:
case SCH_NO_CONNECT_T:
case SCH_BUS_WIRE_ENTRY_T:
case SCH_BUS_BUS_ENTRY_T:
case SCH_LABEL_T:
case SCH_GLOBAL_LABEL_T:
case SCH_HIER_LABEL_T:
case SCH_RULE_AREA_T:
case SCH_DIRECTIVE_LABEL_T:
case SCH_SHEET_PIN_T:
case SCH_SHEET_T:
return true;
default:
return false;
}
}
SCH_ITEM* SCH_ITEM::Duplicate( bool addToParentGroup, SCH_COMMIT* aCommit, bool doClone ) const
{
SCH_ITEM* newItem = (SCH_ITEM*) Clone();
if( !doClone )
const_cast<KIID&>( newItem->m_Uuid ) = KIID();
if( newItem->GetParentGroup() )
newItem->GetParentGroup()->AddItem( newItem );
newItem->ClearFlags( SELECTED | BRIGHTENED );
newItem->RunOnChildren(
@ -148,6 +177,17 @@ SCH_ITEM* SCH_ITEM::Duplicate( bool doClone ) const
},
RECURSE_MODE::NO_RECURSE );
if( addToParentGroup )
{
wxCHECK_MSG( aCommit, newItem, "Must supply a commit to update parent group" );
if( newItem->GetParentGroup() )
{
aCommit->Modify( newItem->GetParentGroup()->AsEdaItem() );
newItem->GetParentGroup()->AddItem( newItem );
}
}
return newItem;
}
@ -176,33 +216,17 @@ void SCH_ITEM::SetBodyStyleProp( int aBodyStyle )
SCHEMATIC* SCH_ITEM::Schematic() const
{
EDA_ITEM* parent = GetParent();
while( parent )
{
if( parent->Type() == SCHEMATIC_T )
return static_cast<SCHEMATIC*>( parent );
else
parent = parent->GetParent();
}
return nullptr;
return static_cast<SCHEMATIC*>( findParent( SCHEMATIC_T ) );
}
const SYMBOL* SCH_ITEM::GetParentSymbol() const
{
const EDA_ITEM* parent = GetParent();
if( SYMBOL* sch_symbol = static_cast<SCH_SYMBOL*>( findParent( SCH_SYMBOL_T ) ) )
return sch_symbol;
while( parent )
{
if( parent->Type() == SCH_SYMBOL_T )
return static_cast<const SCH_SYMBOL*>( parent );
else if( parent->Type() == LIB_SYMBOL_T )
return static_cast<const LIB_SYMBOL*>( parent );
else
parent = parent->GetParent();
}
if( SYMBOL* lib_symbol = static_cast<LIB_SYMBOL*>( findParent( LIB_SYMBOL_T ) ) )
return lib_symbol;
return nullptr;
}
@ -210,17 +234,11 @@ const SYMBOL* SCH_ITEM::GetParentSymbol() const
SYMBOL* SCH_ITEM::GetParentSymbol()
{
EDA_ITEM* parent = GetParent();
if( SYMBOL* sch_symbol = static_cast<SCH_SYMBOL*>( findParent( SCH_SYMBOL_T ) ) )
return sch_symbol;
while( parent )
{
if( parent->Type() == SCH_SYMBOL_T )
return static_cast<SCH_SYMBOL*>( parent );
else if( parent->Type() == LIB_SYMBOL_T )
return static_cast<LIB_SYMBOL*>( parent );
else
parent = parent->GetParent();
}
if( SYMBOL* lib_symbol = static_cast<LIB_SYMBOL*>( findParent( LIB_SYMBOL_T ) ) )
return lib_symbol;
return nullptr;
}
@ -381,16 +399,13 @@ void SCH_ITEM::SwapItemData( SCH_ITEM* aImage )
if( aImage == nullptr )
return;
EDA_ITEM* parent = GetParent();
EDA_GROUP* group = GetParentGroup();
EDA_ITEM* parent = GetParent();
SetParentGroup( nullptr );
aImage->SetParentGroup( nullptr );
SwapFlags( aImage );
std::swap( m_group, aImage->m_group );
swapData( aImage );
// Restore pointers to be sure they are not broken
SetParent( parent );
SetParentGroup( group );
}

View File

@ -42,6 +42,7 @@ class CONNECTION_GRAPH;
class SCH_CONNECTION;
class SCH_SHEET_PATH;
class SCHEMATIC;
class SCH_COMMIT;
class SYMBOL;
class LINE_READER;
class SCH_EDIT_FRAME;
@ -202,6 +203,8 @@ public:
return false;
}
bool IsGroupableType() const;
/**
* Swap data between \a aItem and \a aImage.
*
@ -224,10 +227,13 @@ public:
*
* The new object is not put in draw list (not linked).
*
* @param doClone (default = false) indicates unique values (such as timestamp and
* sheet name) should be duplicated. Use only for undo/redo operations.
* @param addToParentGroup Indicates whether or not the new item is added to the group
* (if any) containing the old item. If true, aCommit must be
* provided.
* @param aDoClone (default = false) indicates unique values (such as timestamp and
* sheet name) should be duplicated. Use only for undo/redo operations.
*/
SCH_ITEM* Duplicate( bool doClone = false ) const;
SCH_ITEM* Duplicate( bool addToParentGroup, SCH_COMMIT* aCommit = nullptr, bool doClone = false ) const;
static wxString GetUnitDescription( int aUnit );
static wxString GetBodyStyleDescription( int aBodyStyle );

View File

@ -60,8 +60,6 @@ EDA_ITEM* SCH_JUNCTION::Clone() const
void SCH_JUNCTION::swapData( SCH_ITEM* aItem )
{
SCH_ITEM::SwapFlags( aItem );
wxCHECK_RET( ( aItem != nullptr ) && ( aItem->Type() == SCH_JUNCTION_T ),
wxT( "Cannot swap junction data with invalid item." ) );

View File

@ -543,9 +543,9 @@ SCH_LINE* SCH_LINE::MergeOverlap( SCH_SCREEN* aScreen, SCH_LINE* aLine, bool aCh
}
SCH_LINE* SCH_LINE::BreakAt( const VECTOR2I& aPoint )
SCH_LINE* SCH_LINE::BreakAt( SCH_COMMIT* aCommit, const VECTOR2I& aPoint )
{
SCH_LINE* newSegment = static_cast<SCH_LINE*>( Duplicate() );
SCH_LINE* newSegment = static_cast<SCH_LINE*>( Duplicate( aCommit ) );
newSegment->SetStartPoint( aPoint );
newSegment->SetConnectivityDirty( true );
@ -804,8 +804,6 @@ bool SCH_LINE::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) con
void SCH_LINE::swapData( SCH_ITEM* aItem )
{
SCH_ITEM::SwapFlags( aItem );
SCH_LINE* item = (SCH_LINE*) aItem;
std::swap( m_layer, item->m_layer );

View File

@ -256,7 +256,7 @@ public:
* @param aPoint Point at which to break the segment
* @return The newly created segment.
*/
SCH_LINE* BreakAt( const VECTOR2I& aPoint );
SCH_LINE* BreakAt( SCH_COMMIT* aCommit, const VECTOR2I& aPoint );
bool IsParallel( const SCH_LINE* aLine ) const;

View File

@ -70,8 +70,6 @@ EDA_ITEM* SCH_MARKER::Clone() const
void SCH_MARKER::swapData( SCH_ITEM* aItem )
{
SCH_ITEM::SwapFlags( aItem );
SCH_MARKER* item = static_cast<SCH_MARKER*>( aItem );
std::swap( m_isLegacyMarker, item->m_isLegacyMarker );
@ -84,6 +82,7 @@ void SCH_MARKER::swapData( SCH_ITEM* aItem )
std::swap( m_scalingFactor, item->m_scalingFactor );
std::swap( m_shapeBoundingBox, item->m_shapeBoundingBox );
// TODO: isn't this going to swap all the stuff above a second time?
std::swap( *((SCH_MARKER*) this), *((SCH_MARKER*) aItem ) );
}
@ -106,7 +105,7 @@ wxString SCH_MARKER::SerializeToString() const
|| m_rcItem->GetErrorCode() == ERCE_GENERIC_ERROR
|| m_rcItem->GetErrorCode() == ERCE_UNRESOLVED_VARIABLE )
{
SCH_ITEM* sch_item = Schematic()->GetItem( erc->GetMainItemID() );
SCH_ITEM* sch_item = Schematic()->ResolveItem( erc->GetMainItemID() );
SCH_ITEM* parent = static_cast<SCH_ITEM*>( sch_item->GetParent() );
EDA_TEXT* text_item = dynamic_cast<EDA_TEXT*>( sch_item );
@ -163,7 +162,7 @@ SCH_MARKER* SCH_MARKER::DeserializeFromString( const SCH_SHEET_LIST& aSheetList,
if( !props[4].IsEmpty() )
{
KIID uuid = niluuid;
SCH_ITEM* parent = aSheetList.GetItem( KIID( props[3] ) );
SCH_ITEM* parent = aSheetList.ResolveItem( KIID( props[3] ) );
// Check fields and pins for a match
parent->RunOnChildren(
@ -386,10 +385,10 @@ void SCH_MARKER::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_
EDA_ITEM* auxItem = nullptr;
if( m_rcItem->GetMainItemID() != niluuid )
mainItem = aFrame->GetItem( m_rcItem->GetMainItemID() );
mainItem = aFrame->ResolveItem( m_rcItem->GetMainItemID() );
if( m_rcItem->GetAuxItemID() != niluuid )
auxItem = aFrame->GetItem( m_rcItem->GetAuxItemID() );
auxItem = aFrame->ResolveItem( m_rcItem->GetAuxItemID() );
if( mainItem )
mainText = mainItem->GetItemDescription( aFrame, true );

View File

@ -57,8 +57,6 @@ EDA_ITEM* SCH_NO_CONNECT::Clone() const
void SCH_NO_CONNECT::swapData( SCH_ITEM* aItem )
{
SCH_ITEM::SwapFlags( aItem );
wxCHECK_RET( ( aItem != nullptr ) && ( aItem->Type() == SCH_NO_CONNECT_T ),
wxT( "Cannot swap no connect data with invalid item." ) );

View File

@ -306,13 +306,6 @@ void SCH_SCREEN::FreeDrawList()
std::copy_if( m_rtree.begin(), m_rtree.end(), std::back_inserter( delete_list ),
[]( SCH_ITEM* aItem )
{
// Untangle group parents before doing any deleting
if( aItem->Type() == SCH_GROUP_T )
{
for( EDA_ITEM* item : static_cast<SCH_GROUP*>(aItem)->GetItems() )
item->SetParentGroup( nullptr );
}
return ( aItem->Type() != SCH_SHEET_PIN_T && aItem->Type() != SCH_FIELD_T );
} );

View File

@ -59,8 +59,6 @@ int SCH_SHAPE::GetArcToSegMaxErrorIU( bool aHighDefinition ) const
void SCH_SHAPE::swapData( SCH_ITEM* aItem )
{
SCH_ITEM::SwapFlags( aItem );
SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( aItem );
EDA_SHAPE::SwapShape( shape );

View File

@ -327,8 +327,6 @@ bool SCH_SHEET::ResolveTextVar( const SCH_SHEET_PATH* aPath, wxString* token, in
void SCH_SHEET::swapData( SCH_ITEM* aItem )
{
SCH_ITEM::SwapFlags( aItem );
wxCHECK_RET( aItem->Type() == SCH_SHEET_T,
wxString::Format( wxT( "SCH_SHEET object cannot swap data with %s object." ),
aItem->GetClass() ) );

View File

@ -978,11 +978,11 @@ void SCH_SHEET_LIST::ClearModifyStatus()
}
SCH_ITEM* SCH_SHEET_LIST::GetItem( const KIID& aID, SCH_SHEET_PATH* aPathOut ) const
SCH_ITEM* SCH_SHEET_LIST::ResolveItem( const KIID& aID, SCH_SHEET_PATH* aPathOut, bool aAllowNullptrReturn ) const
{
for( const SCH_SHEET_PATH& sheet : *this )
{
SCH_ITEM* item = sheet.GetItem( aID );
SCH_ITEM* item = sheet.ResolveItem( aID );
if( item )
{
@ -994,11 +994,14 @@ SCH_ITEM* SCH_SHEET_LIST::GetItem( const KIID& aID, SCH_SHEET_PATH* aPathOut ) c
}
// Not found; weak reference has been deleted.
return DELETED_SHEET_ITEM::GetInstance();
if( aAllowNullptrReturn )
return nullptr;
else
return DELETED_SHEET_ITEM::GetInstance();
}
SCH_ITEM* SCH_SHEET_PATH::GetItem( const KIID& aID ) const
SCH_ITEM* SCH_SHEET_PATH::ResolveItem( const KIID& aID ) const
{
for( SCH_ITEM* aItem : LastScreen()->Items() )
{

View File

@ -270,7 +270,7 @@ public:
/**
* Fetch a SCH_ITEM by ID.
*/
SCH_ITEM* GetItem( const KIID& aID ) const;
SCH_ITEM* ResolveItem( const KIID& aID ) const;
/**
* Return the path of time stamps which do not changes even when editing sheet parameters.
@ -487,7 +487,8 @@ public:
*
* Also returns the sheet the item was found on in \a aPathOut.
*/
SCH_ITEM* GetItem( const KIID& aID, SCH_SHEET_PATH* aPathOut = nullptr ) const;
SCH_ITEM* ResolveItem( const KIID& aID, SCH_SHEET_PATH* aPathOut = nullptr,
bool aAllowNullptrReturn = false ) const;
/**
* Fill an item cache for temporary use when many items need to be fetched.

View File

@ -1202,8 +1202,6 @@ std::vector<SCH_PIN*> SCH_SYMBOL::GetPins() const
void SCH_SYMBOL::swapData( SCH_ITEM* aItem )
{
SCH_ITEM::SwapFlags( aItem );
wxCHECK_RET( aItem != nullptr && aItem->Type() == SCH_SYMBOL_T,
wxT( "Cannot swap data with invalid symbol." ) );

View File

@ -78,8 +78,6 @@ SCH_TABLE::~SCH_TABLE()
void SCH_TABLE::swapData( SCH_ITEM* aItem )
{
SCH_ITEM::SwapFlags( aItem );
wxCHECK_RET( aItem != nullptr && aItem->Type() == SCH_TABLE_T,
wxT( "Cannot swap data with invalid table." ) );

View File

@ -230,8 +230,6 @@ void SCH_TEXT::MirrorSpinStyle( bool aLeftRight )
void SCH_TEXT::swapData( SCH_ITEM* aItem )
{
SCH_ITEM::SwapFlags( aItem );
SCH_TEXT* item = static_cast<SCH_TEXT*>( aItem );
std::swap( m_layer, item->m_layer );

View File

@ -440,7 +440,7 @@ bool SCHEMATIC::ResolveCrossReference( wxString* token, int aDepth ) const
KIID_PATH path( ref );
KIID uuid = path.back();
SCH_SHEET_PATH sheetPath;
SCH_ITEM* refItem = GetItem( KIID( uuid ), &sheetPath );
SCH_ITEM* refItem = ResolveItem( KIID( uuid ), &sheetPath, true );
if( path.size() > 1 )
{
@ -602,7 +602,7 @@ wxString SCHEMATIC::ConvertKIIDsToRefs( const wxString& aSource ) const
KIID_PATH path( ref );
KIID uuid = path.back();
SCH_SHEET_PATH sheetPath;
SCH_ITEM* refItem = GetItem( uuid, &sheetPath );
SCH_ITEM* refItem = ResolveItem( uuid, &sheetPath, true );
if( path.size() > 1 )
{
@ -768,7 +768,7 @@ void SCHEMATIC::FixupJunctions()
// Breakup wires
for( SCH_LINE* wire : screen->GetBusesAndWires( point, true ) )
{
SCH_LINE* newSegment = wire->BreakAt( point );
SCH_LINE* newSegment = wire->BreakAt( nullptr, point );
screen->Append( newSegment );
}
}
@ -858,7 +858,7 @@ void SCHEMATIC::ResolveERCExclusionsPostUpdate()
for( SCH_MARKER* marker : ResolveERCExclusions() )
{
SCH_SHEET_PATH errorPath;
ignore_unused( sheetList.GetItem( marker->GetRCItem()->GetMainItemID(), &errorPath ) );
ignore_unused( sheetList.ResolveItem( marker->GetRCItem()->GetMainItemID(), &errorPath ) );
if( errorPath.LastScreen() )
errorPath.LastScreen()->Append( marker );

View File

@ -109,9 +109,10 @@ public:
void RefreshHierarchy();
SCH_ITEM* GetItem( const KIID& aID, SCH_SHEET_PATH* aPathOut = nullptr ) const
SCH_ITEM* ResolveItem( const KIID& aID, SCH_SHEET_PATH* aPathOut = nullptr,
bool aAllowNullptrReturn = false ) const
{
return BuildUnorderedSheetList().GetItem( aID, aPathOut );
return BuildUnorderedSheetList().ResolveItem( aID, aPathOut, aAllowNullptrReturn );
}
SCH_SHEET& Root() const

View File

@ -143,7 +143,7 @@ void SCH_EDIT_FRAME::SaveCopyInUndoList( SCH_SCREEN* aScreen, SCH_ITEM* aItem,
switch( aCommandType )
{
case UNDO_REDO::CHANGED: /* Create a copy of item */
itemWrapper.SetLink( aItem->Duplicate( true ) );
itemWrapper.SetLink( aItem->Duplicate( IGNORE_PARENT_GROUP, nullptr, true ) );
commandToUndo->PushItem( itemWrapper );
break;
@ -246,7 +246,7 @@ void SCH_EDIT_FRAME::SaveCopyInUndoList( const PICKED_ITEMS_LIST& aItemsList,
* If this link is not null, the copy is already done
*/
if( commandToUndo->GetPickedItemLink( ii ) == nullptr )
commandToUndo->SetPickedItemLink( sch_item->Duplicate( true ), ii );
commandToUndo->SetPickedItemLink( sch_item->Duplicate( IGNORE_PARENT_GROUP, nullptr, true ), ii );
wxASSERT( commandToUndo->GetPickedItemLink( ii ) );
break;
@ -255,8 +255,6 @@ void SCH_EDIT_FRAME::SaveCopyInUndoList( const PICKED_ITEMS_LIST& aItemsList,
case UNDO_REDO::DELETED:
case UNDO_REDO::PAGESETTINGS:
case UNDO_REDO::REPEAT_ITEM:
case UNDO_REDO::REGROUP:
case UNDO_REDO::UNGROUP:
break;
default:
@ -390,26 +388,6 @@ void SCH_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList )
bulkAddedItems.emplace_back( schItem );
}
else if( status == UNDO_REDO::REGROUP ) /* grouped items are ungrouped */
{
aList->SetPickedItemStatus( UNDO_REDO::UNGROUP, ii );
if( EDA_GROUP* group = eda_item->GetParentGroup() )
{
aList->SetPickedItemGroupId( group->AsEdaItem()->m_Uuid, ii );
group->RemoveItem( eda_item );
}
}
else if( status == UNDO_REDO::UNGROUP ) /* ungrouped items are re-added to their previuos groups */
{
aList->SetPickedItemStatus( UNDO_REDO::REGROUP, ii );
EDA_GROUP* group = dynamic_cast<EDA_GROUP*>( Schematic().GetItem( aList->GetPickedItemGroupId( ii ) ) );
if( group )
group->AddItem( eda_item );
}
else if( status == UNDO_REDO::PAGESETTINGS )
{
if( GetCurrentSheet() != undoSheet )
@ -450,8 +428,6 @@ void SCH_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList )
wxCHECK2( itemCopy, continue );
EDA_GROUP* parentGroup = schItem->GetParentGroup();
if( schItem->HasConnectivityChanges( itemCopy, &GetCurrentSheet() ) )
propagateConnectivityDamage( schItem );
@ -485,24 +461,8 @@ void SCH_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList )
}
}
if( parentGroup )
parentGroup->RemoveItem( schItem );
schItem->SwapItemData( itemCopy );
if( SCH_GROUP* group = dynamic_cast<SCH_GROUP*>( schItem ) )
{
group->RunOnChildren( [&]( SCH_ITEM* child )
{
child->SetParentGroup( group );
},
RECURSE_MODE::NO_RECURSE );
}
if( parentGroup )
parentGroup->AddItem( schItem );
bulkChangedItems.emplace_back( schItem );
// Special cases for items which have instance data
@ -544,6 +504,32 @@ void SCH_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList )
}
}
// We have now swapped all the group parent and group member pointers. But it is a
// risky proposition to bet on the pointers being invariant, so validate them all.
for( int ii = 0; ii < (int) aList->GetCount(); ++ii )
{
ITEM_PICKER& wrapper = aList->GetItemWrapper( ii );
SCH_ITEM* parentGroup = Schematic().ResolveItem( wrapper.GetGroupId(), nullptr, true );
wrapper.GetItem()->SetParentGroup( dynamic_cast<SCH_GROUP*>( parentGroup ) );
if( EDA_GROUP* group = dynamic_cast<SCH_GROUP*>( wrapper.GetItem() ) )
{
// Items list may contain dodgy pointers, so don't use RemoveAll()
group->GetItems().clear();
for( const KIID& member : wrapper.GetGroupMembers() )
{
if( SCH_ITEM* memberItem = Schematic().ResolveItem( member, nullptr, true ) )
group->AddItem( memberItem );
}
}
// And prepare for a redo by updating group info based on current image
if( EDA_ITEM* item = wrapper.GetLink() )
wrapper.SetLink( item );
}
GetCanvas()->GetView()->ClearHiddenFlags();
// Notify our listeners

View File

@ -1653,7 +1653,7 @@ void SIMULATOR_FRAME_UI::AddTuner( const SCH_SHEET_PATH& aSheetPath, SCH_SYMBOL*
void SIMULATOR_FRAME_UI::UpdateTunerValue( const SCH_SHEET_PATH& aSheetPath, const KIID& aSymbol,
const wxString& aRef, const wxString& aValue )
{
SCH_ITEM* item = aSheetPath.GetItem( aSymbol );
SCH_ITEM* item = aSheetPath.ResolveItem( aSymbol );
SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
if( !symbol )
@ -2156,7 +2156,7 @@ void SIMULATOR_FRAME_UI::applyTuners()
SCH_SHEET_PATH sheetPath;
wxString ref = tuner->GetSymbolRef();
KIID symbolId = tuner->GetSymbol( &sheetPath );
SCH_ITEM* schItem = sheetPath.GetItem( symbolId );
SCH_ITEM* schItem = sheetPath.ResolveItem( symbolId );
SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( schItem );
if( !symbol )

View File

@ -52,14 +52,3 @@ std::vector<int> SYMBOL::ViewGetLayers() const
LAYER_FIELDS, LAYER_DEVICE_BACKGROUND, LAYER_SHAPES_BACKGROUND,
LAYER_NOTES_BACKGROUND, LAYER_SELECTION_SHADOWS };
}
SYMBOL::~SYMBOL()
{
// Untangle group parents before doing any deleting
RunOnChildren( []( EDA_ITEM* item )
{
if( item->Type() == SCH_GROUP_T)
item->SetParentGroup( nullptr );
}, RECURSE_MODE::RECURSE );
}

View File

@ -104,7 +104,7 @@ public:
return *this;
};
~SYMBOL() override;
~SYMBOL() override = default;
virtual const LIB_ID& GetLibId() const = 0;
virtual wxString GetDescription() const = 0;

View File

@ -37,8 +37,6 @@ void SYMBOL_EDIT_FRAME::PushSymbolToUndoList( const wxString& aDescription, LIB_
if( !aSymbolCopy )
return;
auto* drawingTool = GetToolManager()->GetTool<SYMBOL_EDITOR_DRAWING_TOOLS>();
PICKED_ITEMS_LIST* lastcmd = new PICKED_ITEMS_LIST();
// Clear current flags (which can be temporary set by a current edit command).
@ -47,7 +45,6 @@ void SYMBOL_EDIT_FRAME::PushSymbolToUndoList( const wxString& aDescription, LIB_
aSymbolCopy->SetFlags( UR_TRANSIENT );
ITEM_PICKER wrapper( GetScreen(), aSymbolCopy, aUndoType );
wrapper.SetGroupId( drawingTool->GetLastPin() );
lastcmd->PushItem( wrapper );
lastcmd->SetDescription( aDescription );
PushCommandToUndoList( lastcmd );
@ -91,7 +88,6 @@ void SYMBOL_EDIT_FRAME::GetSymbolFromRedoList()
oldSymbol->SetFlags( UR_TRANSIENT );
ITEM_PICKER undoWrapper( GetScreen(), oldSymbol, undoRedoType );
undoWrapper.SetGroupId( drawingTool->GetLastPin() );
undoCommand->SetDescription( description );
undoCommand->PushItem( undoWrapper );
PushCommandToUndoList( undoCommand );
@ -147,7 +143,6 @@ void SYMBOL_EDIT_FRAME::GetSymbolFromUndoList()
oldSymbol->SetFlags( UR_TRANSIENT );
ITEM_PICKER redoWrapper( GetScreen(), oldSymbol, undoRedoType );
redoWrapper.SetGroupId( drawingTool->GetLastPin() );
redoCommand->PushItem( redoWrapper );
redoCommand->SetDescription( description );
PushCommandToRedoList( redoCommand );

View File

@ -487,7 +487,7 @@ int SCH_DRAWING_TOOLS::PlaceSymbol( const TOOL_EVENT& aEvent )
// We are either stepping to the next unit or next symbol
if( m_frame->eeconfig()->m_SymChooserPanel.keep_symbol || new_unit > 1 )
{
nextSymbol = static_cast<SCH_SYMBOL*>( symbol->Duplicate() );
nextSymbol = static_cast<SCH_SYMBOL*>( symbol->Duplicate( IGNORE_PARENT_GROUP ) );
nextSymbol->SetUnit( new_unit );
nextSymbol->SetUnitSelection( new_unit );
@ -757,7 +757,9 @@ int SCH_DRAWING_TOOLS::ImportSheet( const TOOL_EVENT& aEvent )
newItems.emplace_back( item );
}
else
{
item->ClearFlags( SKIP_STRUCT );
}
}
if( placeAsGroup )
@ -766,7 +768,9 @@ int SCH_DRAWING_TOOLS::ImportSheet( const TOOL_EVENT& aEvent )
selectionTool->AddItemToSel( group );
}
else
{
selectionTool->AddItemsToSel( &newItems, true );
}
cursorPos = grid.Align( controls->GetMousePosition(),
grid.GetSelectionGrid( selectionTool->GetSelection() ) );

View File

@ -824,7 +824,7 @@ int SCH_EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
rotPoint = m_frame->GetNearestHalfGridPosition( head->GetBoundingBox().GetCenter() );
if( !moving )
commit->Modify( head, m_frame->GetScreen() );
commit->Modify( head, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
switch( head->Type() )
{
@ -989,7 +989,7 @@ int SCH_EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
continue;
if( !moving )
commit->Modify( item, m_frame->GetScreen() );
commit->Modify( item, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
if( item->Type() == SCH_LINE_T )
{
@ -1092,7 +1092,7 @@ int SCH_EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
if( selection.GetSize() == 1 )
{
if( !moving )
commit->Modify( item, m_frame->GetScreen() );
commit->Modify( item, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
switch( item->Type() )
{
@ -1193,7 +1193,7 @@ int SCH_EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
item = static_cast<SCH_ITEM*>( edaItem );
if( !moving )
commit->Modify( item, m_frame->GetScreen() );
commit->Modify( item, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
if( item->Type() == SCH_SHEET_PIN_T )
{
@ -1511,12 +1511,13 @@ int SCH_EDIT_TOOL::RepeatDrawItem( const TOOL_EVENT& aEvent )
m_toolMgr->RunAction( ACTIONS::selectionClear );
SCH_COMMIT commit( m_toolMgr );
SCH_SELECTION newItems;
SCH_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
SCH_COMMIT commit( m_toolMgr );
SCH_SELECTION newItems;
for( const std::unique_ptr<SCH_ITEM>& item : sourceItems )
{
SCH_ITEM* newItem = item->Duplicate();
SCH_ITEM* newItem = item->Duplicate( IGNORE_PARENT_GROUP );
SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
EESCHEMA_SETTINGS* cfg = mgr.GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" );
bool restore_state = false;
@ -1525,6 +1526,15 @@ int SCH_EDIT_TOOL::RepeatDrawItem( const TOOL_EVENT& aEvent )
// a list of items to repeat must be attached to this current screen
newItem->SetParent( m_frame->GetScreen() );
if( SCH_GROUP* enteredGroup = selectionTool->GetEnteredGroup() )
{
if( newItem->IsGroupableType() )
{
commit.Modify( enteredGroup );
enteredGroup->AddItem( newItem );
}
}
if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( newItem ) )
{
// If incrementing tries to go below zero, tell user why the value is repeated
@ -1716,14 +1726,7 @@ int SCH_EDIT_TOOL::DoDelete( const TOOL_EVENT& aEvent )
}
else if( sch_item->Type() == SCH_GROUP_T )
{
// Groups need to have all children ungrouped, then deleted
sch_item->RunOnChildren(
[&]( SCH_ITEM* aChild )
{
commit.Stage( aChild, CHT_UNGROUP, m_frame->GetScreen() );
},
RECURSE_MODE::RECURSE );
// Groups need to delete their children
sch_item->RunOnChildren(
[&]( SCH_ITEM* aChild )
{

View File

@ -2338,14 +2338,15 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
SCH_ITEM* item = nullptr;
group->RunOnChildren(
[&]( SCH_ITEM* schItem )
{
if( !found && schItem->Type() != SCH_GROUP_T )
[&]( SCH_ITEM* schItem )
{
item = schItem;
found = true;
}
}, RECURSE_MODE::RECURSE );
if( !found && schItem->Type() != SCH_GROUP_T )
{
item = schItem;
found = true;
}
},
RECURSE_MODE::RECURSE );
if( found )
selection.SetReferencePoint( item->GetPosition() );
@ -2900,12 +2901,15 @@ int SCH_EDITOR_CONTROL::RepairSchematic( const TOOL_EVENT& aEvent )
{
processItem( item );
item->RunOnChildren(
[&]( SCH_ITEM* aChild )
{
processItem( item );
},
RECURSE_MODE::NO_RECURSE );
if( item->Type() != SCH_GROUP_T )
{
item->RunOnChildren(
[&]( SCH_ITEM* aChild )
{
processItem( item );
},
RECURSE_MODE::NO_RECURSE );
}
}
}

View File

@ -69,16 +69,7 @@ int SCH_FIND_REPLACE_TOOL::UpdateFind( const TOOL_EVENT& aEvent )
[&]()
{
for( SCH_ITEM* item : m_frame->GetScreen()->Items() )
{
visit( item, &m_frame->GetCurrentSheet() );
item->RunOnChildren(
[&]( SCH_ITEM* aChild )
{
visit( aChild, &m_frame->GetCurrentSheet() );
},
RECURSE_MODE::NO_RECURSE );
}
};
if( aEvent.IsAction( &ACTIONS::find ) || aEvent.IsAction( &ACTIONS::findAndReplace )

View File

@ -133,10 +133,12 @@ int SCH_GROUP_TOOL::Group( const TOOL_EVENT& aEvent )
for( EDA_ITEM* item : selection.GetItems() )
{
if( !SCH_GROUP::IsGroupableType( item->Type() ) )
SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
if( !schItem->IsGroupableType() )
selection.Remove( item );
if( isSymbolEditor && static_cast<SCH_ITEM*>( item )->GetParentSymbol() )
if( isSymbolEditor && schItem->GetParentSymbol() )
selection.Remove( item );
}
@ -151,11 +153,13 @@ int SCH_GROUP_TOOL::Group( const TOOL_EVENT& aEvent )
else
group->SetParent( screen );
m_commit->Add( group, screen );
for( EDA_ITEM* eda_item : selection )
m_commit->Stage( eda_item, CHT_GROUP, screen );
{
m_commit->Modify( eda_item, screen );
group->AddItem( eda_item );
}
m_commit->Add( group, screen );
m_commit->Push( _( "Group Items" ) );
m_toolMgr->RunAction( ACTIONS::selectionClear );

View File

@ -250,7 +250,7 @@ void SCH_INSPECTION_TOOL::InspectERCError( const std::shared_ptr<RC_ITEM>& aERCI
wxCHECK( frame, /* void */ );
EDA_ITEM* a = frame->GetItem( aERCItem->GetMainItemID() );
EDA_ITEM* a = frame->ResolveItem( aERCItem->GetMainItemID() );
if( aERCItem->GetErrorCode() == ERCE_BUS_TO_NET_CONFLICT )
{

View File

@ -291,6 +291,7 @@ int SCH_LINE_WIRE_BUS_TOOL::DrawSegments( const TOOL_EVENT& aEvent )
REENTRANCY_GUARD guard( &m_inDrawingTool );
const DRAW_SEGMENT_EVENT_PARAMS* params = aEvent.Parameter<const DRAW_SEGMENT_EVENT_PARAMS*>();
SCH_COMMIT commit( m_toolMgr );
m_frame->PushTool( aEvent );
m_toolMgr->RunAction( ACTIONS::selectionClear );
@ -304,10 +305,10 @@ int SCH_LINE_WIRE_BUS_TOOL::DrawSegments( const TOOL_EVENT& aEvent )
grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !aEvent.DisableGridSnapping() );
VECTOR2D cursorPos = grid.BestSnapAnchor( aEvent.Position(), gridType, nullptr );
startSegments( params->layer, cursorPos, params->sourceSegment );
startSegments( commit, params->layer, cursorPos, params->sourceSegment );
}
return doDrawSegments( aEvent, params->layer, params->quitOnDraw );
return doDrawSegments( aEvent, commit, params->layer, params->quitOnDraw );
}
@ -318,9 +319,10 @@ int SCH_LINE_WIRE_BUS_TOOL::UnfoldBus( const TOOL_EVENT& aEvent )
REENTRANCY_GUARD guard( &m_inDrawingTool );
wxString* netPtr = aEvent.Parameter<wxString*>();
wxString net;
SCH_LINE* segment = nullptr;
SCH_COMMIT commit( m_toolMgr );
wxString* netPtr = aEvent.Parameter<wxString*>();
wxString net;
SCH_LINE* segment = nullptr;
m_frame->PushTool( aEvent );
Activate();
@ -366,12 +368,12 @@ int SCH_LINE_WIRE_BUS_TOOL::UnfoldBus( const TOOL_EVENT& aEvent )
// Break a wire for the given net out of the bus
if( !net.IsEmpty() )
segment = doUnfoldBus( net );
segment = doUnfoldBus( commit, net );
// If we have an unfolded wire to draw, then draw it
if( segment )
{
return doDrawSegments( aEvent, LAYER_WIRE, false );
return doDrawSegments( aEvent, commit, LAYER_WIRE, false );
}
else
{
@ -388,7 +390,7 @@ SCH_LINE* SCH_LINE_WIRE_BUS_TOOL::getBusForUnfolding()
}
SCH_LINE* SCH_LINE_WIRE_BUS_TOOL::doUnfoldBus( const wxString& aNet,
SCH_LINE* SCH_LINE_WIRE_BUS_TOOL::doUnfoldBus( SCH_COMMIT& aCommit, const wxString& aNet,
const std::optional<VECTOR2I>& aPos )
{
SCHEMATIC_SETTINGS& cfg = getModel<SCHEMATIC>()->Settings();
@ -398,9 +400,8 @@ SCH_LINE* SCH_LINE_WIRE_BUS_TOOL::doUnfoldBus( const wxString& aN
if ( bus == nullptr )
{
wxASSERT_MSG( false,
wxString::Format( "Couldn't find the originating bus line (but had a net: %s )",
aNet ) );
wxASSERT_MSG( false, wxString::Format( "Couldn't find the originating bus line (but had a net: %s )",
aNet ) );
return nullptr;
}
@ -441,7 +442,7 @@ SCH_LINE* SCH_LINE_WIRE_BUS_TOOL::doUnfoldBus( const wxString& aN
m_busUnfold.entry->SetEndDangling( false );
m_busUnfold.label->SetIsDangling( false );
return startSegments( LAYER_WIRE, m_busUnfold.entry->GetEnd() );
return startSegments( aCommit, LAYER_WIRE, m_busUnfold.entry->GetEnd() );
}
@ -601,7 +602,8 @@ void SCH_LINE_WIRE_BUS_TOOL::computeBreakPoint( const std::pair<SCH_LINE*, SCH_L
}
int SCH_LINE_WIRE_BUS_TOOL::doDrawSegments( const TOOL_EVENT& aTool, int aType, bool aQuitOnDraw )
int SCH_LINE_WIRE_BUS_TOOL::doDrawSegments( const TOOL_EVENT& aTool, SCH_COMMIT& aCommit,
int aType, bool aQuitOnDraw )
{
SCH_SCREEN* screen = m_frame->GetScreen();
SCH_LINE* segment = nullptr;
@ -717,7 +719,7 @@ int SCH_LINE_WIRE_BUS_TOOL::doDrawSegments( const TOOL_EVENT& aTool, int aType,
segment->SetEndPoint( cursorPos );
// Create a new segment, and chain it after the current segment.
segment = static_cast<SCH_LINE*>( segment->Duplicate() );
segment = static_cast<SCH_LINE*>( segment->Duplicate( true, &aCommit ) );
segment->SetFlags( IS_NEW | IS_MOVING );
segment->SetStartPoint( cursorPos );
m_wires.push_back( segment );
@ -778,9 +780,11 @@ int SCH_LINE_WIRE_BUS_TOOL::doDrawSegments( const TOOL_EVENT& aTool, int aType,
{
if( segment || m_busUnfold.in_progress )
{
finishSegments();
finishSegments( aCommit );
segment = nullptr;
aCommit.Push( _( "Draw Wires" ) );
if( aQuitOnDraw )
{
m_frame->PopTool( aTool );
@ -807,7 +811,7 @@ int SCH_LINE_WIRE_BUS_TOOL::doDrawSegments( const TOOL_EVENT& aTool, int aType,
if( !segment )
{
segment = startSegments( aType, VECTOR2D( cursorPos ) );
segment = startSegments( aCommit, aType, VECTOR2D( cursorPos ) );
}
// Create a new segment if we're out of previously-created ones
else if( !segment->IsNull()
@ -817,9 +821,11 @@ int SCH_LINE_WIRE_BUS_TOOL::doDrawSegments( const TOOL_EVENT& aTool, int aType,
// wire or bus.
if( screen->IsTerminalPoint( cursorPos, segment->GetLayer() ) )
{
finishSegments();
finishSegments( aCommit );
segment = nullptr;
aCommit.Push( _( "Draw Wires" ) );
if( aQuitOnDraw )
{
m_frame->PopTool( aTool );
@ -841,7 +847,7 @@ int SCH_LINE_WIRE_BUS_TOOL::doDrawSegments( const TOOL_EVENT& aTool, int aType,
for( int i = 0; i < placedSegments; i++ )
{
// Create a new segment, and chain it after the current segment.
segment = static_cast<SCH_LINE*>( segment->Duplicate() );
segment = static_cast<SCH_LINE*>( segment->Duplicate( true, &aCommit ) );
segment->SetFlags( IS_NEW | IS_MOVING );
segment->SetStartPoint( cursorPos );
m_wires.push_back( segment );
@ -859,9 +865,11 @@ int SCH_LINE_WIRE_BUS_TOOL::doDrawSegments( const TOOL_EVENT& aTool, int aType,
currentMode, posture );
}
finishSegments();
finishSegments( aCommit );
segment = nullptr;
aCommit.Push( _( "Draw Wires" ) );
if( aQuitOnDraw )
{
m_frame->PopTool( aTool );
@ -1031,7 +1039,7 @@ int SCH_LINE_WIRE_BUS_TOOL::doDrawSegments( const TOOL_EVENT& aTool, int aType,
aType = LAYER_WIRE;
wxString net = *evt->Parameter<wxString*>();
segment = doUnfoldBus( net, contextMenuPos );
segment = doUnfoldBus( aCommit, net, contextMenuPos );
}
}
//------------------------------------------------------------------------
@ -1073,7 +1081,7 @@ int SCH_LINE_WIRE_BUS_TOOL::doDrawSegments( const TOOL_EVENT& aTool, int aType,
}
SCH_LINE* SCH_LINE_WIRE_BUS_TOOL::startSegments( int aType, const VECTOR2D& aPos,
SCH_LINE* SCH_LINE_WIRE_BUS_TOOL::startSegments( SCH_COMMIT& aCommit, int aType, const VECTOR2D& aPos,
SCH_LINE* aSegment )
{
if( !aSegment )
@ -1093,7 +1101,7 @@ SCH_LINE* SCH_LINE_WIRE_BUS_TOOL::startSegments( int aType, const VECTOR2D& aPos
}
else
{
aSegment = static_cast<SCH_LINE*>( aSegment->Duplicate() );
aSegment = static_cast<SCH_LINE*>( aSegment->Duplicate( true, &aCommit ) );
aSegment->SetStartPoint( aPos );
}
@ -1107,7 +1115,7 @@ SCH_LINE* SCH_LINE_WIRE_BUS_TOOL::startSegments( int aType, const VECTOR2D& aPos
// horizontal and vertical lines only switch is on.
if( m_frame->eeconfig()->m_Drawing.line_mode )
{
aSegment = static_cast<SCH_LINE*>( aSegment->Duplicate() );
aSegment = static_cast<SCH_LINE*>( aSegment->Duplicate( true, &aCommit ) );
aSegment->SetFlags( IS_NEW | IS_MOVING );
m_wires.push_back( aSegment );
@ -1165,7 +1173,7 @@ void SCH_LINE_WIRE_BUS_TOOL::simplifyWireList()
}
void SCH_LINE_WIRE_BUS_TOOL::finishSegments()
void SCH_LINE_WIRE_BUS_TOOL::finishSegments( SCH_COMMIT& aCommit )
{
// Clear selection when done so that a new wire can be started.
// NOTE: this must be done before simplifyWireList is called or we might end up with
@ -1173,7 +1181,6 @@ void SCH_LINE_WIRE_BUS_TOOL::finishSegments()
m_toolMgr->RunAction( ACTIONS::selectionClear );
SCH_SCREEN* screen = m_frame->GetScreen();
SCH_COMMIT commit( m_toolMgr );
// Remove segments backtracking over others
simplifyWireList();
@ -1198,17 +1205,17 @@ void SCH_LINE_WIRE_BUS_TOOL::finishSegments()
new_ends.push_back( pt );
}
commit.Added( wire, screen );
aCommit.Added( wire, screen );
}
if( m_busUnfold.in_progress && m_busUnfold.label_placed )
{
wxASSERT( m_busUnfold.entry && m_busUnfold.label );
commit.Added( m_busUnfold.entry, screen );
aCommit.Added( m_busUnfold.entry, screen );
m_frame->SaveCopyForRepeatItem( m_busUnfold.entry );
commit.Added( m_busUnfold.label, screen );
aCommit.Added( m_busUnfold.label, screen );
m_frame->AddCopyForRepeatItem( m_busUnfold.label );
m_busUnfold.label->ClearEditFlags();
}
@ -1239,7 +1246,7 @@ void SCH_LINE_WIRE_BUS_TOOL::finishSegments()
getViewControls()->SetAutoPan( false );
// Correct and remove segments that need to be merged.
m_frame->SchematicCleanUp( &commit );
m_frame->SchematicCleanUp( &aCommit );
std::vector<SCH_ITEM*> symbols;
@ -1256,14 +1263,14 @@ void SCH_LINE_WIRE_BUS_TOOL::finishSegments()
for( auto pt = pts.begin(); pt != pts.end(); pt++ )
{
for( auto secondPt = pt + 1; secondPt != pts.end(); secondPt++ )
m_frame->TrimWire( &commit, *pt, *secondPt );
m_frame->TrimWire( &aCommit, *pt, *secondPt );
}
}
for( const VECTOR2I& pt : new_ends )
{
if( m_frame->GetScreen()->IsExplicitJunctionNeeded( pt ) )
m_frame->AddJunction( &commit, m_frame->GetScreen(), pt );
m_frame->AddJunction( &aCommit, m_frame->GetScreen(), pt );
}
if( m_busUnfold.in_progress )
@ -1271,8 +1278,6 @@ void SCH_LINE_WIRE_BUS_TOOL::finishSegments()
for( SCH_ITEM* item : m_frame->GetScreen()->Items() )
item->ClearEditFlags();
commit.Push( _( "Draw Wires" ) );
}

View File

@ -96,9 +96,11 @@ public:
int TrimOverLappingWires( SCH_COMMIT* aCommit, SCH_SELECTION* aSelection );
private:
int doDrawSegments( const TOOL_EVENT& aTool, int aType, bool aQuitOnDraw );
int doDrawSegments( const TOOL_EVENT& aTool, SCH_COMMIT& aCommit, int aType,
bool aQuitOnDraw );
SCH_LINE* startSegments( int aType, const VECTOR2D& aPos, SCH_LINE* aSegment = nullptr );
SCH_LINE* startSegments( SCH_COMMIT& aCommit, int aType, const VECTOR2D& aPos,
SCH_LINE* aSegment = nullptr );
/**
* Choose a bus to unfold based on the current tool selection.
@ -112,10 +114,10 @@ private:
* @param aPos The position to unfold the bus from, which will be the cursor if
* not provided, and will then be snapped to the selected bus segment.
*/
SCH_LINE* doUnfoldBus( const wxString& aNet,
SCH_LINE* doUnfoldBus( SCH_COMMIT& aCommit, const wxString& aNet,
const std::optional<VECTOR2I>& aPos = std::nullopt );
void finishSegments();
void finishSegments( SCH_COMMIT& aCommit );
/**
* Iterate over the wire list and removes the null segments and

View File

@ -625,36 +625,48 @@ bool SCH_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COMMIT* aComm
for( EDA_ITEM* item : selection )
{
if( item->IsNew() )
SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
if( schItem->IsNew() )
{
// Item was added to commit in a previous command
// While SCH_COMMIT::Push() will add any new items to the entered group,
// we need to do it earlier so that the previews while moving are correct.
if( SCH_GROUP* enteredGroup = m_selectionTool->GetEnteredGroup() )
{
if( schItem->IsGroupableType() && !schItem->GetParentGroup() )
{
aCommit->Modify( enteredGroup );
enteredGroup->AddItem( schItem );
}
}
}
else if( item->GetParent() && item->GetParent()->IsSelected() )
else if( schItem->GetParent() && schItem->GetParent()->IsSelected() )
{
// Item will be (or has been) added to commit by parent
}
else
{
aCommit->Modify( item, m_frame->GetScreen() );
aCommit->Modify( schItem, m_frame->GetScreen() );
}
item->SetFlags( IS_MOVING );
schItem->SetFlags( IS_MOVING );
if( SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( item ) )
if( SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( schItem ) )
{
shape->SetHatchingDirty();
shape->UpdateHatching();
}
static_cast<SCH_ITEM*>( item )->RunOnChildren(
schItem->RunOnChildren(
[&]( SCH_ITEM* schItem )
{
item->SetFlags( IS_MOVING );
},
RECURSE_MODE::RECURSE );
if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( item ) )
schItem->SetStoredPos( schItem->GetPosition() );
schItem->SetStoredPos( schItem->GetPosition() );
}
// Set up the starting position and move/drag offset
@ -1670,7 +1682,7 @@ int SCH_MOVE_TOOL::AlignToGrid( const TOOL_EVENT& aEvent )
auto doMoveItem =
[&]( EDA_ITEM* item, const VECTOR2I& delta )
{
commit.Modify( item, m_frame->GetScreen() );
commit.Modify( item, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
// Ensure only one end is moved when calling moveItem
// i.e. we are in drag mode

View File

@ -1076,7 +1076,7 @@ int SCH_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
if( lastRolloverItem != niluuid && lastRolloverItem != rolloverItem )
{
EDA_ITEM* item = m_frame->GetItem( lastRolloverItem );
EDA_ITEM* item = m_frame->ResolveItem( lastRolloverItem );
if( item->IsRollover() )
{
@ -1091,7 +1091,7 @@ int SCH_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
if( rolloverItem != niluuid )
{
EDA_ITEM* item = m_frame->GetItem( rolloverItem );
EDA_ITEM* item = m_frame->ResolveItem( rolloverItem );
if( !item->IsRollover() )
{
@ -1164,6 +1164,7 @@ void SCH_SELECTION_TOOL::EnterGroup()
{
if( aChild->Type() == SCH_LINE_T )
aChild->SetFlags( STARTPOINT | ENDPOINT );
select( aChild );
},
RECURSE_MODE::NO_RECURSE );
@ -1897,13 +1898,12 @@ SCH_SELECTION& SCH_SELECTION_TOOL::RequestSelection( const std::vector<KICAD_T>&
if( item->Type() == SCH_GROUP_T )
{
static_cast<SCH_ITEM*>(item)
->RunOnChildren( [&]( SCH_ITEM* aChild )
{
if( aChild->IsType( aScanTypes ) )
selectedChildren.insert( aChild );
},
RECURSE_MODE::RECURSE );
static_cast<SCH_ITEM*>(item)->RunOnChildren( [&]( SCH_ITEM* aChild )
{
if( aChild->IsType( aScanTypes ) )
selectedChildren.insert( aChild );
},
RECURSE_MODE::RECURSE );
unselect( item );
anyUnselected = true;
}

View File

@ -290,7 +290,7 @@ int SYMBOL_EDITOR_DRAWING_TOOLS::TwoClickPlace( const TOOL_EVENT& aEvent )
switch( item->Type() )
{
case SCH_PIN_T:
pinTool->PlacePin( static_cast<SCH_PIN*>( item ) );
pinTool->PlacePin( &commit, static_cast<SCH_PIN*>( item ) );
item->ClearEditFlags();
commit.Push( _( "Place Pin" ) );
break;

View File

@ -183,7 +183,7 @@ int SYMBOL_EDITOR_EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
commit = &localCommit;
if( !item->IsMoving() )
commit->Modify( m_frame->GetCurSymbol(), m_frame->GetScreen() );
commit->Modify( m_frame->GetCurSymbol(), m_frame->GetScreen(), RECURSE_MODE::RECURSE );
if( selection.GetSize() == 1 )
rotPoint = item->GetPosition();
@ -1015,7 +1015,7 @@ int SYMBOL_EDITOR_EDIT_TOOL::Paste( const TOOL_EVENT& aEvent )
if( item.Type() == SCH_FIELD_T )
continue;
SCH_ITEM* newItem = item.Duplicate();
SCH_ITEM* newItem = item.Duplicate( true, &commit );
newItem->SetParent( symbol );
newItem->SetFlags( IS_NEW | IS_PASTED | SELECTED );
@ -1093,7 +1093,7 @@ int SYMBOL_EDITOR_EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent )
for( EDA_ITEM* item : oldItems )
{
SCH_ITEM* oldItem = static_cast<SCH_ITEM*>( item );
SCH_ITEM* newItem = oldItem->Duplicate();
SCH_ITEM* newItem = oldItem->Duplicate( true, &commit );
if( newItem->Type() == SCH_PIN_T )
{

View File

@ -29,6 +29,7 @@
#include <eda_item.h>
#include <gal/graphics_abstraction_layer.h>
#include <sch_shape.h>
#include <sch_group.h>
#include <sch_commit.h>
#include <wx/debug.h>
#include <view/view_controls.h>
@ -232,8 +233,21 @@ bool SYMBOL_EDITOR_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COM
// Drag items to the current cursor position
for( EDA_ITEM* item : selection )
{
moveItem( item, delta );
updateItem( item, false );
SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
moveItem( schItem, delta );
updateItem( schItem, false );
// While SCH_COMMIT::Push() will add any new items to the entered group,
// we need to do it earlier so that the previews while moving are correct.
if( SCH_GROUP* enteredGroup = m_selectionTool->GetEnteredGroup() )
{
if( schItem->IsGroupableType() && !item->GetParentGroup() )
{
aCommit->Modify( enteredGroup );
enteredGroup->AddItem( schItem );
}
}
}
m_anchorPos = m_cursor;
@ -329,7 +343,7 @@ bool SYMBOL_EDITOR_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COM
{
SCH_PIN* curr_pin = static_cast<SCH_PIN*>( selection.Front() );
if( pinTool->PlacePin( curr_pin ) )
if( pinTool->PlacePin( aCommit, curr_pin ) )
{
// PlacePin() clears the current selection, which we don't want. Not only
// is it a poor user experience, but it also prevents us from doing the
@ -383,21 +397,17 @@ int SYMBOL_EDITOR_MOVE_TOOL::AlignElements( const TOOL_EVENT& aEvent )
SCH_SELECTION& selection = m_selectionTool->RequestSelection();
SCH_COMMIT commit( m_toolMgr );
auto doMoveItem =
[&]( EDA_ITEM* item, const VECTOR2I& delta )
{
commit.Modify( item, m_frame->GetScreen() );
static_cast<SCH_ITEM*>( item )->Move( delta );
updateItem( item, true );
};
for( EDA_ITEM* item : selection )
{
VECTOR2I newPos = grid.AlignGrid( item->GetPosition(), grid.GetItemGrid( item ) );
VECTOR2I delta = newPos - item->GetPosition();
if( delta != VECTOR2I( 0, 0 ) )
doMoveItem( item, delta );
{
commit.Modify( item, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
static_cast<SCH_ITEM*>( item )->Move( delta );
updateItem( item, true );
};
if( SCH_PIN* pin = dynamic_cast<SCH_PIN*>( item ) )
{

View File

@ -24,8 +24,6 @@
#include "symbol_editor_pin_tool.h"
#include <tools/sch_selection_tool.h>
#include <symbol_edit_frame.h>
#include <sch_commit.h>
#include <kidialog.h>
#include <sch_actions.h>
@ -217,7 +215,7 @@ bool SYMBOL_EDITOR_PIN_TOOL::EditPinProperties( SCH_PIN* aPin, bool aFocusPinNum
}
bool SYMBOL_EDITOR_PIN_TOOL::PlacePin( SCH_PIN* aPin )
bool SYMBOL_EDITOR_PIN_TOOL::PlacePin( SCH_COMMIT* aCommit, SCH_PIN* aPin )
{
LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
bool ask_for_pin = true; // Test for another pin in same position in other units
@ -266,7 +264,7 @@ bool SYMBOL_EDITOR_PIN_TOOL::PlacePin( SCH_PIN* aPin )
g_LastPinShape = aPin->GetShape();
if( m_frame->SynchronizePins() )
CreateImagePins( aPin );
CreateImagePins( aCommit, aPin );
symbol->AddDrawItem( aPin );
aPin->ClearFlags( IS_NEW );
@ -325,7 +323,7 @@ SCH_PIN* SYMBOL_EDITOR_PIN_TOOL::CreatePin( const VECTOR2I& aPosition, LIB_SYMBO
}
void SYMBOL_EDITOR_PIN_TOOL::CreateImagePins( SCH_PIN* aPin )
void SYMBOL_EDITOR_PIN_TOOL::CreateImagePins( SCH_COMMIT* aCommit, SCH_PIN* aPin )
{
int ii;
SCH_PIN* newPin;
@ -348,7 +346,9 @@ void SYMBOL_EDITOR_PIN_TOOL::CreateImagePins( SCH_PIN* aPin )
if( ii == aPin->GetUnit() )
continue;
newPin = static_cast<SCH_PIN*>( aPin->Duplicate() );
// Already called Modify() on parent symbol; no need for Modify() calls on individual items
SCH_COMMIT dummy( m_toolMgr );
newPin = static_cast<SCH_PIN*>( aPin->Duplicate( true, &dummy ) );
// To avoid mistakes, gives this pin a new pin number because
// it does no have the save pin number as the master pin
@ -366,8 +366,7 @@ void SYMBOL_EDITOR_PIN_TOOL::CreateImagePins( SCH_PIN* aPin )
}
catch( const boost::bad_pointer& e )
{
wxFAIL_MSG( wxString::Format( wxT( "Boost pointer exception occurred: %s" ),
e.what() ));
wxFAIL_MSG( wxString::Format( wxT( "Boost pointer exception occurred: %s" ), e.what() ));
delete newPin;
return;
}
@ -423,7 +422,7 @@ SCH_PIN* SYMBOL_EDITOR_PIN_TOOL::RepeatPin( const SCH_PIN* aSourcePin )
commit.Modify( symbol );
SCH_PIN* pin = static_cast<SCH_PIN*>( aSourcePin->Duplicate() );
SCH_PIN* pin = static_cast<SCH_PIN*>( aSourcePin->Duplicate( true, &commit ) );
VECTOR2I step;
pin->ClearFlags();
@ -454,7 +453,7 @@ SCH_PIN* SYMBOL_EDITOR_PIN_TOOL::RepeatPin( const SCH_PIN* aSourcePin )
if( m_frame->SynchronizePins() )
pin->SetFlags( IS_LINKED );
if( PlacePin( pin ) )
if( PlacePin( &commit, pin ) )
{
commit.Push( _( "Repeat Pin" ) );
return pin;

View File

@ -44,8 +44,8 @@ public:
SCH_PIN* CreatePin( const VECTOR2I& aPosition, LIB_SYMBOL* aSymbol );
SCH_PIN* RepeatPin( const SCH_PIN* aSourcePin );
bool PlacePin( SCH_PIN* aPin );
void CreateImagePins( SCH_PIN* aPin );
bool PlacePin( SCH_COMMIT* aCommit, SCH_PIN* aPin );
void CreateImagePins( SCH_COMMIT* aCommit, SCH_PIN* aPin );
bool EditPinProperties( SCH_PIN* aPin, bool aFocusPinNumber );
int PushPinProperties( const TOOL_EVENT& aEvent );

View File

@ -37,6 +37,7 @@
class BOARD;
class BOARD_DESIGN_SETTINGS;
class BOARD_ITEM_CONTAINER;
class BOARD_COMMIT;
class SHAPE_POLY_SET;
class SHAPE_SEGMENT;
class PCB_BASE_FRAME;
@ -87,6 +88,8 @@ public:
virtual void CopyFrom( const BOARD_ITEM* aOther );
bool IsGroupableType() const;
// Do not create a copy constructor & operator=.
// The ones generated by the compiler are adequate.
int GetX() const
@ -284,8 +287,11 @@ public:
/**
* Create a copy of this #BOARD_ITEM.
*
* @param addToParentGroup Indicates whether or not the new item is added to the group
* containing the old item. If true, aCommit must be provided.
*/
virtual BOARD_ITEM* Duplicate() const;
virtual BOARD_ITEM* Duplicate( bool addToParentGroup, BOARD_COMMIT* aCommit = nullptr ) const;
/**
* Swap data between \a aItem and \a aImage.

View File

@ -33,8 +33,9 @@
#include <wx/string.h>
#include <undo_redo_container.h>
#include <kiid.h>
#include <eda_item.h>
class EDA_ITEM;
class EDA_GROUP;
class BASE_SCREEN;
///< Types of changes
@ -42,9 +43,7 @@ enum CHANGE_TYPE {
CHT_ADD = 1,
CHT_REMOVE = 2,
CHT_MODIFY = 4,
CHT_GROUP = 8,
CHT_UNGROUP = 16,
CHT_TYPE = CHT_ADD | CHT_REMOVE | CHT_MODIFY | CHT_GROUP | CHT_UNGROUP,
CHT_TYPE = CHT_ADD | CHT_REMOVE | CHT_MODIFY,
CHT_DONE = 32, ///< Flag to indicate the change is already applied
CHT_FLAGS = CHT_DONE
@ -105,9 +104,10 @@ public:
*
* @note Must be called before modification is performed.
*/
COMMIT& Modify( EDA_ITEM* aItem, BASE_SCREEN *aScreen = nullptr )
COMMIT& Modify( EDA_ITEM* aItem, BASE_SCREEN *aScreen = nullptr,
RECURSE_MODE aRecurse = RECURSE_MODE::NO_RECURSE )
{
return Stage( aItem, CHT_MODIFY, aScreen );
return Stage( aItem, CHT_MODIFY, aScreen, aRecurse );
}
/**
@ -131,8 +131,8 @@ public:
}
/// Add a change of the item aItem of type aChangeType to the change list.
virtual COMMIT& Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType,
BASE_SCREEN *aScreen = nullptr );
virtual COMMIT& Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen = nullptr,
RECURSE_MODE aRecurse = RECURSE_MODE::NO_RECURSE );
virtual COMMIT& Stage( std::vector<EDA_ITEM*>& container, CHANGE_TYPE aChangeType,
BASE_SCREEN *aScreen = nullptr );

View File

@ -431,7 +431,10 @@ public:
/**
* Fetch an item by KIID. Frame-type-specific implementation.
*/
virtual EDA_ITEM* GetItem( const KIID& aId ) const { return nullptr; }
virtual EDA_ITEM* ResolveItem( const KIID& aId, bool aAllowNullptrReturn = false ) const
{
return nullptr;
}
/**
* Use to start up the GAL drawing canvas.

View File

@ -51,6 +51,7 @@ enum RECURSE_MODE
NO_RECURSE,
};
#define IGNORE_PARENT_GROUP false
/**
* Additional flag values wxFindReplaceData::m_Flags
@ -95,7 +96,7 @@ typedef const INSPECTOR_FUNC& INSPECTOR;
class EDA_ITEM : public KIGFX::VIEW_ITEM, public SERIALIZABLE
{
public:
virtual ~EDA_ITEM() { wxASSERT( m_group == nullptr ); };
virtual ~EDA_ITEM() = default;
/**
* Returns the type of object.
@ -113,6 +114,8 @@ public:
virtual void SetParentGroup( EDA_GROUP* aGroup ) { m_group = aGroup; }
virtual EDA_GROUP* GetParentGroup() const { return m_group; }
KIID GetParentGroupId() const;
virtual bool IsLocked() const { return false; }
virtual void SetLocked( bool aLocked ) {}
@ -498,6 +501,8 @@ protected:
*/
bool Matches( const wxString& aText, const EDA_SEARCH_DATA& aSearchData ) const;
EDA_ITEM* findParent( KICAD_T aType ) const;
public:
const KIID m_Uuid;
@ -511,8 +516,8 @@ private:
protected:
EDA_ITEM_FLAGS m_flags;
EDA_ITEM* m_parent; ///< Linked list: Link (parent struct).
EDA_GROUP* m_group; ///< The group this item belongs to
EDA_ITEM* m_parent; ///< Owner.
EDA_GROUP* m_group; ///< The group this item belongs to, if any. No ownership implied.
bool m_forceVisible;
bool m_isRollover;
};

View File

@ -213,7 +213,7 @@ public:
*/
virtual BOARD_ITEM_CONTAINER* GetModel() const = 0;
EDA_ITEM* GetItem( const KIID& aId ) const override;
EDA_ITEM* ResolveItem( const KIID& aId, bool aAllowNullptrReturn = false ) const override;
void FocusOnItem( EDA_ITEM* aItem ) override;
void FocusOnItem( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer = UNDEFINED_LAYER );

View File

@ -68,9 +68,6 @@ enum class UNDO_REDO {
DRILLORIGIN, // origin changed (like CHANGED, contains the origin and a copy)
GRIDORIGIN, // origin changed (like CHANGED, contains the origin and a copy)
PAGESETTINGS, // page settings or title block changes
REGROUP, // new group of items created (NB: can't use GROUP because of collision
// with a header on msys2)
UNGROUP, // existing group destroyed (items not destroyed)
REPEAT_ITEM // storage entry for the editor's global repeatItems list
};
@ -78,32 +75,24 @@ enum class UNDO_REDO {
class ITEM_PICKER
{
public:
// ITEM_PICKER( EDA_ITEM* aItem = NULL, UNDO_REDO aStatus = UNSPECIFIED );
ITEM_PICKER();
ITEM_PICKER( BASE_SCREEN* aScreen, EDA_ITEM* aItem,
UNDO_REDO aStatus = UNDO_REDO::UNSPECIFIED );
EDA_ITEM* GetItem() const { return m_pickedItem; }
void SetItem( EDA_ITEM* aItem );
KICAD_T GetItemType() const { return m_pickedItemType; }
void SetStatus( UNDO_REDO aStatus ) { m_undoRedoStatus = aStatus; }
UNDO_REDO GetStatus() const { return m_undoRedoStatus; }
void SetFlags( EDA_ITEM_FLAGS aFlags ) { m_pickerFlags = aFlags; }
EDA_ITEM_FLAGS GetFlags() const { return m_pickerFlags; }
void SetLink( EDA_ITEM* aItem ) { m_link = aItem; }
void SetLink( EDA_ITEM* aItem );
EDA_ITEM* GetLink() const { return m_link; }
KIID GetGroupId() const { return m_groupId; }
void SetGroupId( KIID aId ) { m_groupId = aId; }
KIID_VECT_LIST GetGroupMembers() const { return m_groupMembers; }
BASE_SCREEN* GetScreen() const { return m_screen; }
@ -122,8 +111,8 @@ private:
* duplicate) m_Item points the duplicate (i.e the old
* copy of an active item) and m_Link points the active
* item in schematic */
KIID m_groupId; /* Id of the group of items in case this is a
* group/ungroup command */
KIID m_groupId; /* Id of the parent group */
KIID_VECT_LIST m_groupMembers; /* Ids of the members of a group */
BASE_SCREEN* m_screen; /* For new and deleted items the screen the item should
* be added to/removed from. */
@ -159,14 +148,6 @@ public:
*/
bool ContainsItem( const EDA_ITEM* aItem ) const;
/**
* Check the undo/redo list for any #EDA_ITEM of type \a aItemType.
*
* @param aItemType is an #EDA_ITEM type from the list of #KICAD_T types.
* @return true if an item of \a aItemType is found in the pick list.
*/
bool ContainsItemType( KICAD_T aItemType ) const;
/**
* @return Index of the searched item. If the item is not stored in the list, negative value
* is returned.
@ -206,7 +187,8 @@ public:
* @param aIdx Index of the picker in the picked list if this picker does not exist,
* a picker is returned, with its members set to 0 or NULL.
*/
ITEM_PICKER GetItemWrapper( unsigned int aIdx ) const;
const ITEM_PICKER& GetItemWrapper( unsigned int aIdx ) const;
ITEM_PICKER& GetItemWrapper( unsigned int aIdx );
/**
* @return A pointer to the picked item.
@ -233,12 +215,6 @@ public:
*/
UNDO_REDO GetPickedItemStatus( unsigned int aIdx ) const;
/**
* @return The group id of the picked item, or null KIID if does not exist.
* @param aIdx Index of the picked item in the picked list.
*/
KIID GetPickedItemGroupId( unsigned int aIdx ) const;
/**
* Return the value of the picker flag.
*
@ -254,14 +230,6 @@ public:
*/
bool SetPickedItem( EDA_ITEM* aItem, unsigned aIdx );
/**
* @param aItem A pointer to the item to pick.
* @param aStatus The type of undo/redo operation associated to the item to pick.
* @param aIdx Index of the picker in the picked list.
* @return True if the picker exists or false if does not exist.
*/
bool SetPickedItem( EDA_ITEM* aItem, UNDO_REDO aStatus, unsigned aIdx );
/**
* Set the link associated to a given picked item.
*
@ -271,15 +239,6 @@ public:
*/
bool SetPickedItemLink( EDA_ITEM* aLink, unsigned aIdx );
/**
* Set the group id associated to a given picked item.
*
* @param aId is the group id to associate to the picked item.
* @param aIdx is index of the picker in the picked list.
* @return true if the picker exists, or false if does not exist.
*/
bool SetPickedItemGroupId( KIID aId, unsigned aIdx );
/**
* Set the type of undo/redo operation for a given picked item.
*

View File

@ -270,9 +270,9 @@ std::unique_ptr<COMMIT> API_HANDLER_PCB::createCommit()
std::optional<BOARD_ITEM*> API_HANDLER_PCB::getItemById( const KIID& aId ) const
{
BOARD_ITEM* item = frame()->GetBoard()->GetItem( aId );
BOARD_ITEM* item = frame()->GetBoard()->ResolveItem( aId, true );
if( item == DELETED_BOARD_ITEM::GetInstance() )
if( !item )
return std::nullopt;
return item;
@ -649,7 +649,7 @@ void API_HANDLER_PCB::deleteItemsInternal( std::map<KIID, ItemDeletionStatus>& a
for( std::pair<const KIID, ItemDeletionStatus> pair : aItemsToDelete )
{
if( BOARD_ITEM* item = board->GetItem( pair.first ) )
if( BOARD_ITEM* item = board->ResolveItem( pair.first, true ) )
{
validatedItems.push_back( item );
aItemsToDelete[pair.first] = ItemDeletionStatus::IDS_OK;

View File

@ -147,19 +147,6 @@ BOARD::BOARD() :
BOARD::~BOARD()
{
// Untangle group parents before doing any deleting
for( PCB_GROUP* group : m_groups )
{
for( EDA_ITEM* item : group->GetItems() )
item->SetParentGroup( nullptr );
}
for( PCB_GENERATOR* generator : m_generators )
{
for( EDA_ITEM* item : generator->GetItems() )
item->SetParentGroup( nullptr );
}
m_itemByIdCache.clear();
// Clean up the owned elements
@ -390,7 +377,7 @@ std::vector<PCB_MARKER*> BOARD::ResolveDRCExclusions( bool aCreateMarkers )
// Check to see if items still exist
for( const KIID& guid : marker->GetRCItem()->GetIDs() )
{
if( GetItem( guid ) == DELETED_BOARD_ITEM::GetInstance() )
if( !ResolveItem( guid, true ) )
{
delete marker;
marker = nullptr;
@ -444,9 +431,9 @@ bool BOARD::ResolveTextVar( wxString* token, int aDepth ) const
{
if( token->Contains( ':' ) )
{
wxString remainder;
wxString ref = token->BeforeFirst( ':', &remainder );
BOARD_ITEM* refItem = GetItem( KIID( ref ) );
wxString remainder;
wxString ref = token->BeforeFirst( ':', &remainder );
BOARD_ITEM* refItem = ResolveItem( KIID( ref ), true );
if( refItem && refItem->Type() == PCB_FOOTPRINT_T )
{
@ -1322,11 +1309,6 @@ void BOARD::Remove( BOARD_ITEM* aBoardItem, REMOVE_MODE aRemoveMode )
aBoardItem->SetFlags( STRUCT_DELETED );
EDA_GROUP* parentGroup = aBoardItem->GetParentGroup();
if( parentGroup && !( parentGroup->AsEdaItem()->GetFlags() & STRUCT_DELETED ) )
parentGroup->RemoveItem( aBoardItem );
m_connectivity->Remove( aBoardItem );
if( aRemoveMode != REMOVE_MODE::BULK )
@ -1509,7 +1491,7 @@ void BOARD::DetachAllFootprints()
}
BOARD_ITEM* BOARD::GetItem( const KIID& aID ) const
BOARD_ITEM* BOARD::ResolveItem( const KIID& aID, bool aAllowNullptrReturn ) const
{
if( aID == niluuid )
return nullptr;
@ -1517,6 +1499,22 @@ BOARD_ITEM* BOARD::GetItem( const KIID& aID ) const
if( m_itemByIdCache.count( aID ) )
return m_itemByIdCache.at( aID );
// Main clients include highlighting, group undo/redo and DRC items. Since
// everything but group undo/redo will be spread over all object types, we
// might as well prioritize group undo/redo and search them first.
for( PCB_GROUP* group : m_groups )
{
if( group->m_Uuid == aID )
return group;
}
for( PCB_GENERATOR* generator : m_generators )
{
if( generator->m_Uuid == aID )
return generator;
}
for( PCB_TRACK* track : Tracks() )
{
if( track->m_Uuid == aID )
@ -1586,19 +1584,6 @@ BOARD_ITEM* BOARD::GetItem( const KIID& aID ) const
return marker;
}
for( PCB_GROUP* group : m_groups )
{
if( group->m_Uuid == aID )
return group;
}
for( PCB_GENERATOR* generator : m_generators )
{
if( generator->m_Uuid == aID )
return generator;
}
for( NETINFO_ITEM* netInfo : m_NetInfo )
{
if( netInfo->m_Uuid == aID )
@ -1609,7 +1594,10 @@ BOARD_ITEM* BOARD::GetItem( const KIID& aID ) const
return const_cast<BOARD*>( this );
// Not found; weak reference has been deleted.
return DELETED_BOARD_ITEM::GetInstance();
if( aAllowNullptrReturn )
return nullptr;
else
return DELETED_BOARD_ITEM::GetInstance();
}
@ -1733,7 +1721,7 @@ wxString BOARD::ConvertKIIDsToCrossReferences( const wxString& aSource ) const
{
wxString remainder;
wxString ref = token.BeforeFirst( ':', &remainder );
BOARD_ITEM* refItem = GetItem( KIID( ref ) );
BOARD_ITEM* refItem = ResolveItem( KIID( ref ), true );
if( refItem && refItem->Type() == PCB_FOOTPRINT_T )
{

View File

@ -476,9 +476,10 @@ public:
void DetachAllFootprints();
/**
* @return null if aID is null. Returns an object of Type() == NOT_USED if the aID is not found.
* @return null if aID is null. If \a aID cannot be resolved, returns either an object of
* Type() == NOT_USED or null, depending on \a aAllowNullptrReturn.
*/
BOARD_ITEM* GetItem( const KIID& aID ) const;
BOARD_ITEM* ResolveItem( const KIID& aID, bool aAllowNullptrReturn = false ) const;
void FillItemMap( std::map<KIID, EDA_ITEM*>& aMap );

View File

@ -94,20 +94,15 @@ BOARD* BOARD_COMMIT::GetBoard() const
}
COMMIT& BOARD_COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType, BASE_SCREEN* aScreen )
COMMIT& BOARD_COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType, BASE_SCREEN* aScreen,
RECURSE_MODE aRecurse )
{
// Many operations (move, rotate, etc.) are applied directly to a group's children, so they
// must be staged as well.
if( aChangeType == CHT_MODIFY )
if( aRecurse == RECURSE_MODE::RECURSE )
{
if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( aItem ) )
{
group->RunOnChildren(
[&]( BOARD_ITEM* child )
{
Stage( child, aChangeType );
},
RECURSE_MODE::NO_RECURSE );
for( EDA_ITEM* member : group->GetItems() )
Stage( member, aChangeType, aScreen, aRecurse );
}
}
@ -175,6 +170,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
PCB_BASE_FRAME* frame = dynamic_cast<PCB_BASE_FRAME*>( m_toolMgr->GetToolHolder() );
PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
PCB_GROUP* enteredGroup = selTool ? selTool->GetEnteredGroup() : nullptr;
// Notification info
PICKED_ITEMS_LIST undoList;
@ -186,7 +182,6 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
bool autofillZones = false;
std::vector<BOARD_ITEM*> staleTeardropPadsAndVias;
std::set<PCB_TRACK*> staleTeardropTracks;
PCB_GROUP* addedGroup = nullptr;
std::vector<ZONE*> staleZonesStorage;
std::vector<ZONE*>* staleZones = nullptr;
@ -269,14 +264,21 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
if( !staleTeardropPadsAndVias.empty() || !staleTeardropTracks.empty() )
teardropMgr.RemoveTeardrops( *this, &staleTeardropPadsAndVias, &staleTeardropTracks );
auto updateComponentClasses = [this]( BOARD_ITEM* boardItem )
{
if( boardItem->Type() != PCB_FOOTPRINT_T )
return;
auto updateComponentClasses =
[this]( BOARD_ITEM* boardItem )
{
if( boardItem->Type() != PCB_FOOTPRINT_T )
return;
FOOTPRINT* footprint = static_cast<FOOTPRINT*>( boardItem );
GetBoard()->GetComponentClassManager().RebuildRequiredCaches( footprint );
};
// We don't know that anything will be added to the entered group, but it does no harm to
// add it to the commit anyway.
if( enteredGroup )
Modify( enteredGroup );
FOOTPRINT* footprint = static_cast<FOOTPRINT*>( boardItem );
GetBoard()->GetComponentClassManager().RebuildRequiredCaches( footprint );
};
for( COMMIT_LINE& ent : m_changes )
{
@ -290,11 +292,8 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
switch( changeType )
{
case CHT_ADD:
if( selTool && selTool->GetEnteredGroup() && !boardItem->GetParentGroup()
&& PCB_GROUP::IsGroupableType( boardItem->Type() ) )
{
selTool->GetEnteredGroup()->AddItem( boardItem );
}
if( enteredGroup && boardItem->IsGroupableType() && !boardItem->GetParentGroup() )
enteredGroup->AddItem( boardItem );
if( !( aCommitFlags & SKIP_UNDO ) )
undoList.PushItem( ITEM_PICKER( nullptr, boardItem, UNDO_REDO::NEWITEM ) );
@ -319,12 +318,6 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
}
}
if( boardItem->Type() == PCB_GROUP_T || boardItem->Type() == PCB_GENERATOR_T )
{
wxASSERT_MSG( !addedGroup, "Multiple groups in a single commit. This is not supported." );
addedGroup = static_cast<PCB_GROUP*>( boardItem );
}
if( boardItem->Type() != PCB_MARKER_T )
propagateDamage( boardItem, staleZones );
@ -341,7 +334,12 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
EDA_GROUP* parentGroup = boardItem->GetParentGroup();
if( !( aCommitFlags & SKIP_UNDO ) )
undoList.PushItem( ITEM_PICKER( nullptr, boardItem, UNDO_REDO::DELETED ) );
{
ITEM_PICKER itemWrapper( nullptr, boardItem, UNDO_REDO::DELETED );
itemWrapper.SetLink( ent.m_copy );
ent.m_copy = nullptr; // We've transferred ownership to the undo list
undoList.PushItem( itemWrapper );
}
if( boardItem->IsSelected() )
{
@ -417,65 +415,27 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
// The item has been removed from the board; it is now owned by undo/redo.
boardItem->SetFlags( UR_TRANSIENT );
break;
}
case CHT_UNGROUP:
if( EDA_GROUP* group = boardItem->GetParentGroup() )
{
if( !( aCommitFlags & SKIP_UNDO ) )
{
ITEM_PICKER itemWrapper( nullptr, boardItem, UNDO_REDO::UNGROUP );
itemWrapper.SetGroupId( group->AsEdaItem()->m_Uuid );
undoList.PushItem( itemWrapper );
}
group->RemoveItem( boardItem );
}
break;
case CHT_GROUP:
if( addedGroup )
{
addedGroup->AddItem( boardItem );
if( !( aCommitFlags & SKIP_UNDO ) )
undoList.PushItem( ITEM_PICKER( nullptr, boardItem, UNDO_REDO::REGROUP ) );
}
break;
case CHT_MODIFY:
{
BOARD_ITEM* boardItemCopy = nullptr;
if( ent.m_copy && ent.m_copy->IsBOARD_ITEM() )
boardItemCopy = static_cast<BOARD_ITEM*>( ent.m_copy );
BOARD_ITEM* boardItemCopy = dynamic_cast<BOARD_ITEM*>( ent.m_copy );
if( !( aCommitFlags & SKIP_UNDO ) )
{
ITEM_PICKER itemWrapper( nullptr, boardItem, UNDO_REDO::CHANGED );
wxASSERT( boardItemCopy );
itemWrapper.SetLink( boardItemCopy );
itemWrapper.SetLink( ent.m_copy );
ent.m_copy = nullptr; // We've transferred ownership to the undo list
undoList.PushItem( itemWrapper );
}
if( !( aCommitFlags & SKIP_CONNECTIVITY ) )
{
if( boardItemCopy )
connectivity->MarkItemNetAsDirty( boardItemCopy );
connectivity->MarkItemNetAsDirty( boardItemCopy );
connectivity->Update( boardItem );
}
if( boardItem->Type() == PCB_GROUP_T || boardItem->Type() == PCB_GENERATOR_T )
{
wxASSERT_MSG( !addedGroup, "Multiple groups in a single commit. This is not supported." );
addedGroup = static_cast<PCB_GROUP*>( boardItem );
}
if( boardItem->Type() != PCB_MARKER_T )
{
propagateDamage( boardItemCopy, staleZones ); // before
@ -488,11 +448,6 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
view->Update( boardItem );
itemsChanged.push_back( boardItem );
// if no undo entry is needed, the copy would create a memory leak
if( aCommitFlags & SKIP_UNDO )
delete ent.m_copy;
break;
}
@ -501,6 +456,10 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
break;
}
// Delete any copies we still have ownership of
delete ent.m_copy;
ent.m_copy = nullptr;
boardItem->ClearEditFlags();
boardItem->RunOnChildren(
[&]( BOARD_ITEM* item )
@ -656,10 +615,6 @@ EDA_ITEM* BOARD_COMMIT::makeImage( EDA_ITEM* aItem ) const
EDA_ITEM* BOARD_COMMIT::MakeImage( EDA_ITEM* aItem )
{
EDA_ITEM* clone = aItem->Clone();
if( clone->IsBOARD_ITEM() )
static_cast<BOARD_ITEM*>( clone )->SetParentGroup( nullptr );
clone->SetFlags( UR_TRANSIENT );
return clone;
@ -700,14 +655,6 @@ void BOARD_COMMIT::Revert()
switch( changeType )
{
case CHT_ADD:
// Items are auto-added to the parent group by BOARD_ITEM::Duplicate(), not when
// the commit is pushed.
if( EDA_GROUP* parentGroup = boardItem->GetParentGroup() )
{
if( GetStatus( parentGroup->AsEdaItem() ) == 0 )
parentGroup->RemoveItem( boardItem );
}
if( !( changeFlags & CHT_DONE ) )
break;
@ -732,18 +679,17 @@ void BOARD_COMMIT::Revert()
view->Add( boardItem );
// Note: parent can be nullptr, because entry.m_parent is only set for children
// of footprints.
BOARD_ITEM* parent = board->GetItem( entry.m_parent );
if( parent && parent->Type() == PCB_FOOTPRINT_T )
if( BOARD_ITEM* parent = board->ResolveItem( entry.m_parent, true ) )
{
static_cast<FOOTPRINT*>( parent )->Add( boardItem, ADD_MODE::INSERT );
}
else
{
board->Add( boardItem, ADD_MODE::INSERT );
bulkAddedItems.push_back( boardItem );
if( parent->Type() == PCB_FOOTPRINT_T )
{
static_cast<FOOTPRINT*>( parent )->Add( boardItem, ADD_MODE::INSERT );
}
else
{
board->Add( boardItem, ADD_MODE::INSERT );
bulkAddedItems.push_back( boardItem );
}
}
updateComponentClasses( boardItem );

View File

@ -61,7 +61,8 @@ public:
virtual void Revert() override;
COMMIT& Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType,
BASE_SCREEN* aScreen = nullptr ) override;
BASE_SCREEN* aScreen = nullptr,
RECURSE_MODE aRecurse = RECURSE_MODE::NO_RECURSE ) override;
COMMIT& Stage( std::vector<EDA_ITEM*>& container, CHANGE_TYPE aChangeType,
BASE_SCREEN* aScreen = nullptr ) override;
COMMIT& Stage( const PICKED_ITEMS_LIST& aItems,

View File

@ -38,6 +38,37 @@
#include <font/font.h>
bool BOARD_ITEM::IsGroupableType() const
{
switch ( Type() )
{
case PCB_FOOTPRINT_T:
case PCB_PAD_T:
case PCB_SHAPE_T:
case PCB_REFERENCE_IMAGE_T:
case PCB_FIELD_T:
case PCB_TEXT_T:
case PCB_TEXTBOX_T:
case PCB_TABLE_T:
case PCB_GROUP_T:
case PCB_GENERATOR_T:
case PCB_TRACE_T:
case PCB_VIA_T:
case PCB_ARC_T:
case PCB_DIMENSION_T:
case PCB_DIM_ALIGNED_T:
case PCB_DIM_LEADER_T:
case PCB_DIM_CENTER_T:
case PCB_DIM_RADIAL_T:
case PCB_DIM_ORTHOGONAL_T:
case PCB_ZONE_T:
return true;
default:
return false;
}
}
void BOARD_ITEM::CopyFrom( const BOARD_ITEM* aOther )
{
wxCHECK( aOther, /* void */ );
@ -50,12 +81,7 @@ const BOARD* BOARD_ITEM::GetBoard() const
if( Type() == PCB_T )
return static_cast<const BOARD*>( this );
BOARD_ITEM* parent = GetParent();
if( parent )
return parent->GetBoard();
return nullptr;
return static_cast<const BOARD*>( findParent( PCB_T ) );
}
@ -64,23 +90,28 @@ BOARD* BOARD_ITEM::GetBoard()
if( Type() == PCB_T )
return static_cast<BOARD*>( this );
BOARD_ITEM* parent = GetParent();
return static_cast<BOARD*>( findParent( PCB_T ) );
}
if( parent )
return parent->GetBoard();
return nullptr;
FOOTPRINT* BOARD_ITEM::GetParentFootprint() const
{
return static_cast<FOOTPRINT*>( findParent( PCB_FOOTPRINT_T ) );
}
bool BOARD_ITEM::IsLocked() const
{
if( GetParentGroup() && static_cast<PCB_GROUP*>( GetParentGroup() )->IsLocked() )
return true;
if( EDA_GROUP* group = GetParentGroup() )
{
if( group->AsEdaItem()->IsLocked() )
return true;
}
const BOARD* board = GetBoard();
if( !GetBoard() || GetBoard()->GetBoardUse() == BOARD_USE::FPHOLDER )
return false;
return board && board->GetBoardUse() != BOARD_USE::FPHOLDER && m_isLocked;
return m_isLocked;
}
@ -220,6 +251,7 @@ void BOARD_ITEM::DeleteStructure()
void BOARD_ITEM::swapData( BOARD_ITEM* aImage )
{
UNIMPLEMENTED_FOR( GetClass() );
}
@ -228,26 +260,29 @@ void BOARD_ITEM::SwapItemData( BOARD_ITEM* aImage )
if( aImage == nullptr )
return;
EDA_ITEM* parent = GetParent();
EDA_GROUP* group = GetParentGroup();
EDA_ITEM* parent = GetParent();
SetParentGroup( nullptr );
aImage->SetParentGroup( nullptr );
swapData( aImage );
// Restore pointers to be sure they are not broken
SetParent( parent );
SetParentGroup( group );
}
BOARD_ITEM* BOARD_ITEM::Duplicate() const
BOARD_ITEM* BOARD_ITEM::Duplicate( bool addToParentGroup, BOARD_COMMIT* aCommit ) const
{
BOARD_ITEM* dupe = static_cast<BOARD_ITEM*>( Clone() );
const_cast<KIID&>( dupe->m_Uuid ) = KIID();
if( dupe->GetParentGroup() )
dupe->GetParentGroup()->AddItem( dupe );
if( addToParentGroup )
{
wxCHECK_MSG( aCommit, dupe, "Must supply a commit to update parent group" );
if( dupe->GetParentGroup() )
{
aCommit->Modify( dupe->GetParentGroup()->AsEdaItem() );
dupe->GetParentGroup()->AddItem( dupe );
}
}
return dupe;
}
@ -296,35 +331,6 @@ std::shared_ptr<SHAPE_SEGMENT> BOARD_ITEM::GetEffectiveHoleShape() const
}
FOOTPRINT* BOARD_ITEM::GetParentFootprint() const
{
// EDA_ITEM::IsType is too slow here.
auto isContainer = []( BOARD_ITEM_CONTAINER* aTest )
{
switch( aTest->Type() )
{
case PCB_GROUP_T:
case PCB_GENERATOR_T:
case PCB_TABLE_T:
return true;
default:
return false;
}
};
BOARD_ITEM_CONTAINER* ancestor = GetParent();
while( ancestor && isContainer( ancestor ) )
ancestor = ancestor->GetParent();
if( ancestor && ancestor->Type() == PCB_FOOTPRINT_T )
return static_cast<FOOTPRINT*>( ancestor );
return nullptr;
}
VECTOR2I BOARD_ITEM::GetFPRelativePosition() const
{
VECTOR2I pos = GetPosition();

View File

@ -321,7 +321,6 @@ void collectItemsForSyncParts( ItemContainer& aItems, std::set<wxString>& parts
PCB_GROUP* group = static_cast<PCB_GROUP*>( item );
collectItemsForSyncParts( group->GetItems(), parts );
break;
}
case PCB_FOOTPRINT_T:
@ -330,7 +329,6 @@ void collectItemsForSyncParts( ItemContainer& aItems, std::set<wxString>& parts
wxString ref = footprint->GetReference();
parts.emplace( wxT( "F" ) + EscapeString( ref, CTX_IPC ) );
break;
}
@ -341,7 +339,6 @@ void collectItemsForSyncParts( ItemContainer& aItems, std::set<wxString>& parts
parts.emplace( wxT( "P" ) + EscapeString( ref, CTX_IPC ) + wxT( "/" )
+ EscapeString( pad->GetNumber(), CTX_IPC ) );
break;
}

View File

@ -139,15 +139,18 @@ void DIALOG_CLEANUP_GRAPHICS::doCleanup( bool aDryRun )
void DIALOG_CLEANUP_GRAPHICS::OnSelectItem( wxDataViewEvent& aEvent )
{
const KIID& itemID = RC_TREE_MODEL::ToUUID( aEvent.GetItem() );
BOARD_ITEM* item = m_parentFrame->GetBoard()->GetItem( itemID );
WINDOW_THAWER thawer( m_parentFrame );
const KIID& itemID = RC_TREE_MODEL::ToUUID( aEvent.GetItem() );
if( item && !item->GetLayerSet().test( m_parentFrame->GetActiveLayer() ) )
m_parentFrame->SetActiveLayer( item->GetLayerSet().UIOrder().front() );
if( BOARD_ITEM* item = m_parentFrame->GetBoard()->ResolveItem( itemID, true ) )
{
WINDOW_THAWER thawer( m_parentFrame );
m_parentFrame->FocusOnItem( item );
m_parentFrame->GetCanvas()->Refresh();
if( !item->GetLayerSet().test( m_parentFrame->GetActiveLayer() ) )
m_parentFrame->SetActiveLayer( item->GetLayerSet().UIOrder().front() );
m_parentFrame->FocusOnItem( item );
m_parentFrame->GetCanvas()->Refresh();
}
aEvent.Skip();
}

View File

@ -313,12 +313,15 @@ void DIALOG_CLEANUP_TRACKS_AND_VIAS::doCleanup( bool aDryRun )
void DIALOG_CLEANUP_TRACKS_AND_VIAS::OnSelectItem( wxDataViewEvent& aEvent )
{
const KIID& itemID = RC_TREE_MODEL::ToUUID( aEvent.GetItem() );
BOARD_ITEM* item = m_brd->GetItem( itemID );
WINDOW_THAWER thawer( m_parentFrame );
const KIID& itemID = RC_TREE_MODEL::ToUUID( aEvent.GetItem() );
m_parentFrame->FocusOnItem( item );
m_parentFrame->GetCanvas()->Refresh();
if( BOARD_ITEM* item = m_brd->ResolveItem( itemID, true ) )
{
WINDOW_THAWER thawer( m_parentFrame );
m_parentFrame->FocusOnItem( item );
m_parentFrame->GetCanvas()->Refresh();
}
aEvent.Skip();
}

View File

@ -462,22 +462,21 @@ void DIALOG_DRC::OnDRCItemSelected( wxDataViewEvent& aEvent )
}
const KIID& itemID = RC_TREE_MODEL::ToUUID( aEvent.GetItem() );
BOARD_ITEM* item = board->GetItem( itemID );
BOARD_ITEM* item = board->ResolveItem( itemID, true );
if( !item || item == DELETED_BOARD_ITEM::GetInstance() )
if( item )
{
// nothing to highlight / focus on
aEvent.Skip();
return;
}
PCB_LAYER_ID principalLayer;
LSET violationLayers;
BOARD_ITEM* a = board->GetItem( rc_item->GetMainItemID() );
BOARD_ITEM* b = board->GetItem( rc_item->GetAuxItemID() );
BOARD_ITEM* c = board->GetItem( rc_item->GetAuxItem2ID() );
BOARD_ITEM* d = board->GetItem( rc_item->GetAuxItem3ID() );
BOARD_ITEM* a = board->ResolveItem( rc_item->GetMainItemID(), true );
BOARD_ITEM* b = board->ResolveItem( rc_item->GetAuxItemID(), true );
BOARD_ITEM* c = board->ResolveItem( rc_item->GetAuxItem2ID(), true );
BOARD_ITEM* d = board->ResolveItem( rc_item->GetAuxItem3ID(), true );
if( rc_item->GetErrorCode() == DRCE_MALFORMED_COURTYARD )
{
@ -491,7 +490,7 @@ void DIALOG_DRC::OnDRCItemSelected( wxDataViewEvent& aEvent )
principalLayer = F_CrtYd;
}
}
else if (rc_item->GetErrorCode() == DRCE_INVALID_OUTLINE )
else if( rc_item->GetErrorCode() == DRCE_INVALID_OUTLINE )
{
principalLayer = Edge_Cuts;
}
@ -596,7 +595,7 @@ void DIALOG_DRC::OnDRCItemSelected( wxDataViewEvent& aEvent )
for( const KIID& id : rc_item->GetIDs() )
{
auto* candidate = dynamic_cast<BOARD_CONNECTED_ITEM*>( board->GetItem( id ) );
auto* candidate = dynamic_cast<BOARD_CONNECTED_ITEM*>( board->ResolveItem( id, true ) );
if( candidate && candidate->GetNetCode() == net )
items.push_back( candidate );

View File

@ -235,7 +235,7 @@ void DIALOG_FOOTPRINT_CHECKER::OnSelectItem( wxDataViewEvent& aEvent )
BOARD* board = m_frame->GetBoard();
RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aEvent.GetItem() );
const KIID& itemID = node ? RC_TREE_MODEL::ToUUID( aEvent.GetItem() ) : niluuid;
BOARD_ITEM* item = board->GetItem( itemID );
BOARD_ITEM* item = board->ResolveItem( itemID, true );
if( m_centerMarkerOnIdle )
{
@ -257,7 +257,7 @@ void DIALOG_FOOTPRINT_CHECKER::OnSelectItem( wxDataViewEvent& aEvent )
if( rc_item->GetErrorCode() == DRCE_MALFORMED_COURTYARD )
{
BOARD_ITEM* a = board->GetItem( rc_item->GetMainItemID() );
BOARD_ITEM* a = board->ResolveItem( rc_item->GetMainItemID(), true );
if( a && ( a->GetFlags() & MALFORMED_B_COURTYARD ) > 0
&& ( a->GetFlags() & MALFORMED_F_COURTYARD ) == 0 )
@ -275,10 +275,10 @@ void DIALOG_FOOTPRINT_CHECKER::OnSelectItem( wxDataViewEvent& aEvent )
}
else
{
BOARD_ITEM* a = board->GetItem( rc_item->GetMainItemID() );
BOARD_ITEM* b = board->GetItem( rc_item->GetAuxItemID() );
BOARD_ITEM* c = board->GetItem( rc_item->GetAuxItem2ID() );
BOARD_ITEM* d = board->GetItem( rc_item->GetAuxItem3ID() );
BOARD_ITEM* a = board->ResolveItem( rc_item->GetMainItemID(), true );
BOARD_ITEM* b = board->ResolveItem( rc_item->GetAuxItemID(), true );
BOARD_ITEM* c = board->ResolveItem( rc_item->GetAuxItem2ID(), true );
BOARD_ITEM* d = board->ResolveItem( rc_item->GetAuxItem3ID(), true );
if( a || b || c || d )
violationLayers = LSET::AllLayersMask();

View File

@ -410,7 +410,7 @@ void DIALOG_GENERATORS::OnItemSelected( wxDataViewEvent& aEvent )
wxVariant var;
model->GetValue( var, viewItem, uuidCol );
BOARD_ITEM* brdItem = m_currentBoard->GetItem( var.GetString() );
BOARD_ITEM* brdItem = m_currentBoard->ResolveItem( var.GetString() );
if( !brdItem || brdItem->Type() != KICAD_T::PCB_GENERATOR_T )
continue;
@ -447,7 +447,7 @@ void DIALOG_GENERATORS::OnRebuildTypeClick( wxCommandEvent& event )
wxVariant var;
model->GetValueByRow( var, row, uuidCol );
BOARD_ITEM* item = m_currentBoard->GetItem( var.GetString() );
BOARD_ITEM* item = m_currentBoard->ResolveItem( var.GetString() );
if( !item || item->Type() != KICAD_T::PCB_GENERATOR_T )
continue;

View File

@ -134,12 +134,7 @@ void DIALOG_GLOBAL_DELETION::DoGlobalDeletions()
[&]( BOARD_ITEM* item, const LSET& layers_mask )
{
if( ( item->GetLayerSet() & layers_mask ).any() )
{
if( item->GetParentGroup() )
commit.Stage( item, CHT_UNGROUP );
commit.Remove( item );
}
};
auto processConnectedItem =
@ -147,9 +142,6 @@ void DIALOG_GLOBAL_DELETION::DoGlobalDeletions()
{
if( ( item->GetLayerSet() & layers_mask ).any() )
{
if( item->GetParentGroup() )
commit.Stage( item, CHT_UNGROUP );
commit.Remove( item );
gen_rastnest = true;
}

View File

@ -291,10 +291,6 @@ DIALOG_PAD_PROPERTIES::~DIALOG_PAD_PROPERTIES()
m_page = m_notebook->GetSelection();
// Remove the preview pad from the group of the actual pad before deletion
if( m_previewPad )
m_previewPad->SetParentGroup( nullptr );
delete m_previewPad;
delete m_axisOrigin;
}

View File

@ -845,9 +845,6 @@ DIALOG_SHAPE_PROPERTIES::DIALOG_SHAPE_PROPERTIES( PCB_BASE_EDIT_FRAME* aParent,
m_solderMaskMargin( aParent, m_solderMaskMarginLabel, m_solderMaskMarginCtrl, m_solderMaskMarginUnit ),
m_workingCopy( *m_item )
{
m_workingCopy.SetParentGroup( nullptr );
m_workingCopy.SetParent( nullptr );
SetTitle( wxString::Format( GetTitle(), m_item->GetFriendlyName() ) );
m_hash_key = TO_UTF8( GetTitle() );
@ -1135,10 +1132,6 @@ bool DIALOG_SHAPE_PROPERTIES::TransferDataFromWindow()
if( !pushCommit )
m_item->SetFlags( IN_EDIT );
// Ensure parent and parent group item are restored. m_workingCopy had previously
// these parents cleared and not restored for some PCB_SHAPE types (i.e. POLY)
m_workingCopy.SetParentGroup( m_item->GetParentGroup() );
m_workingCopy.SetParent( m_item->GetParent() );
*m_item = m_workingCopy;
bool wasLocked = m_item->IsLocked();

View File

@ -194,22 +194,18 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run()
edges.back()->SetShape( SHAPE_T::SEGMENT );
edges.back()->SetEndX( shape->GetStartX() );
edges.back()->SetStroke( stroke );
edges.back()->SetParentGroup( nullptr );
edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
edges.back()->SetShape( SHAPE_T::SEGMENT );
edges.back()->SetEndY( shape->GetStartY() );
edges.back()->SetStroke( stroke );
edges.back()->SetParentGroup( nullptr );
edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
edges.back()->SetShape( SHAPE_T::SEGMENT );
edges.back()->SetStartX( shape->GetEndX() );
edges.back()->SetStroke( stroke );
edges.back()->SetParentGroup( nullptr );
edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
edges.back()->SetShape( SHAPE_T::SEGMENT );
edges.back()->SetStartY( shape->GetEndY() );
edges.back()->SetStroke( stroke );
edges.back()->SetParentGroup( nullptr );
}
else if( shape->GetShape() == SHAPE_T::POLY && !shape->IsSolidFill() )
{
@ -225,14 +221,12 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run()
edges.back()->SetStart( seg.A );
edges.back()->SetEnd( seg.B );
edges.back()->SetStroke( stroke );
edges.back()->SetParentGroup( nullptr );
}
}
else
{
edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
edges.back()->SetStroke( stroke );
edges.back()->SetParentGroup( nullptr );
}
return true;

View File

@ -155,8 +155,7 @@ void PCB_EDIT_FRAME::OnEditItemRequest( BOARD_ITEM* aItem )
break;
case PCB_GROUP_T:
m_toolManager->RunAction( ACTIONS::groupProperties,
static_cast<EDA_GROUP*>( static_cast<PCB_GROUP*>( aItem ) ) );
m_toolManager->RunAction( ACTIONS::groupProperties, static_cast<PCB_GROUP*>( aItem ) );
break;
case PCB_GENERATOR_T:

View File

@ -233,13 +233,6 @@ FOOTPRINT::FOOTPRINT( FOOTPRINT&& aFootprint ) :
FOOTPRINT::~FOOTPRINT()
{
// Untangle group parents before doing any deleting
for( PCB_GROUP* group : m_groups )
{
for( EDA_ITEM* item : group->GetItems() )
item->SetParentGroup( nullptr );
}
// Clean up the owned elements
delete m_initial_comments;
@ -1213,11 +1206,6 @@ void FOOTPRINT::Remove( BOARD_ITEM* aBoardItem, REMOVE_MODE aMode )
}
aBoardItem->SetFlags( STRUCT_DELETED );
EDA_GROUP* parentGroup = aBoardItem->GetParentGroup();
if( parentGroup && !( parentGroup->AsEdaItem()->GetFlags() & STRUCT_DELETED ) )
parentGroup->RemoveItem( aBoardItem );
}
@ -1313,10 +1301,6 @@ BOX2I FOOTPRINT::GetFpPadsLocalBbox() const
for( PAD* pad : dummy.Pads() )
bbox.Merge( pad->GetBoundingBox() );
// Remove the parent and the group from the dummy footprint before deletion
dummy.SetParent( nullptr );
dummy.SetParentGroup( nullptr );
return bbox;
}
@ -2569,9 +2553,9 @@ void FOOTPRINT::SetOrientation( const EDA_ANGLE& aNewAngle )
}
BOARD_ITEM* FOOTPRINT::Duplicate() const
BOARD_ITEM* FOOTPRINT::Duplicate( bool addToParentGroup, BOARD_COMMIT* aCommit ) const
{
FOOTPRINT* dupe = static_cast<FOOTPRINT*>( BOARD_ITEM::Duplicate() );
FOOTPRINT* dupe = static_cast<FOOTPRINT*>( BOARD_ITEM::Duplicate( addToParentGroup, aCommit ) );
dupe->RunOnChildren( [&]( BOARD_ITEM* child )
{
@ -2583,7 +2567,8 @@ BOARD_ITEM* FOOTPRINT::Duplicate() const
}
BOARD_ITEM* FOOTPRINT::DuplicateItem( const BOARD_ITEM* aItem, bool aAddToFootprint )
BOARD_ITEM* FOOTPRINT::DuplicateItem( bool addToParentGroup, BOARD_COMMIT* aCommit,
const BOARD_ITEM* aItem, bool addToFootprint )
{
BOARD_ITEM* new_item = nullptr;
@ -2594,7 +2579,7 @@ BOARD_ITEM* FOOTPRINT::DuplicateItem( const BOARD_ITEM* aItem, bool aAddToFootpr
PAD* new_pad = new PAD( *static_cast<const PAD*>( aItem ) );
const_cast<KIID&>( new_pad->m_Uuid ) = KIID();
if( aAddToFootprint )
if( addToFootprint )
m_pads.push_back( new_pad );
new_item = new_pad;
@ -2606,7 +2591,7 @@ BOARD_ITEM* FOOTPRINT::DuplicateItem( const BOARD_ITEM* aItem, bool aAddToFootpr
ZONE* new_zone = new ZONE( *static_cast<const ZONE*>( aItem ) );
const_cast<KIID&>( new_zone->m_Uuid ) = KIID();
if( aAddToFootprint )
if( addToFootprint )
m_zones.push_back( new_zone );
new_item = new_zone;
@ -2626,11 +2611,11 @@ BOARD_ITEM* FOOTPRINT::DuplicateItem( const BOARD_ITEM* aItem, bool aAddToFootpr
case FIELD_T::REFERENCE: new_text->SetText( wxT( "${REFERENCE}" ) ); break;
case FIELD_T::VALUE: new_text->SetText( wxT( "${VALUE}" ) ); break;
case FIELD_T::DATASHEET: new_text->SetText( wxT( "${DATASHEET}" ) ); break;
default: break;
default: break;
}
}
if( aAddToFootprint )
if( addToFootprint )
Add( new_text );
new_item = new_text;
@ -2642,7 +2627,7 @@ BOARD_ITEM* FOOTPRINT::DuplicateItem( const BOARD_ITEM* aItem, bool aAddToFootpr
PCB_SHAPE* new_shape = new PCB_SHAPE( *static_cast<const PCB_SHAPE*>( aItem ) );
const_cast<KIID&>( new_shape->m_Uuid ) = KIID();
if( aAddToFootprint )
if( addToFootprint )
Add( new_shape );
new_item = new_shape;
@ -2654,7 +2639,7 @@ BOARD_ITEM* FOOTPRINT::DuplicateItem( const BOARD_ITEM* aItem, bool aAddToFootpr
PCB_TEXTBOX* new_textbox = new PCB_TEXTBOX( *static_cast<const PCB_TEXTBOX*>( aItem ) );
const_cast<KIID&>( new_textbox->m_Uuid ) = KIID();
if( aAddToFootprint )
if( addToFootprint )
Add( new_textbox );
new_item = new_textbox;
@ -2667,9 +2652,10 @@ BOARD_ITEM* FOOTPRINT::DuplicateItem( const BOARD_ITEM* aItem, bool aAddToFootpr
case PCB_DIM_RADIAL_T:
case PCB_DIM_ORTHOGONAL_T:
{
PCB_DIMENSION_BASE* dimension = static_cast<PCB_DIMENSION_BASE*>( aItem->Duplicate() );
PCB_DIMENSION_BASE* dimension = static_cast<PCB_DIMENSION_BASE*>( aItem->Duplicate( addToParentGroup,
aCommit ) );
if( aAddToFootprint )
if( addToFootprint )
Add( dimension );
new_item = dimension;
@ -2678,9 +2664,9 @@ BOARD_ITEM* FOOTPRINT::DuplicateItem( const BOARD_ITEM* aItem, bool aAddToFootpr
case PCB_GROUP_T:
{
PCB_GROUP* group = static_cast<const PCB_GROUP*>( aItem )->DeepDuplicate();
PCB_GROUP* group = static_cast<const PCB_GROUP*>( aItem )->DeepDuplicate( addToParentGroup, aCommit );
if( aAddToFootprint )
if( addToFootprint )
{
group->RunOnChildren(
[&]( BOARD_ITEM* aCurrItem )

View File

@ -846,14 +846,15 @@ public:
KIID GetLink() const { return m_link; }
void SetLink( const KIID& aLink ) { m_link = aLink; }
BOARD_ITEM* Duplicate() const override;
BOARD_ITEM* Duplicate( bool addToParentGroup, BOARD_COMMIT* aCommit = nullptr ) const override;
/**
* Duplicate a given item within the footprint, optionally adding it to the board.
*
* @return the new item, or NULL if the item could not be duplicated.
*/
BOARD_ITEM* DuplicateItem( const BOARD_ITEM* aItem, bool aAddToFootprint = false );
BOARD_ITEM* DuplicateItem( bool addToParentGroup, BOARD_COMMIT* aCommit, const BOARD_ITEM* aItem,
bool addToFootprint = false );
/**
* Add \a a3DModel definition to the end of the 3D model list.

View File

@ -244,8 +244,7 @@ void FOOTPRINT_EDIT_FRAME::OnEditItemRequest( BOARD_ITEM* aItem )
}
case PCB_GROUP_T:
m_toolManager->RunAction( ACTIONS::groupProperties,
static_cast<EDA_GROUP*>( static_cast<PCB_GROUP*>( aItem ) ) );
m_toolManager->RunAction( ACTIONS::groupProperties, static_cast<PCB_GROUP*>( aItem ) );
break;
case PCB_MARKER_T:

View File

@ -636,7 +636,7 @@ void PCB_EDIT_FRAME::ExportFootprintsToLibrary( bool aStoreInNewLib, const wxStr
if( !footprint->GetFPID().GetLibItemName().empty() ) // Handle old boards.
{
FOOTPRINT* fpCopy = static_cast<FOOTPRINT*>( footprint->Duplicate() );
FOOTPRINT* fpCopy = static_cast<FOOTPRINT*>( footprint->Duplicate( IGNORE_PARENT_GROUP ) );
// Reset reference designator and group membership before saving
resetReference( fpCopy );
@ -694,7 +694,7 @@ void PCB_EDIT_FRAME::ExportFootprintsToLibrary( bool aStoreInNewLib, const wxStr
{
if( !footprint->GetFPID().GetLibItemName().empty() ) // Handle old boards.
{
FOOTPRINT* fpCopy = static_cast<FOOTPRINT*>( footprint->Duplicate() );
FOOTPRINT* fpCopy = static_cast<FOOTPRINT*>( footprint->Duplicate( IGNORE_PARENT_GROUP ) );
// Reset reference designator and group membership before saving
resetReference( fpCopy );
@ -743,10 +743,8 @@ bool FOOTPRINT_EDIT_FRAME::SaveFootprint( FOOTPRINT* aFootprint )
m_footprintNameWhenLoaded = footprintName;
return true;
}
else
{
return false;
}
return false;
}
else if( libraryName.IsEmpty() || footprintName.IsEmpty() )
{
@ -756,10 +754,8 @@ bool FOOTPRINT_EDIT_FRAME::SaveFootprint( FOOTPRINT* aFootprint )
SyncLibraryTree( true );
return true;
}
else
{
return false;
}
return false;
}
FP_LIB_TABLE* tbl = PROJECT_PCB::PcbFootprintLibs( &Prj() );

View File

@ -182,7 +182,7 @@ bool FOOTPRINT_PREVIEW_PANEL::DisplayFootprint( const LIB_ID& aFPID )
aFPID.GetLibItemName() );
if( fp )
m_currentFootprint.reset( static_cast<FOOTPRINT*>( fp->Duplicate() ) );
m_currentFootprint.reset( static_cast<FOOTPRINT*>( fp->Duplicate( IGNORE_PARENT_GROUP ) ) );
else
m_currentFootprint.reset();
}

View File

@ -757,7 +757,7 @@ void FOOTPRINT_VIEWER_FRAME::AddFootprintToPCB()
BOARD_COMMIT commit( pcbframe );
// Create the "new" footprint
FOOTPRINT* newFootprint = (FOOTPRINT*) GetBoard()->GetFirstFootprint()->Duplicate();
FOOTPRINT* newFootprint = (FOOTPRINT*) GetBoard()->GetFirstFootprint()->Duplicate( IGNORE_PARENT_GROUP );
newFootprint->SetParent( pcbframe->GetBoard() );
newFootprint->SetLink( niluuid );
newFootprint->SetFlags(IS_NEW ); // whatever

View File

@ -1225,17 +1225,6 @@ void PCB_TUNING_PATTERN::Remove( GENERATOR_TOOL* aTool, BOARD* aBoard, BOARD_COM
PNS::ROUTER* router = aTool->Router();
PNS_KICAD_IFACE* iface = aTool->GetInterface();
// Ungroup first so that undo works
if( !GetItems().empty() )
{
PCB_GENERATOR* group = this;
for( EDA_ITEM* member : group->GetItems() )
aCommit->Stage( member, CHT_UNGROUP );
group->GetItems().clear();
}
aCommit->Remove( this );
aTool->ClearRouterChanges();
@ -1637,7 +1626,6 @@ void PCB_TUNING_PATTERN::EditPush( GENERATOR_TOOL* aTool, BOARD* aBoard, BOARD_C
&& bounds.PointInside( track->GetEnd(), epsilon ) )
{
AddItem( item );
aCommit->Stage( item, CHT_GROUP );
}
}
}

View File

@ -273,9 +273,10 @@ void CLIPBOARD_IO::SaveSelection( const PCB_SELECTION& aSelected, bool isFootpri
// some PCB_TEXT (reference and value) cannot be added to the footprint
std::vector<BOARD_ITEM*> skipped_items;
if( copy->Type() == PCB_GROUP_T || copy->Type() == PCB_GENERATOR_T )
// Will catch at least PCB_GROUP_T and PCB_GENERATOR_T
if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( copy ) )
{
copy->RunOnChildren(
group->RunOnChildren(
[&]( BOARD_ITEM* descendant )
{
// One cannot add an additional mandatory field to a given footprint:

View File

@ -864,9 +864,8 @@ bool BOARD_NETLIST_UPDATER::updateFootprintGroup( FOOTPRINT* aPcbFootprint,
EscapeHTML( existingGroup->GetName() ) );
changed = true;
m_commit.Modify( aPcbFootprint->GetParentGroup()->AsEdaItem() );
aPcbFootprint->GetParentGroup()->RemoveItem( aPcbFootprint );
aPcbFootprint->SetParentGroup( nullptr );
m_commit.Modify( existingGroup );
existingGroup->RemoveItem( aPcbFootprint );
}
m_reporter->Report( msg, RPT_SEVERITY_ACTION );
@ -906,7 +905,6 @@ bool BOARD_NETLIST_UPDATER::updateFootprintGroup( FOOTPRINT* aPcbFootprint,
}
newGroup->AddItem( aPcbFootprint );
aPcbFootprint->SetParentGroup( newGroup );
}
m_reporter->Report( msg, RPT_SEVERITY_ACTION );
@ -1624,9 +1622,6 @@ bool BOARD_NETLIST_UPDATER::UpdateNetlist( NETLIST& aNetlist )
}
else
{
if( footprint->GetParentGroup() )
m_commit.Stage( footprint, CHT_UNGROUP );
m_commit.Remove( footprint );
msg.Printf( _( "Removed unused footprint %s." ),
footprint->GetReference() );

View File

@ -2190,7 +2190,7 @@ std::vector<PCB_SHAPE*> PAD::Recombine( bool aIsDryRun, int maxError )
if( EDA_GROUP* group = fpShape->GetParentGroup(); group )
group->RemoveItem( fpShape );
PCB_SHAPE* primitive = static_cast<PCB_SHAPE*>( fpShape->Duplicate() );
PCB_SHAPE* primitive = static_cast<PCB_SHAPE*>( fpShape->Duplicate( IGNORE_PARENT_GROUP ) );
primitive->SetParent( nullptr );

Some files were not shown because too many files have changed in this diff Show More