commit 92f39c6d275d07b9a49fc1a412c6482551190831 Author: Tomas Touceda chiiph@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 +
tor-commits@lists.torproject.org