KiCad PCB EDA Suite
dialog_imported_layers.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2020 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com>
5  * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation, either version 3 of the License, or (at your
10  * option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 
23 #include <dialog_imported_layers.h>
24 
25 #include <wx/msgdlg.h>
26 
27 
28 wxString DIALOG_IMPORTED_LAYERS::WrapRequired( const wxString& aLayerName )
29 {
30  return aLayerName + " *";
31 }
32 
33 
34 wxString DIALOG_IMPORTED_LAYERS::UnwrapRequired( const wxString& aLayerName )
35 {
36  if( !aLayerName.EndsWith( " *" ) )
37  return aLayerName;
38 
39  return aLayerName.Left( aLayerName.Length() - 2 );
40 }
41 
42 
44  const wxString& aLayerName ) const
45 {
46  wxString layerName = UnwrapRequired( aLayerName );
47 
48  for( const INPUT_LAYER_DESC& layerDescription : m_input_layers )
49  {
50  if( layerDescription.Name == layerName )
51  return &layerDescription;
52  }
53 
54  return nullptr;
55 }
56 
57 
59 {
60  // First check if there is a KiCad element selected
61  wxString selectedKiCadLayerName;
62  long itemIndex = -1;
63 
64  if( ( itemIndex = m_kicad_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
65  wxLIST_STATE_SELECTED ) ) != wxNOT_FOUND )
66  {
67  selectedKiCadLayerName = m_kicad_layers_list->GetItemText( itemIndex );
68  }
69  else
70  {
72  }
73 
74  // There should only be one selected (or none) as the list is set with wxLC_SINGLE_SEL style
75  wxASSERT_MSG( ( m_kicad_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
76  wxLIST_STATE_SELECTED ) ) == wxNOT_FOUND,
77  "There are more than one KiCad layer selected (unexpected)" );
78 
79  for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
80  {
81  if( LayerName( ToLAYER_ID( layer ) ) == selectedKiCadLayerName )
82  return ToLAYER_ID( layer );
83  }
84 
86 }
87 
88 
90 {
91  wxString pureInputLayerName = UnwrapRequired( aInputLayerName );
92  for( INPUT_LAYER_DESC inputLayerDesc : m_input_layers )
93  {
94  if( inputLayerDesc.Name == pureInputLayerName
95  && inputLayerDesc.AutoMapLayer != PCB_LAYER_ID::UNSELECTED_LAYER )
96  {
97  return inputLayerDesc.AutoMapLayer;
98  }
99  }
100 
102 }
103 
104 
106 {
107  PCB_LAYER_ID selectedKiCadLayerID = GetSelectedLayerID();
108 
109  if( selectedKiCadLayerID == PCB_LAYER_ID::UNDEFINED_LAYER )
110  return;
111 
112  // Now iterate through each selected layer in the unmatched layers list
113  int itemIndex = -1;
114  wxArrayInt rowsToDelete;
115 
116  while( ( itemIndex = m_unmatched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
117  wxLIST_STATE_SELECTED ) )
118  != wxNOT_FOUND )
119  {
120  wxString selectedLayerName = m_unmatched_layers_list->GetItemText( itemIndex );
121  wxString kiName = LayerName( selectedKiCadLayerID );
122 
123  // add layer pair to the GUI list and also to the map
124  long newItemIndex = m_matched_layers_list->InsertItem( 0, selectedLayerName );
125  m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
126 
127  m_matched_layers_map.insert(
128  { UnwrapRequired( selectedLayerName ), selectedKiCadLayerID } );
129 
130  // remove selected layer from vector and also GUI list
131  for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end();
132  ++iter )
133  {
134  if( *iter == selectedLayerName )
135  {
136  m_unmatched_layer_names.erase( iter );
137  break;
138  }
139  }
140 
141  rowsToDelete.Add( itemIndex );
142  }
143 
144  DeleteListItems( rowsToDelete, m_unmatched_layers_list );
145 
146  // Auto select the first item to improve ease-of-use
147  m_unmatched_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
148 }
149 
150 
152 {
153  wxArrayInt rowsToDelete;
154  int itemIndex = -1;
155 
156  while( ( itemIndex = m_matched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL, aStatus ) )
157  != wxNOT_FOUND )
158  {
159  wxString selectedLayerName = m_matched_layers_list->GetItemText( itemIndex, 0 );
160  wxString pureSelectedLayerName = UnwrapRequired( selectedLayerName );
161 
162  wxCHECK( m_matched_layers_map.find( pureSelectedLayerName ) != m_matched_layers_map.end(),
163  /*void*/ );
164 
165  m_matched_layers_map.erase( pureSelectedLayerName );
166  rowsToDelete.Add( itemIndex );
167 
168  m_unmatched_layers_list->InsertItem( 0, selectedLayerName );
169  m_unmatched_layer_names.push_back( selectedLayerName );
170  }
171 
172  DeleteListItems( rowsToDelete, m_matched_layers_list );
173 }
174 
175 
176 void DIALOG_IMPORTED_LAYERS::DeleteListItems( const wxArrayInt& aRowsToDelete,
177  wxListCtrl* aListCtrl )
178 {
179  for( long n = ( aRowsToDelete.GetCount() - 1 ); 0 <= n; n-- )
180  {
181  aListCtrl->DeleteItem( aRowsToDelete[n] );
182  }
183 }
184 
185 
187 {
188  // Iterate through each selected layer in the unmatched layers list
189  int itemIndex = -1;
190  wxArrayInt rowsToDelete;
191 
192  while( ( itemIndex = m_unmatched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
193  wxLIST_STATE_DONTCARE ) )
194  != wxNOT_FOUND )
195  {
196  wxString layerName = m_unmatched_layers_list->GetItemText( itemIndex );
197  PCB_LAYER_ID autoMatchLayer = GetAutoMatchLayerID( layerName );
198 
199  if( autoMatchLayer == PCB_LAYER_ID::UNDEFINED_LAYER )
200  continue;
201 
202  wxString kiName = LayerName( autoMatchLayer );
203 
204  // add layer pair to the GUI list and also to the map
205  long newItemIndex = m_matched_layers_list->InsertItem( 0, layerName );
206  m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
207 
208  m_matched_layers_map.insert( { UnwrapRequired( layerName ), autoMatchLayer } );
209 
210  // remove selected layer from vector and also GUI list
211  for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end();
212  ++iter )
213  {
214  if( *iter == layerName )
215  {
216  m_unmatched_layer_names.erase( iter );
217  break;
218  }
219  }
220 
221  rowsToDelete.Add( itemIndex );
222  }
223 
224  DeleteListItems( rowsToDelete, m_unmatched_layers_list );
225 }
226 
227 
229  const std::vector<INPUT_LAYER_DESC>& aLayerDesc ) :
230  DIALOG_IMPORTED_LAYERS_BASE( aParent )
231 {
232  LSET kiCadLayers;
233 
234  // Read in the input layers
235  for( INPUT_LAYER_DESC inLayer : aLayerDesc )
236  {
237  m_input_layers.push_back( inLayer );
238  wxString layerName = inLayer.Required ? WrapRequired( inLayer.Name ) : inLayer.Name;
239  m_unmatched_layer_names.push_back( layerName );
240  kiCadLayers |= inLayer.PermittedLayers;
241  }
242 
243  int maxTextWidth = GetTextExtent( _( "Imported Layer" ) ).x;
244 
245  for( const INPUT_LAYER_DESC& layer : m_input_layers )
246  maxTextWidth = std::max( maxTextWidth, GetTextExtent( layer.Name ).x );
247 
248  // Initialize columns in the wxListCtrl elements:
249  wxListItem importedLayersHeader;
250  importedLayersHeader.SetId( 0 );
251  importedLayersHeader.SetText( _( "Imported Layer" ) );
252  importedLayersHeader.SetWidth( maxTextWidth + 15 );
253  m_unmatched_layers_list->InsertColumn( 0, importedLayersHeader );
254 
255  int kicadMaxTextWidth = GetTextExtent( wxT( "User.Drawings" ) ).x;
256 
257  wxListItem kicadLayersHeader;
258  kicadLayersHeader.SetId( 0 );
259  kicadLayersHeader.SetText( _( "KiCad Layer" ) );
260  kicadLayersHeader.SetWidth( kicadMaxTextWidth + 5 );
261  m_kicad_layers_list->InsertColumn( 0, kicadLayersHeader );
262 
263  kicadLayersHeader.SetId( 1 );
264  importedLayersHeader.SetWidth( maxTextWidth + 15 );
265  kicadLayersHeader.SetWidth( kicadMaxTextWidth + 5 );
266  m_matched_layers_list->InsertColumn( 0, importedLayersHeader );
267  m_matched_layers_list->InsertColumn( 1, kicadLayersHeader );
268 
269  // Load the input layer list to unmatched layers
270  int row = 0;
271 
272  for( wxString importedLayerName : m_unmatched_layer_names )
273  {
274  wxListItem item;
275  item.SetId( row );
276  item.SetText( importedLayerName );
277  m_unmatched_layers_list->InsertItem( item );
278  ++row;
279  }
280 
281  // Auto select the first item to improve ease-of-use
282  m_unmatched_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
283 
284  // Load the KiCad Layer names
285  row = 0;
286  LSEQ kicadLayersSeq = kiCadLayers.Seq();
287 
288  for( PCB_LAYER_ID layer : kicadLayersSeq )
289  {
290  wxString kiName = LayerName( layer );
291 
292  wxListItem item;
293  item.SetId( row );
294  item.SetText( kiName );
295  m_kicad_layers_list->InsertItem( item );
296  ++row;
297  }
298 
299  // Auto select the first item to improve ease-of-use
300  m_kicad_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
301 
302  m_sdbSizerOK->SetDefault();
303 
304  Fit();
306 }
307 
308 
310 {
311  std::vector<wxString> unmappedLayers;
312 
313  for( const wxString& layerName : m_unmatched_layer_names )
314  {
315  const INPUT_LAYER_DESC* layerDesc = GetLayerDescription( layerName );
316  wxASSERT_MSG( layerDesc != nullptr, "Expected to find layer description" );
317 
318  if( layerDesc->Required )
319  unmappedLayers.push_back( layerDesc->Name );
320  }
321 
322  return unmappedLayers;
323 }
324 
325 
326 std::map<wxString, PCB_LAYER_ID>
328  const std::vector<INPUT_LAYER_DESC>& aLayerDesc )
329 {
330  DIALOG_IMPORTED_LAYERS dlg( aParent, aLayerDesc );
331  bool dataOk = false;
332 
333  while( !dataOk )
334  {
335  dlg.ShowModal();
336 
337  if( dlg.GetUnmappedRequiredLayers().size() > 0 )
338  {
339  wxMessageBox( _( "All required layers (marked with '*') must be matched. "
340  "Please click on 'Auto-Match Layers' to "
341  "automatically match the remaining layers" ),
342  _( "Unmatched Layers" ), wxICON_ERROR | wxOK );
343  }
344  else
345  {
346  dataOk = true;
347  }
348  }
349 
350  return dlg.m_matched_layers_map;
351 }
void DeleteListItems(const wxArrayInt &aRowsToDelete, wxListCtrl *aListCtrl)
Class DIALOG_IMPORTED_LAYERS_BASE.
std::vector< wxString > m_unmatched_layer_names
static wxString WrapRequired(const wxString &aLayerName)
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:411
Describes an imported layer and how it could be mapped to KiCad Layers.
PCB_LAYER_ID
A quick note on layer IDs:
LSET is a set of PCB_LAYER_IDs.
static wxString UnwrapRequired(const wxString &aLayerName)
bool Required
Should we require the layer to be assigned?
PCB_LAYER_ID GetAutoMatchLayerID(const wxString &aInputLayerName)
std::vector< wxString > GetUnmappedRequiredLayers() const
Return a list of layers names that are required, but they are not mapped.
#define _(s)
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
Definition: layer_id.cpp:27
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
int LAYER_NUM
This can be replaced with int and removed.
DIALOG_IMPORTED_LAYERS(wxWindow *aParent, const std::vector< INPUT_LAYER_DESC > &aLayerDesc)
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
static std::map< wxString, PCB_LAYER_ID > GetMapModal(wxWindow *aParent, const std::vector< INPUT_LAYER_DESC > &aLayerDesc)
Create and show a dialog (modal) and returns the data from it after completion.
Board layer functions and definitions.
wxString Name
Imported layer name as displayed in original application.
const INPUT_LAYER_DESC * GetLayerDescription(const wxString &aLayerName) const
std::vector< INPUT_LAYER_DESC > m_input_layers
void OnAutoMatchLayersClicked(wxCommandEvent &event) override
std::map< wxString, PCB_LAYER_ID > m_matched_layers_map
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:905