kicad-source/pcbnew/dialogs/dialog_map_layers.cpp
Seth Hillbrand 0b2d4d4879 Revise Copyright statement to align with TLF
Recommendation is to avoid using the year nomenclature as this
information is already encoded in the git repo.  Avoids needing to
repeatly update.

Also updates AUTHORS.txt from current repo with contributor names
2025-01-01 14:12:04 -08:00

349 lines
11 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com>
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <layer_ids.h>
#include <lseq.h>
#include <dialog_map_layers.h>
#include <wx/msgdlg.h>
wxString DIALOG_MAP_LAYERS::WrapRequired( const wxString& aLayerName )
{
return aLayerName + wxT( " *" );
}
wxString DIALOG_MAP_LAYERS::UnwrapRequired( const wxString& aLayerName )
{
if( !aLayerName.EndsWith( wxT( " *" ) ) )
return aLayerName;
return aLayerName.Left( aLayerName.Length() - 2 );
}
const INPUT_LAYER_DESC* DIALOG_MAP_LAYERS::GetLayerDescription( const wxString& aLayerName ) const
{
wxString layerName = UnwrapRequired( aLayerName );
for( const INPUT_LAYER_DESC& layerDescription : m_input_layers )
{
if( layerDescription.Name == layerName )
return &layerDescription;
}
return nullptr;
}
PCB_LAYER_ID DIALOG_MAP_LAYERS::GetSelectedLayerID()
{
// First check if there is a KiCad element selected
wxString selectedKiCadLayerName;
long itemIndex = -1;
if( ( itemIndex = m_kicad_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
wxLIST_STATE_SELECTED ) ) != wxNOT_FOUND )
{
selectedKiCadLayerName = m_kicad_layers_list->GetItemText( itemIndex );
}
else
{
return PCB_LAYER_ID::UNDEFINED_LAYER;
}
// There should only be one selected (or none) as the list is set with wxLC_SINGLE_SEL style
wxASSERT_MSG( ( m_kicad_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
wxLIST_STATE_SELECTED ) ) == wxNOT_FOUND,
wxT( "There are more than one KiCad layer selected (unexpected)" ) );
for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
{
if( LayerName( ToLAYER_ID( layer ) ) == selectedKiCadLayerName )
return ToLAYER_ID( layer );
}
return PCB_LAYER_ID::UNDEFINED_LAYER;
}
PCB_LAYER_ID DIALOG_MAP_LAYERS::GetAutoMatchLayerID( const wxString& aInputLayerName )
{
wxString pureInputLayerName = UnwrapRequired( aInputLayerName );
for( const INPUT_LAYER_DESC& inputLayerDesc : m_input_layers )
{
if( inputLayerDesc.Name == pureInputLayerName
&& inputLayerDesc.AutoMapLayer != PCB_LAYER_ID::UNSELECTED_LAYER )
{
return inputLayerDesc.AutoMapLayer;
}
}
return PCB_LAYER_ID::UNDEFINED_LAYER;
}
void DIALOG_MAP_LAYERS::AddMappings()
{
PCB_LAYER_ID selectedKiCadLayerID = GetSelectedLayerID();
if( selectedKiCadLayerID == PCB_LAYER_ID::UNDEFINED_LAYER )
return;
// Now iterate through each selected layer in the unmatched layers list
int itemIndex = -1;
wxArrayInt rowsToDelete;
while( ( itemIndex = m_unmatched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
wxLIST_STATE_SELECTED ) )
!= wxNOT_FOUND )
{
wxString selectedLayerName = m_unmatched_layers_list->GetItemText( itemIndex );
wxString kiName = LayerName( selectedKiCadLayerID );
// add layer pair to the GUI list and also to the map
long newItemIndex = m_matched_layers_list->InsertItem( 0, selectedLayerName );
m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
m_matched_layers_map.insert(
{ UnwrapRequired( selectedLayerName ), selectedKiCadLayerID } );
// remove selected layer from vector and also GUI list
for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end();
++iter )
{
if( *iter == selectedLayerName )
{
m_unmatched_layer_names.erase( iter );
break;
}
}
rowsToDelete.Add( itemIndex );
}
DeleteListItems( rowsToDelete, m_unmatched_layers_list );
// Auto select the first item to improve ease-of-use
m_unmatched_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
}
void DIALOG_MAP_LAYERS::RemoveMappings( int aStatus )
{
wxArrayInt rowsToDelete;
int itemIndex = -1;
while( ( itemIndex = m_matched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL, aStatus ) )
!= wxNOT_FOUND )
{
wxString selectedLayerName = m_matched_layers_list->GetItemText( itemIndex, 0 );
wxString pureSelectedLayerName = UnwrapRequired( selectedLayerName );
wxCHECK( m_matched_layers_map.find( pureSelectedLayerName ) != m_matched_layers_map.end(),
/*void*/ );
m_matched_layers_map.erase( pureSelectedLayerName );
rowsToDelete.Add( itemIndex );
m_unmatched_layers_list->InsertItem( 0, selectedLayerName );
m_unmatched_layer_names.push_back( selectedLayerName );
}
DeleteListItems( rowsToDelete, m_matched_layers_list );
}
void DIALOG_MAP_LAYERS::DeleteListItems( const wxArrayInt& aRowsToDelete, wxListCtrl* aListCtrl )
{
for( long n = (long) aRowsToDelete.GetCount() - 1; 0 <= n; n-- )
aListCtrl->DeleteItem( aRowsToDelete[n] );
}
void DIALOG_MAP_LAYERS::OnAutoMatchLayersClicked( wxCommandEvent& event )
{
// Iterate through each selected layer in the unmatched layers list
int itemIndex = -1;
wxArrayInt rowsToDelete;
while( ( itemIndex = m_unmatched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
wxLIST_STATE_DONTCARE ) )
!= wxNOT_FOUND )
{
wxString layerName = m_unmatched_layers_list->GetItemText( itemIndex );
PCB_LAYER_ID autoMatchLayer = GetAutoMatchLayerID( layerName );
if( autoMatchLayer == PCB_LAYER_ID::UNDEFINED_LAYER )
continue;
wxString kiName = LayerName( autoMatchLayer );
// add layer pair to the GUI list and also to the map
long newItemIndex = m_matched_layers_list->InsertItem( 0, layerName );
m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
m_matched_layers_map.insert( { UnwrapRequired( layerName ), autoMatchLayer } );
// remove selected layer from vector and also GUI list
for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end();
++iter )
{
if( *iter == layerName )
{
m_unmatched_layer_names.erase( iter );
break;
}
}
rowsToDelete.Add( itemIndex );
}
DeleteListItems( rowsToDelete, m_unmatched_layers_list );
}
DIALOG_MAP_LAYERS::DIALOG_MAP_LAYERS( wxWindow* aParent,
const std::vector<INPUT_LAYER_DESC>& aLayerDesc ) :
DIALOG_IMPORTED_LAYERS_BASE( aParent )
{
LSET kiCadLayers;
// Read in the input layers
for( const INPUT_LAYER_DESC& inLayer : aLayerDesc )
{
m_input_layers.push_back( inLayer );
wxString layerName = inLayer.Required ? WrapRequired( inLayer.Name ) : inLayer.Name;
m_unmatched_layer_names.push_back( layerName );
kiCadLayers |= inLayer.PermittedLayers;
}
int maxTextWidth = GetTextExtent( _( "Imported Layer" ) ).x;
for( const INPUT_LAYER_DESC& layer : m_input_layers )
maxTextWidth = std::max( maxTextWidth, GetTextExtent( layer.Name ).x );
// Initialize columns in the wxListCtrl elements:
wxListItem importedLayersHeader;
importedLayersHeader.SetId( 0 );
importedLayersHeader.SetText( _( "Imported Layer" ) );
importedLayersHeader.SetWidth( maxTextWidth + 15 );
m_unmatched_layers_list->InsertColumn( 0, importedLayersHeader );
int kicadMaxTextWidth = GetTextExtent( wxT( "User.Drawings" ) ).x;
wxListItem kicadLayersHeader;
kicadLayersHeader.SetId( 0 );
kicadLayersHeader.SetText( _( "KiCad Layer" ) );
kicadLayersHeader.SetWidth( kicadMaxTextWidth + 5 );
m_kicad_layers_list->InsertColumn( 0, kicadLayersHeader );
kicadLayersHeader.SetId( 1 );
importedLayersHeader.SetWidth( maxTextWidth + 15 );
kicadLayersHeader.SetWidth( kicadMaxTextWidth + 5 );
m_matched_layers_list->InsertColumn( 0, importedLayersHeader );
m_matched_layers_list->InsertColumn( 1, kicadLayersHeader );
// Load the input layer list to unmatched layers
int row = 0;
for( const wxString& importedLayerName : m_unmatched_layer_names )
{
wxListItem item;
item.SetId( row );
item.SetText( importedLayerName );
m_unmatched_layers_list->InsertItem( item );
++row;
}
// Auto select the first item to improve ease-of-use
m_unmatched_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
// Load the KiCad Layer names
row = 0;
LSEQ kicadLayersSeq = kiCadLayers.UIOrder();
for( PCB_LAYER_ID layer : kicadLayersSeq )
{
wxString kiName = LayerName( layer );
wxListItem item;
item.SetId( row );
item.SetText( kiName );
m_kicad_layers_list->InsertItem( item );
++row;
}
// Auto select the first item to improve ease-of-use
m_kicad_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
SetupStandardButtons();
Fit();
finishDialogSettings();
}
std::vector<wxString> DIALOG_MAP_LAYERS::GetUnmappedRequiredLayers() const
{
std::vector<wxString> unmappedLayers;
for( const wxString& layerName : m_unmatched_layer_names )
{
const INPUT_LAYER_DESC* layerDesc = GetLayerDescription( layerName );
wxASSERT_MSG( layerDesc != nullptr, wxT( "Expected to find layer description" ) );
if( layerDesc->Required )
unmappedLayers.push_back( layerDesc->Name );
}
return unmappedLayers;
}
std::map<wxString, PCB_LAYER_ID>
DIALOG_MAP_LAYERS::RunModal( wxWindow* aParent, const std::vector<INPUT_LAYER_DESC>& aLayerDesc )
{
DIALOG_MAP_LAYERS dlg( aParent, aLayerDesc );
bool dataOk = false;
while( !dataOk )
{
dlg.ShowModal();
if( dlg.GetUnmappedRequiredLayers().size() > 0 )
{
wxMessageBox( _( "All required layers (marked with '*') must be matched. "
"Please click on 'Auto-Match Layers' to "
"automatically match the remaining layers" ),
_( "Unmatched Layers" ), wxICON_ERROR | wxOK );
}
else
{
dataOk = true;
}
}
return dlg.m_matched_layers_map;
}