KiCad PCB EDA Suite
dialog_import_gfx.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 (C) 1992-2020 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, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
26 
27 #include "dialog_import_gfx.h"
28 #include <kiface_i.h>
29 #include <locale_io.h>
30 #include <pcb_layer_box_selector.h>
32 #include <board.h>
33 #include <bitmaps.h>
34 #include <map>
35 #include "dxf_import_plugin.h"
36 #include <wx/filedlg.h>
37 
38 #include <memory>
39 
40 // Static members of DIALOG_IMPORT_GFX, to remember the user's choices during the session
45 double DIALOG_IMPORT_GFX::m_scaleImport = 1.0; // Do not change the imported items size
46 int DIALOG_IMPORT_GFX::m_originUnits = 0; // millimeter
47 int DIALOG_IMPORT_GFX::m_lineWidthUnits = 0; // millimeter
48 int DIALOG_IMPORT_GFX::m_dxfUnits = 0; // first entry in the dxfUnits map below
49 
50 const std::map<DXF_IMPORT_UNITS, wxString> dxfUnitsMap = {
51  { DXF_IMPORT_UNITS::INCHES, _( "Inches" ) },
52  { DXF_IMPORT_UNITS::MILLIMETERS, _( "Millimeters" ) },
53  { DXF_IMPORT_UNITS::MILS, _( "Mils" ) },
54  { DXF_IMPORT_UNITS::CENTIMETERS, _( "Centimeter" ) },
55  { DXF_IMPORT_UNITS::FEET, _( "Feet" ) },
56 };
57 
58 
59 DIALOG_IMPORT_GFX::DIALOG_IMPORT_GFX( PCB_BASE_FRAME* aParent, bool aImportAsFootprintGraphic )
60  : DIALOG_IMPORT_GFX_BASE( aParent )
61 {
62  m_parent = aParent;
63 
64  if( aImportAsFootprintGraphic )
65  m_importer = std::make_unique<GRAPHICS_IMPORTER_FOOTPRINT>( m_parent->GetBoard()->GetFirstFootprint() );
66  else
67  m_importer = std::make_unique<GRAPHICS_IMPORTER_BOARD>( m_parent->GetBoard() );
68 
69  // construct an import manager with options from config
70  {
72  // Currently: all types are allowed, so the blacklist is empty
73  // (no GFX_FILE_T in the blacklist)
74  // To disable SVG import, enable these 2 lines
75  // if( !ADVANCED_CFG::GetCfg().m_enableSvgImport )
76  // blacklist.push_back( GRAPHICS_IMPORT_MGR::SVG );
77  // The SVG import has currently a flaw: all SVG shapes are imported as curves and
78  // converted to a lot of segments. A better approach is to convert to polylines
79  // (not yet existing in Pcbnew) and keep arcs and circles as primitives (not yet
80  // possible with tinysvg library).
81 
82  m_gfxImportMgr = std::make_unique<GRAPHICS_IMPORT_MGR>( blacklist );
83  }
84 
85  m_originUnits = 0;
86  m_origin.x = 0.0; // always in mm
87  m_origin.y = 0.0; // always in mm
88  m_lineWidth = 0.2; // always in mm
89  m_lineWidthUnits = 0;
90 
91  auto cfg = m_parent->GetPcbNewSettings();
92 
93  m_layer = cfg->m_ImportGraphics.layer;
94  m_placementInteractive = cfg->m_ImportGraphics.interactive_placement;
95  m_filename = cfg->m_ImportGraphics.last_file;
96  m_lineWidth = cfg->m_ImportGraphics.line_width;
97  m_lineWidthUnits = cfg->m_ImportGraphics.line_width_units;
98  m_originUnits = cfg->m_ImportGraphics.origin_units;
99  m_origin.x = cfg->m_ImportGraphics.origin_x;
100  m_origin.y = cfg->m_ImportGraphics.origin_y;
101  m_dxfUnits = cfg->m_ImportGraphics.dxf_units;
102 
103  m_choiceUnitLineWidth->SetSelection( m_lineWidthUnits );
105 
106  m_DxfPcbPositionUnits->SetSelection( m_originUnits );
108 
109  m_textCtrlFileName->SetValue( m_filename );
112  m_groupItems->SetValue( m_shouldGroupItems );
113 
115 
116  // Configure the layers list selector
117  m_SelLayerBox->SetLayersHotkeys( false ); // Do not display hotkeys
120 
122  {
123  m_layer = Dwgs_User;
125  }
126 
127  for( auto& unitEntry : dxfUnitsMap )
128  m_choiceDxfUnits->Append( unitEntry.second );
129 
130  m_choiceDxfUnits->SetSelection( m_dxfUnits );
131 
133 
135  m_sdbSizerOK->SetDefault();
136  GetSizer()->Fit( this );
137  GetSizer()->SetSizeHints( this );
138  Centre();
139 }
140 
141 
143 {
144  auto cfg = m_parent->GetPcbNewSettings();
145 
147  cfg->m_ImportGraphics.interactive_placement = m_placementInteractive;
148  cfg->m_ImportGraphics.last_file = m_filename;
149  cfg->m_ImportGraphics.line_width = m_lineWidth;
150  cfg->m_ImportGraphics.line_width_units = m_lineWidthUnits;
151  cfg->m_ImportGraphics.origin_units = m_originUnits;
152  cfg->m_ImportGraphics.origin_x = m_origin.x;
153  cfg->m_ImportGraphics.origin_y = m_origin.y;
154  cfg->m_ImportGraphics.dxf_units = m_dxfUnits;
155 }
156 
157 
158 void DIALOG_IMPORT_GFX::DIALOG_IMPORT_GFX::onUnitPositionSelection( wxCommandEvent& event )
159 {
160  // Collect last entered values:
161  updatePcbImportOffsets_mm();
162 
163  m_originUnits = m_DxfPcbPositionUnits->GetSelection();;
164  showPcbImportOffsets();
165 }
166 
167 
169 {
170  double value = DoubleValueFromString( EDA_UNITS::UNSCALED, m_textCtrlLineWidth->GetValue() );
171 
172  switch( m_lineWidthUnits )
173  {
174  default:
175  case 0: break; // display units = mm
176  case 1: value *= 25.4 / 1000; break; // display units = mil
177  case 2: value *= 25.4; break; // display units = inch
178  }
179 
180  return value; // value is in mm
181 }
182 
183 
184 void DIALOG_IMPORT_GFX::onUnitWidthSelection( wxCommandEvent& event )
185 {
187 
188  // Switch to new units
189  m_lineWidthUnits = m_choiceUnitLineWidth->GetSelection();
191 }
192 
193 
195 {
196  // Display m_origin value according to the unit selection:
197  VECTOR2D offset = m_origin;
198 
199  if( m_originUnits ) // Units are inches
200  offset = m_origin / 25.4;
201 
202  m_DxfPcbXCoord->SetValue( wxString::Format( "%f", offset.x ) );
203  m_DxfPcbYCoord->SetValue( wxString::Format( "%f", offset.y ) );
204 
205 }
206 
207 
209 {
210  double value;
211 
212  switch( m_lineWidthUnits )
213  {
214  default:
215  case 0: value = m_lineWidth; break; // display units = mm
216  case 1: value = m_lineWidth / 25.4 * 1000; break; // display units = mil
217  case 2: value = m_lineWidth / 25.4; break; // display units = inch
218  }
219 
220  m_textCtrlLineWidth->SetValue( wxString::Format( "%f", value ) );
221 }
222 
223 
224 void DIALOG_IMPORT_GFX::onBrowseFiles( wxCommandEvent& event )
225 {
226  wxString path;
227  wxString filename;
228 
229  if( !m_filename.IsEmpty() )
230  {
231  wxFileName fn( m_filename );
232  path = fn.GetPath();
233  filename = fn.GetFullName();
234  }
235 
236  // Generate the list of handled file formats
237  wxString wildcardsDesc;
238  wxString allWildcards;
239 
240  for( auto pluginType : m_gfxImportMgr->GetImportableFileTypes() )
241  {
242  auto plugin = m_gfxImportMgr->GetPlugin( pluginType );
243  const auto extensions = plugin->GetFileExtensions();
244 
245  wildcardsDesc += "|" + plugin->GetName() + AddFileExtListToFilter( extensions );
246  allWildcards += plugin->GetWildcards() + ";";
247  }
248 
249  wildcardsDesc = _( "All supported formats|" ) + allWildcards + wildcardsDesc;
250 
251  wxFileDialog dlg( m_parent, _( "Open File" ), path, filename, wildcardsDesc,
252  wxFD_OPEN | wxFD_FILE_MUST_EXIST );
253 
254  if( dlg.ShowModal() != wxID_OK )
255  return;
256 
257  wxString fileName = dlg.GetPath();
258 
259  if( fileName.IsEmpty() )
260  return;
261 
262  m_filename = fileName;
263  m_textCtrlFileName->SetValue( fileName );
264 }
265 
266 
268 {
269  if( !wxDialog::TransferDataFromWindow() )
270  return false;
271 
272  m_filename = m_textCtrlFileName->GetValue();
273 
274  if( m_filename.IsEmpty() )
275  {
276  wxMessageBox( _( "No file selected!" ) );
277  return false;
278  }
279 
280  m_originUnits = m_DxfPcbPositionUnits->GetSelection();
281  updatePcbImportOffsets_mm(); // Update m_originX and m_originY;
282 
284 
285  if( m_layer < 0 )
286  {
287  wxMessageBox( _( "Please select a valid layer." ) );
288  return false;
289  }
290 
291  m_lineWidthUnits = m_choiceUnitLineWidth->GetSelection();
293 
294  m_dxfUnits = m_choiceDxfUnits->GetSelection();
295 
296  m_importer->SetLayer( PCB_LAYER_ID( m_layer ) );
297 
298  auto plugin = m_gfxImportMgr->GetPluginByExt( wxFileName( m_filename ).GetExt() );
299 
300  if( plugin )
301  {
302  DXF_IMPORT_PLUGIN* dxfPlugin = dynamic_cast<DXF_IMPORT_PLUGIN*>( plugin.get() );
303  if( dxfPlugin != nullptr )
304  {
305  auto it = dxfUnitsMap.begin();
306  std::advance( it, m_dxfUnits );
307 
308  if( it == dxfUnitsMap.end() )
309  dxfPlugin->SetUnit( DXF_IMPORT_UNITS::DEFAULT );
310  else
311  dxfPlugin->SetUnit( it->first );
312  }
313 
314  // Set coordinates offset for import (offset is given in mm)
315  m_importer->SetImportOffsetMM( m_origin );
317  m_textCtrlImportScale->GetValue() );
318 
319  // The line width is meant to be in pcbnew units, so we scale the import width before
320  // applying
321  m_importer->SetLineWidthMM( m_lineWidth * m_scaleImport );
322  m_importer->SetPlugin( std::move( plugin ) );
323 
324  LOCALE_IO dummy; // Ensure floats can be read.
325 
326  if( m_importer->Load( m_filename ) )
327  m_importer->Import( m_scaleImport );
328 
329  // Get warning messages:
330  wxString warnings = m_importer->GetMessages();
331 
332  // This isn't a fatal error so allow the dialog to close with wxID_OK.
333  if( !warnings.empty() )
334  {
335  HTML_MESSAGE_BOX dlg( this, _( "Warning" ) );
336  dlg.MessageSet( _( "Items in the imported file could not be handled properly." ) );
337  warnings.Replace( "\n", "<br/>" );
338  dlg.AddHTML_Text( warnings );
339  dlg.ShowModal();
340  }
341  }
342  else
343  {
344  wxMessageBox( _( "There is no plugin to handle this file type." ) );
345  return false;
346  }
347 
348  return true;
349 }
350 
351 
352 void DIALOG_IMPORT_GFX::originOptionOnUpdateUI( wxUpdateUIEvent& event )
353 {
356 
357  if( m_rbAbsolutePlacement->GetValue() == m_placementInteractive )
359 
361  m_DxfPcbXCoord->Enable( not m_placementInteractive );
362  m_DxfPcbYCoord->Enable( not m_placementInteractive );
363 }
364 
365 
367 {
370 
371  if( m_originUnits ) // Units are inches
372  m_origin = m_origin * 25.4;
373 }
374 
375 
std::unique_ptr< GRAPHICS_IMPORT_MGR > m_gfxImportMgr
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
const std::map< DXF_IMPORT_UNITS, wxString > dxfUnitsMap
void originOptionOnUpdateUI(wxUpdateUIEvent &event) override
bool TransferDataFromWindow() override
Class DIALOG_IMPORT_GFX_BASE.
DIALOG_IMPORT_GFX(PCB_BASE_FRAME *aParent, bool aUseModuleItems=false)
void onBrowseFiles(wxCommandEvent &event) override
void onUnitWidthSelection(wxCommandEvent &event) override
wxRadioButton * m_rbInteractivePlacement
static LIB_PART * dummy()
Used to draw a dummy shape when a LIB_PART is not found in library.
Definition: sch_symbol.cpp:69
void SetBoardFrame(PCB_BASE_FRAME *aFrame)
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition: dialog_shim.h:98
LAYER_NUM GetLayerSelection() const
PCB_LAYER_ID
A quick note on layer IDs:
FOOTPRINT * GetFirstFootprint() const
Gets the first footprint on the board or nullptr.
Definition: board.h:382
void SetUnit(DXF_IMPORT_UNITS aUnit)
Set the default units when importing DXFs DXFs can lack units by design which requires the importing ...
PCB_LAYER_BOX_SELECTOR * m_SelLayerBox
static int m_lineWidthUnits
static int m_originUnits
PCB_BASE_FRAME * m_parent
Definition of file extensions used in Kicad.
int SetLayerSelection(LAYER_NUM layer)
HTML_MESSAGE_BOX.
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:104
bool SetLayersHotkeys(bool value)
std::vector< GFX_FILE_T > TYPE_LIST
void MessageSet(const wxString &message)
Add a message (in bold) to message list.
static LAYER_NUM m_layer
int LAYER_NUM
This can be replaced with int and removed.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
static wxString m_filename
static double m_scaleImport
#define _(s)
Definition: 3d_actions.cpp:33
wxRadioButton * m_rbAbsolutePlacement
void AddHTML_Text(const wxString &message)
Add HTML text (without any change) to message list.
PCBNEW_SETTINGS * GetPcbNewSettings() const
static bool m_placementInteractive
static bool m_shouldGroupItems
DIALOG_IMPORT_GRAPHICS m_ImportGraphics
BOARD * GetBoard() const
double DoubleValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:293
std::unique_ptr< GRAPHICS_IMPORTER_PCBNEW > m_importer
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
wxString AddFileExtListToFilter(const std::vector< std::string > &aExts)
Build the wildcard extension file dialog wildcard filter to add to the base message dialog.