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
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21
22#include <layer_ids.h>
23#include <lseq.h>
24#include <dialog_map_layers.h>
25#include <board.h>
26#include <pcb_base_frame.h>
27#include <pcbnew_settings.h>
28
29#include <wx/checkbox.h>
30#include <wx/msgdlg.h>
31
32
33wxString DIALOG_MAP_LAYERS::WrapRequired( const wxString& aLayerName )
34{
35 return aLayerName + wxT( " *" );
36}
37
38
39wxString DIALOG_MAP_LAYERS::UnwrapRequired( const wxString& aLayerName )
40{
41 if( !aLayerName.EndsWith( wxT( " *" ) ) )
42 return aLayerName;
43
44 return aLayerName.Left( aLayerName.Length() - 2 );
45}
46
47
49{
50 if( m_board )
51 return m_board->GetLayerName( aLayer );
52
53 return LayerName( aLayer );
54}
55
56
57const INPUT_LAYER_DESC* DIALOG_MAP_LAYERS::GetLayerDescription( const wxString& aLayerName ) const
58{
59 wxString layerName = UnwrapRequired( aLayerName );
60
61 for( const INPUT_LAYER_DESC& layerDescription : m_input_layers )
62 {
63 if( layerDescription.Name == layerName )
64 return &layerDescription;
65 }
66
67 return nullptr;
68}
69
70
72{
73 // First check if there is a KiCad element selected
74 wxString selectedKiCadLayerName;
75 long itemIndex = -1;
76
77 if( ( itemIndex = m_kicad_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
78 wxLIST_STATE_SELECTED ) ) != wxNOT_FOUND )
79 {
80 selectedKiCadLayerName = m_kicad_layers_list->GetItemText( itemIndex );
81 }
82 else
83 {
85 }
86
87 // There should only be one selected (or none) as the list is set with wxLC_SINGLE_SEL style
88 wxASSERT_MSG( ( m_kicad_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
89 wxLIST_STATE_SELECTED ) ) == wxNOT_FOUND,
90 wxT( "There are more than one KiCad layer selected (unexpected)" ) );
91
92 for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
93 {
94 if( KiCadLayerName( ToLAYER_ID( layer ) ) == selectedKiCadLayerName )
95 return ToLAYER_ID( layer );
96 }
97
99}
100
101
102PCB_LAYER_ID DIALOG_MAP_LAYERS::GetAutoMatchLayerID( const wxString& aInputLayerName )
103{
104 wxString pureInputLayerName = UnwrapRequired( aInputLayerName );
105
106 for( const INPUT_LAYER_DESC& inputLayerDesc : m_input_layers )
107 {
108 if( inputLayerDesc.Name == pureInputLayerName
109 && inputLayerDesc.AutoMapLayer != PCB_LAYER_ID::UNSELECTED_LAYER )
110 {
111 return inputLayerDesc.AutoMapLayer;
112 }
113 }
114
116}
117
118
120{
121 PCB_LAYER_ID selectedKiCadLayerID = GetSelectedLayerID();
122
123 if( selectedKiCadLayerID == PCB_LAYER_ID::UNDEFINED_LAYER )
124 return;
125
126 // Now iterate through each selected layer in the unmatched layers list
127 int itemIndex = -1;
128 wxArrayInt rowsToDelete;
129
130 while( ( itemIndex = m_unmatched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
131 wxLIST_STATE_SELECTED ) )
132 != wxNOT_FOUND )
133 {
134 wxString selectedLayerName = m_unmatched_layers_list->GetItemText( itemIndex );
135 wxString kiName = KiCadLayerName( selectedKiCadLayerID );
136
137 // add layer pair to the GUI list and also to the map
138 long newItemIndex = m_matched_layers_list->InsertItem( 0, selectedLayerName );
139 m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
140
142 { UnwrapRequired( selectedLayerName ), selectedKiCadLayerID } );
143
144 // remove selected layer from vector and also GUI list
145 for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end();
146 ++iter )
147 {
148 if( *iter == selectedLayerName )
149 {
150 m_unmatched_layer_names.erase( iter );
151 break;
152 }
153 }
154
155 rowsToDelete.Add( itemIndex );
156 }
157
159
160 // Auto select the first item to improve ease-of-use
161 if( m_unmatched_layers_list->GetItemCount() > 0 )
162 m_unmatched_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
163}
164
165
167{
168 wxArrayInt rowsToDelete;
169 int itemIndex = -1;
170
171 while( ( itemIndex = m_matched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL, aStatus ) ) != wxNOT_FOUND )
172 {
173 wxString selectedLayerName = m_matched_layers_list->GetItemText( itemIndex, 0 );
174 wxString pureSelectedLayerName = UnwrapRequired( selectedLayerName );
175
176 wxCHECK( m_matched_layers_map.find( pureSelectedLayerName ) != m_matched_layers_map.end(),
177 /*void*/ );
178
179 m_matched_layers_map.erase( pureSelectedLayerName );
180 rowsToDelete.Add( itemIndex );
181
182 m_unmatched_layers_list->InsertItem( 0, selectedLayerName );
183 m_unmatched_layer_names.push_back( selectedLayerName );
184 }
185
186 DeleteListItems( rowsToDelete, m_matched_layers_list );
187}
188
189
190void DIALOG_MAP_LAYERS::DeleteListItems( const wxArrayInt& aRowsToDelete, wxListCtrl* aListCtrl )
191{
192 for( long n = (long) aRowsToDelete.GetCount() - 1; 0 <= n; n-- )
193 aListCtrl->DeleteItem( aRowsToDelete[n] );
194}
195
196
198{
199 int itemIndex = -1;
200 wxArrayInt rowsToDelete;
201
202 while( ( itemIndex = m_unmatched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
203 wxLIST_STATE_DONTCARE ) ) != wxNOT_FOUND )
204 {
205 wxString layerName = m_unmatched_layers_list->GetItemText( itemIndex );
206 PCB_LAYER_ID autoMatchLayer = GetAutoMatchLayerID( layerName );
207
208 if( autoMatchLayer == PCB_LAYER_ID::UNDEFINED_LAYER )
209 continue;
210
211 wxString kiName = KiCadLayerName( autoMatchLayer );
212
213 // add layer pair to the GUI list and also to the map
214 long newItemIndex = m_matched_layers_list->InsertItem( 0, layerName );
215 m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
216
217 m_matched_layers_map.insert( { UnwrapRequired( layerName ), autoMatchLayer } );
218
219 // remove selected layer from vector and also GUI list
220 for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end(); ++iter )
221 {
222 if( *iter == layerName )
223 {
224 m_unmatched_layer_names.erase( iter );
225 break;
226 }
227 }
228
229 rowsToDelete.Add( itemIndex );
230 }
231
233}
234
235
237{
239}
240
241
242DIALOG_MAP_LAYERS::DIALOG_MAP_LAYERS( wxWindow* aParent, const std::vector<INPUT_LAYER_DESC>& aLayerDesc ) :
244{
245 if( PCB_BASE_FRAME* frame = dynamic_cast<PCB_BASE_FRAME*>( aParent ) )
246 m_board = frame->GetBoard();
247
248 LSET kiCadLayers;
249
250 // Read in the input layers
251 for( const INPUT_LAYER_DESC& inLayer : aLayerDesc )
252 {
253 m_input_layers.push_back( inLayer );
254 wxString layerName = inLayer.Required ? WrapRequired( inLayer.Name ) : inLayer.Name;
255 m_unmatched_layer_names.push_back( layerName );
256 kiCadLayers |= inLayer.PermittedLayers;
257 }
258
259 int maxTextWidth = GetTextExtent( _( "Imported Layer" ) ).x;
260
261 for( const INPUT_LAYER_DESC& layer : m_input_layers )
262 maxTextWidth = std::max( maxTextWidth, GetTextExtent( layer.Name ).x );
263
264 // Initialize columns in the wxListCtrl elements:
265 wxListItem importedLayersHeader;
266 importedLayersHeader.SetId( 0 );
267 importedLayersHeader.SetText( _( "Imported Layer" ) );
268 importedLayersHeader.SetWidth( maxTextWidth + 15 );
269 m_unmatched_layers_list->InsertColumn( 0, importedLayersHeader );
270
271 int kicadMaxTextWidth = GetTextExtent( wxT( "User.Drawings" ) ).x;
272
273 wxListItem kicadLayersHeader;
274 kicadLayersHeader.SetId( 0 );
275 kicadLayersHeader.SetText( _( "KiCad Layer" ) );
276 kicadLayersHeader.SetWidth( kicadMaxTextWidth + 5 );
277 m_kicad_layers_list->InsertColumn( 0, kicadLayersHeader );
278
279 kicadLayersHeader.SetId( 1 );
280 importedLayersHeader.SetWidth( maxTextWidth + 15 );
281 kicadLayersHeader.SetWidth( kicadMaxTextWidth + 5 );
282 m_matched_layers_list->InsertColumn( 0, importedLayersHeader );
283 m_matched_layers_list->InsertColumn( 1, kicadLayersHeader );
284
285 // Load the input layer list to unmatched layers
286 int row = 0;
287
288 for( const wxString& importedLayerName : m_unmatched_layer_names )
289 {
290 wxListItem item;
291 item.SetId( row );
292 item.SetText( importedLayerName );
293 m_unmatched_layers_list->InsertItem( item );
294 ++row;
295 }
296
297 // Auto select the first item to improve ease-of-use
298 m_unmatched_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
299
300 // Load the KiCad Layer names
301 row = 0;
302 LSEQ kicadLayersSeq = kiCadLayers.UIOrder();
303
304 for( PCB_LAYER_ID layer : kicadLayersSeq )
305 {
306 wxString kiName = KiCadLayerName( layer );
307
308 wxListItem item;
309 item.SetId( row );
310 item.SetText( kiName );
311 m_kicad_layers_list->InsertItem( item );
312 ++row;
313 }
314
315 // Auto select the first item to improve ease-of-use
316 m_kicad_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
317
318 if( auto* frame = dynamic_cast<PCB_BASE_FRAME*>( m_parentFrame ) )
319 m_cbKeepKiCadLayerNames->SetValue( frame->GetPcbNewSettings()->m_ImportKeepKiCadLayerNames );
320
322 m_sdbSizerOK->SetDefault();
323 m_sdbSizerOK->SetFocus();
324
325 Fit();
327}
328
329
331{
332 std::vector<wxString> unmappedLayers;
333
334 for( const wxString& layerName : m_unmatched_layer_names )
335 {
336 const INPUT_LAYER_DESC* layerDesc = GetLayerDescription( layerName );
337 wxASSERT_MSG( layerDesc != nullptr, wxT( "Expected to find layer description" ) );
338
339 if( layerDesc->Required )
340 unmappedLayers.push_back( layerDesc->Name );
341 }
342
343 return unmappedLayers;
344}
345
346
347std::map<wxString, PCB_LAYER_ID>
348DIALOG_MAP_LAYERS::RunModal( wxWindow* aParent, const std::vector<INPUT_LAYER_DESC>& aLayerDesc )
349{
350 DIALOG_MAP_LAYERS dlg( aParent, aLayerDesc );
351 bool dataOk = false;
352
353 while( !dataOk )
354 {
355 dlg.ShowModal();
356
357 // If the user accepted with no mappings, apply suggested AutoMapLayer values.
358 if( dlg.m_matched_layers_map.empty() )
359 dlg.AutoMatchLayers();
360
361 if( dlg.GetUnmappedRequiredLayers().size() > 0 )
362 {
363 wxMessageBox( _( "All required layers (marked with '*') must be matched. Please click "
364 "'Auto-Match Layers' to automatically match the remaining layers" ),
365 _( "Unmatched Layers" ), wxICON_ERROR | wxOK );
366 }
367 else
368 {
369 dataOk = true;
370 }
371 }
372
373 if( auto* frame = dynamic_cast<PCB_BASE_FRAME*>( dlg.m_parentFrame ) )
374 frame->GetPcbNewSettings()->m_ImportKeepKiCadLayerNames = dlg.m_cbKeepKiCadLayerNames->GetValue();
375
376 return dlg.m_matched_layers_map;
377}
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.
wxString KiCadLayerName(PCB_LAYER_ID aLayer) const
KiCad layer name to display, using the destination board's names when available.
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
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:56
@ UNSELECTED_LAYER
Definition layer_ids.h:58
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:167
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp:750
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.