KiCad PCB EDA Suite
dialog_import_gfx.cpp
Go to the documentation of this file.
1 
6 /*
7  * This program source code file is part of KiCad, a free EDA CAD application.
8  *
9  * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
10  * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, you may find one here:
24  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
25  * or you may search the http://www.gnu.org website for the version 2 license,
26  * or you may write to the Free Software Foundation, Inc.,
27  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
28  */
29 
30 #include "dialog_import_gfx.h"
31 
32 #include <advanced_config.h>
33 #include <convert_to_biu.h>
34 #include <kiface_i.h>
35 #include <pcb_layer_box_selector.h>
37 
38 #include <class_board.h>
39 #include <class_module.h>
40 #include <class_edge_mod.h>
41 #include <class_text_mod.h>
42 #include <class_pcb_text.h>
43 
44 // Configuration path (group) to store entry keys below.
45 #define IMPORT_GFX_GROUP "ImportGraphics"
46 
47 // Entry keys to store setup in config
48 #define IMPORT_GFX_LAYER_OPTION_KEY "BoardLayer"
49 #define IMPORT_GFX_PLACEMENT_INTERACTIVE_KEY "InteractivePlacement"
50 #define IMPORT_GFX_LAST_FILE_KEY "LastFile"
51 #define IMPORT_GFX_POSITION_UNITS_KEY "PositionUnits"
52 #define IMPORT_GFX_POSITION_X_KEY "PositionX"
53 #define IMPORT_GFX_POSITION_Y_KEY "PositionY"
54 #define IMPORT_GFX_LINEWIDTH_UNITS_KEY "LineWidthUnits"
55 #define IMPORT_GFX_LINEWIDTH_KEY "LineWidth"
56 
57 
58 // Static members of DIALOG_IMPORT_GFX, to remember the user's choices during the session
62 double DIALOG_IMPORT_GFX::m_scaleImport = 1.0; // Do not change the imported items size
63 int DIALOG_IMPORT_GFX::m_originUnits = 0; // millimeter
64 int DIALOG_IMPORT_GFX::m_lineWidthUnits = 0; // millimeter
65 
66 
67 DIALOG_IMPORT_GFX::DIALOG_IMPORT_GFX( PCB_BASE_FRAME* aParent, bool aImportAsFootprintGraphic )
68  : DIALOG_IMPORT_GFX_BASE( aParent )
69 {
70  m_parent = aParent;
71 
72  if( aImportAsFootprintGraphic )
74  else
76 
77  // construct an import manager with options from config
78  {
80  // Currently: all types are allowed, so the blacklist is empty
81  // (no GFX_FILE_T in the blacklist)
82  // To disable SVG import, enable these 2 lines
83  // if( !ADVANCED_CFG::GetCfg().m_enableSvgImport )
84  // blacklist.push_back( GRAPHICS_IMPORT_MGR::SVG );
85  // The SVG import has currently a flaw:
86  // All SVG shapes are imported as curves and converted to a lot of segments.
87  // A better approach is to convert to polylines (not yet existing in Pcbnew) and keep
88  // arcs and circles as primitives (not yet possible with tinysvg library).
89 
90  m_gfxImportMgr = std::make_unique<GRAPHICS_IMPORT_MGR>( blacklist );
91  }
92 
94  m_originUnits = 0;
95  m_origin.x = 0.0; // always in mm
96  m_origin.y = 0.0; // always in mm
97  m_lineWidth = 0.2; // always in mm
98  m_lineWidthUnits = 0;
99 
100  if( m_config )
101  {
102  wxString tmp = m_config->GetPath();
103  m_config->SetPath( IMPORT_GFX_GROUP );
106  m_filename = m_config->Read( IMPORT_GFX_LAST_FILE_KEY, wxEmptyString );
112  m_config->SetPath( tmp );
113  }
114 
115  m_choiceUnitLineWidth->SetSelection( m_lineWidthUnits );
117 
118  m_DxfPcbPositionUnits->SetSelection( m_originUnits );
120 
121  m_textCtrlFileName->SetValue( m_filename );
124 
126 
127  // Configure the layers list selector
128  m_SelLayerBox->SetLayersHotkeys( false ); // Do not display hotkeys
129  m_SelLayerBox->SetNotAllowedLayerSet( LSET::AllCuMask() ); // Do not use copper layers
132 
134  {
135  m_layer = Dwgs_User;
137  }
138 
140  m_sdbSizerOK->SetDefault();
141  GetSizer()->Fit( this );
142  GetSizer()->SetSizeHints( this );
143  Centre();
144 }
145 
146 
148 {
149  if( m_config )
150  {
151  wxString tmp = m_config->GetPath();
152  m_config->SetPath( IMPORT_GFX_GROUP );
156 
160 
163  m_config->SetPath( tmp );
164  }
165 }
166 
167 
168 void DIALOG_IMPORT_GFX::DIALOG_IMPORT_GFX::onUnitPositionSelection( wxCommandEvent& event )
169 {
170  // Collect last entered values:
171  updatePcbImportOffsets_mm();
172 
173  m_originUnits = m_DxfPcbPositionUnits->GetSelection();;
174  showPcbImportOffsets();
175 }
176 
177 
179 {
180  double value = DoubleValueFromString( UNSCALED_UNITS, m_textCtrlLineWidth->GetValue() );
181 
182  switch( m_lineWidthUnits )
183  {
184  default:
185  case 0: // display units = mm
186  break;
187 
188  case 1: // display units = mil
189  value *= 25.4 / 1000;
190  break;
191 
192  case 2: // display units = inch
193  value *= 25.4;
194  break;
195  }
196 
197  return value; // value is in mm
198 }
199 
200 
201 void DIALOG_IMPORT_GFX::onUnitWidthSelection( wxCommandEvent& event )
202 {
204 
205  // Switch to new units
206  m_lineWidthUnits = m_choiceUnitLineWidth->GetSelection();
208 }
209 
210 
212 {
213  // Display m_origin value according to the unit selection:
214  VECTOR2D offset = m_origin;
215 
216  if( m_originUnits ) // Units are inches
217  offset = m_origin / 25.4;
218 
219  m_DxfPcbXCoord->SetValue( wxString::Format( "%f", offset.x ) );
220  m_DxfPcbYCoord->SetValue( wxString::Format( "%f", offset.y ) );
221 
222 }
223 
224 
226 {
227  double value;
228 
229  switch( m_lineWidthUnits )
230  {
231  default:
232  case 0: // display units = mm
233  value = m_lineWidth;
234  break;
235 
236  case 1: // display units = mil
237  value = m_lineWidth / 25.4 * 1000;
238  break;
239 
240  case 2: // display units = inch
241  value = m_lineWidth / 25.4;
242  break;
243  }
244 
245  m_textCtrlLineWidth->SetValue( wxString::Format( "%f", value ) );
246 }
247 
248 
249 void DIALOG_IMPORT_GFX::onBrowseFiles( wxCommandEvent& event )
250 {
251  wxString path;
252  wxString filename;
253 
254  if( !m_filename.IsEmpty() )
255  {
256  wxFileName fn( m_filename );
257  path = fn.GetPath();
258  filename = fn.GetFullName();
259  }
260 
261  // Generate the list of handled file formats
262  wxString wildcardsDesc;
263  wxString allWildcards;
264 
265  for( auto pluginType : m_gfxImportMgr->GetImportableFileTypes() )
266  {
267  auto plugin = m_gfxImportMgr->GetPlugin( pluginType );
268  const auto wildcards = plugin->GetWildcards();
269 
270  wildcardsDesc += "|" + plugin->GetName() + " (" + wildcards + ")|" + wildcards;
271  allWildcards += wildcards + ";";
272  }
273 
274  wildcardsDesc = _( "All supported formats|" ) + allWildcards + wildcardsDesc;
275 
276  wxFileDialog dlg( m_parent, _( "Open File" ), path, filename,
277  wildcardsDesc, wxFD_OPEN|wxFD_FILE_MUST_EXIST );
278 
279  if( dlg.ShowModal() != wxID_OK )
280  return;
281 
282  wxString fileName = dlg.GetPath();
283 
284  if( fileName.IsEmpty() )
285  return;
286 
287  m_filename = fileName;
288  m_textCtrlFileName->SetValue( fileName );
289 }
290 
291 
293 {
294  if( !wxDialog::TransferDataFromWindow() )
295  return false;
296 
297  m_filename = m_textCtrlFileName->GetValue();
298 
299  if( m_filename.IsEmpty() )
300  {
301  wxMessageBox( _( "No file selected!" ) );
302  return false;
303  }
304 
305  m_originUnits = m_DxfPcbPositionUnits->GetSelection();
306  updatePcbImportOffsets_mm(); // Update m_originX and m_originY;
307 
309 
310  if( m_layer < 0 )
311  {
312  wxMessageBox( _( "Please select a valid layer." ) );
313  return false;
314  }
315 
316  m_lineWidthUnits = m_choiceUnitLineWidth->GetSelection();
318 
319  m_importer->SetLayer( PCB_LAYER_ID( m_layer ) );
320 
321  auto plugin = m_gfxImportMgr->GetPluginByExt( wxFileName( m_filename ).GetExt() );
322 
323  if( plugin )
324  {
325  // Set coordinates offset for import (offset is given in mm)
326  m_importer->SetImportOffsetMM( m_origin );
328 
329  m_importer->SetLineWidthMM( m_lineWidth );
330  m_importer->SetPlugin( std::move( plugin ) );
331 
332  LOCALE_IO dummy; // Ensure floats can be read.
333 
334  if( m_importer->Load( m_filename ) )
335  m_importer->Import( m_scaleImport );
336 
337  // Get warning messages:
338  const std::string& warnings = m_importer->GetMessages();
339 
340  // This isn't a fatal error so allow the dialog to close with wxID_OK.
341  if( !warnings.empty() )
342  wxMessageBox( warnings.c_str(), _( "Items Not Handled" ) );
343  }
344  else
345  {
346  wxMessageBox( _( "There is no plugin to handle this file type." ) );
347  return false;
348  }
349 
350  return true;
351 }
352 
353 
354 void DIALOG_IMPORT_GFX::originOptionOnUpdateUI( wxUpdateUIEvent& event )
355 {
358 
359  if( m_rbAbsolutePlacement->GetValue() == m_placementInteractive )
361 
363  m_DxfPcbXCoord->Enable( not m_placementInteractive );
364  m_DxfPcbYCoord->Enable( not m_placementInteractive );
365 }
366 
367 
369 {
372 
373  if( m_originUnits ) // Units are inches
374  {
375  m_origin = m_origin * 25.4;
376  }
377 
378  return;
379 }
380 
381 
382 // Used only in legacy canvas by the board editor.
384 {
385  DIALOG_IMPORT_GFX dlg( aCaller );
386 
387  if( dlg.ShowModal() != wxID_OK )
388  return false;
389 
390  auto& list = dlg.GetImportedItems();
391 
392  // Ensure the list is not empty:
393  if( list.empty() )
394  {
395  wxMessageBox( _( "No graphic items found in file to import." ) );
396  return false;
397  }
398 
399  PICKED_ITEMS_LIST picklist; // the pick list for undo command
400  ITEM_PICKER item_picker( nullptr, UR_NEW );
401  BOARD* board = aCaller->GetBoard();
402 
403  // Now prepare a block move command to place the new items, if interactive placement,
404  // and prepare the undo command.
405  EDA_RECT bbox; // the new items bounding box, for block move if interactive placement.
406  bool bboxInit = true; // true until the bounding box is initialized
407  BLOCK_SELECTOR& blockmove = aCaller->GetScreen()->m_BlockLocate;
408 
409  if( dlg.IsPlacementInteractive() )
410  aCaller->HandleBlockBegin( NULL, BLOCK_PRESELECT_MOVE, wxPoint( 0, 0 ) );
411 
412  PICKED_ITEMS_LIST& blockitemsList = blockmove.GetItems();
413 
414  for( auto it = list.begin(); it != list.end(); ++it )
415  {
416  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it->release() );
417 
418  if( dlg.IsPlacementInteractive() )
419  item->SetFlags( IS_MOVED );
420 
421  board->Add( item );
422 
423  item_picker.SetItem( item );
424  picklist.PushItem( item_picker );
425 
426  if( dlg.IsPlacementInteractive() )
427  {
428  blockitemsList.PushItem( item_picker );
429 
430  if( bboxInit )
431  bbox = item->GetBoundingBox();
432  else
433  bbox.Merge( item->GetBoundingBox() );
434 
435  bboxInit = false;
436  }
437  }
438 
439  aCaller->SaveCopyInUndoList( picklist, UR_NEW, wxPoint( 0, 0 ) );
440  aCaller->OnModify();
441 
442  if( dlg.IsPlacementInteractive() )
443  {
444  // Finish block move command:
445  wxPoint cpos = aCaller->GetNearestGridPosition( bbox.Centre() );
446  blockmove.SetOrigin( bbox.GetOrigin() );
447  blockmove.SetSize( bbox.GetSize() );
448  blockmove.SetLastCursorPosition( cpos );
449  aCaller->HandleBlockEnd( NULL );
450  }
451 
452  return true;
453 }
454 
455 
456 // Used only in legacy canvas by the footprint editor.
458 {
459  if( !aModule )
460  return false;
461 
462  DIALOG_IMPORT_GFX dlg( aCaller, true );
463 
464  if( dlg.ShowModal() != wxID_OK )
465  return false;
466 
467  auto& list = dlg.GetImportedItems();
468 
469  // Ensure the list is not empty:
470  if( list.empty() )
471  {
472  wxMessageBox( _( "No graphic items found in file to import" ) );
473  return false;
474  }
475 
476  aCaller->SaveCopyInUndoList( aModule, UR_CHANGED );
477 
478  PICKED_ITEMS_LIST picklist; // the pick list for undo command
479  ITEM_PICKER item_picker( nullptr, UR_NEW );
480 
481  // Now prepare a block move command to place the new items, if interactive placement,
482  // and prepare the undo command.
483  EDA_RECT bbox; // the new items bounding box, for block move if interactive placement.
484  bool bboxInit = true; // true until the bounding box is initialized
485  BLOCK_SELECTOR& blockmove = aCaller->GetScreen()->m_BlockLocate;
486 
487  if( dlg.IsPlacementInteractive() )
488  aCaller->HandleBlockBegin( nullptr, BLOCK_PRESELECT_MOVE, wxPoint( 0, 0 ) );
489 
490  PICKED_ITEMS_LIST& blockitemsList = blockmove.GetItems();
491 
492  for( auto it = list.begin(); it != list.end(); ++it )
493  {
494  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it->release() );
495  aModule->Add( item );
496 
497  if( dlg.IsPlacementInteractive() )
498  {
499  item->SetFlags( IS_MOVED );
500  item_picker.SetItem( item );
501  blockitemsList.PushItem( item_picker );
502 
503  if( bboxInit )
504  bbox = item->GetBoundingBox();
505  else
506  bbox.Merge( item->GetBoundingBox() );
507 
508  bboxInit = false;
509  }
510  }
511 
512  aCaller->OnModify();
513 
514  if( dlg.IsPlacementInteractive() )
515  {
516  // Finish block move command:
517  wxPoint cpos = aCaller->GetNearestGridPosition( bbox.Centre() );
518  blockmove.SetOrigin( bbox.GetOrigin() );
519  blockmove.SetSize( bbox.GetSize() );
520  blockmove.SetLastCursorPosition( cpos );
521  aCaller->HandleBlockEnd( NULL );
522  }
523 
524  return true;
525 }
std::unique_ptr< GRAPHICS_IMPORT_MGR > m_gfxImportMgr
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Function AllCuMask returns a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:676
#define IMPORT_GFX_GROUP
BLOCK_SELECTOR m_BlockLocate
Block description for block commands.
Definition: base_screen.h:214
void Merge(const EDA_RECT &aRect)
Function Merge modifies the position and size of the rectangle in order to contain aRect.
TEXTE_PCB class definition.
bool InvokeDialogImportGfxModule(PCB_BASE_FRAME *aCaller, MODULE *aModule)
shows the modal DIALOG_IMPORT_GFX for importing a DXF file as footprint outlines.
bool InvokeDialogImportGfxBoard(PCB_BASE_FRAME *aCaller)
Shows the modal DIALOG_IMPORT_GFX for importing a DXF file to a board.
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: common.h:177
Class BOARD_ITEM is a base class for any item which can be embedded within the BOARD container class,...
#define IMPORT_GFX_LINEWIDTH_KEY
void SetNotAllowedLayerSet(LSET aMask)
#define IMPORT_GFX_LINEWIDTH_UNITS_KEY
Class BOARD to handle a board.
std::list< std::unique_ptr< EDA_ITEM > > & GetImportedItems()
#define IMPORT_GFX_POSITION_Y_KEY
void originOptionOnUpdateUI(wxUpdateUIEvent &event) override
wxConfigBase * KifaceSettings() const
Definition: kiface_i.h:103
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
void SetOrigin(const wxPoint &pos)
Definition: eda_rect.h:124
wxRadioButton * m_rbInteractivePlacement
void PushItem(const ITEM_PICKER &aItem)
Function PushItem pushes aItem to the top of the list.
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:116
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
Definition: kicad.cpp:52
virtual bool HandleBlockBegin(wxDC *aDC, EDA_KEY aKey, const wxPoint &aPosition, int aExplicitCommand=0)
Initialize a block command.
LAYER_NUM GetLayerSelection() const
PCB_LAYER_ID
A quick note on layer IDs:
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_INSERT) override
Footprint text class description.
void SetFlags(STATUS_FLAGS aMask)
Definition: base_struct.h:259
#define IMPORT_GFX_PLACEMENT_INTERACTIVE_KEY
const wxPoint GetOrigin() const
Definition: eda_rect.h:112
double DoubleValueFromString(EDA_UNITS_T aUnits, const wxString &aTextValue, bool aUseMils)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:302
PCB_LAYER_BOX_SELECTOR * m_SelLayerBox
static int m_lineWidthUnits
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_INSERT) override
Adds an item to the container.
static int m_originUnits
PCB_BASE_FRAME * m_parent
#define IMPORT_GFX_POSITION_X_KEY
Definition of file extensions used in Kicad.
int SetLayerSelection(LAYER_NUM layer)
#define IMPORT_GFX_LAYER_OPTION_KEY
Class PICKED_ITEMS_LIST is a holder to handle information on schematic or board items.
void SetItem(EDA_ITEM *aItem)
bool SetLayersHotkeys(bool value)
std::vector< GFX_FILE_T > TYPE_LIST
static LAYER_NUM m_layer
wxConfigBase * m_config
int LAYER_NUM
Type LAYER_NUM can be replaced with int and removed.
virtual bool HandleBlockEnd(wxDC *DC)
Handle the "end" of a block command, i.e.
wxPoint GetNearestGridPosition(const wxPoint &aPosition, wxRealPoint *aGridSize=NULL) const
Return the nearest aGridSize location to aPosition.
static wxString m_filename
virtual void OnModify()
Function OnModify Virtual Must be called after a change in order to set the "modify" flag of the curr...
void SetSize(const wxSize &size)
Definition: eda_rect.h:126
#define IMPORT_GFX_LAST_FILE_KEY
static double m_scaleImport
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
static LIB_PART * dummy()
Used when a LIB_PART is not found in library to draw a dummy shape This component is a 400 mils squar...
Class BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:170
DLIST< MODULE > m_Modules
Definition: class_board.h:248
wxRadioButton * m_rbAbsolutePlacement
Class EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
#define IMPORT_GFX_POSITION_UNITS_KEY
virtual void SaveCopyInUndoList(BOARD_ITEM *aItemToCopy, UNDO_REDO_T aTypeCommand, const wxPoint &aTransformPoint=wxPoint(0, 0))=0
Function SaveCopyInUndoList (virtual pure) Creates a new entry in undo list of commands.
PCB_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
wxPoint Centre() const
Definition: eda_rect.h:60
PICKED_ITEMS_LIST & GetItems()
static bool m_placementInteractive
Module description (excepted pads)
BOARD * GetBoard() const
virtual const EDA_RECT GetBoundingBox() const
Function GetBoundingBox returns the orthogonal, bounding box of this object for display purposes.
EDGE_MODULE class definition.
std::unique_ptr< GRAPHICS_IMPORTER_PCBNEW > m_importer
void SetLastCursorPosition(const wxPoint &aPosition)
Function SetLastCursorPosition sets the last cursor position to aPosition.
class PCB_BASE_FRAME basic PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
const wxSize GetSize() const
Definition: eda_rect.h:101
#define IS_MOVED
Item being moved.
Definition: base_struct.h:113