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