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 // Keys to store setup in config
45 #define IMPORT_GFX_LAYER_OPTION_KEY "GfxImportBrdLayer"
46 #define IMPORT_GFX_PLACEMENT_INTERACTIVE_KEY "GfxImportPlacementInteractive"
47 #define IMPORT_GFX_LAST_FILE_KEY "GfxImportLastFile"
48 #define IMPORT_GFX_POSITION_UNITS_KEY "GfxImportPositionUnits"
49 #define IMPORT_GFX_POSITION_X_KEY "GfxImportPositionX"
50 #define IMPORT_GFX_POSITION_Y_KEY "GfxImportPositionY"
51 #define IMPORT_GFX_LINEWIDTH_UNITS_KEY "GfxImportLineWidthUnits"
52 #define IMPORT_GFX_LINEWIDTH_KEY "GfxImportLineWidth"
53 
54 // Static members of DIALOG_IMPORT_GFX, to remember
55 // the user's choices during the session
59 double DIALOG_IMPORT_GFX::m_scaleImport = 1.0; // Do not change the imported items size
60 
61 DIALOG_IMPORT_GFX::DIALOG_IMPORT_GFX( PCB_BASE_FRAME* aParent, bool aImportAsFootprintGraphic )
62  : DIALOG_IMPORT_GFX_BASE( aParent )
63 {
64  m_parent = aParent;
65 
66  if( aImportAsFootprintGraphic )
68  else
70 
71  // construct an import manager with options from config
72  {
74  // Currently: all types are allowed, so the blacklist is empty
75  // (no GFX_FILE_T in the blacklist)
76  // To disable SVG import, enable these 2 lines
77  // if( !ADVANCED_CFG::GetCfg().m_enableSvgImport )
78  // blacklist.push_back( GRAPHICS_IMPORT_MGR::SVG );
79  // The SVG import has currently a flaw:
80  // All SVG shapes are imported as curves and converted to a lot of segments.
81  // A better approach is to convert to polylines (not yet existing in Pcbnew) and keep
82  // arcs and circles as primitives (not yet possible with tinysvg library).
83 
84  m_gfxImportMgr = std::make_unique<GRAPHICS_IMPORT_MGR>( blacklist );
85  }
86 
89  m_importOrigin.x = 0.0; // always in mm
90  m_importOrigin.y = 0.0; // always in mm
91  m_default_lineWidth = 0.2; // always in mm
93 
94  if( m_config )
95  {
98  m_filename = m_config->Read( IMPORT_GFX_LAST_FILE_KEY, wxEmptyString );
103  }
104 
107 
110 
111  m_textCtrlFileName->SetValue( m_filename );
114 
116 
117  // Configure the layers list selector
118  m_SelLayerBox->SetLayersHotkeys( false ); // Do not display hotkeys
119  m_SelLayerBox->SetNotAllowedLayerSet( LSET::AllCuMask() ); // Do not use copper layers
122 
124  {
125  m_layer = Dwgs_User;
127  }
128 
129  m_sdbSizerOK->SetDefault();
130  GetSizer()->Fit( this );
131  GetSizer()->SetSizeHints( this );
132  Centre();
133 }
134 
135 
137 {
140 
141  if( m_config )
142  {
146 
150 
154  }
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_originImportUnits = m_DxfPcbPositionUnits->GetSelection();;
164  showPcbImportOffsets();
165 }
166 
167 
169 {
170  double value = DoubleValueFromString( UNSCALED_UNITS, m_textCtrlLineWidth->GetValue() );
171 
172  switch( m_lineWidthImportUnits )
173  {
174  default:
175  case 0: // display units = mm
176  break;
177 
178  case 1: // display units = mil
179  value *= 25.4 / 1000;
180  break;
181 
182  case 2: // display units = inch
183  value *= 25.4;
184  break;
185  }
186 
187  return value; // value is in mm
188 }
189 
190 
191 void DIALOG_IMPORT_GFX::onUnitWidthSelection( wxCommandEvent& event )
192 {
194 
195  // Switch to new units
198 }
199 
200 
202 {
203  // Display m_importOrigin value according to the unit selection:
204  VECTOR2D offset = m_importOrigin;
205 
206  if( m_originImportUnits ) // Units are inches
207  offset = m_importOrigin / 25.4;
208 
209  m_DxfPcbXCoord->SetValue( wxString::Format( "%f", offset.x ) );
210  m_DxfPcbYCoord->SetValue( wxString::Format( "%f", offset.y ) );
211 
212 }
213 
214 
216 {
217  double value;
218 
219  switch( m_lineWidthImportUnits )
220  {
221  default:
222  case 0: // display units = mm
223  value = m_default_lineWidth;
224  break;
225 
226  case 1: // display units = mil
227  value = m_default_lineWidth / 25.4 * 1000;
228  break;
229 
230  case 2: // display units = inch
231  value = m_default_lineWidth / 25.4;
232  break;
233  }
234 
235  m_textCtrlLineWidth->SetValue( wxString::Format( "%f", value ) );
236 }
237 
238 
239 void DIALOG_IMPORT_GFX::onBrowseFiles( wxCommandEvent& event )
240 {
241  wxString path;
242  wxString filename;
243 
244  if( !m_filename.IsEmpty() )
245  {
246  wxFileName fn( m_filename );
247  path = fn.GetPath();
248  filename = fn.GetFullName();
249  }
250 
251  // Generate the list of handled file formats
252  wxString wildcardsDesc;
253  wxString allWildcards;
254 
255  for( auto pluginType : m_gfxImportMgr->GetImportableFileTypes() )
256  {
257  auto plugin = m_gfxImportMgr->GetPlugin( pluginType );
258  const auto wildcards = plugin->GetWildcards();
259 
260  wildcardsDesc += "|" + plugin->GetName() + " (" + wildcards + ")|" + wildcards;
261  allWildcards += wildcards + ";";
262  }
263 
264  wildcardsDesc = "All supported formats|" + allWildcards + wildcardsDesc;
265 
266  wxFileDialog dlg( m_parent, _( "Open File" ), path, filename,
267  wildcardsDesc, wxFD_OPEN|wxFD_FILE_MUST_EXIST );
268 
269  if( dlg.ShowModal() != wxID_OK )
270  return;
271 
272  wxString fileName = dlg.GetPath();
273 
274  if( fileName.IsEmpty() )
275  return;
276 
277  m_filename = fileName;
278  m_textCtrlFileName->SetValue( fileName );
279 }
280 
281 
282 void DIALOG_IMPORT_GFX::onOKClick( wxCommandEvent& event )
283 {
284  m_filename = m_textCtrlFileName->GetValue();
285 
286  if( m_filename.IsEmpty() )
287  {
288  wxMessageBox( _( "Error: No DXF filename!" ) );
289  return;
290  }
291 
292  updatePcbImportOffsets_mm(); // Update m_importOriginX and m_importOriginY;
293 
295 
296  if( m_layer < 0 )
297  {
298  wxMessageBox( _( "Please, select a valid layer" ) );
299  return;
300  }
301 
303 
304  m_importer->SetLayer( PCB_LAYER_ID( m_layer ) );
305 
306  auto plugin = m_gfxImportMgr->GetPluginByExt( wxFileName( m_filename ).GetExt() );
307 
308  if( plugin )
309  {
310  // Set coordinates offset for import (offset is given in mm)
311  m_importer->SetImportOffsetMM( m_importOrigin );
313 
314  m_importer->SetLineWidthMM( m_default_lineWidth );
315  m_importer->SetPlugin( std::move( plugin ) );
316 
317  LOCALE_IO dummy; // Ensure floats can be read.
318 
319  if( m_importer->Load( m_filename ) )
320  m_importer->Import( m_scaleImport );
321 
322  // Get warning messages:
323  const std::string& warnings = m_importer->GetMessages();
324 
325  if( !warnings.empty() )
326  wxMessageBox( warnings.c_str(), _( "Not Handled Items" ) );
327 
328  event.Skip();
329  }
330  else
331  {
332  wxMessageBox( _( "There is no plugin to handle this file type" ) );
333  }
334 }
335 
336 
337 // Used only in legacy canvas by the board editor.
339 {
340  DIALOG_IMPORT_GFX dlg( aCaller );
341 
342  if( dlg.ShowModal() != wxID_OK )
343  return false;
344 
345  auto& list = dlg.GetImportedItems();
346 
347  // Ensure the list is not empty:
348  if( list.empty() )
349  {
350  wxMessageBox( _( "No graphic items found in file to import") );
351  return false;
352  }
353 
354  PICKED_ITEMS_LIST picklist; // the pick list for undo command
355  ITEM_PICKER item_picker( nullptr, UR_NEW );
356  BOARD* board = aCaller->GetBoard();
357 
358  // Now prepare a block move command to place the new items, if interactive placement,
359  // and prepare the undo command.
360  EDA_RECT bbox; // the new items bounding box, for block move if interactive placement.
361  bool bboxInit = true; // true until the bounding box is initialized
362  BLOCK_SELECTOR& blockmove = aCaller->GetScreen()->m_BlockLocate;
363 
364  if( dlg.IsPlacementInteractive() )
365  aCaller->HandleBlockBegin( NULL, BLOCK_PRESELECT_MOVE, wxPoint( 0, 0) );
366 
367  PICKED_ITEMS_LIST& blockitemsList = blockmove.GetItems();
368 
369  for( auto it = list.begin(); it != list.end(); ++it )
370  {
371  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it->release() );
372 
373  if( dlg.IsPlacementInteractive() )
374  item->SetFlags( IS_MOVED );
375 
376  board->Add( item );
377 
378  item_picker.SetItem( item );
379  picklist.PushItem( item_picker );
380 
381  if( dlg.IsPlacementInteractive() )
382  {
383  blockitemsList.PushItem( item_picker );
384 
385  if( bboxInit )
386  bbox = item->GetBoundingBox();
387  else
388  bbox.Merge( item->GetBoundingBox() );
389 
390  bboxInit = false;
391  }
392  }
393 
394  aCaller->SaveCopyInUndoList( picklist, UR_NEW, wxPoint( 0, 0 ) );
395  aCaller->OnModify();
396 
397  if( dlg.IsPlacementInteractive() )
398  {
399  // Finish block move command:
400  wxPoint cpos = aCaller->GetNearestGridPosition( bbox.Centre() );
401  blockmove.SetOrigin( bbox.GetOrigin() );
402  blockmove.SetSize( bbox.GetSize() );
403  blockmove.SetLastCursorPosition( cpos );
404  aCaller->HandleBlockEnd( NULL );
405  }
406 
407  return true;
408 }
409 
410 
411 // Used only in legacy canvas by the footprint editor.
413 {
414  if( !aModule )
415  return false;
416 
417  DIALOG_IMPORT_GFX dlg( aCaller, true );
418 
419  if( dlg.ShowModal() != wxID_OK )
420  return false;
421 
422  auto& list = dlg.GetImportedItems();
423 
424  // Ensure the list is not empty:
425  if( list.empty() )
426  {
427  wxMessageBox( _( "No graphic items found in file to import") );
428  return false;
429  }
430 
431  aCaller->SaveCopyInUndoList( aModule, UR_CHANGED );
432 
433  PICKED_ITEMS_LIST picklist; // the pick list for undo command
434  ITEM_PICKER item_picker( nullptr, UR_NEW );
435 
436  // Now prepare a block move command to place the new items, if interactive placement,
437  // and prepare the undo command.
438  EDA_RECT bbox; // the new items bounding box, for block move if interactive placement.
439  bool bboxInit = true; // true until the bounding box is initialized
440  BLOCK_SELECTOR& blockmove = aCaller->GetScreen()->m_BlockLocate;
441 
442  if( dlg.IsPlacementInteractive() )
443  aCaller->HandleBlockBegin( nullptr, BLOCK_PRESELECT_MOVE, wxPoint( 0, 0) );
444 
445  PICKED_ITEMS_LIST& blockitemsList = blockmove.GetItems();
446 
447  for( auto it = list.begin(); it != list.end(); ++it )
448  {
449  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it->release() );
450  aModule->Add( item );
451 
452  if( dlg.IsPlacementInteractive() )
453  {
454  item->SetFlags( IS_MOVED );
455  item_picker.SetItem( item );
456  blockitemsList.PushItem( item_picker );
457 
458  if( bboxInit )
459  bbox = item->GetBoundingBox();
460  else
461  bbox.Merge( item->GetBoundingBox() );
462 
463  bboxInit = false;
464  }
465  }
466 
467  aCaller->OnModify();
468 
469  if( dlg.IsPlacementInteractive() )
470  {
471  // Finish block move command:
472  wxPoint cpos = aCaller->GetNearestGridPosition( bbox.Centre() );
473  blockmove.SetOrigin( bbox.GetOrigin() );
474  blockmove.SetSize( bbox.GetSize() );
475  blockmove.SetLastCursorPosition( cpos );
476  aCaller->HandleBlockEnd( NULL );
477  }
478 
479  return true;
480 }
481 
482 
483 void DIALOG_IMPORT_GFX::originOptionOnUpdateUI( wxUpdateUIEvent& event )
484 {
487 
488  if( m_rbAbsolutePlacement->GetValue() == m_placementInteractive )
490 
492  m_DxfPcbXCoord->Enable( not m_placementInteractive );
493  m_DxfPcbYCoord->Enable( not m_placementInteractive );
494 }
495 
496 
498 {
501 
502  if( m_originImportUnits ) // Units are inches
503  {
505  }
506 
507  return;
508 }
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
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:180
void onOKClick(wxCommandEvent &event) override
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()
Function GetImportedItems()
#define IMPORT_GFX_POSITION_Y_KEY
void originOptionOnUpdateUI(wxUpdateUIEvent &event) override
wxConfigBase * KifaceSettings() const
Definition: kiface_i.h:103
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)
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:256
#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
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_INSERT) override
Adds an item to the container.
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:171
DLIST< MODULE > m_Modules
Definition: class_board.h:249
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