KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_import_graphics.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) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU 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
22
24#include <dialog_map_layers.h>
26#include <base_units.h>
27#include <board.h>
28#include <kiface_base.h>
29#include <locale_io.h>
32#include <bitmaps.h>
34#include <map>
35#include <footprint.h>
36#include <wx/filedlg.h>
37#include <wx/msgdlg.h>
38#include <kiplatform/ui.h>
39
40#include <memory>
41
42
43const std::map<DXF_IMPORT_UNITS, wxString> dxfUnitsMap = {
44 { DXF_IMPORT_UNITS::INCH, _( "Inches" ) },
45 { DXF_IMPORT_UNITS::MM, _( "Millimeters" ) },
46 { DXF_IMPORT_UNITS::MILS, _( "Mils" ) },
47 { DXF_IMPORT_UNITS::CM, _( "Centimeter" ) },
48 { DXF_IMPORT_UNITS::FEET, _( "Feet" ) },
49};
50
51
52static PCB_LAYER_ID getAutoMappedLayer( const wxString& aSourceLayer, const LSET& aPermittedLayers )
53{
54 for( PCB_LAYER_ID layer : aPermittedLayers.UIOrder() )
55 {
56 if( aSourceLayer == LayerName( layer ) || aSourceLayer == LSET::Name( layer ) )
57 return layer;
58 }
59
61}
62
63
64static std::vector<INPUT_LAYER_DESC> buildDxfLayerDescriptions( const std::vector<wxString>& aSourceLayers,
65 const LSET& aPermittedLayers )
66{
67 std::vector<INPUT_LAYER_DESC> layerDescriptions;
68
69 for( const wxString& sourceLayer : aSourceLayers )
70 {
72 desc.Name = sourceLayer;
73 desc.PermittedLayers = aPermittedLayers;
74 desc.AutoMapLayer = getAutoMappedLayer( sourceLayer, aPermittedLayers );
75 desc.Required = false;
76
77 layerDescriptions.push_back( desc );
78 }
79
80 return layerDescriptions;
81}
82
83
85{
86 if( aFrame && aFrame->GetBoard() )
87 return aFrame->GetBoard()->GetEnabledLayers();
88
89 return LSET::AllLayersMask();
90}
91
92
95 m_parent( aParent ),
96 m_xOrigin( aParent, nullptr, m_xCtrl, nullptr ),
97 m_yOrigin( aParent, m_yLabel, m_yCtrl, m_yUnits ),
100{
101 // The SVG import has currently a flaw: all SVG shapes are imported as curves and
102 // converted to a lot of segments. A better approach is to convert to polylines
103 // (not yet existing in Pcbnew) and keep arcs and circles as primitives (not yet
104 // possible with tinysvg library).
105
106 m_importer = std::make_unique<GRAPHICS_IMPORTER_PCBNEW>( aParent->GetModel() );
107 m_gfxImportMgr = std::make_unique<GRAPHICS_IMPORT_MGR>();
108
109 // Configure the layers list selector
110 m_SelLayerBox->SetLayersHotkeys( false ); // Do not display hotkeys
111 m_SelLayerBox->SetBoardFrame( m_parent );
112 m_SelLayerBox->Resync();
113
114 for( const std::pair<const DXF_IMPORT_UNITS, wxString>& unitEntry : dxfUnitsMap )
115 m_dxfUnitsChoice->Append( unitEntry.second );
116
118
119 wxCommandEvent dummy;
120 onFilename( dummy );
121
124
125 GetSizer()->Fit( this );
126 GetSizer()->SetSizeHints( this );
127 Centre();
128
129 m_textCtrlFileName->Connect( wxEVT_COMMAND_TEXT_UPDATED,
130 wxCommandEventHandler( DIALOG_IMPORT_GRAPHICS::onFilename ),
131 nullptr, this );
132}
133
134
136{
137 m_textCtrlFileName->Disconnect( wxEVT_COMMAND_TEXT_UPDATED,
138 wxCommandEventHandler( DIALOG_IMPORT_GRAPHICS::onFilename ),
139 nullptr, this );
140}
141
142
143void DIALOG_IMPORT_GRAPHICS::SetFilenameOverride( const wxString& aFilenameOverride )
144{
145 m_filenameOverride = aFilenameOverride;
146}
147
148
150{
151 DIALOG_SHIM::TransferDataToWindow();
152
153 // We have to set the filename field value here, otherwise it gets overwritten by state loading
154 if( !m_filenameOverride.IsEmpty() )
156
157 return true;
158}
159
160
161void DIALOG_IMPORT_GRAPHICS::onFilename( wxCommandEvent& event )
162{
163 bool enableDXFControls = true;
164 wxString ext = wxFileName( m_textCtrlFileName->GetValue() ).GetExt();
165
166 if( std::unique_ptr<GRAPHICS_IMPORT_PLUGIN> plugin = m_gfxImportMgr->GetPluginByExt( ext ) )
167 enableDXFControls = dynamic_cast<DXF_IMPORT_PLUGIN*>( plugin.get() ) != nullptr;
168
169 m_defaultLineWidth.Enable( enableDXFControls );
170
171 m_dxfUnitsLabel->Enable( enableDXFControls );
172 m_dxfUnitsChoice->Enable( enableDXFControls );
173
174 m_radioBtnMapLayers->Enable( enableDXFControls );
175
176 if( !enableDXFControls && m_radioBtnMapLayers->GetValue() )
177 m_radioBtnSingleLayer->SetValue( true );
178}
179
180
181void DIALOG_IMPORT_GRAPHICS::onBrowseFiles( wxCommandEvent& event )
182{
183 wxString path;
184 wxString filename = m_textCtrlFileName->GetValue();
185
186 if( !filename.IsEmpty() )
187 {
188 wxFileName fn( filename );
189 path = fn.GetPath();
190 filename = fn.GetFullName();
191 }
192
193 // Generate the list of handled file formats
194 wxString wildcardsDesc;
195 wxString allWildcards;
196
197 for( GRAPHICS_IMPORT_MGR::GFX_FILE_T pluginType : m_gfxImportMgr->GetImportableFileTypes() )
198 {
199 std::unique_ptr<GRAPHICS_IMPORT_PLUGIN> plugin = m_gfxImportMgr->GetPlugin( pluginType );
200 const std::vector<std::string> extensions = plugin->GetFileExtensions();
201
202 wildcardsDesc += wxT( "|" ) + plugin->GetName() + AddFileExtListToFilter( extensions );
203 allWildcards += plugin->GetWildcards() + wxT( ";" );
204 }
205
206 wildcardsDesc = _( "All supported formats" ) + wxT( "|" ) + allWildcards + wildcardsDesc;
207
208 wxFileDialog dlg( m_parent, _( "Import Graphics" ), path, filename, wildcardsDesc,
209 wxFD_OPEN | wxFD_FILE_MUST_EXIST );
210
212
213 if( dlg.ShowModal() == wxID_OK && !dlg.GetPath().IsEmpty() )
214 m_textCtrlFileName->SetValue( dlg.GetPath() );
215}
216
217
219{
220 if( !wxDialog::TransferDataFromWindow() )
221 return false;
222
223 if( m_textCtrlFileName->GetValue().IsEmpty() )
224 {
225 wxMessageBox( _( "Please select a file to import." ) );
226 return false;
227 }
228
229 bool useDxfLayerMapping = m_radioBtnMapLayers->IsEnabled() && m_radioBtnMapLayers->GetValue();
230 bool useSingleLayer = m_radioBtnSingleLayer->GetValue();
231
232 if( useSingleLayer && m_SelLayerBox->GetLayerSelection() < 0 )
233 {
234 wxMessageBox( _( "Please select a valid layer." ) );
235 return false;
236 }
237
238 PCBNEW_SETTINGS* cfg = m_parent->GetPcbNewSettings();
239 wxString ext = wxFileName( m_textCtrlFileName->GetValue() ).GetExt();
241 double xscale = scale;
242 double yscale = scale;
243
245 xscale *= -1.0;
246
248 yscale *= -1.0;
249
250 VECTOR2D origin( m_xOrigin.GetDoubleValue() / xscale, m_yOrigin.GetDoubleValue() / yscale );
251
252 if( std::unique_ptr<GRAPHICS_IMPORT_PLUGIN> plugin = m_gfxImportMgr->GetPluginByExt( ext ) )
253 {
254 DXF_IMPORT_PLUGIN* dxfPlugin = dynamic_cast<DXF_IMPORT_PLUGIN*>( plugin.get() );
255
256 if( dxfPlugin )
257 {
258 auto it = dxfUnitsMap.begin();
259 std::advance( it, m_dxfUnitsChoice->GetSelection() );
260
261 if( it == dxfUnitsMap.end() )
263 else
264 dxfPlugin->SetUnit( it->first );
265
266 m_importer->SetLineWidthMM( pcbIUScale.IUTomm( m_defaultLineWidth.GetIntValue() ) );
267 }
268 else
269 {
270 m_importer->SetLineWidthMM( 0.0 );
271 }
272
273 m_importer->SetPlugin( std::move( plugin ) );
274 m_importer->ClearLayerMap();
275
276 if( useSingleLayer )
277 m_importer->SetLayer( PCB_LAYER_ID( m_SelLayerBox->GetLayerSelection() ) );
278 else
279 m_importer->SetLayer( m_parent->GetActiveLayer() );
280
281 m_importer->SetImportOffsetMM( origin * pcbIUScale.IUTomm( 1 ) );
282
283 LOCALE_IO dummy; // Ensure floats can be read.
284
285 if( m_importer->Load( m_textCtrlFileName->GetValue() ) )
286 {
287 if( useDxfLayerMapping && dxfPlugin )
288 {
289 std::vector<wxString> sourceLayers = dxfPlugin->GetSourceLayers();
290
291 if( !sourceLayers.empty() )
292 {
293 LSET permittedLayers = getPermittedImportLayers( m_parent );
295 this, buildDxfLayerDescriptions( sourceLayers, permittedLayers ) ) );
296 }
297 }
298
299 m_importer->Import( VECTOR2D( scale, scale ) );
300 }
301
302 // Get warning messages:
303 wxString warnings = m_importer->GetMessages();
304
305 // This isn't a fatal error so allow the dialog to close with wxID_OK.
306 if( !warnings.empty() )
307 {
308 HTML_MESSAGE_BOX dlg( this, _( "Warning" ) );
309 dlg.MessageSet( _( "Items in the imported file could not be handled properly." ) );
310 warnings.Replace( wxT( "\n" ), wxT( "<br/>" ) );
311 dlg.AddHTML_Text( warnings );
312 dlg.ShowModal();
313 }
314
315 return true;
316 }
317 else
318 {
319 wxMessageBox( _( "There is no plugin to handle this file type." ) );
320 return false;
321 }
322}
323
324
325void DIALOG_IMPORT_GRAPHICS::onUpdateUI( wxUpdateUIEvent& event )
326{
327 m_xOrigin.Enable( m_placeAtCheckbox->GetValue() );
328 m_yOrigin.Enable( m_placeAtCheckbox->GetValue() );
329
330 m_tolerance.Enable( m_rbFixDiscontinuities->GetValue() );
331
332 m_SelLayerBox->Enable( m_radioBtnSingleLayer->GetValue() );
333}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1034
DIALOG_IMPORT_GRAPHICS_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Import Vector Graphics File"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
std::unique_ptr< GRAPHICS_IMPORT_MGR > m_gfxImportMgr
void onFilename(wxCommandEvent &event)
std::unique_ptr< GRAPHICS_IMPORTER_PCBNEW > m_importer
void onBrowseFiles(wxCommandEvent &event) override
void SetFilenameOverride(const wxString &aFilenameOverride)
Set the filename override to be applied in TransferDataToWindow.
void onUpdateUI(wxUpdateUIEvent &event) override
DIALOG_IMPORT_GRAPHICS(PCB_BASE_FRAME *aParent)
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.
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition dialog_shim.h:79
void SetupStandardButtons(std::map< int, wxString > aLabels={})
int ShowModal() override
std::vector< wxString > GetSourceLayers() const
void SetUnit(DXF_IMPORT_UNITS aUnit)
Set the default units when importing DXFs.
GFX_FILE_T
List of handled file types.
void MessageSet(const wxString &message)
Add a message (in bold) to message list.
void AddHTML_Text(const wxString &message)
Add HTML text (without any change) to message list.
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:37
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:739
static const LSET & AllLayersMask()
Definition lset.cpp:637
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:184
DISPLAY_OPTIONS m_Display
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
BOARD * GetBoard() const
virtual BOARD_ITEM_CONTAINER * GetModel() const =0
const std::map< DXF_IMPORT_UNITS, wxString > dxfUnitsMap
static LSET getPermittedImportLayers(PCB_BASE_FRAME *aFrame)
static PCB_LAYER_ID getAutoMappedLayer(const wxString &aSourceLayer, const LSET &aPermittedLayers)
static std::vector< INPUT_LAYER_DESC > buildDxfLayerDescriptions(const std::vector< wxString > &aSourceLayers, const LSET &aPermittedLayers)
#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
@ UNDEFINED_LAYER
Definition layer_ids.h:57
KICOMMON_API double DoubleValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Convert aTextValue to a double.
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:448
const int scale
std::vector< FAB_LAYER_COLOR > dummy
Describes an imported layer and how it could be mapped to KiCad Layers.
PCB_LAYER_ID AutoMapLayer
Best guess as to what the equivalent KiCad layer might be.
bool Required
Should we require the layer to be assigned?
LSET PermittedLayers
KiCad layers that the imported layer can be mapped onto.
wxString Name
Imported layer name as displayed in original application.
std::string path
VECTOR2< double > VECTOR2D
Definition vector2d.h:682
wxString AddFileExtListToFilter(const std::vector< std::string > &aExts)
Build the wildcard extension file dialog wildcard filter to add to the base message dialog.
Definition of file extensions used in Kicad.