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