/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 Roberto Fernandez Bautista * Copyright (C) 2020-2021 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 . */ #include #include #include wxString DIALOG_IMPORTED_LAYERS::WrapRequired( const wxString& aLayerName ) { return aLayerName + wxT( " *" ); } wxString DIALOG_IMPORTED_LAYERS::UnwrapRequired( const wxString& aLayerName ) { if( !aLayerName.EndsWith( wxT( " *" ) ) ) return aLayerName; return aLayerName.Left( aLayerName.Length() - 2 ); } const INPUT_LAYER_DESC* DIALOG_IMPORTED_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_IMPORTED_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( LAYER_NUM 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_IMPORTED_LAYERS::GetAutoMatchLayerID( const wxString& aInputLayerName ) { wxString pureInputLayerName = UnwrapRequired( aInputLayerName ); for( 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_IMPORTED_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_IMPORTED_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_IMPORTED_LAYERS::DeleteListItems( const wxArrayInt& aRowsToDelete, wxListCtrl* aListCtrl ) { for( long n = ( aRowsToDelete.GetCount() - 1 ); 0 <= n; n-- ) { aListCtrl->DeleteItem( aRowsToDelete[n] ); } } void DIALOG_IMPORTED_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_IMPORTED_LAYERS::DIALOG_IMPORTED_LAYERS( wxWindow* aParent, const std::vector& aLayerDesc ) : DIALOG_IMPORTED_LAYERS_BASE( aParent ) { LSET kiCadLayers; // Read in the input layers for( 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( 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.Seq(); 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 ); m_sdbSizerOK->SetDefault(); Fit(); finishDialogSettings(); } std::vector DIALOG_IMPORTED_LAYERS::GetUnmappedRequiredLayers() const { std::vector 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 DIALOG_IMPORTED_LAYERS::GetMapModal( wxWindow* aParent, const std::vector& aLayerDesc ) { DIALOG_IMPORTED_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; }