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#include <pcb_base_frame.h>
26#include <pcbnew_settings.h>
27
28#include <wx/checkbox.h>
29#include <wx/msgdlg.h>
30
31
32wxString DIALOG_MAP_LAYERS::WrapRequired( const wxString& aLayerName )
33{
34 return aLayerName + wxT( " *" );
35}
36
37
38wxString DIALOG_MAP_LAYERS::UnwrapRequired( const wxString& aLayerName )
39{
40 if( !aLayerName.EndsWith( wxT( " *" ) ) )
41 return aLayerName;
42
43 return aLayerName.Left( aLayerName.Length() - 2 );
44}
45
46
47const INPUT_LAYER_DESC* DIALOG_MAP_LAYERS::GetLayerDescription( const wxString& aLayerName ) const
48{
49 wxString layerName = UnwrapRequired( aLayerName );
50
51 for( const INPUT_LAYER_DESC& layerDescription : m_input_layers )
52 {
53 if( layerDescription.Name == layerName )
54 return &layerDescription;
55 }
56
57 return nullptr;
58}
59
60
62{
63 // First check if there is a KiCad element selected
64 wxString selectedKiCadLayerName;
65 long itemIndex = -1;
66
67 if( ( itemIndex = m_kicad_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
68 wxLIST_STATE_SELECTED ) ) != wxNOT_FOUND )
69 {
70 selectedKiCadLayerName = m_kicad_layers_list->GetItemText( itemIndex );
71 }
72 else
73 {
75 }
76
77 // There should only be one selected (or none) as the list is set with wxLC_SINGLE_SEL style
78 wxASSERT_MSG( ( m_kicad_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
79 wxLIST_STATE_SELECTED ) ) == wxNOT_FOUND,
80 wxT( "There are more than one KiCad layer selected (unexpected)" ) );
81
82 for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
83 {
84 if( LayerName( ToLAYER_ID( layer ) ) == selectedKiCadLayerName )
85 return ToLAYER_ID( layer );
86 }
87
89}
90
91
92PCB_LAYER_ID DIALOG_MAP_LAYERS::GetAutoMatchLayerID( const wxString& aInputLayerName )
93{
94 wxString pureInputLayerName = UnwrapRequired( aInputLayerName );
95
96 for( const INPUT_LAYER_DESC& inputLayerDesc : m_input_layers )
97 {
98 if( inputLayerDesc.Name == pureInputLayerName
99 && inputLayerDesc.AutoMapLayer != PCB_LAYER_ID::UNSELECTED_LAYER )
100 {
101 return inputLayerDesc.AutoMapLayer;
102 }
103 }
104
106}
107
108
110{
111 PCB_LAYER_ID selectedKiCadLayerID = GetSelectedLayerID();
112
113 if( selectedKiCadLayerID == PCB_LAYER_ID::UNDEFINED_LAYER )
114 return;
115
116 // Now iterate through each selected layer in the unmatched layers list
117 int itemIndex = -1;
118 wxArrayInt rowsToDelete;
119
120 while( ( itemIndex = m_unmatched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
121 wxLIST_STATE_SELECTED ) )
122 != wxNOT_FOUND )
123 {
124 wxString selectedLayerName = m_unmatched_layers_list->GetItemText( itemIndex );
125 wxString kiName = LayerName( selectedKiCadLayerID );
126
127 // add layer pair to the GUI list and also to the map
128 long newItemIndex = m_matched_layers_list->InsertItem( 0, selectedLayerName );
129 m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
130
132 { UnwrapRequired( selectedLayerName ), selectedKiCadLayerID } );
133
134 // remove selected layer from vector and also GUI list
135 for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end();
136 ++iter )
137 {
138 if( *iter == selectedLayerName )
139 {
140 m_unmatched_layer_names.erase( iter );
141 break;
142 }
143 }
144
145 rowsToDelete.Add( itemIndex );
146 }
147
149
150 // Auto select the first item to improve ease-of-use
151 m_unmatched_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
152}
153
154
156{
157 wxArrayInt rowsToDelete;
158 int itemIndex = -1;
159
160 while( ( itemIndex = m_matched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL, aStatus ) ) != wxNOT_FOUND )
161 {
162 wxString selectedLayerName = m_matched_layers_list->GetItemText( itemIndex, 0 );
163 wxString pureSelectedLayerName = UnwrapRequired( selectedLayerName );
164
165 wxCHECK( m_matched_layers_map.find( pureSelectedLayerName ) != m_matched_layers_map.end(),
166 /*void*/ );
167
168 m_matched_layers_map.erase( pureSelectedLayerName );
169 rowsToDelete.Add( itemIndex );
170
171 m_unmatched_layers_list->InsertItem( 0, selectedLayerName );
172 m_unmatched_layer_names.push_back( selectedLayerName );
173 }
174
175 DeleteListItems( rowsToDelete, m_matched_layers_list );
176}
177
178
179void DIALOG_MAP_LAYERS::DeleteListItems( const wxArrayInt& aRowsToDelete, wxListCtrl* aListCtrl )
180{
181 for( long n = (long) aRowsToDelete.GetCount() - 1; 0 <= n; n-- )
182 aListCtrl->DeleteItem( aRowsToDelete[n] );
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 ) ) != wxNOT_FOUND )
194 {
195 wxString layerName = m_unmatched_layers_list->GetItemText( itemIndex );
196 PCB_LAYER_ID autoMatchLayer = GetAutoMatchLayerID( layerName );
197
198 if( autoMatchLayer == PCB_LAYER_ID::UNDEFINED_LAYER )
199 continue;
200
201 wxString kiName = LayerName( autoMatchLayer );
202
203 // add layer pair to the GUI list and also to the map
204 long newItemIndex = m_matched_layers_list->InsertItem( 0, layerName );
205 m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
206
207 m_matched_layers_map.insert( { UnwrapRequired( layerName ), autoMatchLayer } );
208
209 // remove selected layer from vector and also GUI list
210 for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end(); ++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
226DIALOG_MAP_LAYERS::DIALOG_MAP_LAYERS( wxWindow* aParent, const std::vector<INPUT_LAYER_DESC>& aLayerDesc ) :
228{
229 LSET kiCadLayers;
230
231 // Read in the input layers
232 for( const INPUT_LAYER_DESC& inLayer : aLayerDesc )
233 {
234 m_input_layers.push_back( inLayer );
235 wxString layerName = inLayer.Required ? WrapRequired( inLayer.Name ) : inLayer.Name;
236 m_unmatched_layer_names.push_back( layerName );
237 kiCadLayers |= inLayer.PermittedLayers;
238 }
239
240 int maxTextWidth = GetTextExtent( _( "Imported Layer" ) ).x;
241
242 for( const INPUT_LAYER_DESC& layer : m_input_layers )
243 maxTextWidth = std::max( maxTextWidth, GetTextExtent( layer.Name ).x );
244
245 // Initialize columns in the wxListCtrl elements:
246 wxListItem importedLayersHeader;
247 importedLayersHeader.SetId( 0 );
248 importedLayersHeader.SetText( _( "Imported Layer" ) );
249 importedLayersHeader.SetWidth( maxTextWidth + 15 );
250 m_unmatched_layers_list->InsertColumn( 0, importedLayersHeader );
251
252 int kicadMaxTextWidth = GetTextExtent( wxT( "User.Drawings" ) ).x;
253
254 wxListItem kicadLayersHeader;
255 kicadLayersHeader.SetId( 0 );
256 kicadLayersHeader.SetText( _( "KiCad Layer" ) );
257 kicadLayersHeader.SetWidth( kicadMaxTextWidth + 5 );
258 m_kicad_layers_list->InsertColumn( 0, kicadLayersHeader );
259
260 kicadLayersHeader.SetId( 1 );
261 importedLayersHeader.SetWidth( maxTextWidth + 15 );
262 kicadLayersHeader.SetWidth( kicadMaxTextWidth + 5 );
263 m_matched_layers_list->InsertColumn( 0, importedLayersHeader );
264 m_matched_layers_list->InsertColumn( 1, kicadLayersHeader );
265
266 // Load the input layer list to unmatched layers
267 int row = 0;
268
269 for( const wxString& importedLayerName : m_unmatched_layer_names )
270 {
271 wxListItem item;
272 item.SetId( row );
273 item.SetText( importedLayerName );
274 m_unmatched_layers_list->InsertItem( item );
275 ++row;
276 }
277
278 // Auto select the first item to improve ease-of-use
279 m_unmatched_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
280
281 // Load the KiCad Layer names
282 row = 0;
283 LSEQ kicadLayersSeq = kiCadLayers.UIOrder();
284
285 for( PCB_LAYER_ID layer : kicadLayersSeq )
286 {
287 wxString kiName = LayerName( layer );
288
289 wxListItem item;
290 item.SetId( row );
291 item.SetText( kiName );
292 m_kicad_layers_list->InsertItem( item );
293 ++row;
294 }
295
296 // Auto select the first item to improve ease-of-use
297 m_kicad_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
298
299 if( auto* frame = dynamic_cast<PCB_BASE_FRAME*>( m_parentFrame ) )
300 m_cbKeepKiCadLayerNames->SetValue( frame->GetPcbNewSettings()->m_ImportKeepKiCadLayerNames );
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>
327DIALOG_MAP_LAYERS::RunModal( wxWindow* aParent, const std::vector<INPUT_LAYER_DESC>& aLayerDesc )
328{
329 DIALOG_MAP_LAYERS dlg( aParent, aLayerDesc );
330 bool dataOk = false;
331
332 while( !dataOk )
333 {
334 dlg.ShowModal();
335
336 if( dlg.GetUnmappedRequiredLayers().size() > 0 )
337 {
338 wxMessageBox( _( "All required layers (marked with '*') must be matched. Please click "
339 "'Auto-Match Layers' to automatically match the remaining layers" ),
340 _( "Unmatched Layers" ), wxICON_ERROR | wxOK );
341 }
342 else
343 {
344 dataOk = true;
345 }
346 }
347
348 if( auto* frame = dynamic_cast<PCB_BASE_FRAME*>( dlg.m_parentFrame ) )
349 frame->GetPcbNewSettings()->m_ImportKeepKiCadLayerNames = dlg.m_cbKeepKiCadLayerNames->GetValue();
350
351 return dlg.m_matched_layers_map;
352}
DIALOG_IMPORTED_LAYERS_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Edit Mapping of Imported Layers"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
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...
EDA_BASE_FRAME * m_parentFrame
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:743
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
#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
@ UNSELECTED_LAYER
Definition layer_ids.h:62
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:171
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp:754
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.