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 (C) 2020-2024 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 ) )
158 != wxNOT_FOUND )
159 {
160 wxString selectedLayerName = m_matched_layers_list->GetItemText( itemIndex, 0 );
161 wxString pureSelectedLayerName = UnwrapRequired( selectedLayerName );
162
163 wxCHECK( m_matched_layers_map.find( pureSelectedLayerName ) != m_matched_layers_map.end(),
164 /*void*/ );
165
166 m_matched_layers_map.erase( pureSelectedLayerName );
167 rowsToDelete.Add( itemIndex );
168
169 m_unmatched_layers_list->InsertItem( 0, selectedLayerName );
170 m_unmatched_layer_names.push_back( selectedLayerName );
171 }
172
173 DeleteListItems( rowsToDelete, m_matched_layers_list );
174}
175
176
177void DIALOG_MAP_LAYERS::DeleteListItems( const wxArrayInt& aRowsToDelete, wxListCtrl* aListCtrl )
178{
179 for( long n = (long) aRowsToDelete.GetCount() - 1; 0 <= n; n-- )
180 aListCtrl->DeleteItem( aRowsToDelete[n] );
181}
182
183
185{
186 // Iterate through each selected layer in the unmatched layers list
187 int itemIndex = -1;
188 wxArrayInt rowsToDelete;
189
190 while( ( itemIndex = m_unmatched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
191 wxLIST_STATE_DONTCARE ) )
192 != wxNOT_FOUND )
193 {
194 wxString layerName = m_unmatched_layers_list->GetItemText( itemIndex );
195 PCB_LAYER_ID autoMatchLayer = GetAutoMatchLayerID( layerName );
196
197 if( autoMatchLayer == PCB_LAYER_ID::UNDEFINED_LAYER )
198 continue;
199
200 wxString kiName = LayerName( autoMatchLayer );
201
202 // add layer pair to the GUI list and also to the map
203 long newItemIndex = m_matched_layers_list->InsertItem( 0, layerName );
204 m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
205
206 m_matched_layers_map.insert( { UnwrapRequired( layerName ), autoMatchLayer } );
207
208 // remove selected layer from vector and also GUI list
209 for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end();
210 ++iter )
211 {
212 if( *iter == layerName )
213 {
214 m_unmatched_layer_names.erase( iter );
215 break;
216 }
217 }
218
219 rowsToDelete.Add( itemIndex );
220 }
221
223}
224
225
227 const std::vector<INPUT_LAYER_DESC>& aLayerDesc ) :
229{
230 LSET kiCadLayers;
231
232 // Read in the input layers
233 for( const INPUT_LAYER_DESC& inLayer : aLayerDesc )
234 {
235 m_input_layers.push_back( inLayer );
236 wxString layerName = inLayer.Required ? WrapRequired( inLayer.Name ) : inLayer.Name;
237 m_unmatched_layer_names.push_back( layerName );
238 kiCadLayers |= inLayer.PermittedLayers;
239 }
240
241 int maxTextWidth = GetTextExtent( _( "Imported Layer" ) ).x;
242
243 for( const INPUT_LAYER_DESC& layer : m_input_layers )
244 maxTextWidth = std::max( maxTextWidth, GetTextExtent( layer.Name ).x );
245
246 // Initialize columns in the wxListCtrl elements:
247 wxListItem importedLayersHeader;
248 importedLayersHeader.SetId( 0 );
249 importedLayersHeader.SetText( _( "Imported Layer" ) );
250 importedLayersHeader.SetWidth( maxTextWidth + 15 );
251 m_unmatched_layers_list->InsertColumn( 0, importedLayersHeader );
252
253 int kicadMaxTextWidth = GetTextExtent( wxT( "User.Drawings" ) ).x;
254
255 wxListItem kicadLayersHeader;
256 kicadLayersHeader.SetId( 0 );
257 kicadLayersHeader.SetText( _( "KiCad Layer" ) );
258 kicadLayersHeader.SetWidth( kicadMaxTextWidth + 5 );
259 m_kicad_layers_list->InsertColumn( 0, kicadLayersHeader );
260
261 kicadLayersHeader.SetId( 1 );
262 importedLayersHeader.SetWidth( maxTextWidth + 15 );
263 kicadLayersHeader.SetWidth( kicadMaxTextWidth + 5 );
264 m_matched_layers_list->InsertColumn( 0, importedLayersHeader );
265 m_matched_layers_list->InsertColumn( 1, kicadLayersHeader );
266
267 // Load the input layer list to unmatched layers
268 int row = 0;
269
270 for( const wxString& importedLayerName : m_unmatched_layer_names )
271 {
272 wxListItem item;
273 item.SetId( row );
274 item.SetText( importedLayerName );
275 m_unmatched_layers_list->InsertItem( item );
276 ++row;
277 }
278
279 // Auto select the first item to improve ease-of-use
280 m_unmatched_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
281
282 // Load the KiCad Layer names
283 row = 0;
284 LSEQ kicadLayersSeq = kiCadLayers.UIOrder();
285
286 for( PCB_LAYER_ID layer : kicadLayersSeq )
287 {
288 wxString kiName = LayerName( layer );
289
290 wxListItem item;
291 item.SetId( row );
292 item.SetText( kiName );
293 m_kicad_layers_list->InsertItem( item );
294 ++row;
295 }
296
297 // Auto select the first item to improve ease-of-use
298 m_kicad_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
299
301
302 Fit();
304}
305
306
308{
309 std::vector<wxString> unmappedLayers;
310
311 for( const wxString& layerName : m_unmatched_layer_names )
312 {
313 const INPUT_LAYER_DESC* layerDesc = GetLayerDescription( layerName );
314 wxASSERT_MSG( layerDesc != nullptr, wxT( "Expected to find layer description" ) );
315
316 if( layerDesc->Required )
317 unmappedLayers.push_back( layerDesc->Name );
318 }
319
320 return unmappedLayers;
321}
322
323
324std::map<wxString, PCB_LAYER_ID>
325DIALOG_MAP_LAYERS::RunModal( wxWindow* aParent, const std::vector<INPUT_LAYER_DESC>& aLayerDesc )
326{
327 DIALOG_MAP_LAYERS dlg( aParent, aLayerDesc );
328 bool dataOk = false;
329
330 while( !dataOk )
331 {
332 dlg.ShowModal();
333
334 if( dlg.GetUnmappedRequiredLayers().size() > 0 )
335 {
336 wxMessageBox( _( "All required layers (marked with '*') must be matched. "
337 "Please click on 'Auto-Match Layers' to "
338 "automatically match the remaining layers" ),
339 _( "Unmatched Layers" ), wxICON_ERROR | wxOK );
340 }
341 else
342 {
343 dataOk = true;
344 }
345 }
346
347 return dlg.m_matched_layers_map;
348}
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:36
LSEQ UIOrder() const
Returns the copper, technical and user layers in the order shown in layer widget.
Definition: lset.cpp:799
#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:60
@ PCB_LAYER_ID_COUNT
Definition: layer_ids.h:135
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:810
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.