Layers setup management: Warn user if some removed layers are in use in footprints loaded on the board.

Fix also memory leak and missing connectivity rebuild.
This commit is contained in:
jean-pierre charras 2018-03-27 10:18:20 +02:00
parent 602d9e1972
commit 78161b5922
3 changed files with 110 additions and 19 deletions

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2007-2008 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2004-2017 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2004-2018 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
@ -499,7 +499,12 @@ SEARCH_RESULT PCB_LAYER_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
{
BOARD_ITEM* item = (BOARD_ITEM*) testItem;
if( item->GetLayer() == m_layer_id )
if( item->Type() == PCB_PAD_T ) // multilayer
{
if( static_cast<D_PAD*>( item )->IsOnLayer( m_layer_id ) )
Append( testItem );
}
else if( item->GetLayer() == m_layer_id )
Append( testItem );
return SEARCH_CONTINUE;

View File

@ -29,6 +29,9 @@
#include <confirm.h>
#include <pcbnew.h>
#include <pcb_edit_frame.h>
#include <view/view.h>
#include <invoke_pcb_dialog.h>
#include <class_board.h>
@ -43,11 +46,6 @@
// if not displays always 1=the full set (32 copper layers)
#define HIDE_INACTIVE_LAYERS
// if defined, use the layer manager copper layers order (from FRONT to BACK)
// to display inner layers.
// if not, use the default order (from BACK to FRONT)
#define USE_LAYER_MANAGER_COPPER_LAYERS_ORDER
/**
* Holds the 3 UI control pointers for a single board layer.
@ -138,7 +136,7 @@ static LSEQ dlg_layers()
class DIALOG_LAYERS_SETUP : public DIALOG_LAYERS_SETUP_BASE
{
public:
DIALOG_LAYERS_SETUP( wxTopLevelWindow* aCaller, BOARD* aBoard );
DIALOG_LAYERS_SETUP( PCB_EDIT_FRAME* aCaller, BOARD* aBoard );
private:
int m_copperLayerCount;
@ -152,6 +150,8 @@ private:
void setLayerCheckBox( LAYER_NUM layer, bool isChecked );
void setCopperLayerCheckBoxes( int copperCount );
// Force mandatory non copper layers enabled
void enableMandatoryLayerCheckBoxes();
void showCopperChoice( int copperCount );
void showBoardLayerNames();
@ -179,6 +179,11 @@ private:
*/
LSEQ getRemovedLayersWithItems();
/**
* Return a list of layers in use in footprints, and therefore not removable.
*/
LSEQ getNonRemovableLayers();
/**
* Map \a aLayerNumber to the wx IDs for that layer which are
* the layer name control ID, checkbox control ID, and choice control ID
@ -323,7 +328,7 @@ CTLs DIALOG_LAYERS_SETUP::getCTLs( LAYER_NUM aLayerNumber )
}
DIALOG_LAYERS_SETUP::DIALOG_LAYERS_SETUP( wxTopLevelWindow* aParent, BOARD* aBoard ) :
DIALOG_LAYERS_SETUP::DIALOG_LAYERS_SETUP( PCB_EDIT_FRAME* aParent, BOARD* aBoard ) :
DIALOG_LAYERS_SETUP_BASE( aParent )
{
m_pcb = aBoard;
@ -382,6 +387,7 @@ bool DIALOG_LAYERS_SETUP::TransferDataToWindow()
showSelectedLayerCheckBoxes( m_enabledLayers );
showPresets( m_enabledLayers );
showLayerTypes();
enableMandatoryLayerCheckBoxes();
// All widgets are now initialized. Fix the min sizes:
GetSizer()->SetSizeHints( this );
@ -390,10 +396,23 @@ bool DIALOG_LAYERS_SETUP::TransferDataToWindow()
}
void DIALOG_LAYERS_SETUP::enableMandatoryLayerCheckBoxes()
{
// Currently, do nothing
#if 0
setLayerCheckBox( F_CrtYd, true );
setLayerCheckBox( B_CrtYd, true );
setLayerCheckBox( Edge_Cuts, true );
setLayerCheckBox( Margin, true );
#endif
}
void DIALOG_LAYERS_SETUP::OnSize( wxSizeEvent& event )
{
moveTitles();
event.Skip();
Refresh();
}
@ -634,14 +653,31 @@ bool DIALOG_LAYERS_SETUP::TransferDataFromWindow()
// Check for removed layers with items which will get deleted from the board.
LSEQ removedLayers = getRemovedLayersWithItems();
if( !removedLayers.empty()
&& !IsOK( this, _( "Items have been found on removed layers. This operation will delete "
"all items from removed layers and cannot be undone. Do you wish to "
"continue?" ) ) )
// Check for non copper layers in use in footprints, and therefore not removable.
LSEQ notremovableLayers = getNonRemovableLayers();
if( !notremovableLayers.empty() )
{
for( unsigned int ii = 0; ii < notremovableLayers.size(); ii++ )
msg << m_pcb->GetLayerName( notremovableLayers[ii] ) << "\n";
if( !IsOK( this, wxString::Format( _( "Footprints have some items on removed layers:\n"
"%s\n"
"These items will be no longer accessible\n"
"Do you wish to continue?" ), msg ) ) )
return false;
}
if( !removedLayers.empty() &&
!IsOK( this, _( "Items have been found on removed layers. This operation will delete "
"all items from removed layers and cannot be undone. Do you wish to "
"continue?" ) ) )
return false;
// Delete all objects on layers that have been removed. Leaving them in copper layers
// can (will?) result in DRC errors and it pollutes the board file with cruft.
bool hasRemovedBoardItems = false;
if( !removedLayers.empty() )
{
PCB_LAYER_COLLECTOR collector;
@ -654,8 +690,14 @@ bool DIALOG_LAYERS_SETUP::TransferDataFromWindow()
// Bye-bye items on on removed layer.
if( collector.GetCount() != 0 )
{
hasRemovedBoardItems = true;
for( int i = 0; i < collector.GetCount(); i++ )
m_pcb->Remove( collector[i] );
{
BOARD_ITEM* item = collector[i];
m_pcb->Remove( item );
delete item;
}
}
}
}
@ -686,6 +728,16 @@ bool DIALOG_LAYERS_SETUP::TransferDataFromWindow()
m_pcb->GetDesignSettings().SetBoardThickness( thickness );
// If some board items are deleted: rebuild the connectivity,
// because it is likely some tracks and vias where removed
if( hasRemovedBoardItems )
{
PCB_EDIT_FRAME* editFrame = static_cast<PCB_EDIT_FRAME*>( GetParent() );
// Rebuild list of nets (full ratsnest rebuild)
editFrame->Compile_Ratsnest( NULL, true );
m_pcb->BuildConnectivity();
}
return true;
}
@ -800,7 +852,7 @@ LSEQ DIALOG_LAYERS_SETUP::getRemovedLayersWithItems()
LSET newLayers = getUILayerMask();
LSET curLayers = m_pcb->GetEnabledLayers();
if( newLayers == curLayers )
if( newLayers == curLayers ) // return a empty list if no change
return removedLayers;
PCB_LAYER_COLLECTOR collector;
@ -823,7 +875,40 @@ LSEQ DIALOG_LAYERS_SETUP::getRemovedLayersWithItems()
}
bool InvokeLayerSetup( wxTopLevelWindow* aCaller, BOARD* aBoard )
LSEQ DIALOG_LAYERS_SETUP::getNonRemovableLayers()
{
//Build the list of non copper layers in use in footprints.
LSEQ inUseLayers;
LSET newLayers = getUILayerMask();
LSET curLayers = m_pcb->GetEnabledLayers();
if( newLayers == curLayers ) // return a empty list if no change
return inUseLayers;
PCB_LAYER_COLLECTOR collector;
LSEQ newLayerSeq = newLayers.Seq();
std::vector< PCB_LAYER_ID >::iterator it;
for( auto layer_id : curLayers.Seq() )
{
if( IsCopperLayer( layer_id ) ) // Copper layers are not taken in account here
continue;
if( std::find( newLayerSeq.begin(), newLayerSeq.end(), layer_id ) == newLayerSeq.end() )
{
collector.SetLayerId( layer_id );
collector.Collect( m_pcb, GENERAL_COLLECTOR::ModuleItems );
if( collector.GetCount() != 0 )
inUseLayers.push_back( layer_id );
}
}
return inUseLayers;
}
bool InvokeLayerSetup( PCB_EDIT_FRAME* aCaller, BOARD* aBoard )
{
DIALOG_LAYERS_SETUP dlg( aCaller, aBoard );

View File

@ -5,7 +5,7 @@
/* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2013-2016 KiCad Developers, see change_log.txt for contributors.
* Copyright (C) 2013-2018 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
@ -54,6 +54,7 @@ class MODULE;
// Often this is not used in the prototypes, since wxFrame is good enough and would
// represent maximum information hiding.
class PCB_BASE_FRAME;
class PCB_EDIT_FRAME;
class FOOTPRINT_EDIT_FRAME;
class FP_LIB_TABLE;
class BOARD;
@ -137,11 +138,11 @@ bool InvokeDXFDialogModuleImport( PCB_BASE_FRAME* aCaller, MODULE* aModule );
/**
* Function InvokeLayerSetup
* shows the layer setup dialog
* @param aCaller is the wxTopLevelWindow which is invoking the dialog.
* @param aCaller is the PCB_EDIT_FRAME which is invoking the dialog.
* @param aBoard is the currently edited board.
* @return bool - true if user pressed OK (did not abort), else false.
*/
bool InvokeLayerSetup( wxTopLevelWindow* aCaller, BOARD* aBoard );
bool InvokeLayerSetup( PCB_EDIT_FRAME* aCaller, BOARD* aBoard );
/**
* Function InvokeSVGPrint