[tor-commits] [vidalia/alpha] Plugin framework: initial commit

chiiph at torproject.org chiiph at torproject.org
Sat Jul 2 21:53:46 UTC 2011


commit 92f39c6d275d07b9a49fc1a412c6482551190831
Author: Tomas Touceda <chiiph at torproject.org>
Date:   Wed Jun 1 17:02:07 2011 -0300

    Plugin framework: initial commit
    
    It introduces the basic structure of the plugin management, not the actual
    API. There's some refactoring to do, but the basic layout is working.
---
 CMakeLists.txt                                     |    2 +-
 src/vidalia/CMakeLists.txt                         |   13 ++
 src/vidalia/MainWindow.cpp                         |   24 +++-
 src/vidalia/MainWindow.h                           |    7 +
 src/vidalia/config/VidaliaSettings.cpp             |   13 ++
 src/vidalia/config/VidaliaSettings.h               |    5 +
 src/vidalia/plugin/PluginEngine.cpp                |   64 ++++++++
 src/vidalia/plugin/PluginEngine.h                  |   28 ++++
 src/vidalia/plugin/PluginWrapper.cpp               |  157 ++++++++++++++++++++
 src/vidalia/plugin/PluginWrapper.h                 |   46 ++++++
 .../plugin/prototypes/VidaliaTabPrototype.cpp      |   10 ++
 .../plugin/prototypes/VidaliaTabPrototype.h        |   21 +++
 12 files changed, 388 insertions(+), 2 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index dfd8f3b..0c07d0d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -37,10 +37,10 @@ include(FindQt4)
 find_package(Qt4 REQUIRED)
 set(QT_USE_QTNETWORK  true)
 set(QT_USE_QTXML      true)
+set(QT_USE_QTSCRIPT   true)
 if (USE_MARBLE)
   set(QT_USE_QTSVG    true)
   set(QT_USE_QTWEBKIT true)
-  set(QT_USE_QTSCRIPT true)
   set(QT_USE_QTDBUS   true)
 endif(USE_MARBLE)
 include(${QT_USE_FILE})
diff --git a/src/vidalia/CMakeLists.txt b/src/vidalia/CMakeLists.txt
index 5811320..4cbe458 100644
--- a/src/vidalia/CMakeLists.txt
+++ b/src/vidalia/CMakeLists.txt
@@ -20,6 +20,8 @@ include_directories(
   ${CMAKE_CURRENT_SOURCE_DIR}/help/browser
   ${CMAKE_CURRENT_SOURCE_DIR}/log
   ${CMAKE_CURRENT_SOURCE_DIR}/network
+  ${CMAKE_CURRENT_SOURCE_DIR}/plugin
+  ${CMAKE_CURRENT_SOURCE_DIR}/plugin/prototypes
   ${MARBLE_INCLUDE_DIR}
 )
 
@@ -80,6 +82,17 @@ qt4_wrap_cpp(vidalia_SRCS
   bwgraph/BandwidthGraph.h
   bwgraph/GraphFrame.h
 )
+## Plugin framework sources
+set(vidalia_SRCS ${vidalia_SRCS}
+  plugin/PluginEngine.cpp
+  plugin/PluginWrapper.cpp
+  plugin/prototypes/VidaliaTabPrototype.cpp
+)
+qt4_wrap_cpp(vidalia_SRCS
+  plugin/PluginEngine.h
+  plugin/PluginWrapper.h
+  plugin/prototypes/VidaliaTabPrototype.h
+)
 
 ## Configuration dialog sources
 set(vidalia_SRCS ${vidalia_SRCS}
diff --git a/src/vidalia/MainWindow.cpp b/src/vidalia/MainWindow.cpp
index 70e8044..931df1c 100644
--- a/src/vidalia/MainWindow.cpp
+++ b/src/vidalia/MainWindow.cpp
@@ -37,6 +37,8 @@
 #include "stringutil.h"
 #include "procutil.h"
 
+#include "PluginWrapper.h"
+
 #include <QtGui>
 
 #define IMG_BWGRAPH        ":/images/16x16/utilities-system-monitor.png"
@@ -100,6 +102,8 @@ MainWindow::MainWindow()
   /* Create a new TorControl object, used to communicate with Tor */
   _torControl = Vidalia::torControl(); 
 
+  _engine = new PluginEngine();
+
   createGUI();
   createConnections();
 
@@ -176,7 +180,11 @@ MainWindow::createMenuBar()
   viewMenu->addSeparator();
   viewMenu->addAction(_actionConfigure);
 
-//  QMenu *pluginsMenu = menu->addMenu(tr("Plugins"));
+  QMenu *pluginsMenu = menu->addMenu(tr("Plugins"));
+  foreach(QAction *action, _engine->getAllActions()) {
+    pluginsMenu->addAction(action);
+    connect(action, SIGNAL(triggered()), this, SLOT(showPluginTab()));
+  }
 
   QMenu *helpMenu = menu->addMenu(tr("Help"));
   helpMenu->addAction(_actionVidaliaHelp);
@@ -1539,6 +1547,12 @@ MainWindow::addTab(VidaliaTab *tab)
    * instanse passed */
   if(_tabMap.contains(tab->getTitle())) {
     ui.tabWidget->setCurrentIndex(_tabMap.indexOf(tab->getTitle()));
+
+    /** If we are trying to open the exact same tab twice
+     * don't do anything */
+    if(tab == ui.tabWidget->widget(_tabMap.indexOf(tab->getTitle())))
+      return;
+
     /** Exception for tabs that need to be always created */
     if (tab != _messageLog && 
         tab != &_statusTab && 
@@ -1579,6 +1593,14 @@ MainWindow::delTab(int index)
 }
 
 void
+MainWindow::showPluginTab()
+{
+  QAction *act = qobject_cast<QAction *>(sender());
+  PluginWrapper *wrapper = qobject_cast<PluginWrapper *>(act->parent());
+  addTab(wrapper->buildGUI());
+}
+
+void
 MainWindow::showStatusTab()
 {
   addTab(&_statusTab);
diff --git a/src/vidalia/MainWindow.h b/src/vidalia/MainWindow.h
index 7d386d5..e5366b8 100644
--- a/src/vidalia/MainWindow.h
+++ b/src/vidalia/MainWindow.h
@@ -36,6 +36,8 @@
 
 #include "TorControl.h"
 
+#include "PluginEngine.h"
+
 #include <QMainWindow>
 #include <QTimer>
 #include <QSystemTrayIcon>
@@ -142,6 +144,9 @@ private slots:
   /** Deletes the tab at index if it exists and it isn't the Status tab */
   void delTab(int index = -1);
 
+  /** Handles adding a new tab corresponding to a plugin */
+  void showPluginTab();
+
   /** Called when the web browser or IM client have stopped */
   void onSubprocessFinished(int exitCode, QProcess::ExitStatus exitStatus);
   /** Called periodically to check if the browser is running. If it is not,
@@ -304,6 +309,8 @@ private:
   NetViewer _netViewer; /**< Network map that draws circuits */
   QStringList _tabMap; /**< Map to handle opened tabs */
   BandwidthGraph *_graph; /**< Graph that draws bandwidth usage */
+
+  PluginEngine *_engine;
 };
 
 #endif
diff --git a/src/vidalia/config/VidaliaSettings.cpp b/src/vidalia/config/VidaliaSettings.cpp
index 0812b03..aa4d18b 100644
--- a/src/vidalia/config/VidaliaSettings.cpp
+++ b/src/vidalia/config/VidaliaSettings.cpp
@@ -39,6 +39,7 @@
 #define SETTING_LAST_UPDATE_CHECK   "LastUpdateCheck"
 #define SETTING_USE_LOCAL_GEOIP_DATABASE  "UseLocalGeoIpDatabase"
 #define SETTING_LOCAL_GEOIP_DATABASE "LocalGeoIpDatabase"
+#define SETTING_PLUGIN_PATH         "PluginPath"
 
 #if defined(Q_OS_WIN32)
 #define STARTUP_REG_KEY        "Software\\Microsoft\\Windows\\CurrentVersion\\Run"
@@ -82,6 +83,7 @@ VidaliaSettings::VidaliaSettings()
   setDefault(SETTING_LAST_UPDATE_CHECK, QDateTime());
   setDefault(SETTING_USE_LOCAL_GEOIP_DATABASE, false);
   setDefault(SETTING_LOCAL_GEOIP_DATABASE, "");
+  setDefault(SETTING_PLUGIN_PATH, vApp->dataDirectory());
 }
 
 /** Gets the currently preferred language code for Vidalia. */
@@ -321,3 +323,14 @@ VidaliaSettings::setLocalGeoIpDatabase(const QString &databaseFile)
   setValue(SETTING_LOCAL_GEOIP_DATABASE, databaseFile);
 }
 
+QString 
+VidaliaSettings::pluginPath() const
+{
+  return QDir::convertSeparators(value(SETTING_PLUGIN_PATH).toString());
+}
+
+void 
+VidaliaSettings::setPluginPath(const QString &path)
+{
+  setValue(SETTING_PLUGIN_PATH, path);
+}
diff --git a/src/vidalia/config/VidaliaSettings.h b/src/vidalia/config/VidaliaSettings.h
index 2667b12..029657f 100644
--- a/src/vidalia/config/VidaliaSettings.h
+++ b/src/vidalia/config/VidaliaSettings.h
@@ -125,6 +125,11 @@ public:
   QString localGeoIpDatabase() const;
   /** Sets the file to use as a local GeoIP database. */
   void setLocalGeoIpDatabase(const QString &databaseFile);
+
+  /** Returns the path where the plugins live */
+  QString pluginPath() const;
+  /** Sets the path where the plugins live */
+  void setPluginPath(const QString &path);
 };
 
 #endif
diff --git a/src/vidalia/plugin/PluginEngine.cpp b/src/vidalia/plugin/PluginEngine.cpp
new file mode 100644
index 0000000..fe85883
--- /dev/null
+++ b/src/vidalia/plugin/PluginEngine.cpp
@@ -0,0 +1,64 @@
+#include "PluginEngine.h"
+#include "VidaliaSettings.h"
+#include "PluginWrapper.h"
+
+PluginEngine::PluginEngine(QObject *parent)
+  : QScriptEngine(parent)
+{
+  // load prototypes
+  VidaliaTabPrototype vtabproto;
+  QScriptValue vtabscript = newQObject(&vtabproto, QScriptEngine::ScriptOwnership);
+  setDefaultPrototype(qMetaTypeId<VidaliaTab *>(), vtabscript);
+
+  // load constructors
+  QScriptValue vtabctor = newFunction(VidaliaTabPrototype::constructor, vtabscript);
+  globalObject().setProperty("VidaliaTab", vtabctor);
+
+  loadAllPlugins();
+}
+
+PluginEngine::~PluginEngine() {}
+
+void
+PluginEngine::loadAllPlugins()
+{
+  qWarning() << "loadAllPlugins()";
+
+  VidaliaSettings settings;
+  QDir path = QDir(settings.pluginPath());
+
+  qWarning() << "PluginPath" << path.absolutePath();
+  
+  foreach(QString pdir, path.entryList(QDir::NoDotAndDotDot|QDir::AllDirs)) {
+    qWarning() << "pdir" << pdir;
+    QFileInfo finfo(QString("%1%2%3").arg(path.absolutePath()).arg(QDir::separator()).arg(pdir));
+
+    if(finfo.isDir()) {
+      tryLoadPlugin(finfo.filePath());
+    }
+  }
+}
+
+void
+PluginEngine::tryLoadPlugin(QDir path)
+{
+  qWarning() << "tryLoadPlugin()" << path.absolutePath();
+
+  QStringList files = path.entryList();
+
+  if(!files.contains("info.xml"))
+    return;
+  
+  PluginWrapper *wrapper = new PluginWrapper(QString("%1%2info.xml").arg(path.absolutePath()).arg(QDir::separator()), this);
+  wrappers << wrapper;
+}
+
+QList<QAction *>
+PluginEngine::getAllActions()
+{
+  QList<QAction *> actions;
+  foreach(PluginWrapper *wrapper, wrappers)
+    actions << wrapper->menuAction();
+
+  return actions;
+}
diff --git a/src/vidalia/plugin/PluginEngine.h b/src/vidalia/plugin/PluginEngine.h
new file mode 100644
index 0000000..09bff23
--- /dev/null
+++ b/src/vidalia/plugin/PluginEngine.h
@@ -0,0 +1,28 @@
+#ifndef PLUGINENGINE_H
+#define PLUGINENGINE_H
+
+#include <QtGui>
+#include <QtScript>
+
+#include "VidaliaTabPrototype.h"
+
+class PluginWrapper;
+
+class PluginEngine : public QScriptEngine {
+  Q_OBJECT
+
+  public:
+    PluginEngine(QObject *parent = 0);
+    ~PluginEngine();
+
+    QList<QAction *> getAllActions();
+
+  protected:
+    void loadAllPlugins();
+    void tryLoadPlugin(QDir path);
+
+    QList<PluginWrapper *> wrappers;
+};
+
+#endif
+
diff --git a/src/vidalia/plugin/PluginWrapper.cpp b/src/vidalia/plugin/PluginWrapper.cpp
new file mode 100644
index 0000000..b06b863
--- /dev/null
+++ b/src/vidalia/plugin/PluginWrapper.cpp
@@ -0,0 +1,157 @@
+#include "PluginWrapper.h"
+#include "PluginEngine.h"
+
+#include <QtXml>
+
+PluginWrapper::PluginWrapper(const QString &info_path, PluginEngine *engine, QObject *parent)
+  : QObject(parent), _engine(engine)
+{
+  _action = 0;
+  _gui = false;
+  _persistent = false;
+  processInfo(info_path);
+
+  foreach(QString path, _files) {
+    qWarning() << path;
+    QFile file(path);
+    if(file.open(QIODevice::ReadOnly)) {
+      _engine->evaluate(file.readAll());
+      qWarning() << "evaluated";
+    } else 
+      qWarning() << "Error opening file";
+  }
+}
+
+PluginWrapper::~PluginWrapper() {}
+
+void
+PluginWrapper::processInfo(const QString &path)
+{
+  qWarning() << "processInfo()";
+
+  QDomDocument info("Plugin Info");
+  QFile file(path);
+  if(!file.open(QIODevice::ReadOnly)) {
+    qWarning() << "Problem opening xml file";
+    return;
+  }
+
+  if(!info.setContent(&file)) {
+    qWarning() << "Problem setting contents";
+    file.close();
+    return;
+  }
+
+  QDomElement root = info.documentElement();
+
+  if(root.tagName() != "VidaliaPlugin") {
+    return;
+  }
+
+  QDomNode n = root.firstChild();
+  while(!n.isNull()) {
+    QDomElement e = n.toElement();
+    if(!e.isNull()) {
+      if(e.tagName() == "name")
+        _name = e.text();
+      else if(e.tagName() == "author")
+        _author = e.text();
+      else if(e.tagName() == "date")
+        _date = e.text();
+      else if(e.tagName() == "type") {
+        _persistent = (e.attribute("persistent", "false") == "true");
+        _gui = (e.attribute("gui", "false") == "true");
+      } else if(e.tagName() == "files") {
+        QDomNode froot = e.firstChild();
+        while(!froot.isNull()) {
+          QDomElement fe = froot.toElement();
+          if(fe.tagName() == "file")
+            _files << QString("%1%2%3").arg(QFileInfo(path).path()).arg(QDir::separator()).arg(fe.text());
+          froot = froot.nextSibling();
+        }
+      } else if(e.tagName() == "namespace") {
+        _nspace = e.text();
+      }
+    }
+    n = n.nextSibling();
+  }
+
+  file.close();
+}
+
+void
+PluginWrapper::start()
+{
+  _engine->evaluate(QString("%1.start()").arg(nspace()));
+}
+
+void
+PluginWrapper::stop()
+{
+  _engine->evaluate(QString("%1.stop()").arg(nspace()));
+}
+
+VidaliaTab *
+PluginWrapper::buildGUI()
+{
+  if(!hasGUI())
+    return NULL;
+  VidaliaTab *tab = qscriptvalue_cast<VidaliaTab *>(_engine->evaluate(QString("%1.buildGUI()").arg(nspace())));
+  if(_engine->hasUncaughtException()) {
+    qWarning() << "Exception:";
+    qWarning() << _engine->uncaughtExceptionLineNumber();
+  }
+  qWarning() << "Casted tab:" << tab << nspace();
+  return tab;
+}
+
+bool 
+PluginWrapper::hasGUI()
+{
+  return _gui;
+}
+
+bool 
+PluginWrapper::isPersistent()
+{
+  return _persistent;
+}
+
+QString 
+PluginWrapper::name() const
+{
+  return _name;
+}
+
+QString 
+PluginWrapper::date() const
+{
+  return _date;
+}
+
+QString 
+PluginWrapper::author() const
+{
+  return _author;
+}
+
+QString 
+PluginWrapper::nspace() const
+{
+  return _nspace;
+}
+
+QStringList 
+PluginWrapper::files() const
+{
+  return _files;
+}
+
+QAction *
+PluginWrapper::menuAction()
+{
+  if(hasGUI()) {
+    _action = new QAction(_name, this);
+  }
+  return _action;
+}
diff --git a/src/vidalia/plugin/PluginWrapper.h b/src/vidalia/plugin/PluginWrapper.h
new file mode 100644
index 0000000..1c7ad8f
--- /dev/null
+++ b/src/vidalia/plugin/PluginWrapper.h
@@ -0,0 +1,46 @@
+#ifndef PLUGINWRAPPER_H
+#define PLUGINWRAPPER_H
+
+#include <QtCore>
+
+#include "VidaliaTab.h"
+
+class PluginEngine;
+
+class PluginWrapper : public QObject {
+  Q_OBJECT
+
+  public:
+    PluginWrapper(const QString &info_path, PluginEngine *engine, QObject *parent = 0);
+    ~PluginWrapper();
+
+    bool hasGUI();
+    bool isPersistent();
+
+    QString name() const;
+    QString date() const;
+    QString author() const;
+    QString nspace() const;
+    QStringList files() const;
+
+    QAction *menuAction();
+
+  public slots:
+    void start();
+    void stop();
+    VidaliaTab *buildGUI();
+
+  protected:
+    void processInfo(const QString &path);
+
+    PluginEngine *_engine;
+    QString _name, _date, _author;
+    bool _persistent, _gui;
+    QStringList _files;
+    QString _nspace;
+
+    QAction *_action;
+};
+
+#endif
+
diff --git a/src/vidalia/plugin/prototypes/VidaliaTabPrototype.cpp b/src/vidalia/plugin/prototypes/VidaliaTabPrototype.cpp
new file mode 100644
index 0000000..58170ef
--- /dev/null
+++ b/src/vidalia/plugin/prototypes/VidaliaTabPrototype.cpp
@@ -0,0 +1,10 @@
+#include "VidaliaTabPrototype.h"
+
+VidaliaTabPrototype::VidaliaTabPrototype(QObject *parent)
+  : QObject(parent)
+{}
+
+QScriptValue VidaliaTabPrototype::constructor(QScriptContext *context, QScriptEngine *engine)
+{
+  return engine->newQObject(new VidaliaTab(QString("titulooo"), QString("nombreee")), QScriptEngine::ScriptOwnership);
+}
diff --git a/src/vidalia/plugin/prototypes/VidaliaTabPrototype.h b/src/vidalia/plugin/prototypes/VidaliaTabPrototype.h
new file mode 100644
index 0000000..dbd89cd
--- /dev/null
+++ b/src/vidalia/plugin/prototypes/VidaliaTabPrototype.h
@@ -0,0 +1,21 @@
+#ifndef VIDALIATABPROT_H
+#define VIDALIATABPROT_H
+
+#include <QtGui>
+#include <QtScript>
+
+#include "VidaliaTab.h"
+
+class VidaliaTabPrototype : public QObject, public QScriptable
+{
+  Q_OBJECT
+
+  public:
+    VidaliaTabPrototype(QObject *parent = 0);
+    static QScriptValue constructor(QScriptContext *context, QScriptEngine *engine);
+};
+
+Q_DECLARE_METATYPE(VidaliaTab *);
+
+#endif
+





More information about the tor-commits mailing list