/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2009 Isaac Marino Bavaresco, isaacbavaresco@yahoo.com.br * Copyright (C) 2009 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2009-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 * 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 #include #include #include #include #include #include #include #include #include // some define to choose how copper layers widgets are shown // if defined, display only active copper layers // if not displays always 1=the full set (32 copper layers) #define HIDE_INACTIVE_LAYERS /** * Holds the 3 UI control pointers for a single board layer. */ struct CTLs { CTLs( wxControl* aName, wxCheckBox* aCheckBox, wxControl* aChoiceOrDesc, wxPanel* aPanel = NULL ) { name = aName; checkbox = aCheckBox; choice = aChoiceOrDesc; panel = aPanel; } wxControl* name; wxCheckBox* checkbox; wxControl* choice; wxPanel * panel; }; static LSEQ dlg_layers() { // layers that are put out into the dialog UI, coordinate with wxformbuilder and // getCTLs( LAYER_NUM aLayerNumber ) static const PCB_LAYER_ID layers[] = { F_CrtYd, F_Fab, F_Adhes, F_Paste, F_SilkS, F_Mask, F_Cu, In1_Cu, In2_Cu, In3_Cu, In4_Cu, In5_Cu, In6_Cu, In7_Cu, In8_Cu, In9_Cu, In10_Cu, In11_Cu, In12_Cu, In13_Cu, In14_Cu, In15_Cu, In16_Cu, In17_Cu, In18_Cu, In19_Cu, In20_Cu, In21_Cu, In22_Cu, In23_Cu, In24_Cu, In25_Cu, In26_Cu, In27_Cu, In28_Cu, In29_Cu, In30_Cu, B_Cu, B_Mask, B_SilkS, B_Paste, B_Adhes, B_Fab, B_CrtYd, Edge_Cuts, Margin, Eco2_User, Eco1_User, Cmts_User, Dwgs_User, }; return LSEQ( layers, layers + DIM( layers ) ); } class DIALOG_LAYERS_SETUP : public DIALOG_LAYERS_SETUP_BASE { public: DIALOG_LAYERS_SETUP( PCB_EDIT_FRAME* aCaller, BOARD* aBoard ); private: int m_copperLayerCount; LSET m_enabledLayers; BOARD* m_pcb; wxStaticText* m_nameStaticText; wxStaticText* m_enabledStaticText; wxStaticText* m_typeStaticText; void setLayerCheckBox( LAYER_NUM layer, bool isChecked ); void setCopperLayerCheckBoxes( int copperCount ); // Force mandatory non copper layers enabled void setStateMandatoryLayerCheckBoxes(); void showCopperChoice( int copperCount ); void showBoardLayerNames(); void showSelectedLayerCheckBoxes( LSET enableLayerMask ); void showLayerTypes(); void showPresets( LSET enabledLayerMask ); /** Return the selected layer mask within the UI checkboxes */ LSET getUILayerMask(); wxString getLayerName( LAYER_NUM layer ); int getLayerTypeIndex( LAYER_NUM layer ); void OnInitDialog( wxInitDialogEvent& aEvent ) override; void OnCheckBox( wxCommandEvent& event ) override; void DenyChangeCheckBox( wxCommandEvent& event ) override; void OnPresetsChoice( wxCommandEvent& event ) override; void OnCopperLayersChoice( wxCommandEvent& event ) override; bool TransferDataToWindow() override; bool TransferDataFromWindow() override; bool testLayerNames(); /** * Return a list of layers removed from the board that contain items. */ 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 */ CTLs getCTLs( LAYER_NUM aLayerNumber ); wxControl* getName( LAYER_NUM aLayer ) { return getCTLs( aLayer ).name; } wxCheckBox* getCheckBox( LAYER_NUM aLayer ) { return getCTLs( aLayer ).checkbox; } wxChoice* getChoice( LAYER_NUM aLayer ) { return (wxChoice*) getCTLs( aLayer ).choice; } void moveTitles() { wxArrayInt widths = m_LayerListFlexGridSizer->GetColWidths(); int offset = 0; wxSize txtz; wxSize panel_sz = m_TitlePanel->GetSize(); int voffset = panel_sz.y/2 - VertPixelsFromDU( 4 ); txtz = m_nameStaticText->GetSize(); m_nameStaticText->Move( offset + (widths[0] - txtz.x)/2, voffset ); offset += widths[0]; txtz = m_enabledStaticText->GetSize(); m_enabledStaticText->Move( offset + (widths[1] - txtz.x)/2, voffset ); offset += widths[1]; txtz = m_typeStaticText->GetSize(); m_typeStaticText->Move( offset + (widths[2] - txtz.x)/2, voffset ); } void OnSize( wxSizeEvent& event ) override; }; // Layer bit masks for each defined "Preset Layer Grouping" static const LSET presets[] = { LSET(), // shift the array index up by one, matches with "Custom". // "Two layers, parts on Front only" LSET( 2, F_Cu, B_Cu ) | LSET::FrontTechMask() | LSET::UserMask(), // "Two layers, parts on Back only", LSET( 2, F_Cu, B_Cu ) | LSET::BackTechMask() | LSET::UserMask(), // "Two layers, parts on Front and Back", LSET( 2, F_Cu, B_Cu ) | LSET::FrontTechMask() | LSET::BackTechMask() | LSET::UserMask(), // "Four layers, parts on Front only" LSET( 4, F_Cu, B_Cu, In1_Cu, In2_Cu ) | LSET::FrontTechMask() | LSET::UserMask(), // "Four layers, parts on Front and Back" LSET( 4, F_Cu, B_Cu, In1_Cu, In2_Cu ) | LSET::FrontTechMask() | LSET::BackTechMask() | LSET::UserMask(), // "All layers on", LSET().set(), }; CTLs DIALOG_LAYERS_SETUP::getCTLs( LAYER_NUM aLayerNumber ) { #define RETCOP(x) return CTLs( x##Name, x##CheckBox, x##Choice, x##Panel ); #define RETAUX(x) return CTLs( x##Name, x##CheckBox, x##StaticText, x##Panel ); switch( aLayerNumber ) { case F_CrtYd: RETAUX( m_CrtYdFront ); case F_Fab: RETAUX( m_FabFront ); case F_Adhes: RETAUX( m_AdhesFront ); case F_Paste: RETAUX( m_SoldPFront ); case F_SilkS: RETAUX( m_SilkSFront ); case F_Mask: RETAUX( m_MaskFront ); case F_Cu: RETCOP( m_Front ); case In1_Cu: RETCOP( m_In1 ); case In2_Cu: RETCOP( m_In2 ); case In3_Cu: RETCOP( m_In3 ); case In4_Cu: RETCOP( m_In4 ); case In5_Cu: RETCOP( m_In5 ); case In6_Cu: RETCOP( m_In6 ); case In7_Cu: RETCOP( m_In7 ); case In8_Cu: RETCOP( m_In8 ); case In9_Cu: RETCOP( m_In9 ); case In10_Cu: RETCOP( m_In10 ); case In11_Cu: RETCOP( m_In11 ); case In12_Cu: RETCOP( m_In12 ); case In13_Cu: RETCOP( m_In13 ); case In14_Cu: RETCOP( m_In14 ); case In15_Cu: RETCOP( m_In15 ); case In16_Cu: RETCOP( m_In16 ); case In17_Cu: RETCOP( m_In17 ); case In18_Cu: RETCOP( m_In18 ); case In19_Cu: RETCOP( m_In19 ); case In20_Cu: RETCOP( m_In20 ); case In21_Cu: RETCOP( m_In21 ); case In22_Cu: RETCOP( m_In22 ); case In23_Cu: RETCOP( m_In23 ); case In24_Cu: RETCOP( m_In24 ); case In25_Cu: RETCOP( m_In25 ); case In26_Cu: RETCOP( m_In26 ); case In27_Cu: RETCOP( m_In27 ); case In28_Cu: RETCOP( m_In28 ); case In29_Cu: RETCOP( m_In29 ); case In30_Cu: RETCOP( m_In30 ); case B_Cu: RETCOP( m_Back ); case B_Mask: RETAUX( m_MaskBack ); case B_SilkS: RETAUX( m_SilkSBack ); case B_Paste: RETAUX( m_SoldPBack ); case B_Adhes: RETAUX( m_AdhesBack ); case B_Fab: RETAUX( m_FabBack ); case B_CrtYd: RETAUX( m_CrtYdBack ); case Edge_Cuts: RETAUX( m_PCBEdges ); case Margin: RETAUX( m_Margin ); case Eco2_User: RETAUX( m_Eco2 ); case Eco1_User: RETAUX( m_Eco1 ); case Cmts_User: RETAUX( m_Comments ); case Dwgs_User: RETAUX( m_Drawings ); default: wxASSERT_MSG( 0, wxT( "bad layer id" ) ); return CTLs( 0, 0, 0 ); } #undef RETCOP #undef RETAUX } DIALOG_LAYERS_SETUP::DIALOG_LAYERS_SETUP( PCB_EDIT_FRAME* aParent, BOARD* aBoard ) : DIALOG_LAYERS_SETUP_BASE( aParent ) { m_pcb = aBoard; m_copperLayerCount = m_pcb->GetCopperLayerCount(); m_enabledLayers = m_pcb->GetEnabledLayers(); SetAutoLayout( true ); // these 3 controls are handled outside wxformbuilder so that we can add // them without a sizer. Then we position them manually based on the column // widths from m_LayerListFlexGridSizer->GetColWidths() m_nameStaticText = new wxStaticText( m_TitlePanel, wxID_ANY, _( "Name" ), wxDefaultPosition, wxDefaultSize, 0 ); m_enabledStaticText = new wxStaticText( m_TitlePanel, wxID_ANY, _( "Enabled" ), wxDefaultPosition, wxDefaultSize, 0 ); m_typeStaticText = new wxStaticText( m_TitlePanel, wxID_ANY, _( "Type" ), wxDefaultPosition, wxDefaultSize, 0 ); } void DIALOG_LAYERS_SETUP::OnInitDialog( wxInitDialogEvent& aEvent ) { wxWindowBase::OnInitDialog( aEvent ); m_TitlePanel->SetMinSize( wxSize( -1, VertPixelsFromDU( 10 ) ) ); m_LayersListPanel->ShowScrollbars( wxSHOW_SB_ALWAYS, wxSHOW_SB_ALWAYS ); Layout(); SetSizeInDU( 240, 240 ); Center(); m_sdbSizerOK->SetFocus(); m_sdbSizerOK->SetDefault(); // OnSize() will fix the title spacing. QueueEvent( new wxSizeEvent( GetSize() ) ); } bool DIALOG_LAYERS_SETUP::TransferDataToWindow() { if( !wxDialog::TransferDataToWindow() ) return false; showCopperChoice( m_copperLayerCount ); setCopperLayerCheckBoxes( m_copperLayerCount ); m_staticTextBrdThicknessUnit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); PutValueInLocalUnits( *m_textCtrlBrdThickness, m_pcb->GetDesignSettings().GetBoardThickness() ); showBoardLayerNames(); showSelectedLayerCheckBoxes( m_enabledLayers ); showPresets( m_enabledLayers ); showLayerTypes(); setStateMandatoryLayerCheckBoxes(); // All widgets are now initialized. Fix the min sizes: GetSizer()->SetSizeHints( this ); return true; } void DIALOG_LAYERS_SETUP::setStateMandatoryLayerCheckBoxes() { int layerList[4] = { F_CrtYd, B_CrtYd, Edge_Cuts, Margin }; for( int ii = 0; ii < 4; ii++ ) { setLayerCheckBox( layerList[ii], true ); getCheckBox( layerList[ii] )->Enable( false ); // do not allow changes } } void DIALOG_LAYERS_SETUP::OnSize( wxSizeEvent& event ) { moveTitles(); event.Skip(); Refresh(); } void DIALOG_LAYERS_SETUP::showCopperChoice( int copperCount ) { if( copperCount > MAX_CU_LAYERS ) copperCount = MAX_CU_LAYERS; if( copperCount < 2 ) copperCount = 2; for( int lyrCnt = 2; lyrCnt <= MAX_CU_LAYERS; lyrCnt += 2 ) { // note this will change a one layer board to 2: if( copperCount <= lyrCnt ) { int idx = lyrCnt/2 - 1; m_CopperLayersChoice->SetSelection(idx); break; } } } void DIALOG_LAYERS_SETUP::showBoardLayerNames() { // Establish all the board's layer names into the dialog presentation, by // obtaining them from BOARD::GetLayerName() which calls // BOARD::GetStandardLayerName() for non-coppers. for( LSEQ seq = dlg_layers(); seq; ++seq ) { PCB_LAYER_ID layer = *seq; wxControl* ctl = getName( layer ); wxASSERT( ctl ); if( ctl ) { wxString lname = m_pcb->GetLayerName( layer ); if( ctl->IsKindOf( CLASSINFO( wxTextCtrl ) ) ) ((wxTextCtrl*)ctl)->SetValue( lname ); // wxTextCtrl else ctl->SetLabel( lname ); // wxStaticText } } } void DIALOG_LAYERS_SETUP::showSelectedLayerCheckBoxes( LSET enabledLayers ) { // the check boxes for( LSEQ seq = dlg_layers(); seq; ++seq ) { PCB_LAYER_ID layer = *seq; setLayerCheckBox( layer, enabledLayers[layer] ); } } void DIALOG_LAYERS_SETUP::showPresets( LSET enabledLayers ) { int presetsNdx = 0; // the "Custom" setting, matches nothing for( unsigned i=1; iSetSelection( presetsNdx ); } void DIALOG_LAYERS_SETUP::showLayerTypes() { for( LSEQ seq = LSET::AllCuMask().Seq(); seq; ++seq ) { PCB_LAYER_ID cu_layer = *seq; wxChoice* ctl = getChoice( cu_layer ); ctl->SetSelection( m_pcb->GetLayerType( cu_layer ) ); } } LSET DIALOG_LAYERS_SETUP::getUILayerMask() { LSET layerMaskResult; for( LSEQ seq = dlg_layers(); seq; ++seq ) { PCB_LAYER_ID layer = *seq; wxCheckBox* ctl = getCheckBox( layer ); if( ctl->GetValue() ) { layerMaskResult.set( layer ); } } return layerMaskResult; } void DIALOG_LAYERS_SETUP::setLayerCheckBox( LAYER_NUM aLayer, bool isChecked ) { wxCheckBox* ctl = getCheckBox( aLayer ); ctl->SetValue( isChecked ); } void DIALOG_LAYERS_SETUP::setCopperLayerCheckBoxes( int copperCount ) { if( copperCount > 0 ) { setLayerCheckBox( F_Cu, true ); --copperCount; } if( copperCount > 0 ) { setLayerCheckBox( B_Cu, true ); --copperCount; } for( LSEQ seq = LSET::InternalCuMask().Seq(); seq; ++seq, --copperCount ) { PCB_LAYER_ID layer = *seq; bool state = copperCount > 0; #ifdef HIDE_INACTIVE_LAYERS // This code hides non-active copper layers, or redisplays hidden // layers which are now needed. CTLs ctl = getCTLs( layer ); ctl.name->Show( state ); ctl.checkbox->Show( state ); ctl.choice->Show( state ); if( ctl.panel ) ctl.panel->Show( state ); #endif setLayerCheckBox( layer, state ); } #ifdef HIDE_INACTIVE_LAYERS // Send an size event to force sizers to be updated, // because the number of copper layers can have changed. wxSizeEvent evt_size( m_LayersListPanel->GetSize() ); m_LayersListPanel->GetEventHandler()->ProcessEvent( evt_size ); #endif } void DIALOG_LAYERS_SETUP::OnCheckBox( wxCommandEvent& event ) { m_enabledLayers = getUILayerMask(); showPresets( m_enabledLayers ); } void DIALOG_LAYERS_SETUP::DenyChangeCheckBox( wxCommandEvent& event ) { // user may not change copper layer checkboxes from anything other than // either presets choice or the copper layer choice controls. // I tried to simply disable the copper CheckBoxes but they look like crap, // so leave them enabled and reverse the user's attempt to toggle them. setCopperLayerCheckBoxes( m_copperLayerCount ); } void DIALOG_LAYERS_SETUP::OnPresetsChoice( wxCommandEvent& event ) { unsigned presetNdx = m_PresetsChoice->GetCurrentSelection(); if( presetNdx == 0 ) // the Custom setting controls nothing. return; if( presetNdx < DIM(presets) ) { m_enabledLayers = presets[ presetNdx ]; LSET copperSet = m_enabledLayers & LSET::AllCuMask(); int copperCount = copperSet.count(); m_copperLayerCount = copperCount; showCopperChoice( m_copperLayerCount ); showSelectedLayerCheckBoxes( m_enabledLayers ); setCopperLayerCheckBoxes( m_copperLayerCount ); } // Ensure mandatory layers are activated setStateMandatoryLayerCheckBoxes(); } void DIALOG_LAYERS_SETUP::OnCopperLayersChoice( wxCommandEvent& event ) { m_copperLayerCount = m_CopperLayersChoice->GetCurrentSelection() * 2 + 2; setCopperLayerCheckBoxes( m_copperLayerCount ); m_enabledLayers = getUILayerMask(); showPresets( m_enabledLayers ); } bool DIALOG_LAYERS_SETUP::TransferDataFromWindow() { if( !wxWindow::TransferDataFromWindow() || !testLayerNames() ) return false; wxString msg; // Make sure the board thickness is sane. int thickness = ValueFromTextCtrl( *m_textCtrlBrdThickness ); if( thickness < Millimeter2iu( 0.1 ) || thickness > Millimeter2iu( 10.0 ) ) { msg.Printf( _( "Board thickness %s is out of range." ), StringFromValue( g_UserUnit, thickness, true ) ); DisplayError( this, msg ); return false; } // Check for removed layers with items which will get deleted from the board. LSEQ removedLayers = getRemovedLayersWithItems(); // 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; for( auto layer_id : removedLayers ) { collector.SetLayerId( layer_id ); collector.Collect( m_pcb, GENERAL_COLLECTOR::BoardLevelItems ); // Bye-bye items on on removed layer. if( collector.GetCount() != 0 ) { hasRemovedBoardItems = true; for( int i = 0; i < collector.GetCount(); i++ ) { BOARD_ITEM* item = collector[i]; m_pcb->Remove( item ); delete item; } } } } wxString name; m_enabledLayers = getUILayerMask(); m_pcb->SetEnabledLayers( m_enabledLayers ); /* Ensure enabled layers are also visible * This is mainly to avoid mistakes if some enabled * layers are not visible when exiting this dialog */ m_pcb->SetVisibleLayers( m_enabledLayers ); for( LSEQ seq = LSET::AllCuMask().Seq(); seq; ++seq ) { PCB_LAYER_ID layer = *seq; if( m_enabledLayers[layer] ) { name = getLayerName( layer ); m_pcb->SetLayerName( layer, name ); LAYER_T t = (LAYER_T) getLayerTypeIndex( layer ); m_pcb->SetLayerType( layer, t ); } } 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; } int DIALOG_LAYERS_SETUP::getLayerTypeIndex( LAYER_NUM aLayer ) { wxChoice* ctl = getChoice( aLayer ); int ret = ctl->GetCurrentSelection(); // indices must have same sequence as LAYER_T return ret; } wxString DIALOG_LAYERS_SETUP::getLayerName( LAYER_NUM aLayer ) { wxString ret; wxASSERT( IsCopperLayer( aLayer ) ); wxTextCtrl* ctl = (wxTextCtrl*) getName( aLayer ); ret = ctl->GetValue().Trim(); return ret; } static bool hasOneOf( const wxString& str, const wxString& chars ) { for( unsigned i=0; i names; wxTextCtrl* ctl; for( LSEQ seq = LSET::AllCuMask().Seq(); seq; ++seq ) { PCB_LAYER_ID layer = *seq; // we _can_ rely on m_enabledLayers being current here: if( !m_enabledLayers[layer] ) continue; wxString name = getLayerName( layer ); ctl = (wxTextCtrl*) getName( layer ); // check name for legality. // 1) cannot be blank. // 2) cannot have blanks. // 3) cannot have " chars // 4) cannot be 'signal' // 5) must be unique. // 6) cannot have illegal chars in filenames ( some filenames are built from layer names ) // like : % $ \ " / : wxString badchars = wxFileName::GetForbiddenChars( wxPATH_DOS ); badchars.Append( '%' ); if( !name ) { DisplayError( this, _( "Layer name may not be empty." ) ); ctl->SetFocus(); // on the bad name return false; } if( hasOneOf( name, badchars ) ) { DisplayError( this, _( "Layer name has an illegal character, one of: '" ) + badchars + wxT( "'" ) ); ctl->SetFocus(); // on the bad name return false; } if( name == wxT( "signal" ) ) { DisplayError( this, _( "Layer name 'signal' is reserved." ) ); ctl->SetFocus(); // on the bad name return false; } for( std::vector::iterator it = names.begin(); it != names.end(); ++it ) { if( name == *it ) { DisplayError( this, _( "Duplicate layer names are not permitted." ) ); ctl->SetFocus(); // on the bad name return false; } } names.push_back( name ); } return true; } LSEQ DIALOG_LAYERS_SETUP::getRemovedLayersWithItems() { LSEQ removedLayers; LSET newLayers = getUILayerMask(); LSET curLayers = m_pcb->GetEnabledLayers(); if( newLayers == curLayers ) // return a empty list if no change return removedLayers; PCB_LAYER_COLLECTOR collector; LSEQ newLayerSeq = newLayers.Seq(); std::vector< PCB_LAYER_ID >::iterator it; for( auto layer_id : curLayers.Seq() ) { if( std::find( newLayerSeq.begin(), newLayerSeq.end(), layer_id ) == newLayerSeq.end() ) { collector.SetLayerId( layer_id ); collector.Collect( m_pcb, GENERAL_COLLECTOR::BoardLevelItems ); if( collector.GetCount() != 0 ) removedLayers.push_back( layer_id ); } } return removedLayers; } 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 ); return dlg.ShowModal() == wxID_OK; }