Logo Search packages:      
Sourcecode: packagesearch version File versions

pluginmanager.cpp

//
// C++ Implementation: pluginmanager
//
// Description: 
//
//
// Author: Benjamin Mesing <bensmail@gmx.net>, (C) 2004
//
// Copyright: See COPYING file that comes with this distribution
//
//

#include <iostream>
#include <algorithm>
#include <assert.h>

#include <qapplication.h>
#include <qdir.h>
#include <qstringlist.h>
#include <qlistview.h>
#include <qlabel.h>
#include <qmainwindow.h>
#include <qstatusbar.h>

#include <dlfcn.h>      // for dl*()

#include "extalgorithm.h"

#include "pluginmanager.h"
#include "plugincontainer.h"
#include "ipluginuser.h"
#include "iprovider.h"
#include "pluginlistitem.h"
#include "pluginsettingsdlg.h"
#include "progressdisplaydlg.h"

#include "plugincontrol.h"

#include "xmldata.h"

#include "helpers.h"

namespace NPlugin 
{

00046 PluginManager::PluginManager(vector<string> directories, IProvider* pProvider, IPluginUser* pUser)
      : _settingsVersion("0.1")
{
      _pPluginUser = pUser;
      _pProvider = pProvider;
      _pProgressDlg = 0;
      _directories = directories;
      _pControlDialog = 0;
}


PluginManager::~PluginManager()
{
}


00062 void PluginManager::loadPlugins()
{
      NUtil::ProgressDisplayDlg dlg(_pProvider->mainWindow(), "PluginProgressDlg", true);
      _pProgressDlg = &dlg;
      dlg.show();
      // Function pointer to a function creating a new IProvider
      vector<PluginData> plugins = getAvailablePlugins();
      for (vector<PluginData>::const_iterator it = plugins.begin(); it != plugins.end(); ++it)
      {
            list<PluginData>::iterator jt = find_if( _disabledPlugins.begin(), 
                  _disabledPlugins.end(), PluginEquals(*it) );
            if (jt == _disabledPlugins.end())   // if the plugin is not disabled
            {
                  dlg._pPluginName->setText(it->name());
                  dlg.setProgress(0);
                  qApp->processEvents();
                  loadPlugin(it->directory, it->name()); 
                  dlg.setProgressRange(0, 100, false);
                  dlg.setProgress(100);
                  dlg.setText("");
                  qApp->processEvents();
            }
      }
      _pProgressDlg = 0;
}

00088 vector<PluginContainer*> PluginManager::getLoadedPlugins() const
{
      vector<PluginContainer*> result(_loadedPlugins.size());
      uint i = 0;
      for (map<PluginContainer*, PluginData>::const_iterator it = _loadedPlugins.begin(); 
            it != _loadedPlugins.end(); ++it)
      {
            assert(i<_loadedPlugins.size());
            result[i] = it->first;
            ++i;
      }
      return result;
}


// This function first collects the plugins available in the directories used.
// Then it checks the loaded plugins and searches those in the ones collected 
// above where it appends the load specific information. If a loaded plugin is
// not available in the available ones it will be appended at the result.
00107 vector<PluginManager::PluginData> PluginManager::getAvailablePlugins()
{
      typedef PluginContainer* (*NewPluginFkt)();
      typedef PluginInformation (*GetPluginDataFkt)();
      vector<PluginData> informations;
      for (vector<string>::const_iterator it = _directories.begin(); it != _directories.end(); ++it)
      {
            const string& directory = *it;
            dlerror();  // clear error messages
            QDir pluginDir(toQString(directory), "lib*.so");      // only care for library files
            QStringList pluginFiles = pluginDir.entryList(QDir::Files);
            // try to load the library and fetch the creator function for each file 
            // in the current directory, if both is possible the plugin is available
            for (QStringList::iterator jt = pluginFiles.begin(); jt != pluginFiles.end(); ++jt)
            {

                  PluginData pluginData;
                  string libraryFile = toString(*jt);
                  void* libraryHandle = dlopen((directory + libraryFile).c_str(), RTLD_NOW);
                  checkDlError();
                  if (libraryHandle)      // loading was possible
                  {
                        string pluginName = libraryFile.substr(3,libraryFile.length() - 6);     // remove "lib" and ".so"
                        // fetch the creator function
                        NewPluginFkt func = NewPluginFkt(dlsym( libraryHandle, ("new_" + pluginName).c_str() ));
                        if (func != 0)    // if the function could be received we have a valid plugin
                        {
                              pluginData.directory = directory;
                              GetPluginDataFkt getInformation = GetPluginDataFkt(dlsym(libraryHandle, "get_pluginInformation"));
                              if (getInformation != 0)
                                    pluginData.information = getInformation();
                              informations.push_back(pluginData);
                        }
                        dlclose(libraryHandle); // close the library again
                  }
            }
            dlerror();  // dismiss possible error messages
      }
      // iterate through the loaded plugins and add the their load information to the 
      // available plugins, if there is no matching plugin found collect the plugins
      // in newPlugins and append to the availables
      {
            vector<PluginData> newPlugins;
            for (map<PluginContainer*, PluginData>::const_iterator it = _loadedPlugins.begin();
                  it != _loadedPlugins.end(); ++it )
            {
                  // find the loaded plugin in the available ones
                  vector<PluginData>::iterator jt = informations.begin();
                  jt = find_if( jt, informations.end(), PluginEquals(it->second) );
                  if ( jt == informations.end())      // if the plugin was not found
                  {
                        newPlugins.push_back(it->second);
                  }
                  else
                  {
                        (*jt) = it->second;     // copy the information from the loaded plugin
                  }
            }
            informations.insert(informations.end(), newPlugins.begin(), newPlugins.end());
      }
      return informations;
}

00170 PluginContainer* PluginManager::loadPlugin( 
      const string& directory, const string& libraryName)
{
      typedef PluginContainer* (*NewPluginFkt)();
      typedef PluginInformation (*GetPluginDataFkt)();
      
      PluginContainer* pPlugin = 0; // if the plugin could not be loaded 0 will be returned
      PluginData pluginData;
      dlerror();  // dismiss pending errors
      void* libraryHandle = dlopen((directory + "lib" + libraryName + ".so").c_str(), RTLD_LAZY);
      if (!checkDlError())
      {     // no error occured
            string pluginName = libraryName;    // remove "lib" and ".so"
            // get the function for creating the plugin
            NewPluginFkt func = NewPluginFkt(dlsym(libraryHandle, ("new_" + pluginName).c_str() ));
            if (func != 0)    // if no error occured on load (i.e. the symbol was present)
            {
                  pPlugin = func(); // fetch the plugin
                  cout << "Loaded plugin: " << pPlugin->title() <<endl;
                  pluginData.directory = directory;
                  pluginData.libraryHandle = libraryHandle;
                  pluginData.pPlugin = pPlugin;
                  GetPluginDataFkt getInformation = GetPluginDataFkt(
                        dlsym(libraryHandle, "get_pluginInformation"));
                  if (getInformation != 0)
                        pluginData.information = getInformation();
            }
            else  // an error occured
            {
                  if (libraryHandle)
                        dlclose(libraryHandle);
                  dlerror();  // dismiss possible errors
            }
      }
      if (pPlugin)      // if loading was successfull, add the plugin to the map
            // and remove it from the disbled plugins if neccessary
      {
            pPlugin->addPluginUser(_pProvider->pluginUser());
            if (!pPlugin->init(_pProvider))     // if the container failed to initialize
            {
                  // disable plugin to prevent further attemps to load this plugin
                  _disabledPlugins.push_back(pluginData);
                  delete pPlugin;
                  pPlugin = 0;
            }
            else
            {
                  map<string, const QDomElement>::const_iterator it = _pluginSettings.find(pPlugin->name());
                  if ( it != _pluginSettings.end() )
                  {
                        pPlugin->loadSettings(it->second);
                  }
                  _loadedPlugins.insert(make_pair(pPlugin, pluginData));
                  removeFromDisabled(pluginData);
            }
      }
      return pPlugin;
}


00230 void PluginManager::unloadPlugin(PluginContainer* pPlugin)
{
      QString pluginTitle = pPlugin->title();
      map<PluginContainer*, PluginData>::iterator it = _loadedPlugins.find(pPlugin);
      assert(it != _loadedPlugins.end());
      _disabledPlugins.push_back(it->second);
      // add the settings of the plugin in _pluginSettings
      {
            NXml::XmlData data("SettingsDocument");
            pPlugin->saveSettings(data, data.root());
            // if the plugin has settings
            if (!data.root().firstChild().isNull())
                  // could not use operator[] because this would have violated the const specification
                  _pluginSettings.insert( 
                        make_pair(pPlugin->name(), data.root().firstChild().toElement()) );
      }
      delete pPlugin;
      dlclose(it->second.libraryHandle);
      _loadedPlugins.erase(it);
      _pProvider->statusBar()->message("Unloaded plugin " + pluginTitle, 3000);
}

00252 void PluginManager::saveSettings(NXml::XmlData& outData, QDomElement parent)
{
      // take a look at the pluginmanager.dtd file for the XML structure
      QDomElement pluginManager = outData.addElement(parent, "PluginManager");
      outData.addAttribute(pluginManager, _settingsVersion, "settingsVersion");
      
      QDomElement disabledPlugins = outData.addElement(pluginManager, "DisabledPlugins");
      QDomElement pluginSettings = outData.addElement(pluginManager, "PluginSettings");
      
      for (list<PluginData>::iterator it = _disabledPlugins.begin(); it != _disabledPlugins.end(); ++it)
      {
            QDomElement plugin = outData.addElement(disabledPlugins, "Plugin");
            {
                  outData.addAttribute(plugin, it->name(), "name");
                  outData.addAttribute(plugin, it->version(), "version");
                  outData.addAttribute(plugin, it->author(), "author");
                  outData.addAttribute(plugin, it->directory, "directory");
                  outData.addAttribute(plugin, false, "enabled");
            }
            // store the settings for the disabled plugins
            map<string, const QDomElement>::const_iterator jt = _pluginSettings.find(it->name());
            if ( jt != _pluginSettings.end() )
            {
                  pluginSettings.appendChild(jt->second);
            }
      }
      for (PluginToDataMap::iterator it = _loadedPlugins.begin(); 
            it != _loadedPlugins.end(); ++it)
      {
            it->first->saveSettings(outData, pluginSettings);
      }
}

00285 void PluginManager::loadSettings(const NXml::XmlData& inData, const QDomElement source) 
{
      // take a look at the pluginmanager.dtd file for the XML structure
      QDomElement disabledPlugins = source.firstChild().toElement();
      QDomElement plugin = disabledPlugins.firstChild().toElement();
      while (!plugin.isNull())
      {
            PluginData pd;
            NXml::getAttribute(plugin, pd.information.name, "name");
            NXml::getAttribute(plugin, pd.information.version, "version");
            NXml::getAttribute(plugin, pd.information.author, "author");
            NXml::getAttribute(plugin, pd.directory, "directory");
            bool enabled;
            NXml::getAttribute(plugin, enabled, "enabled");
            if (!enabled)
            {
                  _disabledPlugins.push_back(pd);
            }
            plugin = (plugin.nextSibling().toElement());
      }
      // the node which holds all settings of the different plugins
      const QDomElement pluginSettings = disabledPlugins.nextSibling().toElement();
      const QDomNodeList plugins = pluginSettings.childNodes();
      for (uint i = 0; i < plugins.count(); ++i)
      {
            // the settings for the current plugin
            const QDomElement settings = plugins.item(i).toElement();
            if ( !settings.isNull() )
            {
                  _pluginSettings.insert( make_pair(toString(settings.tagName()),settings) );
            }
      }
}


00320 void PluginManager::showControlDialog(QWidget* pParent)
{
      PluginControl dlg(pParent, "ControlDialog");
      _pControlDialog = &dlg;
      _pPluginListView = dlg._pPluginList;
      vector<PluginData> plugins = getAvailablePlugins();
      for (vector<PluginData>::iterator it = plugins.begin(); it != plugins.end(); ++it)
      {
            PluginListItem* pItem = new PluginListItem(dlg._pPluginList, *it);
            connect( pItem, SIGNAL(toggled(PluginListItem*, bool)), 
                  SLOT(onPluginToggled(PluginListItem*, bool)) );
      }
      dlg.exec();
      _pPluginListView = 0;
      _pControlDialog = 0;
}

00337 void PluginManager::showSettingsDialog(QWidget* pParent)
{
      PluginSettingsDlg dlg;
      vector<PluginContainer*> plugins = getLoadedPlugins();
      for (vector<PluginContainer*>::iterator it = plugins.begin(); it != plugins.end(); ++it)
      {
            dlg.addPlugin(*it);
      }
      if ( dlg.exec() == QDialog::Accepted )
            for_each( plugins.begin(), plugins.end(), mem_fun(&PluginContainer::applySettings) );
}

00349 bool PluginManager::checkDlError()
{
      const char* error = dlerror();
      if (error)
      {
            cerr << "Dynamic Library error: " << error <<endl;
            return true;
      }
      return false;
}

void PluginManager::onPluginToggled(PluginListItem* pSrc, bool state)
{
      const PluginData& pd = pSrc->pluginData();
      if (state)
      {
            assert(pd.libraryHandle==0);
            assert(pd.pPlugin==0);
            NUtil::ProgressDisplayDlg dlg(_pControlDialog, "PluginProgressDlg", true);
            _pProgressDlg = &dlg;
            dlg.show();
            PluginContainer* pPlugin = loadPlugin(pd.directory, pd.information.name);
            _pProgressDlg = 0;
            if (pPlugin)
            {
                  const PluginData& newData = _loadedPlugins[pPlugin];
                  pSrc->setDynamicData(newData.libraryHandle, pPlugin);
            }
            else
            {
                  qDebug("Error loading plugin");
                  // ignore this state change
                  disconnect( pSrc, SIGNAL(toggled(PluginListItem*, bool)), 
                        this, SLOT(onPluginToggled(PluginListItem*, bool)) );
                  pSrc->setState(QCheckListItem::Off);
                  connect( pSrc, SIGNAL(toggled(PluginListItem*, bool)), 
                        SLOT(onPluginToggled(PluginListItem*, bool)) );
            }
      }
      else
      {
            assert(pd.libraryHandle);
            assert(pd.pPlugin);
            unloadPlugin(pd.pPlugin);
            pSrc->setDynamicData(0, 0);
      }
}

00397 bool PluginManager::removeFromDisabled(const PluginData& pd)
{
      list<PluginData>::iterator it = find_if( _disabledPlugins.begin(), _disabledPlugins.end(),
            PluginEquals(pd) );
      if (it != _disabledPlugins.end())
      {
            _disabledPlugins.erase(it);
            return true;
      }
      return false;
}


00410 NUtil::IProgressObserver* PluginManager::progressObserver()
{
      return _pProgressDlg;
}

};

Generated by  Doxygen 1.6.0   Back to index