KiCad PCB EDA Suite
3d_plugin_manager.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) 2015-2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 
25 #include <utility>
26 #include <iostream>
27 #include <sstream>
28 
29 #include <wx/config.h>
30 #include <wx/dir.h>
31 #include <wx/dynlib.h>
32 #include <wx/filename.h>
33 #include <wx/log.h>
34 #include <wx/stdpaths.h>
35 #include <wx/string.h>
36 
37 #include "common.h"
38 #include "pgm_base.h"
39 #include "3d_plugin_dir.h"
40 #include "3d_plugin_manager.h"
41 #include "plugins/3d/3d_plugin.h"
42 #include "3d_cache/sg/scenegraph.h"
43 #include "plugins/ldr/3d/pluginldr3D.h"
44 
45 #define MASK_3D_PLUGINMGR "3D_PLUGIN_MANAGER"
46 
47 
49 {
50  // create the initial file filter list entry
51  m_FileFilters.push_back( _( "All Files (*.*)|*.*" ) );
52 
53  // discover and load plugins
54  loadPlugins();
55 
56 #ifdef DEBUG
57  if( !m_ExtMap.empty() )
58  {
59  std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::const_iterator sM = m_ExtMap.begin();
60  std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::const_iterator eM = m_ExtMap.end();
61  wxLogTrace( MASK_3D_PLUGINMGR, " * Extension [plugin name]:\n" );
62 
63  while( sM != eM )
64  {
65  wxLogTrace( MASK_3D_PLUGINMGR, " + '%s' [%s]\n", sM->first.GetData(),
66  sM->second->GetKicadPluginName() );
67  ++sM;
68  }
69 
70  }
71  else
72  {
73  wxLogTrace( MASK_3D_PLUGINMGR, " * No plugins available\n" );
74  }
75 
76 
77  if( !m_FileFilters.empty() )
78  {
80  std::list< wxString >::const_iterator sFF = m_FileFilters.begin();
81  std::list< wxString >::const_iterator eFF = m_FileFilters.end();
82  wxLogTrace( MASK_3D_PLUGINMGR, " * File filters:\n" );
83 
84  while( sFF != eFF )
85  {
86  wxLogTrace( MASK_3D_PLUGINMGR, " + '%s'\n", (*sFF).GetData() );
87  ++sFF;
88  }
89  }
90  else
91  {
92  wxLogTrace( MASK_3D_PLUGINMGR, " * No file filters available\n" );
93  }
94 #endif // DEBUG
95 
96  return;
97 }
98 
99 
101 {
102  std::list< KICAD_PLUGIN_LDR_3D* >::iterator sP = m_Plugins.begin();
103  std::list< KICAD_PLUGIN_LDR_3D* >::iterator eP = m_Plugins.end();
104 
105  while( sP != eP )
106  {
107  (*sP)->Close();
108  delete *sP;
109  ++sP;
110  }
111 
112  m_Plugins.clear();
113  return;
114 }
115 
116 
118 {
119  std::list< wxString > searchpaths;
120  std::list< wxString > pluginlist;
121  wxFileName fn;
122 
123 #ifndef __WXMAC__
124 
125  #ifdef DEBUG
126  // set up to work from the build directory
127  fn.Assign( wxStandardPaths::Get().GetExecutablePath() );
128  fn.AppendDir( wxT("..") );
129  fn.AppendDir( wxT("plugins") );
130  fn.AppendDir( wxT("3d") );
131 
132  std::string testpath = std::string( fn.GetPathWithSep().ToUTF8() );
133  checkPluginPath( testpath, searchpaths );
134 
135  // add subdirectories too
136  wxDir debugPluginDir( testpath );
137  wxString subdir;
138 
139  if( debugPluginDir.GetFirst( &subdir, wxEmptyString, wxDIR_DIRS ) )
140  {
141  checkPluginPath( testpath + subdir, searchpaths );
142 
143  while( debugPluginDir.GetNext( &subdir ) )
144  checkPluginPath( testpath + subdir, searchpaths );
145  }
146  #endif
147 
148  #ifndef _WIN32
149  // multiarch friendly determination of the plugin directory: the executable dir
150  // is first determined via wxStandardPaths::Get().GetExecutablePath() and then
151  // the CMAKE_INSTALL_LIBDIR path is appended relative to the executable dir.
152 
153  fn.Assign( wxStandardPaths::Get().GetExecutablePath() );
154  fn.RemoveLastDir();
155  wxString tfname = fn.GetPathWithSep();
156  tfname.Append( wxString::FromUTF8Unchecked( PLUGINDIR ) );
157  fn.Assign( tfname, "");
158  fn.AppendDir( "kicad" );
159  #else
160  // on windows the plugins directory is within the executable's directory
161  fn.Assign( wxStandardPaths::Get().GetExecutablePath() );
162  #endif
163 
164  fn.AppendDir( wxT( "plugins" ) );
165  fn.AppendDir( wxT( "3d" ) );
166 
167  // checks plugin directory relative to executable path
168  checkPluginPath( std::string( fn.GetPathWithSep().ToUTF8() ), searchpaths );
169 
170  // check for per-user third party plugins
171  // note: GetUserDataDir() gives '.pcbnew' rather than '.kicad' since it uses the exe name;
172  fn.Assign( wxStandardPaths::Get().GetUserDataDir(), "" );
173  fn.RemoveLastDir();
174  #ifdef _WIN32
175  fn.AppendDir( wxT( "kicad" ) );
176  #else
177  fn.AppendDir( wxT( ".kicad" ) );
178  #endif
179  fn.AppendDir( wxT( "plugins" ) );
180  fn.AppendDir( wxT( "3d" ) );
181  checkPluginPath( fn.GetPathWithSep(), searchpaths );
182 
183 #else
184 
185  // Search path on OS X is
186  // (1) User ~/Library/Application Support/kicad/PlugIns/3d
187  checkPluginPath( GetOSXKicadUserDataDir() + wxT( "/PlugIns/3d" ), searchpaths );
188  // (2) Machine /Library/Application Support/kicad/PlugIns/3d
189  checkPluginPath( GetOSXKicadMachineDataDir() + wxT( "/PlugIns/3d" ), searchpaths );
190  // (3) Bundle kicad.app/Contents/PlugIns/3d
191  fn.Assign( Pgm().GetExecutablePath() );
192  fn.AppendDir( wxT( "Contents" ) );
193  fn.AppendDir( wxT( "PlugIns" ) );
194  fn.AppendDir( wxT( "3d" ) );
195  checkPluginPath( fn.GetPathWithSep(), searchpaths );
196 
197 #endif
198 
199  std::list< wxString >::iterator sPL = searchpaths.begin();
200  std::list< wxString >::iterator ePL = searchpaths.end();
201 
202  while( sPL != ePL )
203  {
204 #ifdef DEBUG
205  do {
206  std::ostringstream ostr;
207  ostr << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << ":\n";
208  ostr << " * [DEBUG] searching path: '" << (*sPL).ToUTF8() << "'";
209  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
210  } while( 0 );
211 #endif
212  listPlugins( *sPL, pluginlist );
213  ++sPL;
214  }
215 
216  if( pluginlist.empty() )
217  return;
218 
219  sPL = pluginlist.begin();
220  ePL = pluginlist.end();
221 
222  while( sPL != ePL )
223  {
224  KICAD_PLUGIN_LDR_3D* pp = new KICAD_PLUGIN_LDR_3D;
225 
226  if( pp->Open( sPL->ToUTF8() ) )
227  {
228 #ifdef DEBUG
229  do {
230  std::ostringstream ostr;
231  ostr << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << ":\n";
232  ostr << "* [DEBUG] adding plugin";
233  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
234  } while( 0 );
235 #endif
236  m_Plugins.push_back( pp );
237  int nf = pp->GetNFilters();
238 
239  #ifdef DEBUG
240  do {
241  std::ostringstream ostr;
242  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
243  ostr << " * [INFO] adding " << nf << " filters";
244  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
245  } while( 0 );
246  #endif
247 
248  for( int i = 0; i < nf; ++i )
249  {
250  char const* cp = pp->GetFileFilter( i );
251 
252  if( cp )
253  addFilterString( wxString::FromUTF8Unchecked( cp ) );
254  }
255 
256  addExtensionMap( pp );
257 
258  // close the loaded library
259  pp->Close();
260  }
261  else
262  {
263 #ifdef DEBUG
264  do {
265  std::ostringstream ostr;
266  ostr << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << ":\n";
267  ostr << "* [DEBUG] deleting plugin";
268  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
269  } while( 0 );
270 #endif
271  delete pp;
272  }
273 
274  ++sPL;
275  }
276 
277 #ifdef DEBUG
278  do {
279  std::ostringstream ostr;
280  ostr << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << ":\n";
281  ostr << "* [DEBUG] plugins loaded";
282  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
283  } while( 0 );
284 #endif
285 
286  return;
287 }
288 
289 
290 void S3D_PLUGIN_MANAGER::listPlugins( const wxString& aPath,
291  std::list< wxString >& aPluginList )
292 {
293  // list potential plugins given a search path
294 
295  wxString nameFilter; // filter for user-loadable libraries (aka modules)
296  wxString lName; // stores name of enumerated files
297  wxString fName; // full name of file
298  wxDir wd;
299  wd.Open( aPath );
300 
301  if( !wd.IsOpened() )
302  return;
303 
304  nameFilter = wxT( "*" );
305 
306 #ifndef __WXMAC__
307  nameFilter.Append( wxDynamicLibrary::GetDllExt( wxDL_MODULE ) );
308 #else
309  // wxDynamicLibrary::GetDllExt( wxDL_MODULE ) will return ".bundle" on OS X.
310  // This might be correct, but cmake builds a ".so" for a library MODULE.
311  // Per definition a loadable "xxx.bundle" is similar to an "xxx.app" app
312  // bundle being a folder with some special content in it. We obviously don't
313  // want to have that here for our loadable module, so just use ".so".
314  nameFilter.Append( ".so" );
315 #endif
316 
317  wxString lp = wd.GetNameWithSep();
318 
319  if( wd.GetFirst( &lName, nameFilter, wxDIR_FILES ) )
320  {
321  fName = lp + lName;
322  checkPluginName( fName, aPluginList );
323 
324  while( wd.GetNext( &lName ) )
325  {
326  fName = lp + lName;
327  checkPluginName( fName, aPluginList );
328  }
329  }
330 
331  wd.Close();
332 
333  return;
334 }
335 
336 
337 void S3D_PLUGIN_MANAGER::checkPluginName( const wxString& aPath,
338  std::list< wxString >& aPluginList )
339 {
340  // check the existence of a plugin name and add it to the list
341 
342  if( aPath.empty() || !wxFileName::FileExists( aPath ) )
343  return;
344 
345  wxFileName path;
346 
347  if( aPath.StartsWith( "${" ) || aPath.StartsWith( "$(" ) )
348  path.Assign( ExpandEnvVarSubstitutions( aPath ) );
349  else
350  path.Assign( aPath );
351 
352  path.Normalize();
353 
354  // determine if the path is already in the list
355  wxString wxpath = path.GetFullPath();
356  std::list< wxString >::iterator bl = aPluginList.begin();
357  std::list< wxString >::iterator el = aPluginList.end();
358 
359  while( bl != el )
360  {
361  if( 0 == (*bl).Cmp( wxpath ) )
362  return;
363 
364  ++bl;
365  }
366 
367  aPluginList.push_back( wxpath );
368 
369  #ifdef DEBUG
370  wxLogTrace( MASK_3D_PLUGINMGR, " * [INFO] found 3D plugin '%s'\n",
371  wxpath.GetData() );
372  #endif
373 
374  return;
375 }
376 
377 
378 void S3D_PLUGIN_MANAGER::checkPluginPath( const wxString& aPath,
379  std::list< wxString >& aSearchList )
380 {
381  // check the existence of a path and add it to the path search list
382  if( aPath.empty() )
383  return;
384 
385  #ifdef DEBUG
386  wxLogTrace( MASK_3D_PLUGINMGR, " * [INFO] checking for 3D plugins in '%s'\n",
387  aPath.GetData() );
388  #endif
389 
390  wxFileName path;
391 
392  if( aPath.StartsWith( "${" ) || aPath.StartsWith( "$(" ) )
393  path.Assign( ExpandEnvVarSubstitutions( aPath ), "" );
394  else
395  path.Assign( aPath, "" );
396 
397  path.Normalize();
398 
399  if( !wxFileName::DirExists( path.GetFullPath() ) )
400  return;
401 
402  // determine if the directory is already in the list
403  wxString wxpath = path.GetFullPath();
404  std::list< wxString >::iterator bl = aSearchList.begin();
405  std::list< wxString >::iterator el = aSearchList.end();
406 
407  while( bl != el )
408  {
409  if( 0 == (*bl).Cmp( wxpath ) )
410  return;
411 
412  ++bl;
413  }
414 
415  aSearchList.push_back( wxpath );
416 
417  return;
418 }
419 
420 
421 void S3D_PLUGIN_MANAGER::addFilterString( const wxString& aFilterString )
422 {
423  // add an entry to the file filter list
424  if( aFilterString.empty() )
425  return;
426 
427  std::list< wxString >::iterator sFF = m_FileFilters.begin();
428  std::list< wxString >::iterator eFF = m_FileFilters.end();
429 
430  while( sFF != eFF )
431  {
432  if( 0 == (*sFF).Cmp( aFilterString ) )
433  return;
434 
435  ++sFF;
436  }
437 
438  m_FileFilters.push_back( aFilterString );
439  return;
440 }
441 
442 
443 void S3D_PLUGIN_MANAGER::addExtensionMap( KICAD_PLUGIN_LDR_3D* aPlugin )
444 {
445  // add entries to the extension map
446  if( NULL == aPlugin )
447  return;
448 
449  int nExt = aPlugin->GetNExtensions();
450 
451  #ifdef DEBUG
452  do {
453  std::ostringstream ostr;
454  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
455  ostr << " * [INFO] adding " << nExt << " extensions";
456  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
457  } while( 0 );
458  #endif
459 
460  for( int i = 0; i < nExt; ++i )
461  {
462  char const* cp = aPlugin->GetModelExtension( i );
463  wxString ws;
464 
465  if( cp )
466  ws = wxString::FromUTF8Unchecked( cp );
467 
468  if( !ws.empty() )
469  {
470  m_ExtMap.insert( std::pair< const wxString, KICAD_PLUGIN_LDR_3D* >( ws, aPlugin ) );
471  }
472 
473  }
474 
475  return;
476 }
477 
478 
479 std::list< wxString > const* S3D_PLUGIN_MANAGER::GetFileFilters( void ) const
480 {
481  return &m_FileFilters;
482 }
483 
484 
485 SCENEGRAPH* S3D_PLUGIN_MANAGER::Load3DModel( const wxString& aFileName, std::string& aPluginInfo )
486 {
487  wxFileName raw( aFileName );
488  wxString ext = raw.GetExt();
489 
490  #ifdef WIN32
491  // note: plugins only have a lowercase filter within Windows; including an uppercase
492  // filter will result in duplicate file entries and should be avoided.
493  ext.LowerCase();
494  #endif
495 
496  std::pair < std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator,
497  std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator > items;
498 
499  items = m_ExtMap.equal_range( ext );
500  std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator sL = items.first;
501 
502  while( sL != items.second )
503  {
504  if( sL->second->CanRender() )
505  {
506  SCENEGRAPH* sp = sL->second->Load( aFileName.ToUTF8() );
507 
508  if( NULL != sp )
509  {
510  sL->second->GetPluginInfo( aPluginInfo );
511  return sp;
512  }
513  }
514 
515  ++sL;
516  }
517 
518  return NULL;
519 }
520 
521 
523 {
524  std::list< KICAD_PLUGIN_LDR_3D* >::iterator sP = m_Plugins.begin();
525  std::list< KICAD_PLUGIN_LDR_3D* >::iterator eP = m_Plugins.end();
526 
527  #ifdef DEBUG
528  do {
529  std::ostringstream ostr;
530  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
531  ostr << " * [INFO] closing " << m_Plugins.size() << " plugins";
532  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
533  } while( 0 );
534  #endif
535 
536  while( sP != eP )
537  {
538  (*sP)->Close();
539  ++sP;
540  }
541 
542  return;
543 }
544 
545 
546 bool S3D_PLUGIN_MANAGER::CheckTag( const char* aTag )
547 {
548  if( NULL == aTag || aTag[0] == 0 || m_Plugins.empty() )
549  return false;
550 
551  std::string tname = aTag;
552  std::string pname; // plugin name
553 
554  size_t cpos = tname.find( ':' );
555 
556  // if there is no colon or plugin name then the tag is bad
557  if( cpos == std::string::npos || cpos == 0 )
558  return false;
559 
560  pname = tname.substr( 0, cpos );
561  std::string ptag; // tag from the plugin
562 
563  std::list< KICAD_PLUGIN_LDR_3D* >::iterator pS = m_Plugins.begin();
564  std::list< KICAD_PLUGIN_LDR_3D* >::iterator pE = m_Plugins.end();
565 
566  while( pS != pE )
567  {
568  ptag.clear();
569  (*pS)->GetPluginInfo( ptag );
570 
571  // if the plugin name matches then the version
572  // must also match
573  if( !ptag.compare( 0, pname.size(), pname ) )
574  {
575  if( ptag.compare( tname ) )
576  return false;
577 
578  return true;
579  }
580 
581  ++pS;
582  }
583 
584  return true;
585 }
std::list< wxString > m_FileFilters
list of file filters
std::multimap< const wxString, KICAD_PLUGIN_LDR_3D * > m_ExtMap
mapping of extensions to available plugins
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: kicad.cpp:65
#define MASK_3D_PLUGINMGR
void addFilterString(const wxString &aFilterString)
add an entry to the file filter list
describes the runtime-loadable interface to support loading and parsing of 3D models.
SCENEGRAPH * Load3DModel(const wxString &aFileName, std::string &aPluginInfo)
void listPlugins(const wxString &aPath, std::list< wxString > &aPluginList)
list potential plugins
defines the basic data set required to represent a 3D model; this model must remain compatible with V...
std::list< KICAD_PLUGIN_LDR_3D * > m_Plugins
list of discovered plugins
const wxString ExpandEnvVarSubstitutions(const wxString &aString)
Function ExpandEnvVarSubstitutions replaces any environment variable references with their values...
Definition: common.cpp:254
void checkPluginPath(const wxString &aPath, std::list< wxString > &aSearchList)
check the existence of a path and add it to the path search list
bool CheckTag(const char *aTag)
Function CheckTag checks the given tag and returns true if the plugin named in the tag is not loaded ...
void addExtensionMap(KICAD_PLUGIN_LDR_3D *aPlugin)
add entries to the extension map
see class PGM_BASE
manages 3D model plugins
void loadPlugins(void)
load plugins
The common library.
void ClosePlugins(void)
Function ClosePlugins iterates through all discovered plugins and closes them to reclaim memory...
void checkPluginName(const wxString &aPath, std::list< wxString > &aPluginList)
check the existence of a plugin name and add it to the list
std::list< wxString > const * GetFileFilters(void) const
Function GetFileFilters returns the list of file filters; this will contain at least the default "All...