diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index fe3f601e99..35d572f9d6 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -108,6 +108,7 @@ set( PCBNEW_DIALOGS dialogs/dialog_reference_image_properties_base.cpp dialogs/dialog_import_settings.cpp dialogs/dialog_import_settings_base.cpp + dialogs/dialog_items_list.cpp dialogs/dialog_imported_layers_base.cpp dialogs/dialog_map_layers.cpp dialogs/dialog_rule_area_properties.cpp diff --git a/pcbnew/dialogs/dialog_items_list.cpp b/pcbnew/dialogs/dialog_items_list.cpp new file mode 100644 index 0000000000..c23cce7bb5 --- /dev/null +++ b/pcbnew/dialogs/dialog_items_list.cpp @@ -0,0 +1,90 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The 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 3 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 "dialog_items_list.h" +#include +#include + +DIALOG_ITEMS_LIST::DIALOG_ITEMS_LIST( wxWindow* aParent, const wxString& aTitle, + const wxString& aMessage, const wxString& aDetailsLabel ) : + DIALOG_SHIM( aParent, wxID_ANY, aTitle, wxDefaultPosition, wxDefaultSize, + wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ) +{ + wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL ); + + m_message = new wxStaticText( this, wxID_ANY, aMessage ); + mainSizer->Add( m_message, 0, wxALL | wxEXPAND, 10 ); + + m_collapsiblePane = new wxCollapsiblePane( this, wxID_ANY, aDetailsLabel ); + mainSizer->Add( m_collapsiblePane, 1, wxALL | wxEXPAND, 10 ); + + wxWindow* pane = m_collapsiblePane->GetPane(); + wxBoxSizer* paneSizer = new wxBoxSizer( wxVERTICAL ); + + m_listCtrl = new wxListCtrl( pane, wxID_ANY, wxDefaultPosition, wxSize( 400, 200 ), + wxLC_REPORT | wxLC_NO_HEADER | wxLC_SINGLE_SEL ); + m_listCtrl->InsertColumn( 0, wxEmptyString ); + + paneSizer->Add( m_listCtrl, 1, wxEXPAND | wxALL, 5 ); + pane->SetSizer( paneSizer ); + + m_collapsiblePane->Bind( wxEVT_COLLAPSIBLEPANE_CHANGED, &DIALOG_ITEMS_LIST::onCollapse, this ); + m_listCtrl->Bind( wxEVT_LIST_ITEM_SELECTED, &DIALOG_ITEMS_LIST::onSelectionChanged, this ); + + wxSizer* buttonsSizer = CreateSeparatedButtonSizer( wxOK | wxCANCEL ); + mainSizer->Add( buttonsSizer, 0, wxEXPAND | wxALL, 10 ); + + SetSizer( mainSizer ); + Layout(); + GetSizer()->Fit( this ); + Center(); +} + +DIALOG_ITEMS_LIST::~DIALOG_ITEMS_LIST() +{ +} + +void DIALOG_ITEMS_LIST::AddItems( const std::vector& aItems ) +{ + for( const wxString& item : aItems ) + m_listCtrl->InsertItem( m_listCtrl->GetItemCount(), item ); + + m_listCtrl->SetColumnWidth( 0, wxLIST_AUTOSIZE_USEHEADER ); +} + +void DIALOG_ITEMS_LIST::SetSelectionCallback( std::function aCallback ) +{ + m_callback = aCallback; +} + +void DIALOG_ITEMS_LIST::onSelectionChanged( wxListEvent& aEvent ) +{ + if( m_callback ) + m_callback( aEvent.GetIndex() ); +} + +void DIALOG_ITEMS_LIST::onCollapse( wxCollapsiblePaneEvent& aEvent ) +{ + Layout(); + GetSizer()->Fit( this ); +} diff --git a/pcbnew/dialogs/dialog_items_list.h b/pcbnew/dialogs/dialog_items_list.h new file mode 100644 index 0000000000..cce5cd80ae --- /dev/null +++ b/pcbnew/dialogs/dialog_items_list.h @@ -0,0 +1,54 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The 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 3 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 + */ + +#ifndef DIALOG_ITEMS_LIST_H +#define DIALOG_ITEMS_LIST_H + +#include +#include +#include +#include +#include +#include + +class DIALOG_ITEMS_LIST : public DIALOG_SHIM +{ +public: + DIALOG_ITEMS_LIST( wxWindow* aParent, const wxString& aTitle, const wxString& aMessage, + const wxString& aDetailsLabel ); + ~DIALOG_ITEMS_LIST(); + + void AddItems( const std::vector& aItems ); + void SetSelectionCallback( std::function aCallback ); + +private: + void onSelectionChanged( wxListEvent& aEvent ); + void onCollapse( wxCollapsiblePaneEvent& aEvent ); + + wxStaticText* m_message; + wxCollapsiblePane* m_collapsiblePane; + wxListCtrl* m_listCtrl; + std::function m_callback; +}; + +#endif // DIALOG_ITEMS_LIST_H diff --git a/pcbnew/dialogs/panel_setup_layers.cpp b/pcbnew/dialogs/panel_setup_layers.cpp index 81ed0b758b..00958a4cc6 100644 --- a/pcbnew/dialogs/panel_setup_layers.cpp +++ b/pcbnew/dialogs/panel_setup_layers.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -888,11 +889,64 @@ bool PANEL_SETUP_LAYERS::TransferDataFromWindow() if( !removedLayers.empty() ) { - if( !IsOK( parent, _( "Items have been found on removed layers. This operation will " - "delete all items from removed layers and cannot be undone.\n" - "Do you wish to continue?" ) ) ) + std::vector items; + std::vector itemDescriptions; + + for( PCB_LAYER_ID layer : removedLayers ) { - return false; + PCB_LAYER_COLLECTOR collector; + collector.SetLayerId( layer ); + collector.Collect( m_pcb, GENERAL_COLLECTOR::BoardLevelItems ); + + for( int i = 0; i < collector.GetCount(); i++ ) + { + BOARD_ITEM* item = collector[i]; + + if( item->Type() == PCB_FOOTPRINT_T || item->GetParentFootprint() ) + continue; + + items.push_back( item ); + itemDescriptions.push_back( item->GetItemDescription( m_frame, true ) ); + } + + for( FOOTPRINT* footprint : m_pcb->Footprints() ) + { + for( PAD* pad : footprint->Pads() ) + { + if( pad->HasExplicitDefinitionForLayer( layer ) ) + { + items.push_back( pad ); + itemDescriptions.push_back( wxString::Format( _( "Pad %s of %s on %s" ), + pad->GetNumber(), + footprint->GetReference(), + m_pcb->GetLayerName( layer ) ) ); + } + } + } + } + + if( !items.empty() ) + { + DIALOG_ITEMS_LIST dlg( parent, _( "Warning" ), + _( "Items have been found on removed layers. This operation will " + "delete all items from removed layers and cannot be undone." ), + _( "Show Details" ) ); + + dlg.AddItems( itemDescriptions ); + + dlg.SetSelectionCallback( + [&]( int index ) + { + if( index >= 0 && index < (int) items.size() ) + { + m_frame->GetToolManager()->RunAction( ACTIONS::selectionClear ); + m_frame->GetToolManager()->RunAction( ACTIONS::selectItem, static_cast( items[index] ) ); + m_frame->GetCanvas()->Refresh(); + } + } ); + + if( dlg.ShowModal() != wxID_OK ) + return false; } } @@ -991,8 +1045,31 @@ LSEQ PANEL_SETUP_LAYERS::getRemovedLayersWithItems() for( PCB_LAYER_ID layer_id : curLayers ) { - if( !newLayers[layer_id] && m_pcb->HasItemsOnLayer( layer_id ) ) - removedLayers.push_back( layer_id ); + if( !newLayers[layer_id] ) + { + bool hasItems = m_pcb->HasItemsOnLayer( layer_id ); + + if( !hasItems ) + { + // Check for pads with custom properties on this layer + for( FOOTPRINT* footprint : m_pcb->Footprints() ) + { + for( PAD* pad : footprint->Pads() ) + { + if( pad->HasExplicitDefinitionForLayer( layer_id ) ) + { + hasItems = true; + break; + } + } + if( hasItems ) + break; + } + } + + if( hasItems ) + removedLayers.push_back( layer_id ); + } } return removedLayers; diff --git a/pcbnew/pad.h b/pcbnew/pad.h index 49599fdc78..b2ebdf8abd 100644 --- a/pcbnew/pad.h +++ b/pcbnew/pad.h @@ -263,6 +263,11 @@ public: } const VECTOR2I& GetSize( PCB_LAYER_ID aLayer ) const { return m_padStack.Size( aLayer ); } + bool HasExplicitDefinitionForLayer( PCB_LAYER_ID aLayer ) const + { + return m_padStack.HasExplicitDefinitionForLayer( aLayer ); + } + // These accessors are for the properties panel, which does not have the ability to deal with // custom padstacks where the properties can vary by layer. The properties should be disabled // in the GUI when the padstack mode is set to anything other than NORMAL, but so that the code diff --git a/pcbnew/padstack.cpp b/pcbnew/padstack.cpp index a8b28bfbe8..84b4ea53da 100644 --- a/pcbnew/padstack.cpp +++ b/pcbnew/padstack.cpp @@ -1349,6 +1349,12 @@ int PADSTACK::Compare( const PADSTACK* aLeft, const PADSTACK* aRight ) } +bool PADSTACK::HasExplicitDefinitionForLayer( PCB_LAYER_ID aLayer ) const +{ + return m_copperProps.count( aLayer ) > 0; +} + + double PADSTACK::Similarity( const PADSTACK& aOther ) const { // TODO: Implement similarity diff --git a/pcbnew/padstack.h b/pcbnew/padstack.h index 4d180dfd78..37c4f49b7e 100644 --- a/pcbnew/padstack.h +++ b/pcbnew/padstack.h @@ -300,6 +300,12 @@ public: */ static int Compare( const PADSTACK* aPadstackRef, const PADSTACK* aPadstackCmp ); + /** + * Check if the padstack has an explicit definition for the given layer. + * This is useful for detecting if a layer removal will cause data loss. + */ + bool HasExplicitDefinitionForLayer( PCB_LAYER_ID aLayer ) const; + /** * Return a measure of how likely the other object is to represent the same * object. The scale runs from 0.0 (definitely different objects) to 1.0 (same)