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 <[email protected]>
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
22#include <layer_ids.h>
24
25#include <wx/msgdlg.h>
26
27
28wxString DIALOG_IMPORTED_LAYERS::WrapRequired( const wxString& aLayerName )
29{
30 return aLayerName + wxT( " *" );
31}
32
33
34wxString DIALOG_IMPORTED_LAYERS::UnwrapRequired( const wxString& aLayerName )
35{
36 if( !aLayerName.EndsWith( wxT( " *" ) ) )
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 wxT( "There are more than one KiCad layer selected (unexpected)" ) );
78
79 for( int 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
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
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
176void 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
225}
226
227
229 const std::vector<INPUT_LAYER_DESC>& aLayerDesc ) :
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
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, wxT( "Expected to find layer description" ) );
317
318 if( layerDesc->Required )
319 unmappedLayers.push_back( layerDesc->Name );
320 }
321
322 return unmappedLayers;
323}
324
325
326std::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}
Class DIALOG_IMPORTED_LAYERS_BASE.
std::map< wxString, PCB_LAYER_ID > m_matched_layers_map
static wxString WrapRequired(const wxString &aLayerName)
DIALOG_IMPORTED_LAYERS(wxWindow *aParent, const std::vector< INPUT_LAYER_DESC > &aLayerDesc)
static wxString UnwrapRequired(const wxString &aLayerName)
std::vector< INPUT_LAYER_DESC > m_input_layers
const INPUT_LAYER_DESC * GetLayerDescription(const wxString &aLayerName) const
PCB_LAYER_ID GetAutoMatchLayerID(const wxString &aInputLayerName)
void OnAutoMatchLayersClicked(wxCommandEvent &event) override
std::vector< wxString > GetUnmappedRequiredLayers() const
Return a list of layers names that are required, but they are not mapped.
void DeleteListItems(const wxArrayInt &aRowsToDelete, wxListCtrl *aListCtrl)
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.
std::vector< wxString > m_unmatched_layer_names
void SetupStandardButtons(std::map< int, wxString > aLabels={})
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition: layer_ids.h:491
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:530
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
#define _(s)
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
Definition: layer_id.cpp:30
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ UNSELECTED_LAYER
Definition: layer_ids.h:61
@ UNDEFINED_LAYER
Definition: layer_ids.h:60
@ PCB_LAYER_ID_COUNT
Definition: layer_ids.h:137
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:926
Describes an imported layer and how it could be mapped to KiCad Layers.
bool Required
Should we require the layer to be assigned?
wxString Name
Imported layer name as displayed in original application.