From 829316fad4f302975aa4cf5b7afa2e23e2bf2cba Mon Sep 17 00:00:00 2001 From: PJM Date: Fri, 7 Aug 2020 10:08:45 -0700 Subject: [PATCH] Pcbnew #4904 : Import settings from another board deleted internal layers When importing settings from a board with more copper layers and applying it to a board with less copper layers, if the user has ticked the checkbox "Layer settings", then KiCad will delete the inner copper layers. Only when the user clicks OK does it then warn that it found deleted items on inner layers. The message is too generic and comes after the layers have already been deleted. This Merge Request tries to address this by warning them early. The changes are: 1 - Added code to check if user is trying to import settings from a board with less copper layers than the current loaded board. This results in KiCad deleting inner copper layers. Now it presents a warning dialog that explains the current settings will result in deleted inner layers, and lets the user stop the import process before making any changes. 2 - Made "Import Settings" dialog disable "Import Settings" button until at least one import option checkbox is checked. 3 - Made "Select All" button on "Import Settings" dialog toggle between "Select All" and "Deselect All" on each click. Items 2&3 were added to improve the overall import settings usability experience. Fixes issue https://gitlab.com/kicad/code/kicad/-/issues/4904 --- pcbnew/dialogs/dialog_board_setup.cpp | 75 +++++++++++------- pcbnew/dialogs/dialog_import_settings.cpp | 77 +++++++++++++++++-- pcbnew/dialogs/dialog_import_settings.h | 28 ++++++- .../dialogs/dialog_import_settings_base.cpp | 14 ++++ .../dialogs/dialog_import_settings_base.fbp | 67 ++++++++-------- pcbnew/dialogs/dialog_import_settings_base.h | 1 + pcbnew/dialogs/panel_setup_layers.cpp | 75 ++++++++++++------ pcbnew/dialogs/panel_setup_layers.h | 14 ++++ 8 files changed, 264 insertions(+), 87 deletions(-) diff --git a/pcbnew/dialogs/dialog_board_setup.cpp b/pcbnew/dialogs/dialog_board_setup.cpp index 077b0ea232..4f23307b79 100644 --- a/pcbnew/dialogs/dialog_board_setup.cpp +++ b/pcbnew/dialogs/dialog_board_setup.cpp @@ -151,6 +151,10 @@ void DIALOG_BOARD_SETUP::OnAuxiliaryAction( wxCommandEvent& event ) return; } + // Flag so user can stop work if it will result in deleted inner copper layers + // and still clean up this function properly. + bool okToProceed = true; + PROJECT* otherPrj = m_frame->GetSettingsManager()->GetProject( projectFn.GetFullPath() ); PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::KICAD_SEXP ) ); @@ -160,6 +164,14 @@ void DIALOG_BOARD_SETUP::OnAuxiliaryAction( wxCommandEvent& event ) try { otherBoard = pi->Load( boardFn.GetFullPath(), nullptr, nullptr ); + + if( importDlg.m_LayersOpt->GetValue() ) + { + BOARD* loadedBoard = m_frame->GetBoard(); + + // Check if "Import Settings" board has more layers than the current board. + okToProceed = m_layers->compareCopperLayerCount( loadedBoard, otherBoard ); + } } catch( const IO_ERROR& ioe ) { @@ -175,37 +187,48 @@ void DIALOG_BOARD_SETUP::OnAuxiliaryAction( wxCommandEvent& event ) return; } - otherBoard->SetProject( otherPrj ); - if( importDlg.m_LayersOpt->GetValue() ) - m_layers->ImportSettingsFrom( otherBoard ); - if( importDlg.m_TextAndGraphicsOpt->GetValue() ) - m_textAndGraphics->ImportSettingsFrom( otherBoard ); - if( importDlg.m_ConstraintsOpt->GetValue() ) - m_constraints->ImportSettingsFrom( otherBoard ); - if( importDlg.m_NetclassesOpt->GetValue() ) - m_netclasses->ImportSettingsFrom( &otherBoard->GetDesignSettings().GetNetClasses() ); - if( importDlg.m_TracksAndViasOpt->GetValue() ) - m_tracksAndVias->ImportSettingsFrom( otherBoard ); - if( importDlg.m_MaskAndPasteOpt->GetValue() ) - m_maskAndPaste->ImportSettingsFrom( otherBoard ); + if( okToProceed ) + { + otherBoard->SetProject( otherPrj ); - // If layers options are imported, import also the stackup - // layers options and stackup are linked, so they cannot be imported - // separately, and stackup can be imported only after layers options - // - // Note also currently only the list of enabled layers can be imported, because - // we import settings from a .pro project file, not the settings inside - // a board, and info only living in the board is not imported. - // TODO: Add import of physical settings now that we are actually loading the board here - if( importDlg.m_LayersOpt->GetValue() ) - m_physicalStackup->ImportSettingsFrom( otherBoard ); + if( importDlg.m_LayersOpt->GetValue() ) + m_layers->ImportSettingsFrom( otherBoard ); - if( importDlg.m_SeveritiesOpt->GetValue() ) - m_severities->ImportSettingsFrom( otherBoard->GetDesignSettings().m_DRCSeverities ); + if( importDlg.m_TextAndGraphicsOpt->GetValue() ) + m_textAndGraphics->ImportSettingsFrom( otherBoard ); - otherBoard->ClearProject(); + if( importDlg.m_ConstraintsOpt->GetValue() ) + m_constraints->ImportSettingsFrom( otherBoard ); + if( importDlg.m_NetclassesOpt->GetValue() ) + m_netclasses->ImportSettingsFrom( &otherBoard->GetDesignSettings().GetNetClasses() ); + + if( importDlg.m_TracksAndViasOpt->GetValue() ) + m_tracksAndVias->ImportSettingsFrom( otherBoard ); + + if( importDlg.m_MaskAndPasteOpt->GetValue() ) + m_maskAndPaste->ImportSettingsFrom( otherBoard ); + + // If layers options are imported, import also the stackup + // layers options and stackup are linked, so they cannot be imported + // separately, and stackup can be imported only after layers options + // + // Note also currently only the list of enabled layers can be imported, because + // we import settings from a .pro project file, not the settings inside + // a board, and info only living in the board is not imported. + // TODO: Add import of physical settings now that we are actually loading the board here + + if( importDlg.m_LayersOpt->GetValue() ) + m_physicalStackup->ImportSettingsFrom( otherBoard ); + + if( importDlg.m_SeveritiesOpt->GetValue() ) + m_severities->ImportSettingsFrom( otherBoard->GetDesignSettings().m_DRCSeverities ); + + otherBoard->ClearProject(); + } + + // Clean up and free memory before leaving m_frame->GetSettingsManager()->UnloadProject( otherPrj, false ); delete otherBoard; diff --git a/pcbnew/dialogs/dialog_import_settings.cpp b/pcbnew/dialogs/dialog_import_settings.cpp index 97b265299d..1eea6e4c1e 100644 --- a/pcbnew/dialogs/dialog_import_settings.cpp +++ b/pcbnew/dialogs/dialog_import_settings.cpp @@ -37,12 +37,67 @@ DIALOG_IMPORT_SETTINGS::DIALOG_IMPORT_SETTINGS( wxWindow* aParent, PCB_EDIT_FRAM DIALOG_IMPORT_SETTINGS_BASE( aParent ), m_frame( aFrame ) { + wxSize sizeNeeded; + m_browseButton->SetBitmap( KiBitmap( folder_xpm ) ); + // Button created in wxFormBuilder is an "OK" button. Change label here m_sdbSizer1OK->SetLabel( _( "Import Settings" ) ); + + // Disable "Import Settings" button until user selects at least one import option + m_sdbSizer1OK->Enable( false ); + + // Make sure "Select All" button is big enough to hold "Deselect All" + m_selectAllButton->SetLabel( _( "Deselect All" ) ); // Change the text temporarily + sizeNeeded = m_selectAllButton->GetBestSize(); // Get control to tell us the width required + m_selectAllButton->SetLabel( _( "Select All" ) ); // Restore "Select All" as default text + sizeNeeded.y = m_selectAllButton->GetSize().y; // Keep the height unchanged + m_selectAllButton->SetMinSize( sizeNeeded ); // Set control to the required size + m_buttonsSizer->Layout(); m_sdbSizer1OK->SetDefault(); + + m_showSelectAllOnBtn = true; // Store state to toggle message/usage of "Select All" button +} + + +void DIALOG_IMPORT_SETTINGS::OnCheckboxClicked( wxCommandEvent& event ) +{ + bool importButtonEnabled = UpdateImportSettingsButton(); + + // If clicking this checkbox clears the last of the import selection checkboxes, + // then make sure the "Select All" button is actually going to select all. + + if( !importButtonEnabled ) + { + m_showSelectAllOnBtn = true; + UpdateSelectAllButton(); + } +} + + +bool DIALOG_IMPORT_SETTINGS::UpdateImportSettingsButton() +{ + // Enable "Import Settings" button if at least one import option is selected + bool buttonEnableState = ( m_LayersOpt->IsChecked() || m_MaskAndPasteOpt->IsChecked() + || m_ConstraintsOpt->IsChecked() || m_NetclassesOpt->IsChecked() + || m_SeveritiesOpt->IsChecked() || m_TextAndGraphicsOpt->IsChecked() + || m_TracksAndViasOpt->IsChecked() ); + + m_sdbSizer1OK->Enable( buttonEnableState ); + + return buttonEnableState; +} + + +void DIALOG_IMPORT_SETTINGS::UpdateSelectAllButton() +{ + // Update message on button + if( m_showSelectAllOnBtn ) + m_selectAllButton->SetLabel( _( "Select All" ) ); + else + m_selectAllButton->SetLabel( _( "Deselect All" ) ); } @@ -82,11 +137,19 @@ bool DIALOG_IMPORT_SETTINGS::TransferDataFromWindow() void DIALOG_IMPORT_SETTINGS::OnSelectAll( wxCommandEvent& event ) { - m_LayersOpt->SetValue( true ); - m_TextAndGraphicsOpt->SetValue( true ); - m_ConstraintsOpt->SetValue( true ); - m_NetclassesOpt->SetValue( true ); - m_TracksAndViasOpt->SetValue( true ); - m_MaskAndPasteOpt->SetValue( true ); - m_SeveritiesOpt->SetValue( true ); + // Select or deselect all options based on internal flag + m_LayersOpt->SetValue( m_showSelectAllOnBtn ); + m_TextAndGraphicsOpt->SetValue( m_showSelectAllOnBtn ); + m_ConstraintsOpt->SetValue( m_showSelectAllOnBtn ); + m_NetclassesOpt->SetValue( m_showSelectAllOnBtn ); + m_TracksAndViasOpt->SetValue( m_showSelectAllOnBtn ); + m_MaskAndPasteOpt->SetValue( m_showSelectAllOnBtn ); + m_SeveritiesOpt->SetValue( m_showSelectAllOnBtn ); + + // Ensure "Import Settings" button state is enabled as appropriate + UpdateImportSettingsButton(); + + // Toggle whether button selects or deselects all. + m_showSelectAllOnBtn = !m_showSelectAllOnBtn; + UpdateSelectAllButton(); } diff --git a/pcbnew/dialogs/dialog_import_settings.h b/pcbnew/dialogs/dialog_import_settings.h index fa129e1ea5..da2d0cc7f9 100644 --- a/pcbnew/dialogs/dialog_import_settings.h +++ b/pcbnew/dialogs/dialog_import_settings.h @@ -34,18 +34,42 @@ class PCB_EDIT_FRAME; class DIALOG_IMPORT_SETTINGS : public DIALOG_IMPORT_SETTINGS_BASE { protected: - PCB_EDIT_FRAME* m_frame; - static wxString m_filePath; + PCB_EDIT_FRAME* m_frame; + static wxString m_filePath; + +private: + + /** + * Stores state used to toggle button between "Select All" and "Deselect All" + */ + bool m_showSelectAllOnBtn; public: DIALOG_IMPORT_SETTINGS( wxWindow* aParent, PCB_EDIT_FRAME* aFrame ); void OnBrowseClicked( wxCommandEvent& event ) override; void OnSelectAll( wxCommandEvent& event ) override; + void OnCheckboxClicked( wxCommandEvent& event ) override; bool TransferDataToWindow() override; bool TransferDataFromWindow() override; + /** + * Enables/Disables "Import Settings" button + * + * This dialog defaults to all import selections cleared, and the "Import + * Settings" button disabled. The user must check at least one of the import + * selection checkboxes for the "Import Settings" button to be enabled. + * + * @return bool - "Import Settings" button enable state + */ + bool UpdateImportSettingsButton(); + + /** + * Update "Select All" button label as appropriate + */ + void UpdateSelectAllButton(); + wxString GetFilePath() { return m_filePath; } }; diff --git a/pcbnew/dialogs/dialog_import_settings_base.cpp b/pcbnew/dialogs/dialog_import_settings_base.cpp index f3e2722191..095b3fdc5a 100644 --- a/pcbnew/dialogs/dialog_import_settings_base.cpp +++ b/pcbnew/dialogs/dialog_import_settings_base.cpp @@ -95,6 +95,13 @@ DIALOG_IMPORT_SETTINGS_BASE::DIALOG_IMPORT_SETTINGS_BASE( wxWindow* parent, wxWi // Connect Events m_browseButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnBrowseClicked ), NULL, this ); + m_LayersOpt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnCheckboxClicked ), NULL, this ); + m_TextAndGraphicsOpt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnCheckboxClicked ), NULL, this ); + m_ConstraintsOpt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnCheckboxClicked ), NULL, this ); + m_TracksAndViasOpt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnCheckboxClicked ), NULL, this ); + m_MaskAndPasteOpt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnCheckboxClicked ), NULL, this ); + m_SeveritiesOpt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnCheckboxClicked ), NULL, this ); + m_NetclassesOpt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnCheckboxClicked ), NULL, this ); m_selectAllButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnSelectAll ), NULL, this ); } @@ -102,6 +109,13 @@ DIALOG_IMPORT_SETTINGS_BASE::~DIALOG_IMPORT_SETTINGS_BASE() { // Disconnect Events m_browseButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnBrowseClicked ), NULL, this ); + m_LayersOpt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnCheckboxClicked ), NULL, this ); + m_TextAndGraphicsOpt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnCheckboxClicked ), NULL, this ); + m_ConstraintsOpt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnCheckboxClicked ), NULL, this ); + m_TracksAndViasOpt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnCheckboxClicked ), NULL, this ); + m_MaskAndPasteOpt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnCheckboxClicked ), NULL, this ); + m_SeveritiesOpt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnCheckboxClicked ), NULL, this ); + m_NetclassesOpt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnCheckboxClicked ), NULL, this ); m_selectAllButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_SETTINGS_BASE::OnSelectAll ), NULL, this ); } diff --git a/pcbnew/dialogs/dialog_import_settings_base.fbp b/pcbnew/dialogs/dialog_import_settings_base.fbp index f4bef5ea2d..b469b58043 100644 --- a/pcbnew/dialogs/dialog_import_settings_base.fbp +++ b/pcbnew/dialogs/dialog_import_settings_base.fbp @@ -27,7 +27,7 @@ UI 1 0 - + 0 wxAUI_MGR_DEFAULT @@ -53,16 +53,16 @@ - + m_MainSizer wxVERTICAL protected - + 5 wxEXPAND|wxTOP|wxRIGHT|wxLEFT 0 - + bupperSizer wxHORIZONTAL @@ -192,11 +192,11 @@ - + 5 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 - + 1 1 1 @@ -267,20 +267,20 @@ - + 10 wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT 0 - + bmiddleSizer wxVERTICAL none - + 5 wxTOP|wxBOTTOM|wxRIGHT 0 - + 1 1 1 @@ -337,11 +337,11 @@ -1 - + 5 wxBOTTOM|wxRIGHT|wxLEFT 0 - + 1 1 1 @@ -399,13 +399,14 @@ + OnCheckboxClicked - + 5 wxBOTTOM|wxRIGHT|wxLEFT 0 - + 1 1 1 @@ -463,13 +464,14 @@ + OnCheckboxClicked - + 5 wxBOTTOM|wxRIGHT|wxLEFT 0 - + 1 1 1 @@ -527,13 +529,14 @@ + OnCheckboxClicked - + 5 wxBOTTOM|wxRIGHT|wxLEFT 0 - + 1 1 1 @@ -591,13 +594,14 @@ + OnCheckboxClicked - + 5 wxBOTTOM|wxRIGHT|wxLEFT 0 - + 1 1 1 @@ -655,13 +659,14 @@ + OnCheckboxClicked - + 5 wxRIGHT|wxLEFT 0 - + 1 1 1 @@ -719,13 +724,14 @@ + OnCheckboxClicked - + 5 wxALL 0 - + 1 1 1 @@ -783,24 +789,25 @@ + OnCheckboxClicked - + 5 wxEXPAND 0 - + m_buttonsSizer wxHORIZONTAL protected - + 10 wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT 0 - + 1 1 1 @@ -869,11 +876,11 @@ OnSelectAll - + 5 wxALL|wxEXPAND 1 - + 0 1 0 diff --git a/pcbnew/dialogs/dialog_import_settings_base.h b/pcbnew/dialogs/dialog_import_settings_base.h index 6f4feab09a..055d94e9ff 100644 --- a/pcbnew/dialogs/dialog_import_settings_base.h +++ b/pcbnew/dialogs/dialog_import_settings_base.h @@ -48,6 +48,7 @@ class DIALOG_IMPORT_SETTINGS_BASE : public DIALOG_SHIM // Virtual event handlers, overide them in your derived class virtual void OnBrowseClicked( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCheckboxClicked( wxCommandEvent& event ) { event.Skip(); } virtual void OnSelectAll( wxCommandEvent& event ) { event.Skip(); } diff --git a/pcbnew/dialogs/panel_setup_layers.cpp b/pcbnew/dialogs/panel_setup_layers.cpp index 7c1f325292..8aa321dc33 100644 --- a/pcbnew/dialogs/panel_setup_layers.cpp +++ b/pcbnew/dialogs/panel_setup_layers.cpp @@ -43,7 +43,7 @@ static LSEQ dlg_layers() { - // layers that are put out into the dialog UI, coordinate with wxformbuilder and + // 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, @@ -260,7 +260,7 @@ void PANEL_SETUP_LAYERS::showCopperChoice( int copperCount ) for( int lyrCnt = 2; lyrCnt <= MAX_CU_LAYERS; lyrCnt += 2 ) { - // note this will change a one layer board to 2: + // Note: This will change a 1 layer board to 2 if( copperCount <= lyrCnt ) { int idx = lyrCnt/2 - 1; @@ -296,7 +296,7 @@ void PANEL_SETUP_LAYERS::showBoardLayerNames() void PANEL_SETUP_LAYERS::showSelectedLayerCheckBoxes( LSET enabledLayers ) { - // the check boxes + // The check boxes for( LSEQ seq = dlg_layers(); seq; ++seq ) { PCB_LAYER_ID layer = *seq; @@ -307,7 +307,7 @@ void PANEL_SETUP_LAYERS::showSelectedLayerCheckBoxes( LSET enabledLayers ) void PANEL_SETUP_LAYERS::showPresets( LSET enabledLayers ) { - int presetsNdx = 0; // the "Custom" setting, matches nothing + int presetsNdx = 0; // The "Custom" setting, matches nothing for( unsigned i=1; iGetLabel(); - // knock the ':' off the end + // Knock the ':' off the end controlLabel = controlLabel.substr( 0, controlLabel.size() - 1 ); msg.Printf( _( "Use the \"%s\" control to change the number of copper layers." ), @@ -488,7 +488,7 @@ bool PANEL_SETUP_LAYERS::TransferDataFromWindow() // 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. + // Check for non-copper layers in use in footprints, and therefore not removable. LSEQ notremovableLayers = getNonRemovableLayers(); if( !notremovableLayers.empty() ) @@ -522,7 +522,7 @@ bool PANEL_SETUP_LAYERS::TransferDataFromWindow() collector.SetLayerId( layer_id ); collector.Collect( m_pcb, GENERAL_COLLECTOR::BoardLevelItems ); - // Bye-bye items on on removed layer. + // Bye-bye items on removed layer. if( collector.GetCount() != 0 ) { hasRemovedBoardItems = true; @@ -562,8 +562,8 @@ bool PANEL_SETUP_LAYERS::TransferDataFromWindow() } } - // If some board items are deleted: rebuild the connectivity, - // because it is likely some tracks and vias where removed + // If some board items are deleted: Rebuild the connectivity, + // because it is likely some tracks and vias were removed if( hasRemovedBoardItems ) { // Rebuild list of nets (full ratsnest rebuild) @@ -577,8 +577,8 @@ bool PANEL_SETUP_LAYERS::TransferDataFromWindow() int PANEL_SETUP_LAYERS::getLayerTypeIndex( LAYER_NUM aLayer ) { - wxChoice* ctl = getChoice( aLayer ); - int ret = ctl->GetCurrentSelection(); // indices must have same sequence as LAYER_T + wxChoice* ctl = getChoice( aLayer ); + int ret = ctl->GetCurrentSelection(); // Indices must have same sequence as LAYER_T return ret; } @@ -616,6 +616,7 @@ bool PANEL_SETUP_LAYERS::testLayerNames() PCB_LAYER_ID layer = *seq; // we _can_ rely on m_enabledLayers being current here: + if( !m_enabledLayers[layer] ) continue; @@ -623,13 +624,13 @@ bool PANEL_SETUP_LAYERS::testLayerNames() 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 ) + // 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( '%' ); @@ -676,7 +677,7 @@ LSEQ PANEL_SETUP_LAYERS::getRemovedLayersWithItems() LSET newLayers = GetUILayerMask(); LSET curLayers = m_pcb->GetEnabledLayers(); - if( newLayers == curLayers ) // return a empty list if no change + if( newLayers == curLayers ) // Return an empty list if no change return removedLayers; PCB_LAYER_COLLECTOR collector; @@ -700,12 +701,12 @@ LSEQ PANEL_SETUP_LAYERS::getRemovedLayersWithItems() LSEQ PANEL_SETUP_LAYERS::getNonRemovableLayers() { - //Build the list of non copper layers in use in footprints. + // 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 + if( newLayers == curLayers ) // Return an empty list if no change return inUseLayers; PCB_LAYER_COLLECTOR collector; @@ -713,7 +714,7 @@ LSEQ PANEL_SETUP_LAYERS::getNonRemovableLayers() for( auto layer_id : curLayers.Seq() ) { - if( IsCopperLayer( layer_id ) ) // Copper layers are not taken in account here + if( IsCopperLayer( layer_id ) ) // Copper layers are not taken into account here continue; if( std::find( newLayerSeq.begin(), newLayerSeq.end(), layer_id ) == newLayerSeq.end() ) @@ -739,3 +740,33 @@ void PANEL_SETUP_LAYERS::ImportSettingsFrom( BOARD* aBoard ) m_pcb = savedBoard; } + + +bool PANEL_SETUP_LAYERS::compareCopperLayerCount( BOARD* aWorkingBoard, BOARD* aImportedBoard ) +{ + /* This function warns users if they are going to delete inner copper layers because + they're importing settings from a board with less copper layers than the board + already loaded. We want to return "true" as default on the assumption no layer will + actually be deleted. */ + bool okToDeleteCopperLayers = true; + + // Get the number of copper layers in the loaded board and the "import settings" board + int currNumLayers = aWorkingBoard->GetCopperLayerCount(); + int newNumLayers = aImportedBoard->GetCopperLayerCount(); + + if( newNumLayers < currNumLayers ) + { + wxMessageDialog dlg( this, + wxString::Format( + wxT( "Imported settings have fewer copper layers than current board (%i instead of %i)." + "\n\nContinue and delete extra inner copper layers from current board?" ), + newNumLayers, currNumLayers ), + _( "Inner Layers To Be Deleted" ), + wxICON_WARNING | wxSTAY_ON_TOP | wxYES | wxNO | wxNO_DEFAULT ); + + if( wxID_NO == dlg.ShowModal() ) + okToDeleteCopperLayers = false; + } + + return okToDeleteCopperLayers; +} \ No newline at end of file diff --git a/pcbnew/dialogs/panel_setup_layers.h b/pcbnew/dialogs/panel_setup_layers.h index 9156e21a2c..f2c152cba6 100644 --- a/pcbnew/dialogs/panel_setup_layers.h +++ b/pcbnew/dialogs/panel_setup_layers.h @@ -59,8 +59,22 @@ public: void ImportSettingsFrom( BOARD* aBoard ); + /** + * Check and warn if inner copper layers will be deleted + * + * This function warns users if they are going to delete inner copper layers because + * they're importing settings from a board with less copper layers than the board + * already loaded. + * @param aWorkingBoard = Currently loaded PCB + * @param aImportedBoard = PCB imported to get settings from + * + * @return bool - Approval to delete inner copper if needed + */ + bool compareCopperLayerCount( BOARD* aWorkingBoard, BOARD* aImportedBoard ); + ///> @return the selected layer mask within the UI checkboxes LSET GetUILayerMask(); + ///> @return the layer name within the UI wxTextCtrl wxString GetLayerName( LAYER_NUM layer );