KiCad PCB EDA Suite
select_layers_to_pcb.cpp
Go to the documentation of this file.
1 
6 /*
7  * This program source code file is part of KiCad, a free EDA CAD application.
8  *
9  * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, you may find one here:
23  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
24  * or you may search the http://www.gnu.org website for the version 2 license,
25  * or you may write to the Free Software Foundation, Inc.,
26  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27  */
28 
29 #include <fctsys.h>
30 #include <gerber_file_image.h>
31 #include <gerber_file_image_list.h>
32 #include <gerbview.h>
33 #include <gerbview_frame.h>
34 #include <gerbview_id.h>
35 #include <gerbview_settings.h>
36 #include <kiface_i.h>
38 
39 #include <select_layers_to_pcb.h>
40 
41 // Imported function
42 extern const wxString GetPCBDefaultLayerName( LAYER_NUM aLayerNumber );
43 
48 };
49 
50 
51 /*
52  * This dialog shows the gerber files loaded, and allows user to choose:
53  * what gerber file and what board layer are used
54  * the number of copper layers
55  */
56 
58 
59 
60 BEGIN_EVENT_TABLE( LAYERS_MAP_DIALOG, LAYERS_MAP_DIALOG_BASE )
62  wxEVT_COMMAND_BUTTON_CLICKED,
63  LAYERS_MAP_DIALOG::OnSelectLayer )
64 END_EVENT_TABLE()
65 
66 
68  LAYERS_MAP_DIALOG_BASE( parent )
69 {
70  m_Parent = parent;
71  initDialog();
72 
73  // Resize the dialog
74  Layout();
75  GetSizer()->SetSizeHints( this );
76  Centre();
77 }
78 
79 
81 {
82  wxStaticText* label;
83  wxStaticText* text;
84  int item_ID;
85  wxString msg;
86  wxSize goodSize;
87 
88  for( int ii = 0; ii < GERBER_DRAWLAYERS_COUNT; ++ii )
89  {
90  // Specify the default value for each member of these arrays.
91  m_buttonTable[ii] = -1;
93  }
94 
95  // Ensure we have:
96  // At least 2 copper layers and less than max pcb copper layers count
97  // Even number of layers because a board *must* have even layers count
99 
100  int idx = ( m_exportBoardCopperLayersCount / 2 ) - 1;
101  m_comboCopperLayersCount->SetSelection( idx );
102 
105 
106  for( unsigned ii = 0; ii < GERBER_DRAWLAYERS_COUNT; ++ii )
107  {
108  if( images->GetGbrImage( ii ) == NULL )
109  break;
110 
113  }
114 
115  if( m_gerberActiveLayersCount <= GERBER_DRAWLAYERS_COUNT / 2 ) // Only one list is enough
116  m_staticlineSep->Hide();
117 
118  wxFlexGridSizer* flexColumnBoxSizer = m_flexLeftColumnBoxSizer;
119 
120  for( int ii = 0; ii < m_gerberActiveLayersCount; ii++ )
121  {
122  // Each Gerber layer has an associated static text string (to
123  // identify that layer), a button (for invoking a child dialog
124  // box to change which Pcbnew layer that the Gerber layer is
125  // mapped to), and a second static text string (to depict which
126  // Pcbnew layer that the Gerber layer has been mapped to). Each
127  // of those items are placed into the left hand column, middle
128  // column, and right hand column (respectively) of the Flexgrid
129  // sizer, and the color of the second text string is set to
130  // fuchsia or blue (to respectively indicate whether the Gerber
131  // layer has been mapped to a Pcbnew layer or is not being
132  // exported at all). (Experimentation has shown that if a text
133  // control is used to depict which Pcbnew layer that each Gerber
134  // layer is mapped to (instead of a static text string), then
135  // those controls do not behave in a fully satisfactory manner
136  // in the Linux version. Even when the read-only attribute is
137  // specified for all of those controls, they can still be selected
138  // when the arrow keys or Tab key is used to step through all of
139  // the controls within the dialog box, and directives to set the
140  // foreground color of the text of each such control to blue (to
141  // indicate that the text is of a read-only nature) are disregarded.
142  // Specify a FlexGrid sizer with an appropriate number of rows
143  // and three columns. If nb_items < 16, then the number of rows
144  // is nb_items; otherwise, the number of rows is 16 (with two
145  // separate columns of controls being used if nb_items > 16).
146 
147  if( ii == GERBER_DRAWLAYERS_COUNT / 2 )
148  flexColumnBoxSizer = m_flexRightColumnBoxSizer;
149 
150  // Provide a text string to identify the Gerber layer
151  msg.Printf( _( "Layer %d" ), m_buttonTable[ii] + 1 );
152 
153  label = new wxStaticText( this, wxID_STATIC, msg );
154  flexColumnBoxSizer->Add( label, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
155 
156  /* Add file name and extension without path. */
157  wxFileName fn( images->GetGbrImage( ii )->m_FileName );
158  label = new wxStaticText( this, wxID_STATIC, fn.GetFullName() );
159  flexColumnBoxSizer->Add( label, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
160 
161  // Provide a button for this layer (which will invoke a child dialog box)
162  item_ID = ID_BUTTON_0 + ii;
163  wxButton * Button = new wxButton( this, item_ID, wxT( "..." ), wxDefaultPosition,
164  wxDefaultSize, wxBU_EXACTFIT );
165 
166  flexColumnBoxSizer->Add( Button, 0, wxALIGN_CENTER_VERTICAL | wxALL );
167 
168  // Provide another text string to specify which Pcbnew layer that this
169  // Gerber layer is mapped to. All layers initially default to
170  // "Do NotExport" (which corresponds to UNSELECTED_LAYER). Whenever
171  // a layer is set to "Do Not Export" it's displayed in blue. When a
172  // user selects a specific KiCad layer to map to, it's displayed in
173  // magenta which indicates it will be exported.
174  item_ID = ID_TEXT_0 + ii;
175 
176  // All layers default to "Do Not Export" displayed in blue
177  msg = _( "Do not export" );
178  text = new wxStaticText( this, item_ID, msg );
179  text->SetForegroundColour( *wxBLUE );
180 
181  // When the first of these text strings is being added, determine what
182  // size is necessary to to be able to display any possible string
183  // without it being truncated. Then specify that size as the minimum
184  // size for all of these text strings. (If this minimum size is not
185  // determined in this fashion, then it is possible for the display of
186  // one or more of these strings to be truncated after different Pcbnew
187  // layers are selected.)
188 
189  if( ii == 0 )
190  {
191  goodSize = text->GetSize();
192 
193  for( LAYER_NUM jj = 0; jj < GERBER_DRAWLAYERS_COUNT; ++jj )
194  {
195  text->SetLabel( GetPCBDefaultLayerName( jj ) );
196 
197  if( goodSize.x < text->GetSize().x )
198  goodSize.x = text->GetSize().x;
199  }
200  text->SetLabel( msg ); // Reset label to default text
201  }
202 
203  text->SetMinSize( goodSize );
204  flexColumnBoxSizer->Add( text, 1, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
205 
206  m_layersList[ii] = text;
207  }
208 
209  std::vector<int> gerber2KicadMapping;
210 
211  // See how many of the loaded Gerbers have Altium file extensions
212  int numAltiumGerbers = findNumAltiumGerbersLoaded( gerber2KicadMapping );
213 
214  if( numAltiumGerbers > 0 )
215  {
216  // See if the user wants to map the Altium Gerbers to known KiCad PCB layers
217  int returnVal = wxMessageBox(
218  _( "Gerbers with known layers: " + wxString::Format( wxT( "%i" ), numAltiumGerbers )
219  + "\n\nAssign to matching KiCad PCB layers?" ),
220  _( "Automatic Layer Assignment" ), wxOK | wxCANCEL | wxOK_DEFAULT );
221 
222  if( returnVal == wxOK )
223  {
224  for( int ii = 0; ii < m_gerberActiveLayersCount; ii++ )
225  {
226  int currLayer = gerber2KicadMapping[ii];
227 
228  // Default to "Do Not Export" for unselected or undefined layer
229  if( ( currLayer == UNSELECTED_LAYER ) || ( currLayer == UNDEFINED_LAYER ) )
230  {
231  m_layersList[ii]->SetLabel( _( "Do not export" ) );
232  m_layersList[ii]->SetForegroundColour( *wxBLUE );
233 
234  // Set the layer internally to unselected
236  }
237  else
238  {
239  m_layersList[ii]->SetLabel( GetPCBDefaultLayerName( currLayer ) );
240  m_layersList[ii]->SetForegroundColour( wxColour( 255, 0, 128 ) );
241 
242  // Set the layer internally to the matching KiCad layer
243  m_layersLookUpTable[ii] = currLayer;
244  }
245  }
246  }
247  }
248 }
249 
250 /* Ensure m_exportBoardCopperLayersCount = 2 to BOARD_COPPER_LAYERS_MAX_COUNT
251  * and it is an even value because Boards have always an even layer count
252  */
254 {
255  if( ( m_exportBoardCopperLayersCount & 1 ) )
257 
260 
263 
264 }
265 
266 /*
267  * Called when user change the current board copper layers count
268  */
269 void LAYERS_MAP_DIALOG::OnBrdLayersCountSelection( wxCommandEvent& event )
270 {
271  int id = event.GetSelection();
272  m_exportBoardCopperLayersCount = (id+1) * 2;
273 }
274 
275 /*
276  * reset pcb layers selection to the default value
277  */
278 void LAYERS_MAP_DIALOG::OnResetClick( wxCommandEvent& event )
279 {
280  wxString msg;
281  int ii;
282  LAYER_NUM layer;
283  for( ii = 0, layer = 0; ii < m_gerberActiveLayersCount; ii++, ++layer )
284  {
286  m_layersList[ii]->SetLabel( _( "Do not export" ) );
287  m_layersList[ii]->SetForegroundColour( *wxBLUE );
288  m_buttonTable[ii] = ii;
289  }
290 }
291 
292 
293 /* Stores the current layers selection in config
294  */
295 void LAYERS_MAP_DIALOG::OnStoreSetup( wxCommandEvent& event )
296 {
297  auto config = static_cast<GERBVIEW_SETTINGS*>( Kiface().KifaceSettings() );
298  config->m_BoardLayersCount = m_exportBoardCopperLayersCount;
299 
300  config->m_GerberToPcbLayerMapping.clear();
301 
302  for( int ii = 0; ii < GERBER_DRAWLAYERS_COUNT; ++ii )
303  {
304  config->m_GerberToPcbLayerMapping.push_back( m_layersLookUpTable[ii] );
305  }
306 }
307 
308 void LAYERS_MAP_DIALOG::OnGetSetup( wxCommandEvent& event )
309 {
310  GERBVIEW_SETTINGS* config = static_cast<GERBVIEW_SETTINGS*>( Kiface().KifaceSettings() );
311 
314 
315  int idx = ( m_exportBoardCopperLayersCount / 2 ) - 1;
316  m_comboCopperLayersCount->SetSelection( idx );
317 
318  for( int ii = 0; ii < GERBER_DRAWLAYERS_COUNT; ++ii )
319  {
320  // Ensure the layer mapping in config exists for this layer, and store it
321  if( (size_t)ii >= config->m_GerberToPcbLayerMapping.size() )
322  break;
323 
325  }
326 
327  for( int ii = 0; ii < m_gerberActiveLayersCount; ii++ )
328  {
329  LAYER_NUM layer = m_layersLookUpTable[ii];
330  if( layer == UNSELECTED_LAYER )
331  {
332  m_layersList[ii]->SetLabel( _( "Do not export" ) );
333  m_layersList[ii]->SetForegroundColour( *wxBLUE );
334  }
335  else if( layer == UNDEFINED_LAYER )
336  {
337  m_layersList[ii]->SetLabel( _( "Hole data" ) );
338  m_layersList[ii]->SetForegroundColour( wxColour( 255, 0, 128 ) );
339  }
340  else
341  {
342  m_layersList[ii]->SetLabel( GetPCBDefaultLayerName( layer ) );
343  m_layersList[ii]->SetForegroundColour( wxColour( 255, 0, 128 ) );
344  }
345  }
346 }
347 
348 void LAYERS_MAP_DIALOG::OnSelectLayer( wxCommandEvent& event )
349 {
350  int ii;
351 
352  ii = event.GetId() - ID_BUTTON_0;
353 
354  if( (ii < 0) || (ii >= GERBER_DRAWLAYERS_COUNT) )
355  {
356  wxFAIL_MSG( wxT("Bad layer id") );
357  return;
358  }
359 
361 
362  if( jj != UNSELECTED_LAYER && jj != UNDEFINED_LAYER && !IsValidLayer( jj ) )
363  jj = B_Cu; // (Defaults to "Copper" layer.)
364 
366 
367  if( jj != UNSELECTED_LAYER && jj != UNDEFINED_LAYER && !IsValidLayer( jj ) )
368  return;
369 
370  if( jj != m_layersLookUpTable[m_buttonTable[ii]] )
371  {
373 
374  if( jj == UNSELECTED_LAYER )
375  {
376  m_layersList[ii]->SetLabel( _( "Do not export" ) );
377 
378  // Change the text color to blue (to highlight
379  // that this layer is *not* being exported)
380  m_layersList[ii]->SetForegroundColour( *wxBLUE );
381  }
382  else if( jj == UNDEFINED_LAYER )
383  {
384  m_layersList[ii]->SetLabel( _( "Hole data" ) );
385 
386  // Change the text color to fuchsia (to highlight
387  // that this layer *is* being exported)
388  m_layersList[ii]->SetForegroundColour( wxColour( 255, 0, 128 ) );
389  }
390  else
391  {
392  m_layersList[ii]->SetLabel( GetPCBDefaultLayerName( jj ) );
393 
394  // Change the text color to fuchsia (to highlight
395  // that this layer *is* being exported)
396  m_layersList[ii]->SetForegroundColour( wxColour( 255, 0, 128 ) );
397  }
398  }
399 }
400 
401 
402 void LAYERS_MAP_DIALOG::OnOkClick( wxCommandEvent& event )
403 {
404  /* Make some test about copper layers:
405  * Board must have enough copper layers to handle selected internal layers
406  */
408 
409  int inner_layer_max = 0;
410  for( int ii = 0; ii < GERBER_DRAWLAYERS_COUNT; ++ii )
411  {
412  if( m_layersLookUpTable[ii] < F_Cu )
413  {
414  if( m_layersLookUpTable[ii ] > inner_layer_max )
415  inner_layer_max = m_layersLookUpTable[ii];
416  }
417  }
418 
419  // inner_layer_max must be less than (or equal to) the number of
420  // internal copper layers
421  // internal copper layers = m_exportBoardCopperLayersCount-2
422  if( inner_layer_max > m_exportBoardCopperLayersCount-2 )
423  {
424  wxMessageBox(
425  _("Exported board does not have enough copper layers to handle selected inner layers") );
426  return;
427  }
428 
429  EndModal( wxID_OK );
430 }
431 
432 int LAYERS_MAP_DIALOG::findNumAltiumGerbersLoaded( std::vector<int>& aGerber2KicadMapping )
433 {
434  // The next comment preserves initializer formatting below it
435  // clang-format off
436  // This map contains the known Altium file extensions for Gerbers that we care about,
437  // along with their corresponding KiCad layer
438  std::map<wxString, PCB_LAYER_ID> altiumExt{
439  { "GTL", F_Cu }, // Top copper
440  { "G1", In1_Cu }, // Inner layers 1 - 30
441  { "G2", In2_Cu },
442  { "G3", In3_Cu },
443  { "G4", In4_Cu },
444  { "G5", In5_Cu },
445  { "G6", In6_Cu },
446  { "G7", In7_Cu },
447  { "G8", In8_Cu },
448  { "G9", In9_Cu },
449  { "G10", In10_Cu },
450  { "G11", In11_Cu },
451  { "G12", In12_Cu },
452  { "G13", In13_Cu },
453  { "G14", In14_Cu },
454  { "G15", In15_Cu },
455  { "G16", In16_Cu },
456  { "G17", In17_Cu },
457  { "G18", In18_Cu },
458  { "G19", In19_Cu },
459  { "G20", In20_Cu },
460  { "G21", In21_Cu },
461  { "G22", In22_Cu },
462  { "G23", In23_Cu },
463  { "G24", In24_Cu },
464  { "G25", In25_Cu },
465  { "G26", In26_Cu },
466  { "G27", In27_Cu },
467  { "G28", In28_Cu },
468  { "G29", In29_Cu },
469  { "G30", In30_Cu },
470  { "GBL", B_Cu }, // Bottom copper
471  { "GTP", F_Paste }, // Paste top
472  { "GBP", B_Paste }, // Paste bottom
473  { "GTO", F_SilkS }, // Silkscreen top
474  { "GBO", B_SilkS }, // Silkscreen bottom
475  { "GTS", F_Mask }, // Soldermask top
476  { "GBS", B_Mask }, // Soldermask bottom
477  { "GM1", Eco1_User }, // Altium mechanical layer 1
478  { "GM2", Eco2_User }, // Altium mechanical layer 2
479  { "GKO", Edge_Cuts } // PCB Outline
480  };
481  // clang-format on
482 
483  std::map<wxString, PCB_LAYER_ID>::iterator it;
484 
485  int numAltiumMatches = 0; // Assume we won't find Altium Gerbers
486 
488 
489  // Loop through all loaded Gerbers looking for any with Altium specific extensions
490  for( int ii = 0; ii < m_gerberActiveLayersCount; ii++ )
491  {
492  // Get file name of Gerber loaded on this layer.
493  wxFileName fn( images->GetGbrImage( ii )->m_FileName );
494 
495  // Get uppercase version of file extension
496  wxString FileExt = fn.GetExt();
497  FileExt.MakeUpper();
498 
499  // Check for matching Altium Gerber file extension we'll handle
500  it = altiumExt.find( FileExt );
501 
502  if( it != altiumExt.end() )
503  {
504  // We got a match, so store the KiCad layer number
505  aGerber2KicadMapping.push_back( it->second );
506  numAltiumMatches++;
507  }
508  else
509  {
510  // If there's no Altium match, then note the layer as unselected
511  aGerber2KicadMapping.push_back( UNSELECTED_LAYER );
512  }
513  }
514 
515  // Return number of Altium Gerbers we found. Each index in the passed vector corresponds to
516  // a loaded Gerber layer, and the entry will contain the index to the matching
517  // KiCad layer for Altium Gerbers, or "UNSELECTED_LAYER" for the rest.
518  return numAltiumMatches;
519 }
GERBER_FILE_IMAGE_LIST is a helper class to handle a list of GERBER_FILE_IMAGE files which are loaded...
void OnResetClick(wxCommandEvent &event) override
int m_buttonTable[int(GERBER_DRAWLAYERS_COUNT)+1]
void OnOkClick(wxCommandEvent &event) override
int SelectPCBLayer(int aDefaultLayer, int aCopperLayerCount)
Install the dialog box for layer selection.
bool IsValidLayer(LAYER_NUM aLayerId)
Function IsValidLayer tests whether a given integer is a valid layer index, i.e.
#define NULL
#define GERBER_DRAWLAYERS_COUNT
GERBER_FILE_IMAGE * GetGbrImage(int aIdx)
GERBVIEW_FRAME * m_Parent
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
EVT_COMMAND_RANGE(ID_BUTTON_0, ID_BUTTON_0+GERBER_DRAWLAYERS_COUNT-1, wxEVT_COMMAND_BUTTON_CLICKED, LAYERS_MAP_DIALOG::OnSelectLayer) LAYERS_MAP_DIALOG
Class LAYERS_MAP_DIALOG_BASE.
wxStaticText * m_layersList[int(GERBER_DRAWLAYERS_COUNT)+1]
LAYER_NUM m_layersLookUpTable[GERBER_DRAWLAYERS_COUNT]
int LAYER_NUM
Type LAYER_NUM can be replaced with int and removed.
const wxString GetPCBDefaultLayerName(LAYER_NUM aLayerNumber)
GBR_LAYOUT * GetGerberLayout() const
Board layer functions and definitions.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
int findNumAltiumGerbersLoaded(std::vector< int > &aGerber2KicadMapping)
Finds number of loaded Gerbers using Altium file extensions.
#define _(s)
Definition: 3d_actions.cpp:33
void OnBrdLayersCountSelection(wxCommandEvent &event) override
void OnGetSetup(wxCommandEvent &event) override
GERBER_FILE_IMAGE_LIST * GetImagesList() const
Definition: gbr_layout.cpp:44
std::vector< int > m_GerberToPcbLayerMapping
A list of GERBER_DRAWLAYERS_COUNT length containing a mapping of gerber layers to PCB layers,...
static int m_exportBoardCopperLayersCount
void OnSelectLayer(wxCommandEvent &event)
void OnStoreSetup(wxCommandEvent &event) override