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 // Iterate through each selected layer in the unmatched layers list
200 int itemIndex = -1;
201 wxArrayInt rowsToDelete;
202
203 while( ( itemIndex = m_unmatched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
204 wxLIST_STATE_DONTCARE ) ) != wxNOT_FOUND )
205 {
206 wxString layerName = m_unmatched_layers_list->GetItemText( itemIndex );
207 PCB_LAYER_ID autoMatchLayer = GetAutoMatchLayerID( layerName );
208
209 if( autoMatchLayer == PCB_LAYER_ID::UNDEFINED_LAYER )
210 continue;
211
212 wxString kiName = KiCadLayerName( autoMatchLayer );
213
214 // add layer pair to the GUI list and also to the map
215 long newItemIndex = m_matched_layers_list->InsertItem( 0, layerName );
216 m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
217
218 m_matched_layers_map.insert( { UnwrapRequired( layerName ), autoMatchLayer } );
219
220 // remove selected layer from vector and also GUI list
221 for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end(); ++iter )
222 {
223 if( *iter == layerName )
224 {
225 m_unmatched_layer_names.erase( iter );
226 break;
227 }
228 }
229
230 rowsToDelete.Add( itemIndex );
231 }
232
234}
235
236
237DIALOG_MAP_LAYERS::DIALOG_MAP_LAYERS( wxWindow* aParent, const std::vector<INPUT_LAYER_DESC>& aLayerDesc ) :
239{
240 if( PCB_BASE_FRAME* frame = dynamic_cast<PCB_BASE_FRAME*>( aParent ) )
241 m_board = frame->GetBoard();
242
243 LSET kiCadLayers;
244
245 // Read in the input layers
246 for( const INPUT_LAYER_DESC& inLayer : aLayerDesc )
247 {
248 m_input_layers.push_back( inLayer );
249 wxString layerName = inLayer.Required ? WrapRequired( inLayer.Name ) : inLayer.Name;
250 m_unmatched_layer_names.push_back( layerName );
251 kiCadLayers |= inLayer.PermittedLayers;
252 }
253
254 int maxTextWidth = GetTextExtent( _( "Imported Layer" ) ).x;
255
256 for( const INPUT_LAYER_DESC& layer : m_input_layers )
257 maxTextWidth = std::max( maxTextWidth, GetTextExtent( layer.Name ).x );
258
259 // Initialize columns in the wxListCtrl elements:
260 wxListItem importedLayersHeader;
261 importedLayersHeader.SetId( 0 );
262 importedLayersHeader.SetText( _( "Imported Layer" ) );
263 importedLayersHeader.SetWidth( maxTextWidth + 15 );
264 m_unmatched_layers_list->InsertColumn( 0, importedLayersHeader );
265
266 int kicadMaxTextWidth = GetTextExtent( wxT( "User.Drawings" ) ).x;
267
268 wxListItem kicadLayersHeader;
269 kicadLayersHeader.SetId( 0 );
270 kicadLayersHeader.SetText( _( "KiCad Layer" ) );
271 kicadLayersHeader.SetWidth( kicadMaxTextWidth + 5 );
272 m_kicad_layers_list->InsertColumn( 0, kicadLayersHeader );
273
274 kicadLayersHeader.SetId( 1 );
275 importedLayersHeader.SetWidth( maxTextWidth + 15 );
276 kicadLayersHeader.SetWidth( kicadMaxTextWidth + 5 );
277 m_matched_layers_list->InsertColumn( 0, importedLayersHeader );
278 m_matched_layers_list->InsertColumn( 1, kicadLayersHeader );
279
280 // Load the input layer list to unmatched layers
281 int row = 0;
282
283 for( const wxString& importedLayerName : m_unmatched_layer_names )
284 {
285 wxListItem item;
286 item.SetId( row );
287 item.SetText( importedLayerName );
288 m_unmatched_layers_list->InsertItem( item );
289 ++row;
290 }
291
292 // Auto select the first item to improve ease-of-use
293 m_unmatched_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
294
295 // Load the KiCad Layer names
296 row = 0;
297 LSEQ kicadLayersSeq = kiCadLayers.UIOrder();
298
299 for( PCB_LAYER_ID layer : kicadLayersSeq )
300 {
301 wxString kiName = KiCadLayerName( layer );
302
303 wxListItem item;
304 item.SetId( row );
305 item.SetText( kiName );
306 m_kicad_layers_list->InsertItem( item );
307 ++row;
308 }
309
310 // Auto select the first item to improve ease-of-use
311 m_kicad_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
312
313 if( auto* frame = dynamic_cast<PCB_BASE_FRAME*>( m_parentFrame ) )
314 m_cbKeepKiCadLayerNames->SetValue( frame->GetPcbNewSettings()->m_ImportKeepKiCadLayerNames );
315
317
318 Fit();
320}
321
322
324{
325 std::vector<wxString> unmappedLayers;
326
327 for( const wxString& layerName : m_unmatched_layer_names )
328 {
329 const INPUT_LAYER_DESC* layerDesc = GetLayerDescription( layerName );
330 wxASSERT_MSG( layerDesc != nullptr, wxT( "Expected to find layer description" ) );
331
332 if( layerDesc->Required )
333 unmappedLayers.push_back( layerDesc->Name );
334 }
335
336 return unmappedLayers;
337}
338
339
340std::map<wxString, PCB_LAYER_ID>
341DIALOG_MAP_LAYERS::RunModal( wxWindow* aParent, const std::vector<INPUT_LAYER_DESC>& aLayerDesc )
342{
343 DIALOG_MAP_LAYERS dlg( aParent, aLayerDesc );
344 bool dataOk = false;
345
346 while( !dataOk )
347 {
348 dlg.ShowModal();
349
350 if( dlg.GetUnmappedRequiredLayers().size() > 0 )
351 {
352 wxMessageBox( _( "All required layers (marked with '*') must be matched. Please click "
353 "'Auto-Match Layers' to automatically match the remaining layers" ),
354 _( "Unmatched Layers" ), wxICON_ERROR | wxOK );
355 }
356 else
357 {
358 dataOk = true;
359 }
360 }
361
362 if( auto* frame = dynamic_cast<PCB_BASE_FRAME*>( dlg.m_parentFrame ) )
363 frame->GetPcbNewSettings()->m_ImportKeepKiCadLayerNames = dlg.m_cbKeepKiCadLayerNames->GetValue();
364
365 return dlg.m_matched_layers_map;
366}
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.