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