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 if( m_unmatched_layers_list->GetItemCount() > 0 )
152 m_unmatched_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
153}
154
155
157{
158 wxArrayInt rowsToDelete;
159 int itemIndex = -1;
160
161 while( ( itemIndex = m_matched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL, aStatus ) ) != wxNOT_FOUND )
162 {
163 wxString selectedLayerName = m_matched_layers_list->GetItemText( itemIndex, 0 );
164 wxString pureSelectedLayerName = UnwrapRequired( selectedLayerName );
165
166 wxCHECK( m_matched_layers_map.find( pureSelectedLayerName ) != m_matched_layers_map.end(),
167 /*void*/ );
168
169 m_matched_layers_map.erase( pureSelectedLayerName );
170 rowsToDelete.Add( itemIndex );
171
172 m_unmatched_layers_list->InsertItem( 0, selectedLayerName );
173 m_unmatched_layer_names.push_back( selectedLayerName );
174 }
175
176 DeleteListItems( rowsToDelete, m_matched_layers_list );
177}
178
179
180void DIALOG_MAP_LAYERS::DeleteListItems( const wxArrayInt& aRowsToDelete, wxListCtrl* aListCtrl )
181{
182 for( long n = (long) aRowsToDelete.GetCount() - 1; 0 <= n; n-- )
183 aListCtrl->DeleteItem( aRowsToDelete[n] );
184}
185
186
188{
189 // Iterate through each selected layer in the unmatched layers list
190 int itemIndex = -1;
191 wxArrayInt rowsToDelete;
192
193 while( ( itemIndex = m_unmatched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
194 wxLIST_STATE_DONTCARE ) ) != wxNOT_FOUND )
195 {
196 wxString layerName = m_unmatched_layers_list->GetItemText( itemIndex );
197 PCB_LAYER_ID autoMatchLayer = GetAutoMatchLayerID( layerName );
198
199 if( autoMatchLayer == PCB_LAYER_ID::UNDEFINED_LAYER )
200 continue;
201
202 wxString kiName = LayerName( autoMatchLayer );
203
204 // add layer pair to the GUI list and also to the map
205 long newItemIndex = m_matched_layers_list->InsertItem( 0, layerName );
206 m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
207
208 m_matched_layers_map.insert( { UnwrapRequired( layerName ), autoMatchLayer } );
209
210 // remove selected layer from vector and also GUI list
211 for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end(); ++iter )
212 {
213 if( *iter == layerName )
214 {
215 m_unmatched_layer_names.erase( iter );
216 break;
217 }
218 }
219
220 rowsToDelete.Add( itemIndex );
221 }
222
224}
225
226
227DIALOG_MAP_LAYERS::DIALOG_MAP_LAYERS( wxWindow* aParent, 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
300 if( auto* frame = dynamic_cast<PCB_BASE_FRAME*>( m_parentFrame ) )
301 m_cbKeepKiCadLayerNames->SetValue( frame->GetPcbNewSettings()->m_ImportKeepKiCadLayerNames );
302
304
305 Fit();
307}
308
309
311{
312 std::vector<wxString> unmappedLayers;
313
314 for( const wxString& layerName : m_unmatched_layer_names )
315 {
316 const INPUT_LAYER_DESC* layerDesc = GetLayerDescription( layerName );
317 wxASSERT_MSG( layerDesc != nullptr, wxT( "Expected to find layer description" ) );
318
319 if( layerDesc->Required )
320 unmappedLayers.push_back( layerDesc->Name );
321 }
322
323 return unmappedLayers;
324}
325
326
327std::map<wxString, PCB_LAYER_ID>
328DIALOG_MAP_LAYERS::RunModal( wxWindow* aParent, const std::vector<INPUT_LAYER_DESC>& aLayerDesc )
329{
330 DIALOG_MAP_LAYERS dlg( aParent, aLayerDesc );
331 bool dataOk = false;
332
333 while( !dataOk )
334 {
335 dlg.ShowModal();
336
337 if( dlg.GetUnmappedRequiredLayers().size() > 0 )
338 {
339 wxMessageBox( _( "All required layers (marked with '*') must be matched. Please click "
340 "'Auto-Match Layers' to automatically match the remaining layers" ),
341 _( "Unmatched Layers" ), wxICON_ERROR | wxOK );
342 }
343 else
344 {
345 dataOk = true;
346 }
347 }
348
349 if( auto* frame = dynamic_cast<PCB_BASE_FRAME*>( dlg.m_parentFrame ) )
350 frame->GetPcbNewSettings()->m_ImportKeepKiCadLayerNames = dlg.m_cbKeepKiCadLayerNames->GetValue();
351
352 return dlg.m_matched_layers_map;
353}
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.