Implement groups for modedit.

This uncovered a memory corruption bug in MODULE's move operator,
several bugs in MODULE's move and copy constructors, and a bug in
BOARD's GetItem() call.

It also bumps the file format for saving/restoring groups inside
footprints.
This commit is contained in:
Jeff Young 2020-10-03 12:16:29 +01:00
parent e76736b7bf
commit ba26e056ec
20 changed files with 527 additions and 431 deletions

View File

@ -117,24 +117,6 @@ private:
return -1;
}
/**
* Function getAllGroups()
* Returns all group ids for the item (collected from all layers the item occupies).
*
* @return vector of group ids.
*/
std::vector<int> getAllGroups() const
{
std::vector<int> groups( m_groupsSize );
for( int i = 0; i < m_groupsSize; ++i )
{
groups[i] = m_groups[i].second;
}
return groups;
}
/**
* Function setGroup()
* Sets a group id for the item and the layer combination.

View File

@ -51,7 +51,7 @@ class VIEW;
class PCB_GROUP : public BOARD_ITEM
{
public:
PCB_GROUP( BOARD* aParent );
PCB_GROUP( BOARD_ITEM* aParent );
static inline bool ClassOf( const EDA_ITEM* aItem )
{

View File

@ -791,6 +791,18 @@ BOARD_ITEM* BOARD::GetItem( const KIID& aID ) const
if( drawing->m_Uuid == aID )
return drawing;
}
for( BOARD_ITEM* zone : module->Zones() )
{
if( zone->m_Uuid == aID )
return zone;
}
for( PCB_GROUP* group : module->Groups() )
{
if( group->m_Uuid == aID )
return group;
}
}
for( ZONE_CONTAINER* zone : Zones() )

View File

@ -92,36 +92,48 @@ MODULE::MODULE( const MODULE& aModule ) :
m_Value = new TEXTE_MODULE( *aModule.m_Value );
m_Value->SetParent( this );
// Copy auxiliary data: Pads
for( D_PAD* pad : aModule.Pads() )
Add( static_cast<D_PAD*>( pad->Clone() ) );
std::map<BOARD_ITEM*, BOARD_ITEM*> ptrMap;
// Copy auxiliary data: Zones
for( MODULE_ZONE_CONTAINER* item : aModule.Zones() )
// Copy pads
for( D_PAD* pad : aModule.Pads() )
{
Add( static_cast<MODULE_ZONE_CONTAINER*>( item->Clone() ) );
D_PAD* newPad = static_cast<D_PAD*>( pad->Clone() );
ptrMap[ pad ] = newPad;
Add( newPad );
}
// Copy zones
for( MODULE_ZONE_CONTAINER* zone : aModule.Zones() )
{
MODULE_ZONE_CONTAINER* newZone = static_cast<MODULE_ZONE_CONTAINER*>( zone->Clone() );
ptrMap[ zone ] = newZone;
Add( newZone );
// Ensure the net info is OK and especially uses the net info list
// living in the current board
// Needed when copying a fp from fp editor that has its own board
// Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
item->SetNetCode( -1 );
newZone->SetNetCode( -1 );
}
// Copy auxiliary data: Drawings
// Copy drawings
for( BOARD_ITEM* item : aModule.GraphicalItems() )
{
switch( item->Type() )
{
case PCB_MODULE_TEXT_T:
case PCB_MODULE_EDGE_T:
Add( static_cast<BOARD_ITEM*>( item->Clone() ) );
break;
BOARD_ITEM* newItem = static_cast<BOARD_ITEM*>( item->Clone() );
ptrMap[ item ] = newItem;
Add( newItem );
}
default:
wxLogMessage( wxT( "Class MODULE copy constructor internal error: unknown type" ) );
break;
}
// Copy groups
for( PCB_GROUP* group : aModule.Groups() )
{
PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( group->Clone() );
const_cast<std::unordered_set<BOARD_ITEM*>*>( &newGroup->GetItems() )->clear();
for( BOARD_ITEM* member : group->GetItems() )
newGroup->AddItem( ptrMap[ member ] );
Add( newGroup );
}
// Copy auxiliary data: 3D_Drawings info
@ -160,11 +172,16 @@ MODULE::~MODULE()
m_pads.clear();
for( MODULE_ZONE_CONTAINER* p : m_fp_zones )
delete p;
for( MODULE_ZONE_CONTAINER* zone : m_fp_zones )
delete zone;
m_fp_zones.clear();
for( PCB_GROUP* group : m_fp_groups )
delete group;
m_fp_groups.clear();
for( BOARD_ITEM* d : m_drawings )
delete d;
@ -209,12 +226,14 @@ MODULE& MODULE::operator=( MODULE&& aOther )
for( D_PAD* pad : aOther.Pads() )
Add( pad );
aOther.Pads().clear();
// Move the zones
m_fp_zones.clear();
for( MODULE_ZONE_CONTAINER* item : aOther.Zones() )
{
Add( static_cast<MODULE_ZONE_CONTAINER*>( item ) );
Add( item );
// Ensure the net info is OK and especially uses the net info list
// living in the current board
@ -223,23 +242,23 @@ MODULE& MODULE::operator=( MODULE&& aOther )
item->SetNetCode( -1 );
}
aOther.Zones().clear();
// Move the drawings
m_drawings.clear();
for( BOARD_ITEM* item : aOther.GraphicalItems() )
{
switch( item->Type() )
{
case PCB_MODULE_TEXT_T:
case PCB_MODULE_EDGE_T:
Add( static_cast<BOARD_ITEM*>( item ) );
break;
Add( item );
default:
wxLogMessage( wxT( "MODULE::operator=() internal error: unknown type" ) );
break;
}
}
aOther.GraphicalItems().clear();
// Move the groups
m_fp_groups.clear();
for( PCB_GROUP* group : aOther.Groups() )
Add( group );
aOther.Groups().clear();
// Copy auxiliary data: 3D_Drawings info
m_3D_Drawings.clear();
@ -295,42 +314,56 @@ MODULE& MODULE::operator=( const MODULE& aOther )
*m_Value = *aOther.m_Value;
m_Value->SetParent( this );
// Copy auxiliary data: Pads
std::map<BOARD_ITEM*, BOARD_ITEM*> ptrMap;
// Copy pads
m_pads.clear();
for( D_PAD* pad : aOther.Pads() )
Add( new D_PAD( *pad ) );
{
D_PAD* newPad = new D_PAD( *pad );
ptrMap[ pad ] = newPad;
Add( newPad );
}
// Copy auxiliary data: Zones
// Copy zones
m_fp_zones.clear();
for( MODULE_ZONE_CONTAINER* item : aOther.Zones() )
for( MODULE_ZONE_CONTAINER* zone : aOther.Zones() )
{
Add( static_cast<MODULE_ZONE_CONTAINER*>( item->Clone() ) );
MODULE_ZONE_CONTAINER* newZone = static_cast<MODULE_ZONE_CONTAINER*>( zone->Clone() );
ptrMap[ zone ] = newZone;
Add( newZone );
// Ensure the net info is OK and especially uses the net info list
// living in the current board
// Needed when copying a fp from fp editor that has its own board
// Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
item->SetNetCode( -1 );
newZone->SetNetCode( -1 );
}
// Copy auxiliary data: Drawings
// Copy drawings
m_drawings.clear();
for( BOARD_ITEM* item : aOther.GraphicalItems() )
{
switch( item->Type() )
{
case PCB_MODULE_TEXT_T:
case PCB_MODULE_EDGE_T:
Add( static_cast<BOARD_ITEM*>( item->Clone() ) );
break;
BOARD_ITEM* newItem = static_cast<BOARD_ITEM*>( item->Clone() );
ptrMap[ item ] = newItem;
Add( newItem );
}
default:
wxLogMessage( wxT( "MODULE::operator=() internal error: unknown type" ) );
break;
}
// Copy groups
m_fp_groups.clear();
for( PCB_GROUP* group : aOther.Groups() )
{
PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( group->Clone() );
const_cast<std::unordered_set<BOARD_ITEM*>*>( &newGroup->GetItems() )->clear();
for( BOARD_ITEM* member : group->GetItems() )
newGroup->AddItem( ptrMap[ member ] );
Add( newGroup );
}
// Copy auxiliary data: 3D_Drawings info
@ -424,6 +457,13 @@ void MODULE::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode )
m_fp_zones.insert( m_fp_zones.begin(), static_cast<MODULE_ZONE_CONTAINER*>( aBoardItem ) );
break;
case PCB_GROUP_T:
if( aMode == ADD_MODE::APPEND )
m_fp_groups.push_back( static_cast<PCB_GROUP*>( aBoardItem ) );
else
m_fp_groups.insert( m_fp_groups.begin(), static_cast<PCB_GROUP*>( aBoardItem ) );
break;
default:
{
wxString msg;
@ -487,6 +527,18 @@ void MODULE::Remove( BOARD_ITEM* aBoardItem )
break;
case PCB_GROUP_T:
for( auto it = m_fp_groups.begin(); it != m_fp_groups.end(); ++it )
{
if( *it == static_cast<PCB_GROUP*>( aBoardItem ) )
{
m_fp_groups.erase( it );
break;
}
}
break;
default:
{
wxString msg;
@ -532,6 +584,8 @@ EDA_RECT MODULE::GetFootprintRect() const
for( MODULE_ZONE_CONTAINER* zone : m_fp_zones )
area.Merge( zone->GetBoundingBox() );
// Groups do not contribute to the rect, only their members
return area;
}
@ -770,6 +824,8 @@ bool MODULE::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) co
return true;
}
// Groups are not hit-tested; only their members
// No items were hit
return false;
}
@ -955,6 +1011,11 @@ SEARCH_RESULT MODULE::Visit( INSPECTOR inspector, void* testData, const KICAD_T
break;
case PCB_GROUP_T:
result = IterateForward<PCB_GROUP*>( m_fp_groups, inspector, testData, p );
++p;
break;
default:
done = true;
break;
@ -1001,6 +1062,9 @@ void MODULE::RunOnChildren( const std::function<void (BOARD_ITEM*)>& aFunction )
for( MODULE_ZONE_CONTAINER* zone : m_fp_zones )
aFunction( static_cast<MODULE_ZONE_CONTAINER*>( zone ) );
for( PCB_GROUP* group : m_fp_groups )
aFunction( static_cast<PCB_GROUP*>( group ) );
for( BOARD_ITEM* drawing : m_drawings )
aFunction( static_cast<BOARD_ITEM*>( drawing ) );
@ -1310,14 +1374,14 @@ void MODULE::MoveAnchorPosition( const wxPoint& aMoveVector )
m_Value->SetDrawCoord();
// Update the pad local coordinates.
for( auto pad : m_pads )
for( D_PAD* pad : m_pads )
{
pad->SetPos0( pad->GetPos0() + moveVector );
pad->SetDrawCoord();
}
// Update the draw element coordinates.
for( auto item : GraphicalItems() )
for( BOARD_ITEM* item : GraphicalItems() )
{
switch( item->Type() )
{
@ -1353,13 +1417,13 @@ void MODULE::SetOrientation( double aNewAngle )
m_Orient = aNewAngle;
for( auto pad : m_pads )
for( D_PAD* pad : m_pads )
{
pad->SetOrientation( pad->GetOrientation() + angleChange );
pad->SetDrawCoord();
}
for( auto zone : m_fp_zones )
for( ZONE_CONTAINER* zone : m_fp_zones )
{
zone->Rotate( GetPosition(), angleChange );
}
@ -1369,7 +1433,7 @@ void MODULE::SetOrientation( double aNewAngle )
m_Value->SetDrawCoord();
// Displace contours and text of the footprint.
for( auto item : m_drawings )
for( BOARD_ITEM* item : m_drawings )
{
if( item->Type() == PCB_MODULE_EDGE_T )
{
@ -1454,7 +1518,7 @@ BOARD_ITEM* MODULE::DuplicateItem( const BOARD_ITEM* aItem, bool aAddToModule )
case PCB_MODULE_EDGE_T:
{
EDGE_MODULE* new_edge = new EDGE_MODULE( *static_cast<const EDGE_MODULE*>(aItem) );
EDGE_MODULE* new_edge = new EDGE_MODULE( *static_cast<const EDGE_MODULE*>( aItem ) );
const_cast<KIID&>( new_edge->m_Uuid ) = KIID();
if( aAddToModule )
@ -1464,6 +1528,10 @@ BOARD_ITEM* MODULE::DuplicateItem( const BOARD_ITEM* aItem, bool aAddToModule )
break;
}
case PCB_GROUP_T:
new_item = static_cast<const PCB_GROUP*>( aItem )->DeepDuplicate();
break;
case PCB_MODULE_T:
// Ignore the module itself
break;

View File

@ -101,6 +101,7 @@ public:
DECL_DEQ_FOR_SWIG( PADS, D_PAD* )
DECL_DEQ_FOR_SWIG( DRAWINGS, BOARD_ITEM* )
DECL_VEC_FOR_SWIG( MODULE_ZONE_CONTAINERS, MODULE_ZONE_CONTAINER* )
DECL_VEC_FOR_SWIG( MODULE_GROUPS, PCB_GROUP* )
DECL_DEQ_FOR_SWIG( MODULES, MODULE* )
class MODULE : public BOARD_ITEM_CONTAINER
@ -178,36 +179,17 @@ public:
const EDA_RECT GetBoundingBox( bool aIncludeInvisibleText ) const;
PADS& Pads()
{
return m_pads;
}
PADS& Pads() { return m_pads; }
const PADS& Pads() const { return m_pads; }
const PADS& Pads() const
{
return m_pads;
}
DRAWINGS& GraphicalItems() { return m_drawings; }
const DRAWINGS& GraphicalItems() const { return m_drawings; }
DRAWINGS& GraphicalItems()
{
return m_drawings;
}
MODULE_ZONE_CONTAINERS& Zones() { return m_fp_zones; }
const MODULE_ZONE_CONTAINERS& Zones() const { return m_fp_zones; }
const DRAWINGS& GraphicalItems() const
{
return m_drawings;
}
MODULE_ZONE_CONTAINERS& Zones()
{
return m_fp_zones;
}
const MODULE_ZONE_CONTAINERS& Zones() const
{
return m_fp_zones;
}
MODULE_GROUPS& Groups() { return m_fp_groups; }
const MODULE_GROUPS& Groups() const { return m_fp_groups; }
bool HasThroughHolePads() const;
@ -713,6 +695,7 @@ private:
DRAWINGS m_drawings; // BOARD_ITEMs for drawings on the board, owned by pointer.
PADS m_pads; // D_PAD items, owned by pointer
MODULE_ZONE_CONTAINERS m_fp_zones; // MODULE_ZONE_CONTAINER items, owned by pointer
MODULE_GROUPS m_fp_groups; // PCB_GROUP items, owned by pointer
double m_Orient; // Orientation in tenths of a degree, 900=90.0 degrees.
wxPoint m_Pos; // Position of module on the board in internal units.

View File

@ -29,7 +29,7 @@
#include <msgpanel.h>
#include <view/view.h>
PCB_GROUP::PCB_GROUP( BOARD*aParent ) :
PCB_GROUP::PCB_GROUP( BOARD_ITEM* aParent ) :
BOARD_ITEM( aParent, PCB_GROUP_T )
{
}
@ -71,11 +71,11 @@ void PCB_GROUP::RemoveAll()
PCB_GROUP* PCB_GROUP::TopLevelGroup( BOARD_ITEM* item, PCB_GROUP* scope )
{
if( item->GetParent() && item->GetParent()->Type() == PCB_MODULE_T )
item = item->GetParent();
PCB_GROUP* candidate = item->GetParentGroup();
if( !candidate && item->GetParent() && item->GetParent()->Type() == PCB_MODULE_T )
candidate = item->GetParent()->GetParentGroup();
while( candidate && candidate->GetParentGroup() && candidate->GetParentGroup() != scope )
candidate = candidate->GetParentGroup();

View File

@ -134,6 +134,7 @@ const KICAD_T GENERAL_COLLECTOR::ModulesAndTheirItems[] = {
PCB_MODULE_EDGE_T,
PCB_PAD_T,
PCB_MODULE_ZONE_AREA_T,
PCB_GROUP_T,
EOT
};
@ -143,6 +144,7 @@ const KICAD_T GENERAL_COLLECTOR::ModuleItems[] = {
PCB_MODULE_EDGE_T,
PCB_PAD_T,
PCB_MODULE_ZONE_AREA_T,
PCB_GROUP_T,
EOT
};

View File

@ -67,6 +67,7 @@
#include <tool/zoom_tool.h>
#include <tools/pcb_editor_conditions.h>
#include <tools/pcb_viewer_tools.h>
#include <tools/group_tool.h>
#include <tools/position_relative_tool.h>
#include <widgets/appearance_controls.h>
#include <widgets/infobar.h>
@ -862,6 +863,7 @@ void FOOTPRINT_EDIT_FRAME::setupTools()
m_toolManager->RegisterTool( new PCBNEW_PICKER_TOOL );
m_toolManager->RegisterTool( new POSITION_RELATIVE_TOOL );
m_toolManager->RegisterTool( new PCB_VIEWER_TOOLS );
m_toolManager->RegisterTool( new GROUP_TOOL );
m_toolManager->RegisterTool( new CONVERT_TOOL );
m_toolManager->GetTool<SELECTION_TOOL>()->SetEditModules( true );
@ -872,6 +874,7 @@ void FOOTPRINT_EDIT_FRAME::setupTools()
m_toolManager->GetTool<PCBNEW_CONTROL>()->SetEditModules( true );
m_toolManager->GetTool<PCBNEW_PICKER_TOOL>()->SetEditModules( true );
m_toolManager->GetTool<POSITION_RELATIVE_TOOL>()->SetEditModules( true );
m_toolManager->GetTool<GROUP_TOOL>()->SetEditModules( true );
m_toolManager->GetTool<PCB_VIEWER_TOOLS>()->SetFootprintFrame( true );
m_toolManager->InitTools();

View File

@ -148,9 +148,9 @@ static IO_MGR::PCB_FILE_T detect_file_type( FILE* aFile, const wxFileName& aFile
* @param aFileType - type of the file
* @param aName - name of the footprint
*/
static MODULE* parse_module_with_plugin(
const wxFileName& aFileName, IO_MGR::PCB_FILE_T aFileType,
const wxString& aName )
static MODULE* parse_module_with_plugin( const wxFileName& aFileName,
IO_MGR::PCB_FILE_T aFileType,
const wxString& aName )
{
wxString path;

View File

@ -414,7 +414,7 @@ public:
* is thrown in the case where aFootprintName cannot be found.
*/
virtual MODULE* FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
const PROPERTIES* aProperties = NULL );
const PROPERTIES* aProperties = NULL );
/**
* Function GetEnumeratedFootprint

View File

@ -633,14 +633,14 @@ void PCB_IO::format( BOARD* aBoard, int aNestLevel ) const
formatHeader( aBoard, aNestLevel );
// Save the modules.
for( auto module : sorted_modules )
for( BOARD_ITEM* module : sorted_modules )
{
Format( module, aNestLevel );
m_out->Print( 0, "\n" );
}
// Save the graphical items on the board (not owned by a module)
for( auto item : sorted_drawings )
for( BOARD_ITEM* item : sorted_drawings )
Format( item, aNestLevel );
if( sorted_drawings.size() )
@ -649,7 +649,7 @@ void PCB_IO::format( BOARD* aBoard, int aNestLevel ) const
// Do not save MARKER_PCBs, they can be regenerated easily.
// Save the tracks and vias.
for( auto track : sorted_tracks )
for( TRACK* track : sorted_tracks )
Format( track, aNestLevel );
if( sorted_tracks.size() )
@ -660,7 +660,7 @@ void PCB_IO::format( BOARD* aBoard, int aNestLevel ) const
Format( zone, aNestLevel );
// Save the groups
for( const auto group : sorted_groups )
for( BOARD_ITEM* group : sorted_groups )
Format( group, aNestLevel );
}
@ -1083,20 +1083,26 @@ void PCB_IO::format( MODULE* aModule, int aNestLevel ) const
aModule->GraphicalItems().end() );
std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_zones( aModule->Zones().begin(),
aModule->Zones().end() );
std::set<BOARD_ITEM*, PCB_GROUP::ptr_cmp> sorted_groups( aModule->Groups().begin(),
aModule->Groups().end() );
// Save drawing elements.
for( auto gr : sorted_drawings )
for( BOARD_ITEM* gr : sorted_drawings )
Format( gr, aNestLevel+1 );
// Save pads.
for( auto pad : sorted_pads )
for( D_PAD* pad : sorted_pads )
Format( pad, aNestLevel+1 );
// Save zones.
for( auto zone : sorted_zones )
for( BOARD_ITEM* zone : sorted_zones )
Format( zone, aNestLevel + 1 );
// Save groups.
for( BOARD_ITEM* group : sorted_groups )
Format( group, aNestLevel + 1 );
// Save 3D info.
auto bs3D = aModule->Models().begin();
auto es3D = aModule->Models().end();
@ -2291,7 +2297,7 @@ void PCB_IO::FootprintSave( const wxString& aLibraryPath, const MODULE* aFootpri
}
// I need my own copy for the cache
MODULE* module = static_cast<MODULE*>( aFootprint->Duplicate() );
MODULE* module = static_cast<MODULE*>( aFootprint->Clone() );
// It should have no parent, orientation should be zero, and it should be on the front layer.
module->SetParent( nullptr );

View File

@ -86,8 +86,9 @@ class TEXTE_PCB;
//#define SEXPR_BOARD_FILE_VERSION 20200909 // Change DIMENSION format
//#define SEXPR_BOARD_FILE_VERSION 20200913 // Add leader dimension
//#define SEXPR_BOARD_FILE_VERSION 20200916 // Add center dimension
//#define SEXPR_BOARD_FILE_VERSION 20200921 // Add orthogonal dimension
#define SEXPR_BOARD_FILE_VERSION 20200922 // Add user name to layer definition.
//#define SEXPR_BOARD_FILE_VERSION 20200921 // Add orthogonal dimension
//#define SEXPR_BOARD_FILE_VERSION 20200922 // Add user name to layer definition.
#define SEXPR_BOARD_FILE_VERSION 20201002 // Add groups in footprints (for footprint editor).
#define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag

View File

@ -195,14 +195,14 @@ void PCB_BASE_FRAME::FocusOnItem( BOARD_ITEM* aItem )
static_cast<MODULE*>( lastItem )->RunOnChildren( [&]( BOARD_ITEM* child )
{
child->ClearBrightened();
});
} );
}
else if( lastItem->Type() == PCB_GROUP_T )
{
static_cast<PCB_GROUP*>( lastItem )->RunOnChildren( [&] ( BOARD_ITEM* child )
{
child->ClearBrightened();
});
static_cast<PCB_GROUP*>( lastItem )->RunOnChildren( [&]( BOARD_ITEM* child )
{
child->ClearBrightened();
} );
}
GetCanvas()->GetView()->Update( lastItem );

View File

@ -516,6 +516,8 @@ BOARD_ITEM* PCB_PARSER::Parse()
THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
}
resolveGroups( item );
return item;
}
@ -618,7 +620,7 @@ BOARD* PCB_PARSER::parseBOARD_unchecked()
break;
case T_group:
parseGROUP();
parseGROUP( m_board );
break;
case T_via:
@ -721,91 +723,81 @@ BOARD* PCB_PARSER::parseBOARD_unchecked()
m_undefinedLayers.clear();
}
// Now that we've parsed the other Uuids in the file we can resolve
// the uuids referred to in the group declarations we saw.
return m_board;
}
void PCB_PARSER::resolveGroups( BOARD_ITEM* aParent )
{
auto getItem = [&]( const KIID& aId )
{
BOARD_ITEM* aItem = nullptr;
if( dynamic_cast<BOARD*>( aParent ) )
{
aItem = static_cast<BOARD*>( aParent )->GetItem( aId );
}
else if( aParent->Type() == PCB_MODULE_T )
{
static_cast<MODULE*>( aParent )->RunOnChildren(
[&]( BOARD_ITEM* child )
{
if( child->m_Uuid == aId )
aItem = child;
} );
}
return aItem;
};
// Now that we've parsed the other Uuids in the file we can resolve the uuids referred
// to in the group declarations we saw.
//
// First add all group objects so subsequent GetItem() calls for nested
// groups work.
// First add all group objects so subsequent GetItem() calls for nested groups work.
for( size_t idx = 0; idx < m_groupInfos.size(); idx++ )
{
auto& aGrp = m_groupInfos[idx];
PCB_GROUP* group = new PCB_GROUP( m_board );
GROUP_INFO& aGrp = m_groupInfos[idx];
PCB_GROUP* group = new PCB_GROUP( aGrp.parent );
group->SetName( aGrp.name );
const_cast<KIID&>( group->m_Uuid ) = aGrp.uuid;
m_board->Add( group );
if( aGrp.parent->Type() == PCB_MODULE_T )
static_cast<MODULE*>( aGrp.parent )->Add( group );
else
static_cast<BOARD*>( aGrp.parent )->Add( group );
}
wxString error;
for( size_t idx = 0; idx < m_groupInfos.size(); idx++ )
{
auto& aGrp = m_groupInfos[idx];
BOARD_ITEM* bItem = m_board->GetItem( aGrp.uuid );
GROUP_INFO& aGrp = m_groupInfos[idx];
BOARD_ITEM* bItem = getItem( aGrp.uuid );
if( bItem == nullptr || bItem->Type() != PCB_GROUP_T )
{
error = wxString::Format( _( "Group %s not found in board" ),
aGrp.uuid.AsString() );
continue;
}
PCB_GROUP* group = static_cast<PCB_GROUP*>( bItem );
for( const auto& aUuid : aGrp.memberUuids )
for( const KIID& aUuid : aGrp.memberUuids )
{
KIID tUuid = aUuid;
BOARD_ITEM* item;
if( m_resetKIIDs )
{
if( m_resetKIIDMap.find( aUuid.AsString() ) == m_resetKIIDMap.end() )
{
if( error == wxEmptyString )
{
error = wxString::Format( _( "Group %s references missing item %s" ),
aGrp.uuid.AsString(), aUuid.AsString() );
}
}
else
{
tUuid = m_resetKIIDMap[ aUuid.AsString() ];
}
}
BOARD_ITEM* item = m_board->GetItem( tUuid );
if( ( item == nullptr ) || ( item->Type() == NOT_USED ) )
{
if( error == wxEmptyString )
{
error = wxString::Format( _( "Group %s references missing item %s" ),
aGrp.uuid.AsString(), tUuid.AsString() );
}
}
item = getItem( m_resetKIIDMap[ aUuid.AsString() ] );
else
{
item = getItem( aUuid );
if( item && item->Type() != NOT_USED )
group->AddItem( item );
}
}
}
wxString sanityResult = m_board->GroupsSanityCheck();
if( error != wxEmptyString || sanityResult != wxEmptyString )
{
wxString errMsg = ( error != wxEmptyString ) ? error : sanityResult;
KIDIALOG dlg( nullptr, wxString::Format(
_( "Error in group structure in file: %s\n\nAttempt repair?" ), errMsg ),
_( "File data error" ), wxOK | wxCANCEL | wxICON_ERROR );
dlg.SetOKLabel( _( "Attempt repair" ) );
if( dlg.ShowModal() == wxID_CANCEL )
THROW_IO_ERROR( _( "File read canceled" ) );
// Don't allow group cycles
if( m_board )
m_board->GroupsSanityCheck( true );
}
return m_board;
}
@ -3012,7 +3004,7 @@ MODULE* PCB_PARSER::parseMODULE_unchecked( wxArrayString* aInitialComments )
module->Add( text, ADD_MODE::APPEND );
}
}
break;
break;
case T_fp_arc:
{
@ -3028,8 +3020,7 @@ MODULE* PCB_PARSER::parseMODULE_unchecked( wxArrayString* aInitialComments )
else
delete em;
}
break;
break;
case T_fp_circle:
case T_fp_curve:
@ -3042,8 +3033,7 @@ MODULE* PCB_PARSER::parseMODULE_unchecked( wxArrayString* aInitialComments )
em->SetDrawCoord();
module->Add( em, ADD_MODE::APPEND );
}
break;
break;
case T_pad:
{
@ -3054,8 +3044,7 @@ MODULE* PCB_PARSER::parseMODULE_unchecked( wxArrayString* aInitialComments )
pad->SetPosition( pt + module->GetPosition() );
module->Add( pad, ADD_MODE::APPEND );
}
break;
break;
case T_model:
module->Add3DModel( parse3DModel() );
@ -3066,7 +3055,11 @@ MODULE* PCB_PARSER::parseMODULE_unchecked( wxArrayString* aInitialComments )
ZONE_CONTAINER* zone = parseZONE_CONTAINER( module.get() );
module->Add( zone, ADD_MODE::APPEND );
}
break;
break;
case T_group:
parseGROUP( module.get() );
break;
default:
Expecting(
@ -3075,7 +3068,7 @@ MODULE* PCB_PARSER::parseMODULE_unchecked( wxArrayString* aInitialComments )
"solder_paste_margin, solder_paste_ratio, clearance, "
"zone_connect, thermal_width, thermal_gap, attr, fp_text, "
"fp_arc, fp_circle, fp_curve, fp_line, fp_poly, fp_rect, pad, "
"zone, or model" );
"zone, group, or model" );
}
}
@ -3945,7 +3938,7 @@ bool PCB_PARSER::parseD_PAD_option( D_PAD* aPad )
// (group <(name “groupName”)> (id 12345679)
// (members id_1 id_2 … id_last )
// )
void PCB_PARSER::parseGROUP()
void PCB_PARSER::parseGROUP( BOARD_ITEM* aParent )
{
wxCHECK_RET( CurTok() == T_group,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_GROUP." ) );
@ -3953,8 +3946,9 @@ void PCB_PARSER::parseGROUP()
wxPoint pt;
T token;
m_groupInfos.push_back( GroupInfo() );
GroupInfo& groupInfo = m_groupInfos.back();
m_groupInfos.push_back( GROUP_INFO() );
GROUP_INFO& groupInfo = m_groupInfos.back();
groupInfo.parent = aParent;
token = NextTok();
@ -3991,9 +3985,9 @@ void PCB_PARSER::parseGROUP()
while( ( token = NextTok() ) != T_RIGHT )
{
// This token is the Uuid of the item in the group.
// Since groups are serialized at the end of the file, the
// Uuid should already have been seen and exist in the board.
KIID uuid( CurStr() );
// Since groups are serialized at the end of the file/module, the Uuid should already
// have been seen and exist in the board.
KIID uuid( CurStr() );
groupInfo.memberUuids.push_back( uuid );
}
@ -4865,16 +4859,19 @@ PCB_TARGET* PCB_PARSER::parsePCB_TARGET()
}
KIID PCB_PARSER::CurStrToKIID() {
KIID aid;
KIID PCB_PARSER::CurStrToKIID()
{
KIID aId;
if( m_resetKIIDs )
{
aid = KIID();
m_resetKIIDMap.insert( std::make_pair( CurStr(), aid ) );
aId = KIID();
m_resetKIIDMap.insert( std::make_pair( CurStr(), aId ) );
}
else
{
aid = KIID( CurStr() );
aId = KIID( CurStr() );
}
return aid;
return aId;
}

View File

@ -91,12 +91,13 @@ class PCB_PARSER : public PCB_LEXER
// them into BOARD_ITEM* after we've parsed the rest of the file.
typedef struct
{
BOARD_ITEM* parent;
wxString name;
KIID uuid;
std::vector<KIID> memberUuids;
} GroupInfo;
} GROUP_INFO;
std::vector<GroupInfo> m_groupInfos;
std::vector<GROUP_INFO> m_groupInfos;
///> Converts net code using the mapping table if available,
///> otherwise returns unchanged net code if < 0 or if is is out of range
@ -181,7 +182,7 @@ class PCB_PARSER : public PCB_LEXER
PCB_TARGET* parsePCB_TARGET();
MARKER_PCB* parseMARKER( BOARD_ITEM_CONTAINER* aParent );
BOARD* parseBOARD();
void parseGROUP();
void parseGROUP( BOARD_ITEM* aParent );
/**
* Function parseBOARD_unchecked
@ -189,7 +190,6 @@ class PCB_PARSER : public PCB_LEXER
*/
BOARD* parseBOARD_unchecked();
/**
* Function lookUpLayer
* parses the current token for the layer definition of a #BOARD_ITEM object.
@ -340,6 +340,12 @@ class PCB_PARSER : public PCB_LEXER
*/
KIID CurStrToKIID();
/**
* Called after parsing a footprint definition or board to build the group membership
* lists.
*/
void resolveGroups( BOARD_ITEM* aParent );
public:
PCB_PARSER( LINE_READER* aReader = NULL ) :

View File

@ -35,12 +35,12 @@
class DIALOG_GROUP_PROPERTIES : public DIALOG_GROUP_PROPERTIES_BASE
{
private:
PCB_EDIT_FRAME* m_brdEditor;
TOOL_MANAGER* m_toolMgr;
PCB_GROUP* m_group;
PCB_BASE_EDIT_FRAME* m_brdEditor;
TOOL_MANAGER* m_toolMgr;
PCB_GROUP* m_group;
public:
DIALOG_GROUP_PROPERTIES( PCB_EDIT_FRAME* aParent, PCB_GROUP* aTarget );
DIALOG_GROUP_PROPERTIES( PCB_BASE_EDIT_FRAME* aParent, PCB_GROUP* aTarget );
~DIALOG_GROUP_PROPERTIES() { }
void OnMemberSelected( wxCommandEvent& event ) override;
@ -55,7 +55,8 @@ private:
};
DIALOG_GROUP_PROPERTIES::DIALOG_GROUP_PROPERTIES( PCB_EDIT_FRAME* aParent, PCB_GROUP* aGroup ) :
DIALOG_GROUP_PROPERTIES::DIALOG_GROUP_PROPERTIES( PCB_BASE_EDIT_FRAME* aParent,
PCB_GROUP* aGroup ) :
DIALOG_GROUP_PROPERTIES_BASE( aParent ),
m_brdEditor( aParent ),
m_toolMgr( aParent->GetToolManager() ),
@ -165,8 +166,49 @@ void DIALOG_GROUP_PROPERTIES::OnRemoveMember( wxCommandEvent& event )
}
class GROUP_CONTEXT_MENU : public ACTION_MENU
{
public:
GROUP_CONTEXT_MENU( ) : ACTION_MENU( true )
{
SetIcon( locked_xpm ); // fixme
SetTitle( _( "Grouping" ) );
Add( PCB_ACTIONS::groupCreate );
Add( PCB_ACTIONS::groupUngroup );
Add( PCB_ACTIONS::groupRemoveItems );
Add( PCB_ACTIONS::groupEnter );
}
ACTION_MENU* create() const override
{
return new GROUP_CONTEXT_MENU();
}
private:
void update() override
{
SELECTION_TOOL* selTool = getToolManager()->GetTool<SELECTION_TOOL>();
BOARD* board = selTool->GetBoard();
const auto& selection = selTool->GetSelection();
wxString check = board->GroupsSanityCheck();
wxCHECK_RET( check == wxEmptyString, _( "Group is in inconsistent state: " ) + check );
BOARD::GroupLegalOpsField legalOps = board->GroupLegalOps( selection );
Enable( PCB_ACTIONS::groupCreate.GetUIId(), legalOps.create );
Enable( PCB_ACTIONS::groupUngroup.GetUIId(), legalOps.ungroup );
Enable( PCB_ACTIONS::groupRemoveItems.GetUIId(), legalOps.removeItems );
Enable( PCB_ACTIONS::groupEnter.GetUIId(), legalOps.enter );
}
};
GROUP_TOOL::GROUP_TOOL() :
PCB_TOOL_BASE( "pcbnew.Groups" ),
m_frame( nullptr ),
m_propertiesDialog( nullptr ),
m_selectionTool( nullptr )
{
@ -175,6 +217,8 @@ GROUP_TOOL::GROUP_TOOL() :
void GROUP_TOOL::Reset( RESET_REASON aReason )
{
m_frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
if( aReason != RUN )
m_commit = std::make_unique<BOARD_COMMIT>( this );
}
@ -182,17 +226,33 @@ void GROUP_TOOL::Reset( RESET_REASON aReason )
bool GROUP_TOOL::Init()
{
m_frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
// Find the selection tool, so they can cooperate
m_selectionTool = m_toolMgr->GetTool<SELECTION_TOOL>();
return m_selectionTool != nullptr;
auto groupMenu = std::make_shared<GROUP_CONTEXT_MENU>();
groupMenu->SetTool( this );
// Add the group control menus to relevant other tools
if( m_selectionTool )
{
auto& toolMenu = m_selectionTool->GetToolMenu();
auto& menu = toolMenu.GetMenu();
toolMenu.AddSubMenu( groupMenu );
menu.AddMenu( groupMenu.get(), SELECTION_CONDITIONS::NotEmpty, 100 );
}
return true;
}
int GROUP_TOOL::GroupProperties( const TOOL_EVENT& aEvent )
{
PCB_EDIT_FRAME* editFrame = getEditFrame<PCB_EDIT_FRAME>();
PCB_GROUP* group = aEvent.Parameter<PCB_GROUP*>();
PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
PCB_GROUP* group = aEvent.Parameter<PCB_GROUP*>();
if( m_propertiesDialog )
m_propertiesDialog->Destroy();
@ -277,8 +337,175 @@ int GROUP_TOOL::PickNewMember( const TOOL_EVENT& aEvent )
}
int GROUP_TOOL::Group( const TOOL_EVENT& aEvent )
{
SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
const PCBNEW_SELECTION& selection = selTool->GetSelection();
BOARD* board = getModel<BOARD>();
PCB_GROUP* group = nullptr;
if( selection.Empty() )
m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true );
if( m_editModules )
{
MODULE* module = board->GetFirstModule();
m_frame->SaveCopyInUndoList( module, UNDO_REDO::CHANGED );
group = new PCB_GROUP( module );
module->Add( group );
}
else
{
PICKED_ITEMS_LIST undoList;
group = new PCB_GROUP( board );
board->Add( group );
undoList.PushItem( ITEM_PICKER( nullptr, group, UNDO_REDO::NEWITEM ) );
for( EDA_ITEM* item : selection )
{
group->AddItem( static_cast<BOARD_ITEM*>( item ) );
undoList.PushItem( ITEM_PICKER( nullptr, item, UNDO_REDO::GROUP ) );
}
m_frame->SaveCopyInUndoList( undoList, UNDO_REDO::GROUP );
}
selTool->ClearSelection();
selTool->select( group );
m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
m_frame->OnModify();
return 0;
}
int GROUP_TOOL::Ungroup( const TOOL_EVENT& aEvent )
{
const PCBNEW_SELECTION& selection = m_toolMgr->GetTool<SELECTION_TOOL>()->GetSelection();
BOARD* board = getModel<BOARD>();
std::vector<BOARD_ITEM*> members;
if( selection.Empty() )
m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true );
PCBNEW_SELECTION selCopy = selection;
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
for( EDA_ITEM* item : selCopy )
{
PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( item );
if( group )
{
if( m_editModules )
{
MODULE* module = board->GetFirstModule();
m_frame->SaveCopyInUndoList( module, UNDO_REDO::CHANGED );
group->RemoveAll();
module->Remove( group );
}
else
{
PICKED_ITEMS_LIST undoList;
for( BOARD_ITEM* member : group->GetItems() )
{
undoList.PushItem( ITEM_PICKER( nullptr, member, UNDO_REDO::UNGROUP ) );
members.push_back( member );
}
group->RemoveAll();
board->Remove( group );
undoList.PushItem( ITEM_PICKER( nullptr, group, UNDO_REDO::DELETED ) );
m_frame->SaveCopyInUndoList( undoList, UNDO_REDO::UNGROUP );
}
group->SetSelected();
}
}
m_toolMgr->RunAction( PCB_ACTIONS::selectItems, true, &members );
m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
m_frame->OnModify();
return 0;
}
int GROUP_TOOL::RemoveFromGroup( const TOOL_EVENT& aEvent )
{
SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
const PCBNEW_SELECTION& selection = selTool->GetSelection();
BOARD_COMMIT commit( m_frame );
if( selection.Empty() )
m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true );
std::map<PCB_GROUP*, std::vector<BOARD_ITEM*>> groupMap;
for( EDA_ITEM* item : selection )
{
BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
PCB_GROUP* group = boardItem->GetParentGroup();
if( group )
groupMap[ group ].push_back( boardItem );
}
for( std::pair<PCB_GROUP*, std::vector<BOARD_ITEM*>> pair : groupMap )
{
commit.Modify( pair.first );
for( BOARD_ITEM* item : pair.second )
pair.first->RemoveItem( item );
}
commit.Push( "Remove Group Items" );
m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
m_frame->OnModify();
return 0;
}
int GROUP_TOOL::EnterGroup( const TOOL_EVENT& aEvent )
{
SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
const PCBNEW_SELECTION& selection = selTool->GetSelection();
if( selection.GetSize() == 1 && selection[0]->Type() == PCB_GROUP_T )
selTool->EnterGroup();
return 0;
}
int GROUP_TOOL::LeaveGroup( const TOOL_EVENT& aEvent )
{
m_toolMgr->GetTool<SELECTION_TOOL>()->ExitGroup( true /* Select the group */ );
return 0;
}
void GROUP_TOOL::setTransitions()
{
Go( &GROUP_TOOL::GroupProperties, PCB_ACTIONS::groupProperties.MakeEvent() );
Go( &GROUP_TOOL::PickNewMember, PCB_ACTIONS::pickNewGroupMember.MakeEvent() );
Go( &GROUP_TOOL::Group, PCB_ACTIONS::groupCreate.MakeEvent() );
Go( &GROUP_TOOL::Ungroup, PCB_ACTIONS::groupUngroup.MakeEvent() );
Go( &GROUP_TOOL::RemoveFromGroup, PCB_ACTIONS::groupRemoveItems.MakeEvent() );
Go( &GROUP_TOOL::EnterGroup, PCB_ACTIONS::groupEnter.MakeEvent() );
Go( &GROUP_TOOL::LeaveGroup, PCB_ACTIONS::groupLeave.MakeEvent() );
}

View File

@ -53,10 +53,27 @@ public:
*/
int PickNewMember( const TOOL_EVENT& aEvent );
///> Groups selected items.
int Group( const TOOL_EVENT& aEvent );
///> Ungroups selected items.
int Ungroup( const TOOL_EVENT& aEvent );
///> Remove selection from group.
int RemoveFromGroup( const TOOL_EVENT& aEvent );
///> Restrict seletion to only member of the group.
int EnterGroup( const TOOL_EVENT& aEvent );
///> Leave the current group (deselect its members and select the group as a whole)
int LeaveGroup( const TOOL_EVENT& aEvent );
private:
///> Sets up handlers for various events.
void setTransitions() override;
private:
PCB_BASE_EDIT_FRAME* m_frame;
DIALOG_GROUP_PROPERTIES* m_propertiesDialog;
SELECTION_TOOL* m_selectionTool;
std::unique_ptr<BOARD_COMMIT> m_commit;

View File

@ -119,46 +119,6 @@ public:
};
class GROUP_CONTEXT_MENU : public ACTION_MENU
{
public:
GROUP_CONTEXT_MENU( ) : ACTION_MENU( true )
{
SetIcon( locked_xpm ); // fixme
SetTitle( _( "Grouping" ) );
Add( PCB_ACTIONS::groupCreate );
Add( PCB_ACTIONS::groupUngroup );
Add( PCB_ACTIONS::groupRemoveItems );
Add( PCB_ACTIONS::groupEnter );
}
ACTION_MENU* create() const override
{
return new GROUP_CONTEXT_MENU();
}
private:
void update() override
{
SELECTION_TOOL* selTool = getToolManager()->GetTool<SELECTION_TOOL>();
BOARD* board = selTool->GetBoard();
const auto& selection = selTool->GetSelection();
wxString check = board->GroupsSanityCheck();
wxCHECK_RET( check == wxEmptyString, _( "Group is in inconsistent state: " ) + check );
BOARD::GroupLegalOpsField legalOps = board->GroupLegalOps( selection );
Enable( PCB_ACTIONS::groupCreate.GetUIId(), legalOps.create );
Enable( PCB_ACTIONS::groupUngroup.GetUIId(), legalOps.ungroup );
Enable( PCB_ACTIONS::groupRemoveItems.GetUIId(), legalOps.removeItems );
Enable( PCB_ACTIONS::groupEnter.GetUIId(), legalOps.enter );
}
};
PCB_EDITOR_CONTROL::PCB_EDITOR_CONTROL() :
PCB_TOOL_BASE( "pcbnew.EditorControl" ),
m_frame( nullptr )
@ -219,9 +179,6 @@ bool PCB_EDITOR_CONTROL::Init()
auto lockMenu = std::make_shared<LOCK_CONTEXT_MENU>();
lockMenu->SetTool( this );
auto groupMenu = std::make_shared<GROUP_CONTEXT_MENU>();
groupMenu->SetTool( this );
// Add the PCB control menus to relevant other tools
SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
@ -237,9 +194,7 @@ bool PCB_EDITOR_CONTROL::Init()
toolMenu.AddSubMenu( zoneMenu );
toolMenu.AddSubMenu( lockMenu );
toolMenu.AddSubMenu( groupMenu );
menu.AddMenu( groupMenu.get(), SELECTION_CONDITIONS::NotEmpty, 100 );
menu.AddMenu( lockMenu.get(), SELECTION_CONDITIONS::OnlyTypes( GENERAL_COLLECTOR::LockableItems ), 100 );
menu.AddMenu( zoneMenu.get(), SELECTION_CONDITIONS::OnlyType( PCB_ZONE_AREA_T ), 200 );
@ -995,138 +950,6 @@ int PCB_EDITOR_CONTROL::modifyLockSelected( MODIFY_MODE aMode )
}
int PCB_EDITOR_CONTROL::Group( const TOOL_EVENT& aEvent )
{
SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
const PCBNEW_SELECTION& selection = selTool->GetSelection();
BOARD* board = getModel<BOARD>();
PICKED_ITEMS_LIST undoList;
if( selection.Empty() )
m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true );
PCB_GROUP* group = new PCB_GROUP( board );
board->Add( group );
undoList.PushItem( ITEM_PICKER( nullptr, group, UNDO_REDO::NEWITEM ) );
for( EDA_ITEM* item : selection )
{
group->AddItem( static_cast<BOARD_ITEM*>( item ) );
undoList.PushItem( ITEM_PICKER( nullptr, item, UNDO_REDO::GROUP ) );
}
m_frame->SaveCopyInUndoList( undoList, UNDO_REDO::GROUP );
selTool->ClearSelection();
selTool->select( group );
m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
m_frame->OnModify();
return 0;
}
int PCB_EDITOR_CONTROL::Ungroup( const TOOL_EVENT& aEvent )
{
const PCBNEW_SELECTION& selection = m_toolMgr->GetTool<SELECTION_TOOL>()->GetSelection();
PICKED_ITEMS_LIST undoList;
std::vector<BOARD_ITEM*> members;
if( selection.Empty() )
m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true );
PCBNEW_SELECTION selCopy = selection;
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
for( EDA_ITEM* item : selCopy )
{
PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( item );
if( group )
{
for( BOARD_ITEM* member : group->GetItems() )
{
undoList.PushItem( ITEM_PICKER( nullptr, member, UNDO_REDO::UNGROUP ) );
members.push_back( member );
}
group->RemoveAll();
m_frame->GetBoard()->Remove( group );
group->SetSelected();
undoList.PushItem( ITEM_PICKER( nullptr, group, UNDO_REDO::DELETED ) );
}
}
m_frame->SaveCopyInUndoList( undoList, UNDO_REDO::UNGROUP );
m_toolMgr->RunAction( PCB_ACTIONS::selectItems, true, &members );
m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
m_frame->OnModify();
return 0;
}
int PCB_EDITOR_CONTROL::RemoveFromGroup( const TOOL_EVENT& aEvent )
{
SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
const PCBNEW_SELECTION& selection = selTool->GetSelection();
BOARD_COMMIT commit( m_frame );
if( selection.Empty() )
m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true );
std::map<PCB_GROUP*, std::vector<BOARD_ITEM*>> groupMap;
for( EDA_ITEM* item : selection )
{
BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
PCB_GROUP* group = boardItem->GetParentGroup();
if( group )
groupMap[ group ].push_back( boardItem );
}
for( std::pair<PCB_GROUP*, std::vector<BOARD_ITEM*>> pair : groupMap )
{
commit.Modify( pair.first );
for( BOARD_ITEM* item : pair.second )
pair.first->RemoveItem( item );
}
commit.Push( "Remove Group Items" );
m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
m_frame->OnModify();
return 0;
}
int PCB_EDITOR_CONTROL::EnterGroup( const TOOL_EVENT& aEvent )
{
SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
const PCBNEW_SELECTION& selection = selTool->GetSelection();
if( selection.GetSize() == 1 && selection[0]->Type() == PCB_GROUP_T )
selTool->EnterGroup();
return 0;
}
int PCB_EDITOR_CONTROL::LeaveGroup( const TOOL_EVENT& aEvent )
{
m_toolMgr->GetTool<SELECTION_TOOL>()->ExitGroup( true /* Select the group */ );
return 0;
}
int PCB_EDITOR_CONTROL::PlaceTarget( const TOOL_EVENT& aEvent )
{
KIGFX::VIEW* view = getView();
@ -1492,11 +1315,6 @@ void PCB_EDITOR_CONTROL::setTransitions()
Go( &PCB_EDITOR_CONTROL::ToggleLockSelected, PCB_ACTIONS::toggleLock.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::LockSelected, PCB_ACTIONS::lock.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::UnlockSelected, PCB_ACTIONS::unlock.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::Group, PCB_ACTIONS::groupCreate.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::Ungroup, PCB_ACTIONS::groupUngroup.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::RemoveFromGroup, PCB_ACTIONS::groupRemoveItems.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::EnterGroup, PCB_ACTIONS::groupEnter.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::LeaveGroup, PCB_ACTIONS::groupLeave.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::UpdatePCBFromSchematic, ACTIONS::updatePcbFromSchematic.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::UpdateSchematicFromPCB, ACTIONS::updateSchematicFromPcb.MakeEvent() );

View File

@ -109,21 +109,6 @@ public:
///> Unlocks selected items.
int UnlockSelected( const TOOL_EVENT& aEvent );
///> Groups selected items.
int Group( const TOOL_EVENT& aEvent );
///> Ungroups selected items.
int Ungroup( const TOOL_EVENT& aEvent );
///> Remove selection from group.
int RemoveFromGroup( const TOOL_EVENT& aEvent );
///> Restrict seletion to only member of the group.
int EnterGroup( const TOOL_EVENT& aEvent );
///> Leave the current group (deselect its members and select the group as a whole)
int LeaveGroup( const TOOL_EVENT& aEvent );
///> Runs the drill origin tool for setting the origin for drill and pick-and-place files.
int DrillOrigin( const TOOL_EVENT& aEvent );

View File

@ -114,28 +114,25 @@ using namespace std::placeholders;
static bool TestForExistingItem( BOARD* aPcb, BOARD_ITEM* aItem )
{
for( auto item : aPcb->Tracks() )
for( TRACK* item : aPcb->Tracks() )
{
if( aItem == static_cast<BOARD_ITEM*>( item ) )
return true;
}
// Append modules:
for( auto item : aPcb->Modules() )
for( MODULE* item : aPcb->Modules() )
{
if( aItem == static_cast<BOARD_ITEM*>( item ) )
return true;
}
// Append drawings
for( auto item : aPcb->Drawings() )
for( BOARD_ITEM* item : aPcb->Drawings() )
{
if( aItem == static_cast<BOARD_ITEM*>( item ) )
return true;
}
// Append zones outlines
for( auto item : aPcb->Zones() )
for( ZONE_CONTAINER* item : aPcb->Zones() )
{
if( aItem == static_cast<BOARD_ITEM*>( item ) )
return true;
@ -149,8 +146,7 @@ static bool TestForExistingItem( BOARD* aPcb, BOARD_ITEM* aItem )
return true;
}
// Append groups:
for( auto item : aPcb->Groups() )
for( PCB_GROUP* item : aPcb->Groups() )
{
if( aItem == static_cast<BOARD_ITEM*>( item ) )
return true;
@ -195,8 +191,6 @@ void PCB_BASE_EDIT_FRAME::SaveCopyInUndoList( const PICKED_ITEMS_LIST& aItemsLis
UNDO_REDO aTypeCommand,
const wxPoint& aTransformPoint )
{
static KICAD_T moduleChildren[] = { PCB_MODULE_TEXT_T, PCB_MODULE_EDGE_T, PCB_PAD_T, EOT };
PICKED_ITEMS_LIST* commandToUndo = new PICKED_ITEMS_LIST();
commandToUndo->m_TransformPoint = aTransformPoint;
@ -210,14 +204,9 @@ void PCB_BASE_EDIT_FRAME::SaveCopyInUndoList( const PICKED_ITEMS_LIST& aItemsLis
BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( aItemsList.GetPickedItem( ii ) );
// For items belonging to modules, we need to save state of the parent module
if( item && item->IsType( moduleChildren ) )
if( item && item->GetParent() && item->GetParent()->Type() == PCB_MODULE_T )
{
// Item to be stored in the undo buffer is the parent module
item = item->GetParent();
wxASSERT( item && item->Type() == PCB_MODULE_T );
if( item == NULL )
continue;
// Check if the parent module has already been saved in another entry
bool found = false;