kicad-source/gerbview/dialogs/dialog_layers_select_to_pcb.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

816 lines
30 KiB
C++

/*
* 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 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 <X2_gerber_attributes.h>
#include <gerber_file_image.h>
#include <gerber_file_image_list.h>
#include <gerbview.h>
#include <gerbview_frame.h>
#include <gerbview_id.h>
#include <gerbview_settings.h>
#include <kiface_base.h>
#include <layer_ids.h>
#include <dialogs/dialog_layers_select_to_pcb.h>
#include <wx/msgdlg.h>
#include <gestfich.h>
extern const wxString GetPCBDefaultLayerName( int aLayerNumber );
enum swap_layer_id {
ID_LAYERS_MAP_DIALOG = ID_GERBER_END_LIST,
ID_BUTTON_0,
ID_TEXT_0 = ID_BUTTON_0 + GERBER_DRAWLAYERS_COUNT
};
int LAYERS_MAP_DIALOG::m_exportBoardCopperLayersCount = 2;
BEGIN_EVENT_TABLE( LAYERS_MAP_DIALOG, LAYERS_MAP_DIALOG_BASE )
EVT_COMMAND_RANGE( ID_BUTTON_0, ID_BUTTON_0 + GERBER_DRAWLAYERS_COUNT-1,
wxEVT_COMMAND_BUTTON_CLICKED,
LAYERS_MAP_DIALOG::OnSelectLayer )
END_EVENT_TABLE()
LAYERS_MAP_DIALOG::LAYERS_MAP_DIALOG( GERBVIEW_FRAME* parent ) :
LAYERS_MAP_DIALOG_BASE( parent )
{
m_Parent = parent;
initDialog();
// Resize the dialog
Layout();
GetSizer()->SetSizeHints( this );
Centre();
}
void LAYERS_MAP_DIALOG::initDialog()
{
wxStaticText* label;
wxStaticText* text;
int item_ID;
wxString msg;
wxSize goodSize;
GERBVIEW_SETTINGS* config = static_cast<GERBVIEW_SETTINGS*>( Kiface().KifaceSettings() );
for( int ii = 0; ii < GERBER_DRAWLAYERS_COUNT; ++ii )
{
// Specify the default value for each member of these arrays.
m_buttonTable[ii] = -1;
m_layersLookUpTable[ii] = UNSELECTED_LAYER;
}
// Ensure we have:
// At least 2 copper layers and less than max pcb copper layers count
// Even number of layers because a board *must* have even layers count
normalizeBrdLayersCount();
int idx = ( m_exportBoardCopperLayersCount / 2 ) - 1;
m_comboCopperLayersCount->SetSelection( idx );
m_gerberActiveLayersCount = 0;
GERBER_FILE_IMAGE_LIST* images = m_Parent->GetGerberLayout()->GetImagesList();
for( unsigned ii = 0; ii < GERBER_DRAWLAYERS_COUNT; ++ii )
{
if( images->GetGbrImage( ii ) == nullptr )
break;
m_buttonTable[m_gerberActiveLayersCount] = ii;
m_gerberActiveLayersCount++;
}
if( m_gerberActiveLayersCount <= GERBER_DRAWLAYERS_COUNT / 2 ) // Only one list is enough
m_staticlineSep->Hide();
wxFlexGridSizer* flexColumnBoxSizer = m_flexLeftColumnBoxSizer;
for( int ii = 0; ii < m_gerberActiveLayersCount; ii++ )
{
// Each Gerber layer has an associated static text string (to
// identify that layer), a button (for invoking a child dialog
// box to change which Pcbnew layer that the Gerber layer is
// mapped to), and a second static text string (to depict which
// Pcbnew layer that the Gerber layer has been mapped to). Each
// of those items are placed into the left hand column, middle
// column, and right hand column (respectively) of the Flexgrid
// sizer, and the color of the second text string is set to
// fuchsia or blue (to respectively indicate whether the Gerber
// layer has been mapped to a Pcbnew layer or is not being
// exported at all). (Experimentation has shown that if a text
// control is used to depict which Pcbnew layer that each Gerber
// layer is mapped to (instead of a static text string), then
// those controls do not behave in a fully satisfactory manner
// in the Linux version. Even when the read-only attribute is
// specified for all of those controls, they can still be selected
// when the arrow keys or Tab key is used to step through all of
// the controls within the dialog box, and directives to set the
// foreground color of the text of each such control to blue (to
// indicate that the text is of a read-only nature) are disregarded.
// Specify a FlexGrid sizer with an appropriate number of rows
// and three columns. If nb_items < 16, then the number of rows
// is nb_items; otherwise, the number of rows is 16 (with two
// separate columns of controls being used if nb_items > 16).
if( ii == GERBER_DRAWLAYERS_COUNT / 2 )
flexColumnBoxSizer = m_flexRightColumnBoxSizer;
// Provide a text string to identify the Gerber layer
msg.Printf( _( "Layer %d" ), m_buttonTable[ii] + 1 );
label = new wxStaticText( this, wxID_STATIC, msg );
flexColumnBoxSizer->Add( label, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
/* Add file name and extension without path. */
wxFileName fn( images->GetGbrImage( ii )->m_FileName );
label = new wxStaticText( this, wxID_STATIC, fn.GetFullName() );
flexColumnBoxSizer->Add( label, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
// Provide a button for this layer (which will invoke a child dialog box)
item_ID = ID_BUTTON_0 + ii;
wxButton * Button = new wxButton( this, item_ID, wxT( "..." ), wxDefaultPosition,
wxDefaultSize, wxBU_EXACTFIT );
flexColumnBoxSizer->Add( Button, 0, wxALIGN_CENTER_VERTICAL | wxALL );
// Provide another text string to specify which Pcbnew layer that this
// Gerber layer is mapped to. All layers initially default to
// "Do NotExport" (which corresponds to UNSELECTED_LAYER). Whenever
// a layer is set to "Do Not Export" it's displayed in blue. When a
// user selects a specific KiCad layer to map to, it's displayed in
// magenta which indicates it will be exported.
item_ID = ID_TEXT_0 + ii;
// All layers default to "Do Not Export" displayed in blue
msg = _( "Do not export" );
text = new wxStaticText( this, item_ID, msg );
text->SetForegroundColour( *wxBLUE );
// When the first of these text strings is being added, determine what
// size is necessary to to be able to display any possible string
// without it being truncated. Then specify that size as the minimum
// size for all of these text strings. (If this minimum size is not
// determined in this fashion, then it is possible for the display of
// one or more of these strings to be truncated after different Pcbnew
// layers are selected.)
if( ii == 0 )
{
goodSize = text->GetSize();
for( int jj = 0; jj < GERBER_DRAWLAYERS_COUNT; ++jj )
{
text->SetLabel( GetPCBDefaultLayerName( jj ) );
if( goodSize.x < text->GetSize().x )
goodSize.x = text->GetSize().x;
}
text->SetLabel( msg ); // Reset label to default text
}
text->SetMinSize( goodSize );
flexColumnBoxSizer->Add( text, 1, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
m_layersList[ii] = text;
}
// If the user has never stored any Gerber to KiCad layer mapping,
// then disable the button to retrieve it
if( config->m_GerberToPcbLayerMapping.size() == 0 )
m_buttonRetrieve->Enable( false );
std::vector<int> gerber2KicadMapping;
// See how many of the loaded Gerbers can be mapped to KiCad layers automatically
int numMappedGerbers = findKnownGerbersLoaded( gerber2KicadMapping );
if( numMappedGerbers > 0 )
{
// See if the user wants to map the Altium Gerbers to known KiCad PCB layers
int returnVal = wxMessageBox(
_( "Gerbers with known layers: " + wxString::Format( wxT( "%i" ), numMappedGerbers )
+ "\n\nAssign to matching KiCad PCB layers?" ),
_( "Automatic Layer Assignment" ), wxOK | wxCANCEL | wxOK_DEFAULT );
if( returnVal == wxOK )
{
for( int ii = 0; ii < m_gerberActiveLayersCount; ii++ )
{
int currLayer = gerber2KicadMapping[ii];
// Default to "Do Not Export" for unselected or undefined layer
if( currLayer == UNSELECTED_LAYER )
{
m_layersList[ii]->SetLabel( _( "Do not export" ) );
m_layersList[ii]->SetForegroundColour( *wxBLUE );
// Set the layer internally to unselected
m_layersLookUpTable[ii] = UNSELECTED_LAYER;
}
else
{
m_layersList[ii]->SetLabel( GetPCBDefaultLayerName( currLayer ) );
m_layersList[ii]->SetForegroundColour( wxColour( 255, 0, 128 ) );
// Set the layer internally to the matching KiCad layer
m_layersLookUpTable[ii] = currLayer;
}
}
}
}
}
void LAYERS_MAP_DIALOG::normalizeBrdLayersCount()
{
if( ( m_exportBoardCopperLayersCount & 1 ) )
m_exportBoardCopperLayersCount++;
if( m_exportBoardCopperLayersCount > GERBER_DRAWLAYERS_COUNT )
m_exportBoardCopperLayersCount = GERBER_DRAWLAYERS_COUNT;
if( m_exportBoardCopperLayersCount < 2 )
m_exportBoardCopperLayersCount = 2;
}
void LAYERS_MAP_DIALOG::OnBrdLayersCountSelection( wxCommandEvent& event )
{
int id = event.GetSelection();
m_exportBoardCopperLayersCount = ( id + 1 ) * 2;
}
void LAYERS_MAP_DIALOG::OnResetClick( wxCommandEvent& event )
{
for( int ii = 0; ii < m_gerberActiveLayersCount; ++ii )
{
m_layersLookUpTable[ii] = UNSELECTED_LAYER;
m_layersList[ii]->SetLabel( _( "Do not export" ) );
m_layersList[ii]->SetForegroundColour( *wxBLUE );
m_buttonTable[ii] = ii;
}
// wxWidgets doesn't appear to invalidate / update the StaticText displays for color change
// so we do it manually
Refresh();
Update();
}
void LAYERS_MAP_DIALOG::OnStoreSetup( wxCommandEvent& event )
{
GERBVIEW_SETTINGS* config = static_cast<GERBVIEW_SETTINGS*>( Kiface().KifaceSettings() );
config->m_BoardLayersCount = m_exportBoardCopperLayersCount;
config->m_GerberToPcbLayerMapping.clear();
for( int ii = 0; ii < GERBER_DRAWLAYERS_COUNT; ++ii )
config->m_GerberToPcbLayerMapping.push_back( m_layersLookUpTable[ii] );
// Enable the "Get Stored Choice" button in case it was disabled in "initDialog()"
// due to no previously stored choices.
m_buttonRetrieve->Enable( true );
}
void LAYERS_MAP_DIALOG::OnGetSetup( wxCommandEvent& event )
{
GERBVIEW_SETTINGS* config = static_cast<GERBVIEW_SETTINGS*>( Kiface().KifaceSettings() );
m_exportBoardCopperLayersCount = config->m_BoardLayersCount;
normalizeBrdLayersCount();
int idx = ( m_exportBoardCopperLayersCount / 2 ) - 1;
m_comboCopperLayersCount->SetSelection( idx );
for( int ii = 0; ii < GERBER_DRAWLAYERS_COUNT; ++ii )
{
// Ensure the layer mapping in config exists for this layer, and store it
if( (size_t)ii >= config->m_GerberToPcbLayerMapping.size() )
break;
m_layersLookUpTable[ii] = config->m_GerberToPcbLayerMapping[ ii ];
}
for( int ii = 0; ii < m_gerberActiveLayersCount; ii++ )
{
int layer = m_layersLookUpTable[ii];
if( layer == UNSELECTED_LAYER )
{
m_layersList[ii]->SetLabel( _( "Do not export" ) );
m_layersList[ii]->SetForegroundColour( *wxBLUE );
}
else if( layer == UNDEFINED_LAYER )
{
m_layersList[ii]->SetLabel( _( "Hole data" ) );
m_layersList[ii]->SetForegroundColour( wxColour( 255, 0, 128 ) );
}
else
{
m_layersList[ii]->SetLabel( GetPCBDefaultLayerName( layer ) );
m_layersList[ii]->SetForegroundColour( wxColour( 255, 0, 128 ) );
}
}
}
void LAYERS_MAP_DIALOG::OnSelectLayer( wxCommandEvent& event )
{
int ii = event.GetId() - ID_BUTTON_0;
if( ii < 0 || ii >= GERBER_DRAWLAYERS_COUNT )
{
wxFAIL_MSG( wxT( "Bad layer id" ) );
return;
}
int jj = m_layersLookUpTable[ m_buttonTable[ii] ];
if( jj != UNSELECTED_LAYER && jj != UNDEFINED_LAYER && !IsValidLayer( jj ) )
jj = B_Cu; // (Defaults to "Copper" layer.)
// Get file name of Gerber loaded on this layer
wxFileName fn( m_Parent->GetGerberLayout()->GetImagesList()->GetGbrImage( ii )->m_FileName );
// Surround it with quotes to make it stand out on the dialog title bar
wxString layerName = wxT( "\"" ) + fn.GetFullName() + wxT( "\"" );
// Display dialog to let user select a layer for the Gerber
jj = m_Parent->SelectPCBLayer( jj, m_exportBoardCopperLayersCount, layerName );
if( jj != UNSELECTED_LAYER && jj != UNDEFINED_LAYER && !IsValidLayer( jj ) )
return;
if( jj != m_layersLookUpTable[m_buttonTable[ii]] )
{
m_layersLookUpTable[m_buttonTable[ii]] = jj;
if( jj == UNSELECTED_LAYER )
{
m_layersList[ii]->SetLabel( _( "Do not export" ) );
// Change the text color to blue (to highlight
// that this layer is *not* being exported)
m_layersList[ii]->SetForegroundColour( *wxBLUE );
}
else if( jj == UNDEFINED_LAYER )
{
m_layersList[ii]->SetLabel( _( "Hole data" ) );
// Change the text color to fuchsia (to highlight
// that this layer *is* being exported)
m_layersList[ii]->SetForegroundColour( wxColour( 255, 0, 128 ) );
}
else
{
m_layersList[ii]->SetLabel( GetPCBDefaultLayerName( jj ) );
// Change the text color to fuchsia (to highlight
// that this layer *is* being exported)
m_layersList[ii]->SetForegroundColour( wxColour( 255, 0, 128 ) );
}
}
// wxWidgets doesn't appear to invalidate / update the StaticText displays for color change
// so we do it manually
Refresh();
Update();
}
bool LAYERS_MAP_DIALOG::TransferDataFromWindow()
{
if( !wxDialog::TransferDataFromWindow() )
return false;
// Board must have enough copper layers to handle selected internal layers.
normalizeBrdLayersCount();
int inner_layer_max = 0;
for( int ii = 0; ii < GERBER_DRAWLAYERS_COUNT; ++ii )
{
if( m_layersLookUpTable[ii] < F_Cu )
{
if( m_layersLookUpTable[ii ] > inner_layer_max )
inner_layer_max = m_layersLookUpTable[ii];
}
}
// inner_layer_max must be less than (or equal to the number of internal copper layers
// internal copper layers = m_exportBoardCopperLayersCount-2
if( inner_layer_max > m_exportBoardCopperLayersCount-2 )
{
wxMessageBox( _( "Exported board does not have enough copper layers to handle selected "
"inner layers" ) );
return false;
}
return true;
}
int LAYERS_MAP_DIALOG::findKnownGerbersLoaded( std::vector<int>& aGerber2KicadMapping )
{
int numKnownGerbers = 0;
// We can automatically map Gerbers using different techniques. The first thing we
// try is to see if any of the loaded Gerbers were created by or use the
// Altium/Protel file extensions
numKnownGerbers += findNumAltiumGerbersLoaded( aGerber2KicadMapping );
// Next we check if any of the loaded Gerbers are X2 Gerbers and if they contain
// layer information in "File Functions". For info about X2 Gerbers see
// http://www.ucamco.com/files/downloads/file/81/the_gerber_file_format_specification.pdf
numKnownGerbers += findNumX2GerbersLoaded( aGerber2KicadMapping );
// Finally, check if any of the loaded Gerbers use the KiCad naming conventions
numKnownGerbers += findNumKiCadGerbersLoaded( aGerber2KicadMapping );
return numKnownGerbers;
}
int LAYERS_MAP_DIALOG::findNumAltiumGerbersLoaded( std::vector<int>& aGerber2KicadMapping )
{
// The next comment preserves initializer formatting below it
// clang-format off
// This map contains the known Altium file extensions for Gerbers that we care about,
// along with their corresponding KiCad layer
std::map<wxString, PCB_LAYER_ID> altiumExt{
{ wxT( "GTL" ), F_Cu }, // Top copper
{ wxT( "G1" ), In1_Cu }, // Inner layers 1 - 30
{ wxT( "G2" ), In2_Cu },
{ wxT( "G3" ), In3_Cu },
{ wxT( "G4" ), In4_Cu },
{ wxT( "G5" ), In5_Cu },
{ wxT( "G6" ), In6_Cu },
{ wxT( "G7" ), In7_Cu },
{ wxT( "G8" ), In8_Cu },
{ wxT( "G9" ), In9_Cu },
{ wxT( "G10" ), In10_Cu },
{ wxT( "G11" ), In11_Cu },
{ wxT( "G12" ), In12_Cu },
{ wxT( "G13" ), In13_Cu },
{ wxT( "G14" ), In14_Cu },
{ wxT( "G15" ), In15_Cu },
{ wxT( "G16" ), In16_Cu },
{ wxT( "G17" ), In17_Cu },
{ wxT( "G18" ), In18_Cu },
{ wxT( "G19" ), In19_Cu },
{ wxT( "G20" ), In20_Cu },
{ wxT( "G21" ), In21_Cu },
{ wxT( "G22" ), In22_Cu },
{ wxT( "G23" ), In23_Cu },
{ wxT( "G24" ), In24_Cu },
{ wxT( "G25" ), In25_Cu },
{ wxT( "G26" ), In26_Cu },
{ wxT( "G27" ), In27_Cu },
{ wxT( "G28" ), In28_Cu },
{ wxT( "G29" ), In29_Cu },
{ wxT( "G30" ), In30_Cu },
{ wxT( "GBL" ), B_Cu }, // Bottom copper
{ wxT( "GTP" ), F_Paste }, // Paste top
{ wxT( "GBP" ), B_Paste }, // Paste bottom
{ wxT( "GTO" ), F_SilkS }, // Silkscreen top
{ wxT( "GBO" ), B_SilkS }, // Silkscreen bottom
{ wxT( "GTS" ), F_Mask }, // Soldermask top
{ wxT( "GBS" ), B_Mask }, // Soldermask bottom
{ wxT( "GM1" ), Eco1_User }, // Altium mechanical layer 1
{ wxT( "GM2" ), Eco2_User }, // Altium mechanical layer 2
{ wxT( "GKO" ), Edge_Cuts } // PCB Outline
};
// clang-format on
std::map<wxString, PCB_LAYER_ID>::iterator it;
int numAltiumMatches = 0; // Assume we won't find Altium Gerbers
GERBER_FILE_IMAGE_LIST* images = m_Parent->GetGerberLayout()->GetImagesList();
// If the passed vector isn't empty but is too small to hold the loaded
// Gerbers, then bail because something isn't right.
if( ( aGerber2KicadMapping.size() != 0 )
&& ( aGerber2KicadMapping.size() != (size_t) m_gerberActiveLayersCount ) )
return numAltiumMatches;
// If the passed vector is empty, set it to the same number of elements as there
// are loaded Gerbers, and set each to "UNSELECTED_LAYER"
if( aGerber2KicadMapping.size() == 0 )
aGerber2KicadMapping.assign( m_gerberActiveLayersCount, UNSELECTED_LAYER );
// Loop through all loaded Gerbers looking for any with Altium specific extensions
for( int ii = 0; ii < m_gerberActiveLayersCount; ii++ )
{
if( images->GetGbrImage( ii ) )
{
// Get file name of Gerber loaded on this layer.
wxFileName fn( images->GetGbrImage( ii )->m_FileName );
// Get uppercase version of file extension
wxString FileExt = fn.GetExt();
FileExt.MakeUpper();
// Check for matching Altium Gerber file extension we'll handle
it = altiumExt.find( FileExt );
if( it != altiumExt.end() )
{
// We got a match, so store the KiCad layer number. We verify it's set to
// "UNSELECTED_LAYER" in case the passed vector already had entries
// matched to other known Gerber files. This will preserve them.
if( aGerber2KicadMapping[ii] == UNSELECTED_LAYER )
{
aGerber2KicadMapping[ii] = it->second;
numAltiumMatches++;
}
}
}
}
// Return number of Altium Gerbers we found. Each index in the passed vector corresponds to
// a loaded Gerber layer, and the entry will contain the index to the matching
// KiCad layer for Altium Gerbers, or "UNSELECTED_LAYER" for the rest.
return numAltiumMatches;
}
int LAYERS_MAP_DIALOG::findNumKiCadGerbersLoaded( std::vector<int>& aGerber2KicadMapping )
{
// The next comment preserves initializer formatting below it
// clang-format off
// This map contains the known KiCad suffixes used for Gerbers that we care about,
// along with their corresponding KiCad layer
std::map<wxString, PCB_LAYER_ID> kicadLayers
{
{ "-F_Cu", F_Cu },
{ "-In1_Cu", In1_Cu },
{ "-In2_Cu", In2_Cu },
{ "-In3_Cu", In3_Cu },
{ "-In4_Cu", In4_Cu },
{ "-In5_Cu", In5_Cu },
{ "-In6_Cu", In6_Cu },
{ "-In7_Cu", In7_Cu },
{ "-In8_Cu", In8_Cu },
{ "-In9_Cu", In9_Cu },
{ "-In10_Cu", In10_Cu },
{ "-In11_Cu", In11_Cu },
{ "-In12_Cu", In12_Cu },
{ "-In13_Cu", In13_Cu },
{ "-In14_Cu", In14_Cu },
{ "-In15_Cu", In15_Cu },
{ "-In16_Cu", In16_Cu },
{ "-In17_Cu", In17_Cu },
{ "-In18_Cu", In18_Cu },
{ "-In19_Cu", In19_Cu },
{ "-In20_Cu", In20_Cu },
{ "-In21_Cu", In21_Cu },
{ "-In22_Cu", In22_Cu },
{ "-In23_Cu", In23_Cu },
{ "-In24_Cu", In24_Cu },
{ "-In25_Cu", In25_Cu },
{ "-In26_Cu", In26_Cu },
{ "-In27_Cu", In27_Cu },
{ "-In28_Cu", In28_Cu },
{ "-In29_Cu", In29_Cu },
{ "-In30_Cu", In30_Cu },
{ "-B_Cu", B_Cu },
{ "-B_Adhes", B_Adhes },
{ "-F_Adhes", F_Adhes },
{ "-B_Paste", B_Paste },
{ "-F_Paste", F_Paste },
{ "-B_SilkS", B_SilkS },
{ "-F_SilkS", F_SilkS },
{ "-B_Mask", B_Mask },
{ "-F_Mask", F_Mask },
{ "-Dwgs_User", Dwgs_User },
{ "-Cmts_User", Cmts_User },
{ "-Eco1_User", Eco1_User },
{ "-Eco2_User", Eco2_User },
{ "-Edge_Cuts", Edge_Cuts }
};
// clang-format on
std::map<wxString, PCB_LAYER_ID>::iterator it;
int numKicadMatches = 0; // Assume we won't find KiCad Gerbers
GERBER_FILE_IMAGE_LIST* images = m_Parent->GetGerberLayout()->GetImagesList();
// If the passed vector isn't empty but is too small to hold the loaded
// Gerbers, then bail because something isn't right.
if( ( aGerber2KicadMapping.size() != 0 )
&& ( aGerber2KicadMapping.size() < (size_t) m_gerberActiveLayersCount ) )
return numKicadMatches;
// If the passed vector is empty, set it to the same number of elements as there
// are loaded Gerbers, and set each to "UNSELECTED_LAYER"
if( aGerber2KicadMapping.size() == 0 )
aGerber2KicadMapping.assign( m_gerberActiveLayersCount, UNSELECTED_LAYER );
// Loop through all loaded Gerbers looking for any with KiCad specific layer names
for( int ii = 0; ii < m_gerberActiveLayersCount; ii++ )
{
if( images->GetGbrImage( ii ) )
{
// Get file name of Gerber loaded on this layer.
wxFileName fn( images->GetGbrImage( ii )->m_FileName );
wxString layerName = fn.GetName();
// To create Gerber file names, KiCad appends a suffix consisting of a "-" and the
// name of the layer to the project name. We need to isolate the suffix if present
// and see if it's a known KiCad layer name. Start by looking for the last "-" in
// the file name.
int dashPos = layerName.Find( '-', true );
// If one was found, isolate the suffix from the "-" to the end of the file name
wxString suffix;
if( dashPos != wxNOT_FOUND )
suffix = layerName.Right( layerName.length() - dashPos );
// Check if the string we've isolated matches any known KiCad layer names
it = kicadLayers.find( suffix );
if( it != kicadLayers.end() )
{
// We got a match, so store the KiCad layer number. We verify it's set to
// "UNSELECTED_LAYER" in case the passed vector already had entries
// matched to other known Gerber files. This will preserve them.
if( aGerber2KicadMapping[ii] == UNSELECTED_LAYER )
{
aGerber2KicadMapping[ii] = it->second;
numKicadMatches++;
}
}
}
}
// Return number of KiCad Gerbers we found. Each index in the passed vector corresponds to
// a loaded Gerber layer, and the entry will contain the index to the matching
// KiCad layer for KiCad Gerbers, or "UNSELECTED_LAYER" for the rest.
return numKicadMatches;
}
int LAYERS_MAP_DIALOG::findNumX2GerbersLoaded( std::vector<int>& aGerber2KicadMapping )
{
// The next comment preserves initializer formatting below it
// clang-format off
// This map contains the known KiCad X2 "File Function" values used for Gerbers that we
// care about, along with their corresponding KiCad layer
std::map<wxString, PCB_LAYER_ID> kicadLayers
{
{ wxT( "Top" ), F_Cu },
{ wxT( "L2" ), In1_Cu },
{ wxT( "L3" ), In2_Cu },
{ wxT( "L4" ), In3_Cu },
{ wxT( "L5" ), In4_Cu },
{ wxT( "L6" ), In5_Cu },
{ wxT( "L7" ), In6_Cu },
{ wxT( "L8" ), In7_Cu },
{ wxT( "L9" ), In8_Cu },
{ wxT( "L10" ), In9_Cu },
{ wxT( "L11" ), In10_Cu },
{ wxT( "L12" ), In11_Cu },
{ wxT( "L13" ), In12_Cu },
{ wxT( "L14" ), In13_Cu },
{ wxT( "L15" ), In14_Cu },
{ wxT( "L16" ), In15_Cu },
{ wxT( "L17" ), In16_Cu },
{ wxT( "L18" ), In17_Cu },
{ wxT( "L19" ), In18_Cu },
{ wxT( "L20" ), In19_Cu },
{ wxT( "L21" ), In20_Cu },
{ wxT( "L22" ), In21_Cu },
{ wxT( "L23" ), In22_Cu },
{ wxT( "L24" ), In23_Cu },
{ wxT( "L25" ), In24_Cu },
{ wxT( "L26" ), In25_Cu },
{ wxT( "L27" ), In26_Cu },
{ wxT( "L28" ), In27_Cu },
{ wxT( "L29" ), In28_Cu },
{ wxT( "L30" ), In29_Cu },
{ wxT( "Bot" ), B_Cu },
{ wxT( "BotGlue" ), B_Adhes },
{ wxT( "TopGlue" ), F_Adhes },
{ wxT( "BotPaste" ), B_Paste },
{ wxT( "TopPaste" ), F_Paste },
{ wxT( "BotLegend" ), B_SilkS },
{ wxT( "TopLegend" ), F_SilkS },
{ wxT( "BotSoldermask" ), B_Mask },
{ wxT( "TopSoldermask" ), F_Mask },
{ wxT( "FabricationDrawing" ), Dwgs_User },
{ wxT( "OtherDrawing" ), Cmts_User },
{ wxT( "TopAssemblyDrawing" ), Eco1_User },
{ wxT( "BotAssemblyDrawing" ), Eco2_User },
{ wxT( "PProfile" ), Edge_Cuts }, // Plated PCB outline
{ wxT( "NPProfile" ), Edge_Cuts } // Non-plated PCB outline
};
// clang-format on
std::map<wxString, PCB_LAYER_ID>::iterator it;
int numKicadMatches = 0; // Assume we won't find KiCad Gerbers
wxString mapThis;
GERBER_FILE_IMAGE_LIST* images = m_Parent->GetGerberLayout()->GetImagesList();
// If the passed vector isn't empty but is too small to hold the loaded
// Gerbers, then bail because something isn't right.
if( ( aGerber2KicadMapping.size() != 0 )
&& ( aGerber2KicadMapping.size() < (size_t) m_gerberActiveLayersCount ) )
return numKicadMatches;
// If the passed vector is empty, set it to the same number of elements as there
// are loaded Gerbers, and set each to "UNSELECTED_LAYER"
if( aGerber2KicadMapping.size() == 0 )
aGerber2KicadMapping.assign( m_gerberActiveLayersCount, UNSELECTED_LAYER );
// Loop through all loaded Gerbers looking for any with X2 File Functions
for( int ii = 0; ii < m_gerberActiveLayersCount; ii++ )
{
if( images->GetGbrImage( ii ) )
{
X2_ATTRIBUTE_FILEFUNCTION* x2 = images->GetGbrImage( ii )->m_FileFunction;
mapThis = "";
if( images->GetGbrImage( ii )->m_IsX2_file )
{
wxCHECK( x2, numKicadMatches );
if( x2->IsCopper() )
{
// This is a copper layer, so figure out which one
mapThis = x2->GetBrdLayerSide(); // Returns "Top", "Bot" or "Inr"
// To map inner layers properly, we need the layer number
if( mapThis.IsSameAs( wxT( "Inr" ), false ) )
mapThis = x2->GetBrdLayerId(); // Returns "L2", "L5", etc
}
else
{
// Create strings like "TopSolderMask" or "BotPaste" for non-copper layers
mapThis << x2->GetBrdLayerId() << x2->GetFileType();
}
// Check if the string we've isolated matches any known X2 layer names
it = kicadLayers.find( mapThis );
if( it != kicadLayers.end() )
{
// We got a match, so store the KiCad layer number. We verify it's set to
// "UNSELECTED_LAYER" in case the passed vector already had entries
// matched to other known Gerber files. This will preserve them.
if( aGerber2KicadMapping[ii] == UNSELECTED_LAYER )
{
aGerber2KicadMapping[ii] = it->second;
numKicadMatches++;
}
}
}
}
}
return numKicadMatches;
}