KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_map_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 The 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>
23#include <lseq.h>
24#include <dialog_map_layers.h>
25
26#include <wx/msgdlg.h>
27
28
29wxString DIALOG_MAP_LAYERS::WrapRequired( const wxString& aLayerName )
30{
31 return aLayerName + wxT( " *" );
32}
33
34
35wxString DIALOG_MAP_LAYERS::UnwrapRequired( const wxString& aLayerName )
36{
37 if( !aLayerName.EndsWith( wxT( " *" ) ) )
38 return aLayerName;
39
40 return aLayerName.Left( aLayerName.Length() - 2 );
41}
42
43
44const INPUT_LAYER_DESC* DIALOG_MAP_LAYERS::GetLayerDescription( 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 {
71 return PCB_LAYER_ID::UNDEFINED_LAYER;
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
85 return PCB_LAYER_ID::UNDEFINED_LAYER;
86}
87
88
89PCB_LAYER_ID DIALOG_MAP_LAYERS::GetAutoMatchLayerID( const wxString& aInputLayerName )
90{
91 wxString pureInputLayerName = UnwrapRequired( aInputLayerName );
92
93 for( const INPUT_LAYER_DESC& inputLayerDesc : m_input_layers )
94 {
95 if( inputLayerDesc.Name == pureInputLayerName
96 && inputLayerDesc.AutoMapLayer != PCB_LAYER_ID::UNSELECTED_LAYER )
97 {
98 return inputLayerDesc.AutoMapLayer;
99 }
100 }
101
102 return PCB_LAYER_ID::UNDEFINED_LAYER;
103}
104
105
107{
108 PCB_LAYER_ID selectedKiCadLayerID = GetSelectedLayerID();
109
110 if( selectedKiCadLayerID == PCB_LAYER_ID::UNDEFINED_LAYER )
111 return;
112
113 // Now iterate through each selected layer in the unmatched layers list
114 int itemIndex = -1;
115 wxArrayInt rowsToDelete;
116
117 while( ( itemIndex = m_unmatched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
118 wxLIST_STATE_SELECTED ) )
119 != wxNOT_FOUND )
120 {
121 wxString selectedLayerName = m_unmatched_layers_list->GetItemText( itemIndex );
122 wxString kiName = LayerName( selectedKiCadLayerID );
123
124 // add layer pair to the GUI list and also to the map
125 long newItemIndex = m_matched_layers_list->InsertItem( 0, selectedLayerName );
126 m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
127
129 { UnwrapRequired( selectedLayerName ), selectedKiCadLayerID } );
130
131 // remove selected layer from vector and also GUI list
132 for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end();
133 ++iter )
134 {
135 if( *iter == selectedLayerName )
136 {
137 m_unmatched_layer_names.erase( iter );
138 break;
139 }
140 }
141
142 rowsToDelete.Add( itemIndex );
143 }
144
146
147 // Auto select the first item to improve ease-of-use
148 m_unmatched_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
149}
150
151
153{
154 wxArrayInt rowsToDelete;
155 int itemIndex = -1;
156
157 while( ( itemIndex = m_matched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL, aStatus ) ) != 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_MAP_LAYERS::DeleteListItems( const wxArrayInt& aRowsToDelete, wxListCtrl* aListCtrl )
177{
178 for( long n = (long) aRowsToDelete.GetCount() - 1; 0 <= n; n-- )
179 aListCtrl->DeleteItem( aRowsToDelete[n] );
180}
181
182
184{
185 // Iterate through each selected layer in the unmatched layers list
186 int itemIndex = -1;
187 wxArrayInt rowsToDelete;
188
189 while( ( itemIndex = m_unmatched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
190 wxLIST_STATE_DONTCARE ) ) != wxNOT_FOUND )
191 {
192 wxString layerName = m_unmatched_layers_list->GetItemText( itemIndex );
193 PCB_LAYER_ID autoMatchLayer = GetAutoMatchLayerID( layerName );
194
195 if( autoMatchLayer == PCB_LAYER_ID::UNDEFINED_LAYER )
196 continue;
197
198 wxString kiName = LayerName( autoMatchLayer );
199
200 // add layer pair to the GUI list and also to the map
201 long newItemIndex = m_matched_layers_list->InsertItem( 0, layerName );
202 m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
203
204 m_matched_layers_map.insert( { UnwrapRequired( layerName ), autoMatchLayer } );
205
206 // remove selected layer from vector and also GUI list
207 for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end(); ++iter )
208 {
209 if( *iter == layerName )
210 {
211 m_unmatched_layer_names.erase( iter );
212 break;
213 }
214 }
215
216 rowsToDelete.Add( itemIndex );
217 }
218
220}
221
222
223DIALOG_MAP_LAYERS::DIALOG_MAP_LAYERS( wxWindow* aParent, const std::vector<INPUT_LAYER_DESC>& aLayerDesc ) :
225{
226 LSET kiCadLayers;
227
228 // Read in the input layers
229 for( const 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( const 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.UIOrder();
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
297
298 Fit();
300}
301
302
304{
305 std::vector<wxString> unmappedLayers;
306
307 for( const wxString& layerName : m_unmatched_layer_names )
308 {
309 const INPUT_LAYER_DESC* layerDesc = GetLayerDescription( layerName );
310 wxASSERT_MSG( layerDesc != nullptr, wxT( "Expected to find layer description" ) );
311
312 if( layerDesc->Required )
313 unmappedLayers.push_back( layerDesc->Name );
314 }
315
316 return unmappedLayers;
317}
318
319
320std::map<wxString, PCB_LAYER_ID>
321DIALOG_MAP_LAYERS::RunModal( wxWindow* aParent, const std::vector<INPUT_LAYER_DESC>& aLayerDesc )
322{
323 DIALOG_MAP_LAYERS dlg( aParent, aLayerDesc );
324 bool dataOk = false;
325
326 while( !dataOk )
327 {
328 dlg.ShowModal();
329
330 if( dlg.GetUnmappedRequiredLayers().size() > 0 )
331 {
332 wxMessageBox( _( "All required layers (marked with '*') must be matched. Please click "
333 "'Auto-Match Layers' to automatically match the remaining layers" ),
334 _( "Unmatched Layers" ), wxICON_ERROR | wxOK );
335 }
336 else
337 {
338 dataOk = true;
339 }
340 }
341
342 return dlg.m_matched_layers_map;
343}
Class DIALOG_IMPORTED_LAYERS_BASE.
std::vector< wxString > GetUnmappedRequiredLayers() const
Return a list of layers names that are required, but they are not mapped.
const INPUT_LAYER_DESC * GetLayerDescription(const wxString &aLayerName) const
void RemoveMappings(int aStatus)
void OnAutoMatchLayersClicked(wxCommandEvent &event) override
std::vector< INPUT_LAYER_DESC > m_input_layers
std::vector< wxString > m_unmatched_layer_names
static wxString UnwrapRequired(const wxString &aLayerName)
std::map< wxString, PCB_LAYER_ID > m_matched_layers_map
PCB_LAYER_ID GetSelectedLayerID()
static std::map< wxString, PCB_LAYER_ID > RunModal(wxWindow *aParent, const std::vector< INPUT_LAYER_DESC > &aLayerDesc)
Create and show a dialog (modal) and returns the data from it after completion.
DIALOG_MAP_LAYERS(wxWindow *aParent, const std::vector< INPUT_LAYER_DESC > &aLayerDesc)
PCB_LAYER_ID GetAutoMatchLayerID(const wxString &aInputLayerName)
static wxString WrapRequired(const wxString &aLayerName)
void DeleteListItems(const wxArrayInt &aRowsToDelete, wxListCtrl *aListCtrl)
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...
int ShowModal() override
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition: lseq.h:47
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
LSEQ UIOrder() const
Return the copper, technical and user layers in the order shown in layer widget.
Definition: lset.cpp:733
#define _(s)
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
Definition: layer_id.cpp:31
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ PCB_LAYER_ID_COUNT
Definition: layer_ids.h:171
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:744
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.