From 78161b592201c85a82eb689d273af1fdaeb18f9a Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Tue, 27 Mar 2018 10:18:20 +0200 Subject: [PATCH] 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. --- pcbnew/collectors.cpp | 9 +- pcbnew/dialogs/dialog_layers_setup.cpp | 113 ++++++++++++++++++++++--- pcbnew/invoke_pcb_dialog.h | 7 +- 3 files changed, 110 insertions(+), 19 deletions(-) diff --git a/pcbnew/collectors.cpp b/pcbnew/collectors.cpp index 2219886c24..f2c5dd8cca 100644 --- a/pcbnew/collectors.cpp +++ b/pcbnew/collectors.cpp @@ -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 - * 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( item )->IsOnLayer( m_layer_id ) ) + Append( testItem ); + } + else if( item->GetLayer() == m_layer_id ) Append( testItem ); return SEARCH_CONTINUE; diff --git a/pcbnew/dialogs/dialog_layers_setup.cpp b/pcbnew/dialogs/dialog_layers_setup.cpp index 28b4d2d33c..71ad24ef77 100644 --- a/pcbnew/dialogs/dialog_layers_setup.cpp +++ b/pcbnew/dialogs/dialog_layers_setup.cpp @@ -29,6 +29,9 @@ #include #include +#include +#include + #include #include @@ -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( 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 ); diff --git a/pcbnew/invoke_pcb_dialog.h b/pcbnew/invoke_pcb_dialog.h index 91247da54f..a08527ad16 100644 --- a/pcbnew/invoke_pcb_dialog.h +++ b/pcbnew/invoke_pcb_dialog.h @@ -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 - * 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