KiCad PCB EDA Suite
pcbnew.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) 2016 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
6  * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
31 #ifdef KICAD_SCRIPTING
32  #include <python_scripting.h>
34 #endif
35 #include <pgm_base.h>
36 #include <kiface_i.h>
37 #include <kiface_ids.h>
38 #include <confirm.h>
39 #include <pcb_edit_frame.h>
40 #include <eda_dde.h>
41 #include <wx/file.h>
42 #include <wx/log.h>
43 #include <wx/snglinst.h>
44 #include <gestfich.h>
45 #include <pcbnew.h>
46 #include <pcbnew_settings.h>
48 #include <board.h>
49 #include <class_draw_panel_gal.h>
50 #include <fp_lib_table.h>
51 #include <footprint_edit_frame.h>
52 #include <footprint_viewer_frame.h>
53 #include <footprint_wizard_frame.h>
55 #include <footprint_info_impl.h>
57 #include <paths.h>
58 #include "invoke_pcb_dialog.h"
61 
62 
63 namespace PCB {
64 
65 static struct IFACE : public KIFACE_I
66 {
67  // Of course all are virtual overloads, implementations of the KIFACE.
68 
69  IFACE( const char* aName, KIWAY::FACE_T aType ) :
70  KIFACE_I( aName, aType )
71  {}
72 
73  bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits ) override;
74 
75  void OnKifaceEnd() override;
76 
77  wxWindow* CreateWindow( wxWindow* aParent, int aClassId, KIWAY* aKiway, int aCtlBits = 0 ) override
78  {
79  switch( aClassId )
80  {
81  case FRAME_PCB_EDITOR:
82  {
83  auto frame = new PCB_EDIT_FRAME( aKiway, aParent );
84 
85 #if defined( KICAD_SCRIPTING )
86  // give the scripting helpers access to our frame
87  ScriptingSetPcbEditFrame( frame );
88 #endif
89 
90  if( Kiface().IsSingle() )
91  {
92  // only run this under single_top, not under a project manager.
93  frame->CreateServer( KICAD_PCB_PORT_SERVICE_NUMBER );
94  }
95 
96  return frame;
97  }
98 
100  return new FOOTPRINT_EDIT_FRAME( aKiway, aParent,
102 
105  return new FOOTPRINT_VIEWER_FRAME( aKiway, aParent, FRAME_T( aClassId ) );
106 
108  return new FOOTPRINT_WIZARD_FRAME( aKiway, aParent, FRAME_T( aClassId ) );
109 
111  return dynamic_cast< wxWindow* >( FOOTPRINT_PREVIEW_PANEL::New( aKiway, aParent ) );
112 
114  {
115  DIALOG_CONFIGURE_PATHS dlg( aParent, aKiway->Prj().Get3DFilenameResolver() );
116 
117  // The dialog's constructor probably failed to set its Kiway because the
118  // dynamic_cast fails when aParent was allocated by a separate compiliation
119  // module. So set it directly.
120  dlg.SetKiway( &dlg, aKiway );
121 
122  // Use QuasiModal so that HTML help window will work
123  if( dlg.ShowQuasiModal() == wxID_OK )
124  aKiway->CommonSettingsChanged( true, false );
125 
126  // Dialog has completed; nothing to return.
127  return nullptr;
128  }
129 
131  InvokePcbLibTableEditor( aKiway, aParent );
132  // Dialog has completed; nothing to return.
133  return nullptr;
134 
135  default:
136  return nullptr;
137  }
138  }
139 
148  void* IfaceOrAddress( int aDataId ) override
149  {
150  switch( aDataId )
151  {
152  // Return a pointer to the global instance of the footprint list.
154  return (void*) &GFootprintList;
155 
156  // Return a new FP_LIB_TABLE with the global table installed as a fallback.
158  return (void*) new FP_LIB_TABLE( &GFootprintTable );
159 
160  // Return a pointer to the global instance of the global footprint table.
162  return (void*) &GFootprintTable;
163 
164  default:
165  return nullptr;
166  }
167  }
168 
175  void SaveFileAs( const wxString& aProjectBasePath, const wxString& aSrcProjectName,
176  const wxString& aNewProjectBasePath, const wxString& aNewProjectName,
177  const wxString& aSrcFilePath, wxString& aErrors ) override;
178 
179 } kiface( "pcbnew", KIWAY::FACE_PCB );
180 
181 } // namespace
182 
183 using namespace PCB;
184 
185 
187 
188 
189 KIFACE_I& Kiface() { return kiface; }
190 
191 
192 // KIFACE_GETTER's actual spelling is a substitution macro found in kiway.h.
193 // KIFACE_GETTER will not have name mangling due to declaration in kiway.h.
194 MY_API( KIFACE* ) KIFACE_GETTER( int* aKIFACEversion, int aKiwayVersion, PGM_BASE* aProgram )
195 {
196  process = aProgram;
197  return &kiface;
198 }
199 
200 #if defined( BUILD_KIWAY_DLL )
201 PGM_BASE& Pgm()
202 {
203  wxASSERT( process ); // KIFACE_GETTER has already been called.
204  return *process;
205 }
206 
207 // Similar to PGM_BASE& Pgm(), but return nullptr when a *.ki_face is run from a python script.
209 {
210  return process;
211 }
212 
213 #endif
214 
215 
216 #if defined( KICAD_SCRIPTING )
217 static bool scriptingSetup()
218 {
219 
220 #if defined( __WINDOWS__ )
221  // If our python.exe (in kicad/bin) exists, force our kicad python environment
222  wxString kipython = FindKicadFile( "python.exe" );
223 
224  // we need only the path:
225  wxFileName fn( kipython );
226  kipython = fn.GetPath();
227 
228  // If our python install is existing inside kicad, use it
229  // Note: this is useful only when another python version is installed
230  if( wxDirExists( kipython ) )
231  {
232  // clear any PYTHONPATH and PYTHONHOME env var definition: the default
233  // values work fine inside Kicad:
234  wxSetEnv( wxT( "PYTHONPATH" ), wxEmptyString );
235  wxSetEnv( wxT( "PYTHONHOME" ), wxEmptyString );
236 
237  // Add our python executable path in first position:
238  wxString ppath;
239  wxGetEnv( wxT( "PATH" ), &ppath );
240 
241  kipython << wxT( ";" ) << ppath;
242  wxSetEnv( wxT( "PATH" ), kipython );
243  }
244 
245 #elif defined( __WXMAC__ )
246 
247  // Add default paths to PYTHONPATH
248  wxString pypath;
249 
250  // Bundle scripting folder (<kicad.app>/Contents/SharedSupport/scripting)
251  pypath += PATHS::GetOSXKicadDataDir() + wxT( "/scripting" );
252 
253  // $(KICAD_PATH)/scripting/plugins is always added in kicadplugins.i
254  if( wxGetenv("KICAD_PATH") != NULL )
255  {
256  pypath += wxT( ":" ) + wxString( wxGetenv("KICAD_PATH") );
257  }
258 
259  // Bundle wxPython folder (<kicad.app>/Contents/Frameworks/python/site-packages)
260  pypath += wxT( ":" ) + Pgm().GetExecutablePath() +
261  wxT( "Contents/Frameworks/python/site-packages" );
262 
263  // Original content of $PYTHONPATH
264  if( wxGetenv( wxT( "PYTHONPATH" ) ) != NULL )
265  {
266  pypath = wxString( wxGetenv( wxT( "PYTHONPATH" ) ) ) + wxT( ":" ) + pypath;
267  }
268 
269  // set $PYTHONPATH
270  wxSetEnv( "PYTHONPATH", pypath );
271 
272 #else
273  wxString pypath;
274 
275  // PYTHON_DEST is the scripts install dir as determined by the build system.
276  pypath = Pgm().GetExecutablePath() + wxT( "../" PYTHON_DEST );
277 
278  if( !wxIsEmpty( wxGetenv( wxT( "PYTHONPATH" ) ) ) )
279  pypath = wxString( wxGetenv( wxT( "PYTHONPATH" ) ) ) + wxT( ":" ) + pypath;
280 
281  wxSetEnv( wxT( "PYTHONPATH" ), pypath );
282 
283 #endif
284 
285  wxFileName path( PyPluginsPath( true ) + wxT("/") );
286 
287  // Ensure the user plugin path exists, and create it if not.
288  // However, if it cannot be created, this is not a fatal error.
289  if( !path.DirExists() && !path.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
290  wxLogError( "Warning: could not create user scripting path %s", path.GetPath() );
291 
293  {
294  wxLogError( "pcbnewInitPythonScripting() failed." );
295  return false;
296  }
297 
298  return true;
299 }
300 #endif // KICAD_SCRIPTING
301 
302 
304 {
305 #if defined( KICAD_SCRIPTING )
306  // Reload plugin list: reload Python plugins if they are newer than the already loaded,
307  // and load new plugins
308  char cmd[1024];
309 
310  snprintf( cmd, sizeof( cmd ), "pcbnew.LoadPlugins(\"%s\", \"%s\")", TO_UTF8( PyScriptingPath() ),
311  TO_UTF8( PyScriptingPath( true ) ) );
312 
313  PyLOCK lock;
314 
315  // ReRun the Python method pcbnew.LoadPlugins (already called when starting Pcbnew)
316  int retv = PyRun_SimpleString( cmd );
317 
318  if( retv != 0 )
319  wxLogError( "Python error %d occurred running command:\n\n`%s`", retv, cmd );
320 #endif
321 }
322 
323 
328 
333 
334 
335 
336 bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits )
337 {
338  // This is process-level-initialization, not project-level-initialization of the DSO.
339  // Do nothing in here pertinent to a project!
342 
343  start_common( aCtlBits );
344 
345  wxFileName fn = FP_LIB_TABLE::GetGlobalTableFileName();
346 
347  if( !fn.FileExists() )
348  {
350 
351  fpDialog.ShowModal();
352  }
353  else
354  {
355  try
356  {
357  // The global table is not related to a specific project. All projects
358  // will use the same global table. So the KIFACE::OnKifaceStart() contract
359  // of avoiding anything project specific is not violated here.
361  return false;
362  }
363  catch( const IO_ERROR& ioe )
364  {
365  // if we are here, a incorrect global footprint library table was found.
366  // Incorrect global symbol library table is not a fatal error:
367  // the user just has to edit the (partially) loaded table.
368  wxString msg = _(
369  "An error occurred attempting to load the global footprint library table.\n"
370  "Please edit this global footprint library table in Preferences menu."
371  );
372 
373  DisplayErrorMessage( NULL, msg, ioe.What() );
374  }
375  }
376 
377 #if defined( KICAD_SCRIPTING )
378  scriptingSetup();
379 #endif
380 
381  return true;
382 }
383 
384 
386 {
387 #if defined( KICAD_SCRIPTING_WXPYTHON )
388  // Restore the thread state and tell Python to cleanup after itself.
389  // wxPython will do its own cleanup as part of that process.
390  // This should only be called if python was setup correctly.
391 
392  if( IsWxPythonLoaded() )
394 #endif
395 
396  end_common();
397 }
398 
399 
400 void IFACE::SaveFileAs( const wxString& aProjectBasePath, const wxString& aSrcProjectName,
401  const wxString& aNewProjectBasePath, const wxString& aNewProjectName,
402  const wxString& aSrcFilePath, wxString& aErrors )
403 {
404  wxFileName destFile( aSrcFilePath );
405  wxString destPath = destFile.GetPathWithSep();
406  wxUniChar pathSep = wxFileName::GetPathSeparator();
407  wxString ext = destFile.GetExt();
408 
409  if( destPath.StartsWith( aProjectBasePath + pathSep ) )
410  destPath.Replace( aProjectBasePath, aNewProjectBasePath, false );
412  wxString srcProjectFootprintLib = pathSep + aSrcProjectName + ".pretty" + pathSep;
413  wxString newProjectFootprintLib = pathSep + aNewProjectName + ".pretty" + pathSep;
414 
415  destPath.Replace( srcProjectFootprintLib, newProjectFootprintLib, true );
416 
417  destFile.SetPath( destPath );
418 
420  {
421  if( destFile.GetName() == aSrcProjectName )
422  destFile.SetName( aNewProjectName );
423 
424  KiCopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
425  }
426  else if( ext == LegacyPcbFileExtension )
427  {
428  if( destFile.GetName() == aSrcProjectName )
429  destFile.SetName( aNewProjectName );
430 
431  KiCopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
432  }
434  {
435  // Footprints are not project-specific. Keep their source names.
436  KiCopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
437  }
438  else if( ext == ComponentFileExtension )
439  {
440  // JEY TODO
441  }
442  else if( ext == "rpt" )
443  {
444  // DRC must be the "gold standard". Since we can't gaurantee that there aren't
445  // any non-deterministic cases in the save-as algorithm, we don't want to certify
446  // the result with the source's DRC report. Therefore copy it under the old
447  // name.
448  KiCopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
449  }
450  else if( destFile.GetName() == "fp-lib-table" )
451  {
452  try
453  {
454  FP_LIB_TABLE fpLibTable;
455  fpLibTable.Load( aSrcFilePath );
456 
457  for( unsigned i = 0; i < fpLibTable.GetCount(); i++ )
458  {
459  LIB_TABLE_ROW& row = fpLibTable.At( i );
460  wxString uri = row.GetFullURI();
461 
462  uri.Replace( "/" + aSrcProjectName + ".pretty", "/" + aNewProjectName + ".pretty" );
463 
464  row.SetFullURI( uri );
465  }
466 
467  fpLibTable.Save( destFile.GetFullPath() );
468  }
469  catch( ... )
470  {
471  wxString msg;
472 
473  if( !aErrors.empty() )
474  aErrors += "\n";
475 
476  msg.Printf( _( "Cannot copy file \"%s\"." ), destFile.GetFullPath() );
477  aErrors += msg;
478  }
479  }
480  else
481  {
482  wxFAIL_MSG( "Unexpected filetype for Pcbnew::SaveFileAs()" );
483  }
484 }
485 
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition: pgm_base.h:167
FOOTPRINT_WIZARD_FRAME.
A KIFACE (I)mplementation.
Definition: kiface_i.h:37
void SetKiway(wxWindow *aDest, KIWAY *aKiway)
It is only used for debugging, since "this" is not a wxWindow*.
#define KICAD_PCB_PORT_SERVICE_NUMBER
< Pcbnew listens on this port for commands from Eeschema
Definition: eda_dde.h:39
const std::string LegacyFootprintLibPathExtension
void SaveFileAs(const wxString &aProjectBasePath, const wxString &aSrcProjectName, const wxString &aNewProjectBasePath, const wxString &aNewProjectName, const wxString &aSrcFilePath, wxString &aErrors) override
Function SaveFileAs Saving a file under a different name is delegated to the various KIFACEs because ...
Definition: pcbnew.cpp:400
bool pcbnewInitPythonScripting(const char *aStockScriptingPath, const char *aUserScriptingPath)
Initialize the python environment and publish the Pcbnew interface inside it.
DDE server & client.
bool start_common(int aCtlBits)
Common things to do for a top program module, during OnKifaceStart().
Definition: kiface_i.cpp:88
Hold a record identifying a library accessed by the appropriate plug in object in the LIB_TABLE.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:266
This file is part of the common library TODO brief description.
bool OnKifaceStart(PGM_BASE *aProgram, int aCtlBits) override
Typically start_common() is called from here.
Definition: pcbnew.cpp:336
This file is part of the common library.
const std::string KiCadFootprintFileExtension
Container for data for KiCad programs.
Definition: pgm_base.h:131
const std::string LegacyPcbFileExtension
FRAME_T
The set of EDA_BASE_FRAME derivatives, typically stored in EDA_BASE_FRAME::m_Ident.
Definition: frame_type.h:32
virtual PROJECT & Prj() const
Return the PROJECT associated with this KIWAY.
Definition: kiway.cpp:174
const std::string BackupFileSuffix
unsigned GetCount() const
Get the number of rows contained in the table.
static PGM_BASE * process
Definition: pcbnew.cpp:186
Component library viewer main window.
MY_API(KIFACE *) KIFACE_GETTER(int *aKIFACEversion
void KiCopyFile(const wxString &aSrcPath, const wxString &aDestPath, wxString &aErrors)
Definition: gestfich.cpp:363
APP_SETTINGS_BASE * KifaceSettings() const
Definition: kiface_i.h:92
const std::string ComponentFileExtension
const std::string KiCadPcbFileExtension
Return the global FP_LIB_TABLE.
Definition: kiface_ids.h:53
void ScriptingSetPcbEditFrame(PCB_EDIT_FRAME *aPcbEditFrame)
void pcbnewFinishPythonScripting()
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
wxString PyScriptingPath(bool aUserPath)
Find the Python scripting path.
not specified: a GAL engine must be set by the client
FP_LIB_TABLE GFootprintTable
The global footprint library table.
Definition: pcbnew.cpp:327
IFACE(const char *aName, KIWAY::FACE_T aType)
Definition: pcbnew.cpp:69
const wxString GetFullURI(bool aSubstituted=false) const
Return the full location specifying URI for the LIB, either in original UI form or in environment var...
void InvokePcbLibTableEditor(KIWAY *aKiway, wxWindow *aCaller)
Function InvokePcbLibTableEditor shows the modal DIALOG_FP_LIB_TABLE for purposes of editing the glob...
void OnKifaceEnd() override
Called just once just before the DSO is to be unloaded.
Definition: pcbnew.cpp:385
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
wxWindow * CreateWindow(wxWindow *aParent, int aClassId, KIWAY *aKiway, int aCtlBits=0) override
Create a wxWindow for the current project.
Definition: pcbnew.cpp:77
#define NULL
bool IsSingle() const
Is this KIFACE_I running under single_top?
Definition: kiface_i.h:104
int aKiwayVersion
Definition: pcbnew.cpp:194
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:29
static bool LoadGlobalTable(FP_LIB_TABLE &aTable)
Load the global footprint library table into aTable.
void end_common()
Common things to do for a top program module, during OnKifaceEnd();.
Definition: kiface_i.cpp:98
PGM_BASE * PgmOrNull()
similar to PGM_BASE& Pgm(), but return a reference that can be nullptr when running a shared lib from...
Definition: cvpcb.cpp:112
wxString PyPluginsPath(bool aUserPath)
Definition of file extensions used in Kicad.
static FOOTPRINT_PREVIEW_PANEL * New(KIWAY *aKiway, wxWindow *aParent)
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition: kiway.h:260
pcbnew DSO
Definition: kiway.h:269
Return a new FP_LIB_TABLE with the global table installed as a fallback.
Definition: kiface_ids.h:46
#define KIFACE_GETTER
Definition: kiway.h:109
void Load(const wxString &aFileName)
Load the library table using the path defined by aFileName aFallBackTable.
wxString FindKicadFile(const wxString &shortname)
Search the executable file shortname in KiCad binary path and return full file name if found or short...
Definition: gestfich.cpp:102
void * IfaceOrAddress(int aDataId) override
Function IfaceOrAddress return a pointer to the requested object.
Definition: pcbnew.cpp:148
PCB::IFACE KIFACE_I kiface("pcbnew", KIWAY::FACE_PCB)
FACE_T
Known KIFACE implementations.
Definition: kiway.h:266
JSON_SETTINGS * RegisterSettings(JSON_SETTINGS *aSettings, bool aLoadNow=true)
Takes ownership of the pointer passed in.
void PythonPluginsReloadBase()
Helper function PythonPluginsReloadBase Reload Python plugins if they are newer than the already load...
Definition: pcbnew.cpp:303
void SetFullURI(const wxString &aFullURI)
Change the full URI for the library.
see class PGM_BASE
LIB_TABLE_ROW & At(unsigned aIndex)
Get the 'n'th LIB_TABLE_ROW object.
#define _(s)
Definition: 3d_actions.cpp:33
Implement a participant in the KIWAY alchemy.
Definition: kiway.h:147
The main frame for Pcbnew.
void Save(const wxString &aFileName) const
Write this library table to aFileName in s-expression form.
virtual void CommonSettingsChanged(bool aEnvVarsChanged, bool aTextVarsChanged)
Call CommonSettingsChanged() on all KIWAY_PLAYERs.
Definition: kiway.cpp:515
void InitSettings(APP_SETTINGS_BASE *aSettings)
Definition: kiface_i.h:94
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
Definition: pcbnew.cpp:189
bool IsWxPythonLoaded()
FOOTPRINT_LIST_IMPL GFootprintList
The global footprint info table.
Definition: pcbnew.cpp:332
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:75
static wxString GetGlobalTableFileName()
int PGM_BASE * aProgram
Definition: pcbnew.cpp:195
Return a pointer to the global instance of FOOTPRINT_LIST from pcbnew.
Definition: kiface_ids.h:39