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 #include <wx/msgdlg.h>
38 
39 #include <memory>
40 
41 // Static members of DIALOG_IMPORT_GFX, to remember the user's choices during the session
46 double DIALOG_IMPORT_GFX::m_scaleImport = 1.0; // Do not change the imported items size
47 int DIALOG_IMPORT_GFX::m_originUnits = 0; // millimeter
48 int DIALOG_IMPORT_GFX::m_lineWidthUnits = 0; // millimeter
49 int DIALOG_IMPORT_GFX::m_dxfUnits = 0; // first entry in the dxfUnits map below
50 
51 const std::map<DXF_IMPORT_UNITS, wxString> dxfUnitsMap = {
52  { DXF_IMPORT_UNITS::INCHES, _( "Inches" ) },
53  { DXF_IMPORT_UNITS::MILLIMETERS, _( "Millimeters" ) },
54  { DXF_IMPORT_UNITS::MILS, _( "Mils" ) },
55  { DXF_IMPORT_UNITS::CENTIMETERS, _( "Centimeter" ) },
56  { DXF_IMPORT_UNITS::FEET, _( "Feet" ) },
57 };
58 
59 
60 DIALOG_IMPORT_GFX::DIALOG_IMPORT_GFX( PCB_BASE_FRAME* aParent, bool aImportAsFootprintGraphic )
61  : DIALOG_IMPORT_GFX_BASE( aParent )
62 {
63  m_parent = aParent;
64 
65  if( aImportAsFootprintGraphic )
66  m_importer = std::make_unique<GRAPHICS_IMPORTER_FOOTPRINT>( m_parent->GetBoard()->GetFirstFootprint() );
67  else
68  m_importer = std::make_unique<GRAPHICS_IMPORTER_BOARD>( m_parent->GetBoard() );
69 
70  // construct an import manager with options from config
71  {
73  // Currently: all types are allowed, so the blacklist is empty
74  // (no GFX_FILE_T in the blacklist)
75  // To disable SVG import, enable these 2 lines
76  // if( !ADVANCED_CFG::GetCfg().m_enableSvgImport )
77  // blacklist.push_back( GRAPHICS_IMPORT_MGR::SVG );
78  // The SVG import has currently a flaw: all SVG shapes are imported as curves and
79  // converted to a lot of segments. A better approach is to convert to polylines
80  // (not yet existing in Pcbnew) and keep arcs and circles as primitives (not yet
81  // possible with tinysvg library).
82 
83  m_gfxImportMgr = std::make_unique<GRAPHICS_IMPORT_MGR>( blacklist );
84  }
85 
86  m_originUnits = 0;
87  m_origin.x = 0.0; // always in mm
88  m_origin.y = 0.0; // always in mm
89  m_lineWidth = 0.2; // always in mm
90  m_lineWidthUnits = 0;
91 
92  auto cfg = m_parent->GetPcbNewSettings();
93 
94  m_layer = cfg->m_ImportGraphics.layer;
95  m_placementInteractive = cfg->m_ImportGraphics.interactive_placement;
96  m_filename = cfg->m_ImportGraphics.last_file;
97  m_lineWidth = cfg->m_ImportGraphics.line_width;
98  m_lineWidthUnits = cfg->m_ImportGraphics.line_width_units;
99  m_originUnits = cfg->m_ImportGraphics.origin_units;
100  m_origin.x = cfg->m_ImportGraphics.origin_x;
101  m_origin.y = cfg->m_ImportGraphics.origin_y;
102  m_dxfUnits = cfg->m_ImportGraphics.dxf_units;
103 
104  m_choiceUnitLineWidth->SetSelection( m_lineWidthUnits );
106 
107  m_DxfPcbPositionUnits->SetSelection( m_originUnits );
109 
110  m_textCtrlFileName->SetValue( m_filename );
113  m_groupItems->SetValue( m_shouldGroupItems );
114 
116 
117  // Configure the layers list selector
118  m_SelLayerBox->SetLayersHotkeys( false ); // Do not display hotkeys
121 
123  {
124  m_layer = Dwgs_User;
126  }
127 
128  for( auto& unitEntry : dxfUnitsMap )
129  m_choiceDxfUnits->Append( unitEntry.second );
130 
131  m_choiceDxfUnits->SetSelection( m_dxfUnits );
132 
134 
136  m_sdbSizerOK->SetDefault();
137  GetSizer()->Fit( this );
138  GetSizer()->SetSizeHints( this );
139  Centre();
140 }
141 
142 
144 {
145  auto cfg = m_parent->GetPcbNewSettings();
146 
148  cfg->m_ImportGraphics.interactive_placement = m_placementInteractive;
149  cfg->m_ImportGraphics.last_file = m_filename;
150  cfg->m_ImportGraphics.line_width = m_lineWidth;
151  cfg->m_ImportGraphics.line_width_units = m_lineWidthUnits;
152  cfg->m_ImportGraphics.origin_units = m_originUnits;
153  cfg->m_ImportGraphics.origin_x = m_origin.x;
154  cfg->m_ImportGraphics.origin_y = m_origin.y;
155  cfg->m_ImportGraphics.dxf_units = m_dxfUnits;
156 }
157 
158 
159 void DIALOG_IMPORT_GFX::DIALOG_IMPORT_GFX::onUnitPositionSelection( wxCommandEvent& event )
160 {
161  // Collect last entered values:
162  updatePcbImportOffsets_mm();
163 
164  m_originUnits = m_DxfPcbPositionUnits->GetSelection();;
165  showPcbImportOffsets();
166 }
167 
168 
170 {
171  double value = DoubleValueFromString( EDA_UNITS::UNSCALED, m_textCtrlLineWidth->GetValue() );
172 
173  switch( m_lineWidthUnits )
174  {
175  default:
176  case 0: break; // display units = mm
177  case 1: value *= 25.4 / 1000; break; // display units = mil
178  case 2: value *= 25.4; break; // display units = inch
179  }
180 
181  return value; // value is in mm
182 }
183 
184 
185 void DIALOG_IMPORT_GFX::onUnitWidthSelection( wxCommandEvent& event )
186 {
188 
189  // Switch to new units
190  m_lineWidthUnits = m_choiceUnitLineWidth->GetSelection();
192 }
193 
194 
196 {
197  // Display m_origin value according to the unit selection:
198  VECTOR2D offset = m_origin;
199 
200  if( m_originUnits ) // Units are inches
201  offset = m_origin / 25.4;
202 
203  m_DxfPcbXCoord->SetValue( wxString::Format( "%f", offset.x ) );
204  m_DxfPcbYCoord->SetValue( wxString::Format( "%f", offset.y ) );
205 
206 }
207 
208 
210 {
211  double value;
212 
213  switch( m_lineWidthUnits )
214  {
215  default:
216  case 0: value = m_lineWidth; break; // display units = mm
217  case 1: value = m_lineWidth / 25.4 * 1000; break; // display units = mil
218  case 2: value = m_lineWidth / 25.4; break; // display units = inch
219  }
220 
221  m_textCtrlLineWidth->SetValue( wxString::Format( "%f", value ) );
222 }
223 
224 
225 void DIALOG_IMPORT_GFX::onBrowseFiles( wxCommandEvent& event )
226 {
227  wxString path;
228  wxString filename;
229 
230  if( !m_filename.IsEmpty() )
231  {
232  wxFileName fn( m_filename );
233  path = fn.GetPath();
234  filename = fn.GetFullName();
235  }
236 
237  // Generate the list of handled file formats
238  wxString wildcardsDesc;
239  wxString allWildcards;
240 
241  for( auto pluginType : m_gfxImportMgr->GetImportableFileTypes() )
242  {
243  auto plugin = m_gfxImportMgr->GetPlugin( pluginType );
244  const auto extensions = plugin->GetFileExtensions();
245 
246  wildcardsDesc += "|" + plugin->GetName() + AddFileExtListToFilter( extensions );
247  allWildcards += plugin->GetWildcards() + ";";
248  }
249 
250  wildcardsDesc = _( "All supported formats|" ) + allWildcards + wildcardsDesc;
251 
252  wxFileDialog dlg( m_parent, _( "Open File" ), path, filename, wildcardsDesc,
253  wxFD_OPEN | wxFD_FILE_MUST_EXIST );
254 
255  if( dlg.ShowModal() != wxID_OK )
256  return;
257 
258  wxString fileName = dlg.GetPath();
259 
260  if( fileName.IsEmpty() )
261  return;
262 
263  m_filename = fileName;
264  m_textCtrlFileName->SetValue( fileName );
265 }
266 
267 
269 {
270  if( !wxDialog::TransferDataFromWindow() )
271  return false;
272 
273  m_filename = m_textCtrlFileName->GetValue();
274 
275  if( m_filename.IsEmpty() )
276  {
277  wxMessageBox( _( "No file selected!" ) );
278  return false;
279  }
280 
281  m_originUnits = m_DxfPcbPositionUnits->GetSelection();
282  updatePcbImportOffsets_mm(); // Update m_originX and m_originY;
283 
285 
286  if( m_layer < 0 )
287  {
288  wxMessageBox( _( "Please select a valid layer." ) );
289  return false;
290  }
291 
292  m_lineWidthUnits = m_choiceUnitLineWidth->GetSelection();
294 
295  m_dxfUnits = m_choiceDxfUnits->GetSelection();
296 
297  m_importer->SetLayer( PCB_LAYER_ID( m_layer ) );
298 
299  auto plugin = m_gfxImportMgr->GetPluginByExt( wxFileName( m_filename ).GetExt() );
300 
301  if( plugin )
302  {
303  DXF_IMPORT_PLUGIN* dxfPlugin = dynamic_cast<DXF_IMPORT_PLUGIN*>( plugin.get() );
304  if( dxfPlugin != nullptr )
305  {
306  auto it = dxfUnitsMap.begin();
307  std::advance( it, m_dxfUnits );
308 
309  if( it == dxfUnitsMap.end() )
310  dxfPlugin->SetUnit( DXF_IMPORT_UNITS::DEFAULT );
311  else
312  dxfPlugin->SetUnit( it->first );
313  }
314 
315  // Set coordinates offset for import (offset is given in mm)
316  m_importer->SetImportOffsetMM( m_origin );
318  m_textCtrlImportScale->GetValue() );
319 
320  // The line width is meant to be in pcbnew units, so we scale the import width before
321  // applying
322  m_importer->SetLineWidthMM( m_lineWidth * m_scaleImport );
323  m_importer->SetPlugin( std::move( plugin ) );
324 
325  LOCALE_IO dummy; // Ensure floats can be read.
326 
327  if( m_importer->Load( m_filename ) )
328  m_importer->Import( m_scaleImport );
329 
330  // Get warning messages:
331  wxString warnings = m_importer->GetMessages();
332 
333  // This isn't a fatal error so allow the dialog to close with wxID_OK.
334  if( !warnings.empty() )
335  {
336  HTML_MESSAGE_BOX dlg( this, _( "Warning" ) );
337  dlg.MessageSet( _( "Items in the imported file could not be handled properly." ) );
338  warnings.Replace( "\n", "<br/>" );
339  dlg.AddHTML_Text( warnings );
340  dlg.ShowModal();
341  }
342  }
343  else
344  {
345  wxMessageBox( _( "There is no plugin to handle this file type." ) );
346  return false;
347  }
348 
349  return true;
350 }
351 
352 
353 void DIALOG_IMPORT_GFX::originOptionOnUpdateUI( wxUpdateUIEvent& event )
354 {
357 
358  if( m_rbAbsolutePlacement->GetValue() == m_placementInteractive )
360 
362  m_DxfPcbXCoord->Enable( not m_placementInteractive );
363  m_DxfPcbYCoord->Enable( not m_placementInteractive );
364 }
365 
366 
368 {
371 
372  if( m_originUnits ) // Units are inches
373  m_origin = m_origin * 25.4;
374 }
375 
376 
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
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:97
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:71
LAYER_NUM GetLayerSelection() const
PCB_LAYER_ID
A quick note on layer IDs:
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:317
void SetUnit(DXF_IMPORT_UNITS aUnit)
Set the default units when importing DXFs.
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.
#define _(s)
int SetLayerSelection(LAYER_NUM layer)
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:105
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
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:307
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.