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;
137  wxString subdir;
138 
139  debugPluginDir.Open( testpath );
140 
141  if( debugPluginDir.IsOpened() &&
142  debugPluginDir.GetFirst( &subdir, wxEmptyString, wxDIR_DIRS ) )
143  {
144  checkPluginPath( testpath + subdir, searchpaths );
145 
146  while( debugPluginDir.GetNext( &subdir ) )
147  checkPluginPath( testpath + subdir, searchpaths );
148  }
149  #endif
150 
151  #ifndef _WIN32
152  // multiarch friendly determination of the plugin directory: the executable dir
153  // is first determined via wxStandardPaths::Get().GetExecutablePath() and then
154  // the CMAKE_INSTALL_LIBDIR path is appended relative to the executable dir.
155 
156  fn.Assign( wxStandardPaths::Get().GetExecutablePath() );
157  fn.RemoveLastDir();
158  wxString tfname = fn.GetPathWithSep();
159  tfname.Append( wxString::FromUTF8Unchecked( PLUGINDIR ) );
160  fn.Assign( tfname, "");
161  fn.AppendDir( "kicad" );
162  #else
163  // on windows the plugins directory is within the executable's directory
164  fn.Assign( wxStandardPaths::Get().GetExecutablePath() );
165  #endif
166 
167  fn.AppendDir( wxT( "plugins" ) );
168  fn.AppendDir( wxT( "3d" ) );
169 
170  // checks plugin directory relative to executable path
171  checkPluginPath( std::string( fn.GetPathWithSep().ToUTF8() ), searchpaths );
172 
173  // check for per-user third party plugins
174  // note: GetUserDataDir() gives '.pcbnew' rather than '.kicad' since it uses the exe name;
175  fn.Assign( wxStandardPaths::Get().GetUserDataDir(), "" );
176  fn.RemoveLastDir();
177  #ifdef _WIN32
178  fn.AppendDir( wxT( "kicad" ) );
179  #else
180  fn.AppendDir( wxT( ".kicad" ) );
181  #endif
182  fn.AppendDir( wxT( "plugins" ) );
183  fn.AppendDir( wxT( "3d" ) );
184  checkPluginPath( fn.GetPathWithSep(), searchpaths );
185 
186 #else
187 
188  // Search path on OS X is
189  // (1) User ~/Library/Application Support/kicad/PlugIns/3d
190  checkPluginPath( GetOSXKicadUserDataDir() + wxT( "/PlugIns/3d" ), searchpaths );
191  // (2) Machine /Library/Application Support/kicad/PlugIns/3d
192  checkPluginPath( GetOSXKicadMachineDataDir() + wxT( "/PlugIns/3d" ), searchpaths );
193  // (3) Bundle kicad.app/Contents/PlugIns/3d
194  fn.Assign( Pgm().GetExecutablePath() );
195  fn.AppendDir( wxT( "Contents" ) );
196  fn.AppendDir( wxT( "PlugIns" ) );
197  fn.AppendDir( wxT( "3d" ) );
198  checkPluginPath( fn.GetPathWithSep(), searchpaths );
199 
200 #endif
201 
202  std::list< wxString >::iterator sPL = searchpaths.begin();
203  std::list< wxString >::iterator ePL = searchpaths.end();
204 
205  while( sPL != ePL )
206  {
207 #ifdef DEBUG
208  do {
209  std::ostringstream ostr;
210  ostr << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << ":\n";
211  ostr << " * [DEBUG] searching path: '" << (*sPL).ToUTF8() << "'";
212  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
213  } while( 0 );
214 #endif
215  listPlugins( *sPL, pluginlist );
216  ++sPL;
217  }
218 
219  if( pluginlist.empty() )
220  return;
221 
222  sPL = pluginlist.begin();
223  ePL = pluginlist.end();
224 
225  while( sPL != ePL )
226  {
227  KICAD_PLUGIN_LDR_3D* pp = new KICAD_PLUGIN_LDR_3D;
228 
229  if( pp->Open( sPL->ToUTF8() ) )
230  {
231 #ifdef DEBUG
232  do {
233  std::ostringstream ostr;
234  ostr << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << ":\n";
235  ostr << "* [DEBUG] adding plugin";
236  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
237  } while( 0 );
238 #endif
239  m_Plugins.push_back( pp );
240  int nf = pp->GetNFilters();
241 
242  #ifdef DEBUG
243  do {
244  std::ostringstream ostr;
245  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
246  ostr << " * [INFO] adding " << nf << " filters";
247  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
248  } while( 0 );
249  #endif
250 
251  for( int i = 0; i < nf; ++i )
252  {
253  char const* cp = pp->GetFileFilter( i );
254 
255  if( cp )
256  addFilterString( wxString::FromUTF8Unchecked( cp ) );
257  }
258 
259  addExtensionMap( pp );
260 
261  // close the loaded library
262  pp->Close();
263  }
264  else
265  {
266 #ifdef DEBUG
267  do {
268  std::ostringstream ostr;
269  ostr << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << ":\n";
270  ostr << "* [DEBUG] deleting plugin";
271  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
272  } while( 0 );
273 #endif
274  delete pp;
275  }
276 
277  ++sPL;
278  }
279 
280 #ifdef DEBUG
281  do {
282  std::ostringstream ostr;
283  ostr << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << ":\n";
284  ostr << "* [DEBUG] plugins loaded";
285  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
286  } while( 0 );
287 #endif
288 
289  return;
290 }
291 
292 
293 void S3D_PLUGIN_MANAGER::listPlugins( const wxString& aPath,
294  std::list< wxString >& aPluginList )
295 {
296  // list potential plugins given a search path
297 
298  wxString nameFilter; // filter for user-loadable libraries (aka modules)
299  wxString lName; // stores name of enumerated files
300  wxString fName; // full name of file
301  wxDir wd;
302  wd.Open( aPath );
303 
304  if( !wd.IsOpened() )
305  return;
306 
307  nameFilter = wxT( "*" );
308 
309 #ifndef __WXMAC__
310  nameFilter.Append( wxDynamicLibrary::GetDllExt( wxDL_MODULE ) );
311 #else
312  // wxDynamicLibrary::GetDllExt( wxDL_MODULE ) will return ".bundle" on OS X.
313  // This might be correct, but cmake builds a ".so" for a library MODULE.
314  // Per definition a loadable "xxx.bundle" is similar to an "xxx.app" app
315  // bundle being a folder with some special content in it. We obviously don't
316  // want to have that here for our loadable module, so just use ".so".
317  nameFilter.Append( ".so" );
318 #endif
319 
320  wxString lp = wd.GetNameWithSep();
321 
322  if( wd.GetFirst( &lName, nameFilter, wxDIR_FILES ) )
323  {
324  fName = lp + lName;
325  checkPluginName( fName, aPluginList );
326 
327  while( wd.GetNext( &lName ) )
328  {
329  fName = lp + lName;
330  checkPluginName( fName, aPluginList );
331  }
332  }
333 
334  wd.Close();
335 
336  return;
337 }
338 
339 
340 void S3D_PLUGIN_MANAGER::checkPluginName( const wxString& aPath,
341  std::list< wxString >& aPluginList )
342 {
343  // check the existence of a plugin name and add it to the list
344 
345  if( aPath.empty() || !wxFileName::FileExists( aPath ) )
346  return;
347 
348  wxFileName path;
349 
350  if( aPath.StartsWith( "${" ) || aPath.StartsWith( "$(" ) )
351  path.Assign( ExpandEnvVarSubstitutions( aPath ) );
352  else
353  path.Assign( aPath );
354 
355  path.Normalize();
356 
357  // determine if the path is already in the list
358  wxString wxpath = path.GetFullPath();
359  std::list< wxString >::iterator bl = aPluginList.begin();
360  std::list< wxString >::iterator el = aPluginList.end();
361 
362  while( bl != el )
363  {
364  if( 0 == (*bl).Cmp( wxpath ) )
365  return;
366 
367  ++bl;
368  }
369 
370  aPluginList.push_back( wxpath );
371 
372  #ifdef DEBUG
373  wxLogTrace( MASK_3D_PLUGINMGR, " * [INFO] found 3D plugin '%s'\n",
374  wxpath.GetData() );
375  #endif
376 
377  return;
378 }
379 
380 
381 void S3D_PLUGIN_MANAGER::checkPluginPath( const wxString& aPath,
382  std::list< wxString >& aSearchList )
383 {
384  // check the existence of a path and add it to the path search list
385  if( aPath.empty() )
386  return;
387 
388  #ifdef DEBUG
389  wxLogTrace( MASK_3D_PLUGINMGR, " * [INFO] checking for 3D plugins in '%s'\n",
390  aPath.GetData() );
391  #endif
392 
393  wxFileName path;
394 
395  if( aPath.StartsWith( "${" ) || aPath.StartsWith( "$(" ) )
396  path.Assign( ExpandEnvVarSubstitutions( aPath ), "" );
397  else
398  path.Assign( aPath, "" );
399 
400  path.Normalize();
401 
402  if( !wxFileName::DirExists( path.GetFullPath() ) )
403  return;
404 
405  // determine if the directory is already in the list
406  wxString wxpath = path.GetFullPath();
407  std::list< wxString >::iterator bl = aSearchList.begin();
408  std::list< wxString >::iterator el = aSearchList.end();
409 
410  while( bl != el )
411  {
412  if( 0 == (*bl).Cmp( wxpath ) )
413  return;
414 
415  ++bl;
416  }
417 
418  aSearchList.push_back( wxpath );
419 
420  return;
421 }
422 
423 
424 void S3D_PLUGIN_MANAGER::addFilterString( const wxString& aFilterString )
425 {
426  // add an entry to the file filter list
427  if( aFilterString.empty() )
428  return;
429 
430  std::list< wxString >::iterator sFF = m_FileFilters.begin();
431  std::list< wxString >::iterator eFF = m_FileFilters.end();
432 
433  while( sFF != eFF )
434  {
435  if( 0 == (*sFF).Cmp( aFilterString ) )
436  return;
437 
438  ++sFF;
439  }
440 
441  m_FileFilters.push_back( aFilterString );
442  return;
443 }
444 
445 
446 void S3D_PLUGIN_MANAGER::addExtensionMap( KICAD_PLUGIN_LDR_3D* aPlugin )
447 {
448  // add entries to the extension map
449  if( NULL == aPlugin )
450  return;
451 
452  int nExt = aPlugin->GetNExtensions();
453 
454  #ifdef DEBUG
455  do {
456  std::ostringstream ostr;
457  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
458  ostr << " * [INFO] adding " << nExt << " extensions";
459  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
460  } while( 0 );
461  #endif
462 
463  for( int i = 0; i < nExt; ++i )
464  {
465  char const* cp = aPlugin->GetModelExtension( i );
466  wxString ws;
467 
468  if( cp )
469  ws = wxString::FromUTF8Unchecked( cp );
470 
471  if( !ws.empty() )
472  {
473  m_ExtMap.insert( std::pair< const wxString, KICAD_PLUGIN_LDR_3D* >( ws, aPlugin ) );
474  }
475 
476  }
477 
478  return;
479 }
480 
481 
482 std::list< wxString > const* S3D_PLUGIN_MANAGER::GetFileFilters( void ) const
483 {
484  return &m_FileFilters;
485 }
486 
487 
488 SCENEGRAPH* S3D_PLUGIN_MANAGER::Load3DModel( const wxString& aFileName, std::string& aPluginInfo )
489 {
490  wxFileName raw( aFileName );
491  wxString ext = raw.GetExt();
492 
493  #ifdef WIN32
494  // note: plugins only have a lowercase filter within Windows; including an uppercase
495  // filter will result in duplicate file entries and should be avoided.
496  ext.LowerCase();
497  #endif
498 
499  std::pair < std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator,
500  std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator > items;
501 
502  items = m_ExtMap.equal_range( ext );
503  std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator sL = items.first;
504 
505  while( sL != items.second )
506  {
507  if( sL->second->CanRender() )
508  {
509  SCENEGRAPH* sp = sL->second->Load( aFileName.ToUTF8() );
510 
511  if( NULL != sp )
512  {
513  sL->second->GetPluginInfo( aPluginInfo );
514  return sp;
515  }
516  }
517 
518  ++sL;
519  }
520 
521  return NULL;
522 }
523 
524 
526 {
527  std::list< KICAD_PLUGIN_LDR_3D* >::iterator sP = m_Plugins.begin();
528  std::list< KICAD_PLUGIN_LDR_3D* >::iterator eP = m_Plugins.end();
529 
530  #ifdef DEBUG
531  do {
532  std::ostringstream ostr;
533  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
534  ostr << " * [INFO] closing " << m_Plugins.size() << " plugins";
535  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
536  } while( 0 );
537  #endif
538 
539  while( sP != eP )
540  {
541  (*sP)->Close();
542  ++sP;
543  }
544 
545  return;
546 }
547 
548 
549 bool S3D_PLUGIN_MANAGER::CheckTag( const char* aTag )
550 {
551  if( NULL == aTag || aTag[0] == 0 || m_Plugins.empty() )
552  return false;
553 
554  std::string tname = aTag;
555  std::string pname; // plugin name
556 
557  size_t cpos = tname.find( ':' );
558 
559  // if there is no colon or plugin name then the tag is bad
560  if( cpos == std::string::npos || cpos == 0 )
561  return false;
562 
563  pname = tname.substr( 0, cpos );
564  std::string ptag; // tag from the plugin
565 
566  std::list< KICAD_PLUGIN_LDR_3D* >::iterator pS = m_Plugins.begin();
567  std::list< KICAD_PLUGIN_LDR_3D* >::iterator pE = m_Plugins.end();
568 
569  while( pS != pE )
570  {
571  ptag.clear();
572  (*pS)->GetPluginInfo( ptag );
573 
574  // if the plugin name matches then the version
575  // must also match
576  if( !ptag.compare( 0, pname.size(), pname ) )
577  {
578  if( ptag.compare( tname ) )
579  return false;
580 
581  return true;
582  }
583 
584  ++pS;
585  }
586 
587  return true;
588 }
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...