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 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 wxString DIALOG_IMPORTED_LAYERS::WrapRequired( const wxString& aLayerName )
28 {
29  return aLayerName + " *";
30 }
31 
32 wxString DIALOG_IMPORTED_LAYERS::UnwrapRequired( const wxString& aLayerName )
33 {
34  if( !aLayerName.EndsWith( " *" ) )
35  return aLayerName;
36  return aLayerName.Left( aLayerName.Length() - 2 );
37 }
38 
40  const wxString& aLayerName ) const
41 {
42  wxString layerName = UnwrapRequired( aLayerName );
43  for( const INPUT_LAYER_DESC& layerDescription : m_input_layers )
44  {
45  if( layerDescription.Name == layerName )
46  return &layerDescription;
47  }
48  return nullptr;
49 }
50 
51 
53 {
54  // First check if there is a KiCad element selected
55  wxString selectedKiCadLayerName;
56  long itemIndex = -1;
57 
58  if( ( itemIndex = m_kicad_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
59  wxLIST_STATE_SELECTED ) ) != wxNOT_FOUND )
60  {
61  selectedKiCadLayerName = m_kicad_layers_list->GetItemText( itemIndex );
62  }
63  else
64  {
66  }
67 
68  // There should only be one selected (or none) as the list is set with wxLC_SINGLE_SEL style
69  wxASSERT_MSG( ( m_kicad_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
70  wxLIST_STATE_SELECTED ) ) == wxNOT_FOUND,
71  "There are more than one KiCad layer selected (unexpected)" );
72 
73  for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
74  {
75  if( LayerName( ToLAYER_ID( layer ) ) == selectedKiCadLayerName )
76  return ToLAYER_ID( layer );
77  }
78 
80 }
81 
82 
84 {
85  wxString pureInputLayerName = UnwrapRequired( aInputLayerName );
86  for( INPUT_LAYER_DESC inputLayerDesc : m_input_layers )
87  {
88  if( inputLayerDesc.Name == pureInputLayerName
89  && inputLayerDesc.AutoMapLayer != PCB_LAYER_ID::UNSELECTED_LAYER )
90  {
91  return inputLayerDesc.AutoMapLayer;
92  }
93  }
94 
96 }
97 
98 
100 {
101  PCB_LAYER_ID selectedKiCadLayerID = GetSelectedLayerID();
102 
103  if( selectedKiCadLayerID == PCB_LAYER_ID::UNDEFINED_LAYER )
104  return;
105 
106  // Now iterate through each selected layer in the unmatched layers list
107  int itemIndex = -1;
108  wxArrayInt rowsToDelete;
109 
110  while( ( itemIndex = m_unmatched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
111  wxLIST_STATE_SELECTED ) )
112  != wxNOT_FOUND )
113  {
114  wxString selectedLayerName = m_unmatched_layers_list->GetItemText( itemIndex );
115  wxString kiName = LayerName( selectedKiCadLayerID );
116 
117  // add layer pair to the GUI list and also to the map
118  long newItemIndex = m_matched_layers_list->InsertItem( 0, selectedLayerName );
119  m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
120 
121  m_matched_layers_map.insert(
122  { UnwrapRequired( selectedLayerName ), selectedKiCadLayerID } );
123 
124  // remove selected layer from vector and also GUI list
125  for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end();
126  ++iter )
127  {
128  if( *iter == selectedLayerName )
129  {
130  m_unmatched_layer_names.erase( iter );
131  break;
132  }
133  }
134 
135  rowsToDelete.Add( itemIndex );
136  }
137 
138  DeleteListItems( rowsToDelete, m_unmatched_layers_list );
139 
140  // Auto select the first item to improve ease-of-use
141  m_unmatched_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
142 }
143 
144 
146 {
147  wxArrayInt rowsToDelete;
148  int itemIndex = -1;
149 
150  while( ( itemIndex = m_matched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL, aStatus ) )
151  != wxNOT_FOUND )
152  {
153  wxString selectedLayerName = m_matched_layers_list->GetItemText( itemIndex, 0 );
154  wxString pureSelectedLayerName = UnwrapRequired( selectedLayerName );
155 
156  wxCHECK( m_matched_layers_map.find( pureSelectedLayerName ) != m_matched_layers_map.end(),
157  /*void*/ );
158 
159  m_matched_layers_map.erase( pureSelectedLayerName );
160  rowsToDelete.Add( itemIndex );
161 
162  m_unmatched_layers_list->InsertItem( 0, selectedLayerName );
163  m_unmatched_layer_names.push_back( selectedLayerName );
164  }
165 
166  DeleteListItems( rowsToDelete, m_matched_layers_list );
167 }
168 
169 
170 void DIALOG_IMPORTED_LAYERS::DeleteListItems( const wxArrayInt& aRowsToDelete,
171  wxListCtrl* aListCtrl )
172 {
173  for( long n = ( aRowsToDelete.GetCount() - 1 ); 0 <= n; n-- )
174  {
175  aListCtrl->DeleteItem( aRowsToDelete[n] );
176  }
177 }
178 
179 
181 {
182  // Iterate through each selected layer in the unmatched layers list
183  int itemIndex = -1;
184  wxArrayInt rowsToDelete;
185 
186  while( ( itemIndex = m_unmatched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
187  wxLIST_STATE_DONTCARE ) )
188  != wxNOT_FOUND )
189  {
190  wxString layerName = m_unmatched_layers_list->GetItemText( itemIndex );
191  PCB_LAYER_ID autoMatchLayer = GetAutoMatchLayerID( layerName );
192 
193  if( autoMatchLayer == PCB_LAYER_ID::UNDEFINED_LAYER )
194  continue;
195 
196  wxString kiName = LayerName( autoMatchLayer );
197 
198  // add layer pair to the GUI list and also to the map
199  long newItemIndex = m_matched_layers_list->InsertItem( 0, layerName );
200  m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
201 
202  m_matched_layers_map.insert( { UnwrapRequired( layerName ), autoMatchLayer } );
203 
204  // remove selected layer from vector and also GUI list
205  for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end();
206  ++iter )
207  {
208  if( *iter == layerName )
209  {
210  m_unmatched_layer_names.erase( iter );
211  break;
212  }
213  }
214 
215  rowsToDelete.Add( itemIndex );
216  }
217 
218  DeleteListItems( rowsToDelete, m_unmatched_layers_list );
219 }
220 
221 
223  const std::vector<INPUT_LAYER_DESC>& aLayerDesc ) :
224  DIALOG_IMPORTED_LAYERS_BASE( aParent )
225 {
226  LSET kiCadLayers;
227 
228  // Read in the input layers
229  for( INPUT_LAYER_DESC inLayer : aLayerDesc )
230  {
231  m_input_layers.push_back( inLayer );
232  wxString layerName = inLayer.Required ? WrapRequired( inLayer.Name ) : inLayer.Name;
233  m_unmatched_layer_names.push_back( layerName );
234  kiCadLayers |= inLayer.PermittedLayers;
235  }
236 
237  int maxTextWidth = GetTextExtent( _( "Imported Layer" ) ).x;
238 
239  for( const INPUT_LAYER_DESC& layer : m_input_layers )
240  maxTextWidth = std::max( maxTextWidth, GetTextExtent( layer.Name ).x );
241 
242  // Initialize columns in the wxListCtrl elements:
243  wxListItem importedLayersHeader;
244  importedLayersHeader.SetId( 0 );
245  importedLayersHeader.SetText( _( "Imported Layer" ) );
246  importedLayersHeader.SetWidth( maxTextWidth + 15 );
247  m_unmatched_layers_list->InsertColumn( 0, importedLayersHeader );
248 
249  int kicadMaxTextWidth = GetTextExtent( wxT( "User.Drawings" ) ).x;
250 
251  wxListItem kicadLayersHeader;
252  kicadLayersHeader.SetId( 0 );
253  kicadLayersHeader.SetText( _( "KiCad Layer" ) );
254  kicadLayersHeader.SetWidth( kicadMaxTextWidth + 5 );
255  m_kicad_layers_list->InsertColumn( 0, kicadLayersHeader );
256 
257  kicadLayersHeader.SetId( 1 );
258  importedLayersHeader.SetWidth( maxTextWidth + 15 );
259  kicadLayersHeader.SetWidth( kicadMaxTextWidth + 5 );
260  m_matched_layers_list->InsertColumn( 0, importedLayersHeader );
261  m_matched_layers_list->InsertColumn( 1, kicadLayersHeader );
262 
263  // Load the input layer list to unmatched layers
264  int row = 0;
265 
266  for( wxString importedLayerName : m_unmatched_layer_names )
267  {
268  wxListItem item;
269  item.SetId( row );
270  item.SetText( importedLayerName );
271  m_unmatched_layers_list->InsertItem( item );
272  ++row;
273  }
274 
275  // Auto select the first item to improve ease-of-use
276  m_unmatched_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
277 
278  // Load the KiCad Layer names
279  row = 0;
280  LSEQ kicadLayersSeq = kiCadLayers.Seq();
281 
282  for( PCB_LAYER_ID layer : kicadLayersSeq )
283  {
284  wxString kiName = LayerName( layer );
285 
286  wxListItem item;
287  item.SetId( row );
288  item.SetText( kiName );
289  m_kicad_layers_list->InsertItem( item );
290  ++row;
291  }
292 
293  // Auto select the first item to improve ease-of-use
294  m_kicad_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
295 
296  m_sdbSizerOK->SetDefault();
297 
298  Fit();
300 }
301 
303 {
304  std::vector<wxString> unmappedLayers;
305  for( const wxString& layerName : m_unmatched_layer_names )
306  {
307  const INPUT_LAYER_DESC* layerDesc = GetLayerDescription( layerName );
308  wxASSERT_MSG( layerDesc != nullptr, "Expected to find layer decription" );
309  if( layerDesc->Required )
310  unmappedLayers.push_back( layerDesc->Name );
311  }
312  return unmappedLayers;
313 }
314 
315 
316 std::map<wxString, PCB_LAYER_ID> DIALOG_IMPORTED_LAYERS::GetMapModal( wxWindow* aParent,
317  const std::vector<INPUT_LAYER_DESC>& aLayerDesc )
318 {
319  DIALOG_IMPORTED_LAYERS dlg( aParent, aLayerDesc );
320  bool dataOk = false;
321 
322  while( !dataOk )
323  {
324  dlg.ShowModal();
325 
326  if( dlg.GetUnmappedRequiredLayers().size() > 0 )
327  {
328  wxMessageBox( _( "All required layers (marked with '*') must be matched. "
329  "Please click on 'Auto-Match Layers' to "
330  "automatically match the remaining layers" ),
331  _( "Unmatched Layers" ), wxICON_ERROR | wxOK );
332  }
333  else
334  {
335  dataOk = true;
336  }
337  }
338 
339  return dlg.m_matched_layers_map;
340 }
void DeleteListItems(const wxArrayInt &aRowsToDelete, wxListCtrl *aListCtrl)
Class DIALOG_IMPORTED_LAYERS_BASE.
std::vector< wxString > m_unmatched_layer_names
PCB_LAYER_ID GetAutoMatchLayerID(wxString aInputLayerName)
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?
std::vector< wxString > GetUnmappedRequiredLayers() const
Return a list of layers names that are required, but they are not mapped.
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)
Creates and shows 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.
#define _(s)
Definition: 3d_actions.cpp:33
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