changeset 1299:91670e4e76a6

Merge from branch plugin-path-config
author Chris Cannam
date Mon, 11 Jun 2018 14:40:36 +0100
parents 51e6125627fa (current diff) f24f64e441ac (diff)
children 1589bc7528b7
files
diffstat 6 files changed, 659 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/files.pri	Tue May 08 14:27:54 2018 +0100
+++ b/files.pri	Mon Jun 11 14:40:36 2018 +0100
@@ -74,6 +74,8 @@
            widgets/Panner.h \
            widgets/PluginParameterBox.h \
            widgets/PluginParameterDialog.h \
+           widgets/PluginPathConfigurator.h \
+           widgets/PluginReviewDialog.h \
            widgets/ProgressDialog.h \
            widgets/PropertyBox.h \
            widgets/PropertyStack.h \
@@ -156,6 +158,8 @@
            widgets/Panner.cpp \
            widgets/PluginParameterBox.cpp \
            widgets/PluginParameterDialog.cpp \
+           widgets/PluginPathConfigurator.cpp \
+           widgets/PluginReviewDialog.cpp \
            widgets/ProgressDialog.cpp \
            widgets/PropertyBox.cpp \
            widgets/PropertyStack.cpp \
--- a/widgets/InteractiveFileFinder.cpp	Tue May 08 14:27:54 2018 +0100
+++ b/widgets/InteractiveFileFinder.cpp	Mon Jun 11 14:40:36 2018 +0100
@@ -19,6 +19,7 @@
 #include "data/fileio/DataFileReaderFactory.h"
 #include "rdf/RDFImporter.h"
 #include "rdf/RDFExporter.h"
+#include "system/System.h"
 
 #include <QFileInfo>
 #include <QMessageBox>
@@ -156,9 +157,12 @@
     };
 
     if (lastPath == "") {
-        char *home = getenv("HOME");
-        if (home) lastPath = home;
-        else lastPath = ".";
+        std::string home;
+        if (getEnvUtf8("HOME", home)) {
+            lastPath = QString::fromStdString(home);
+        } else {
+            lastPath = ".";
+        }
     } else if (QFileInfo(lastPath).isDir()) {
         lastPath = QFileInfo(lastPath).canonicalPath();
     } else {
@@ -305,9 +309,12 @@
     };
 
     if (lastPath == "") {
-        char *home = getenv("HOME");
-        if (home) lastPath = home;
-        else lastPath = ".";
+        std::string home;
+        if (getEnvUtf8("HOME", home)) {
+            lastPath = QString::fromStdString(home);
+        } else {
+            lastPath = ".";
+        }
     } else if (QFileInfo(lastPath).isDir()) {
         lastPath = QFileInfo(lastPath).canonicalPath();
     } else {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/PluginPathConfigurator.cpp	Mon Jun 11 14:40:36 2018 +0100
@@ -0,0 +1,384 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "PluginPathConfigurator.h"
+#include "PluginReviewDialog.h"
+
+#include <QPushButton>
+#include <QListWidget>
+#include <QGridLayout>
+#include <QComboBox>
+#include <QLabel>
+#include <QCheckBox>
+#include <QFileDialog>
+
+#include "IconLoader.h"
+#include "WidgetScale.h"
+
+#include "plugin/PluginPathSetter.h"
+
+PluginPathConfigurator::PluginPathConfigurator(QWidget *parent) :
+    QFrame(parent)
+{
+    m_layout = new QGridLayout;
+    setLayout(m_layout);
+
+    QHBoxLayout *buttons = new QHBoxLayout;
+
+    m_down = new QPushButton;
+    m_down->setIcon(IconLoader().load("down"));
+    m_down->setToolTip(tr("Move the selected location later in the list"));
+    connect(m_down, SIGNAL(clicked()), this, SLOT(downClicked()));
+    buttons->addWidget(m_down);
+
+    m_up = new QPushButton;
+    m_up->setIcon(IconLoader().load("up"));
+    m_up->setToolTip(tr("Move the selected location earlier in the list"));
+    connect(m_up, SIGNAL(clicked()), this, SLOT(upClicked()));
+    buttons->addWidget(m_up);
+
+    m_add = new QPushButton;
+    m_add->setIcon(IconLoader().load("plus"));
+    m_add->setToolTip(tr("Add a new location to the list"));
+    connect(m_add, SIGNAL(clicked()), this, SLOT(addClicked()));
+    buttons->addWidget(m_add);
+    
+    m_delete = new QPushButton;
+    m_delete->setIcon(IconLoader().load("datadelete"));
+    m_delete->setToolTip(tr("Remove the selected location from the list"));
+    connect(m_delete, SIGNAL(clicked()), this, SLOT(deleteClicked()));
+    buttons->addWidget(m_delete);
+
+    m_reset = new QPushButton;
+    m_reset->setText(tr("Reset to Default"));
+    m_reset->setToolTip(tr("Reset the list for this plugin type to its default"));
+    connect(m_reset, SIGNAL(clicked()), this, SLOT(resetClicked()));
+    buttons->addWidget(m_reset);
+
+    buttons->addStretch(50);
+
+    m_seePlugins = new QPushButton;
+    m_seePlugins->setText(tr("Review plugins..."));
+    connect(m_seePlugins, SIGNAL(clicked()), this, SLOT(seePluginsClicked()));
+    buttons->addWidget(m_seePlugins);
+
+    int row = 0;
+    
+    m_header = new QLabel;
+    m_header->setText(tr("Plugin locations for plugin type:"));
+    m_layout->addWidget(m_header, row, 0);
+
+    m_pluginTypeSelector = new QComboBox;
+    m_layout->addWidget(m_pluginTypeSelector, row, 1, Qt::AlignLeft);
+    connect(m_pluginTypeSelector, SIGNAL(currentTextChanged(QString)),
+            this, SLOT(currentTypeChanged(QString)));
+
+    m_layout->setColumnStretch(1, 10);
+    ++row;
+    
+    m_list = new QListWidget;
+    m_layout->addWidget(m_list, row, 0, 1, 3);
+    m_layout->setRowStretch(row, 20);
+    connect(m_list, SIGNAL(currentRowChanged(int)),
+            this, SLOT(currentLocationChanged(int)));
+    ++row;
+
+    m_layout->addLayout(buttons, row, 0, 1, 3);
+    
+    ++row;
+
+    m_envOverride = new QCheckBox;
+    connect(m_envOverride, SIGNAL(stateChanged(int)),
+            this, SLOT(envOverrideChanged(int)));
+    m_layout->addWidget(m_envOverride, row, 0, 1, 3);
+    ++row;
+}
+
+PluginPathConfigurator::~PluginPathConfigurator()
+{
+}
+
+QString
+PluginPathConfigurator::getLabelFor(PluginPathSetter::TypeKey key)
+{
+    if (key.second == KnownPlugins::FormatNative) {
+        switch (key.first) {
+        case KnownPlugins::VampPlugin:
+            return tr("Vamp");
+        case KnownPlugins::LADSPAPlugin:
+            return tr("LADSPA");
+        case KnownPlugins::DSSIPlugin:
+            return tr("DSSI");
+        }
+    } else if (key.second == KnownPlugins::FormatNonNative32Bit) {
+        switch (key.first) {
+        case KnownPlugins::VampPlugin:
+            return tr("Vamp (32-bit)");
+        case KnownPlugins::LADSPAPlugin:
+            return tr("LADSPA (32-bit)");
+        case KnownPlugins::DSSIPlugin:
+            return tr("DSSI (32-bit)");
+        }
+    }
+    SVCERR << "PluginPathConfigurator::getLabelFor: WARNING: "
+           << "Unknown format value " << key.second << endl;
+    return "<unknown>";
+}
+
+PluginPathSetter::TypeKey
+PluginPathConfigurator::getKeyForLabel(QString label)
+{
+    for (const auto &p: m_paths) {
+        auto key = p.first;
+        if (getLabelFor(key) == label) {
+            return key;
+        }
+    }
+    SVCERR << "PluginPathConfigurator::getKeyForLabel: WARNING: "
+           << "Unrecognised label \"" << label << "\"" << endl;
+    return { KnownPlugins::VampPlugin, KnownPlugins::FormatNative };
+}
+
+void
+PluginPathConfigurator::setPaths(PluginPathSetter::Paths paths)
+{
+    m_paths = paths;
+
+    m_defaultPaths = PluginPathSetter::getDefaultPaths();
+
+    m_pluginTypeSelector->clear();
+    for (const auto &p: paths) {
+        m_pluginTypeSelector->addItem(getLabelFor(p.first));
+    }
+    
+    populate();
+}
+
+void
+PluginPathConfigurator::populate()
+{
+    m_list->clear();
+
+    if (m_paths.empty()) return;
+
+    populateFor(m_paths.begin()->first, -1);
+}
+
+void
+PluginPathConfigurator::populateFor(PluginPathSetter::TypeKey key,
+                                    int makeCurrent)
+{
+    QString envVariable = m_paths.at(key).envVariable;
+    bool useEnvVariable = m_paths.at(key).useEnvVariable;
+    QString envVarValue =
+        PluginPathSetter::getOriginalEnvironmentValue(envVariable);
+    QString currentValueRubric;
+    if (envVarValue == QString()) {
+        currentValueRubric = tr("(Variable is currently unset)");
+    } else {
+        if (envVarValue.length() > 100) {
+            QString envVarStart = envVarValue.left(95);
+            currentValueRubric = tr("(Current value begins: \"%1 ...\")")
+                .arg(envVarStart);
+        } else {
+            currentValueRubric = tr("(Currently set to: \"%1\")")
+                .arg(envVarValue);
+        }
+    }        
+    m_envOverride->setText
+        (tr("Allow the %1 environment variable to take priority over this\n%2")
+         .arg(envVariable)
+         .arg(currentValueRubric));
+    m_envOverride->setCheckState(useEnvVariable ? Qt::Checked : Qt::Unchecked);
+
+    m_list->clear();
+
+    for (int i = 0; i < m_pluginTypeSelector->count(); ++i) {
+        if (getLabelFor(key) == m_pluginTypeSelector->itemText(i)) {
+            m_pluginTypeSelector->blockSignals(true);
+            m_pluginTypeSelector->setCurrentIndex(i);
+            m_pluginTypeSelector->blockSignals(false);
+        }
+    }
+    
+    QStringList path = m_paths.at(key).directories;
+    
+    for (int i = 0; i < path.size(); ++i) {
+        m_list->addItem(path[i]);
+    }
+
+    if (makeCurrent < path.size()) {
+        m_list->setCurrentRow(makeCurrent);
+        currentLocationChanged(makeCurrent);
+    }
+}
+
+void
+PluginPathConfigurator::currentLocationChanged(int i)
+{
+    QString label = m_pluginTypeSelector->currentText();
+    PluginPathSetter::TypeKey key = getKeyForLabel(label);
+    QStringList path = m_paths.at(key).directories;
+    m_up->setEnabled(i > 0);
+    m_down->setEnabled(i >= 0 && i + 1 < path.size());
+    m_delete->setEnabled(i >= 0 && i < path.size());
+    m_reset->setEnabled(path != m_defaultPaths.at(key).directories);
+}
+
+void
+PluginPathConfigurator::currentTypeChanged(QString label)
+{
+    populateFor(getKeyForLabel(label), -1);
+}
+
+void
+PluginPathConfigurator::envOverrideChanged(int state)
+{
+    bool useEnvVariable = (state == Qt::Checked);
+    
+    QString label = m_pluginTypeSelector->currentText();
+    PluginPathSetter::TypeKey key = getKeyForLabel(label);
+
+    auto newEntry = m_paths.at(key);
+    newEntry.useEnvVariable = useEnvVariable;
+    m_paths[key] = newEntry;
+
+    emit pathsChanged();
+}
+
+void
+PluginPathConfigurator::upClicked()
+{
+    QString label = m_pluginTypeSelector->currentText();
+    PluginPathSetter::TypeKey key = getKeyForLabel(label);
+    QStringList path = m_paths.at(key).directories;
+        
+    int current = m_list->currentRow();
+    if (current <= 0) return;
+    
+    QStringList newPath;
+    for (int i = 0; i < path.size(); ++i) {
+        if (i + 1 == current) {
+            newPath.push_back(path[i+1]);
+            newPath.push_back(path[i]);
+            ++i;
+        } else {
+            newPath.push_back(path[i]);
+        }
+    }
+
+    auto newEntry = m_paths.at(key);
+    newEntry.directories = newPath;
+    m_paths[key] = newEntry;
+    
+    populateFor(key, current - 1);
+
+    emit pathsChanged();
+}
+
+void
+PluginPathConfigurator::downClicked()
+{
+    QString label = m_pluginTypeSelector->currentText();
+    PluginPathSetter::TypeKey key = getKeyForLabel(label);
+    QStringList path = m_paths.at(key).directories;
+
+    int current = m_list->currentRow();
+    if (current < 0 || current + 1 >= path.size()) return;
+
+    QStringList newPath;
+    for (int i = 0; i < path.size(); ++i) {
+        if (i == current) {
+            newPath.push_back(path[i+1]);
+            newPath.push_back(path[i]);
+            ++i;
+        } else {
+            newPath.push_back(path[i]);
+        }
+    }
+
+    auto newEntry = m_paths.at(key);
+    newEntry.directories = newPath;
+    m_paths[key] = newEntry;
+    
+    populateFor(key, current + 1);
+
+    emit pathsChanged();
+}
+
+void
+PluginPathConfigurator::addClicked()
+{
+    QString label = m_pluginTypeSelector->currentText();
+    PluginPathSetter::TypeKey key = getKeyForLabel(label);
+
+    QString newDir = QFileDialog::getExistingDirectory
+        (this, tr("Choose directory to add"));
+
+    if (newDir == QString()) return;
+
+    auto newEntry = m_paths.at(key);
+    newEntry.directories.push_back(newDir);
+    m_paths[key] = newEntry;
+    
+    populateFor(key, newEntry.directories.size() - 1);
+
+    emit pathsChanged();
+}
+
+void
+PluginPathConfigurator::deleteClicked()
+{
+    QString label = m_pluginTypeSelector->currentText();
+    PluginPathSetter::TypeKey key = getKeyForLabel(label);
+    QStringList path = m_paths.at(key).directories;
+    
+    int current = m_list->currentRow();
+    if (current < 0) return;
+
+    QStringList newPath;
+    for (int i = 0; i < path.size(); ++i) {
+        if (i != current) {
+            newPath.push_back(path[i]);
+        }
+    }
+
+    auto newEntry = m_paths.at(key);
+    newEntry.directories = newPath;
+    m_paths[key] = newEntry;
+    
+    populateFor(key, current < newPath.size() ? current : current-1);
+
+    emit pathsChanged();
+}
+
+void
+PluginPathConfigurator::resetClicked()
+{
+    QString label = m_pluginTypeSelector->currentText();
+    PluginPathSetter::TypeKey key = getKeyForLabel(label);
+    m_paths[key] = m_defaultPaths[key];
+    populateFor(key, -1);
+
+    emit pathsChanged();
+}
+
+void
+PluginPathConfigurator::seePluginsClicked()
+{
+    PluginReviewDialog dialog(this);
+    dialog.populate();
+    dialog.exec();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/PluginPathConfigurator.h	Mon Jun 11 14:40:36 2018 +0100
@@ -0,0 +1,81 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef SV_PLUGIN_PATH_CONFIGURATOR_H
+#define SV_PLUGIN_PATH_CONFIGURATOR_H
+
+#include <QFrame>
+#include <QStringList>
+
+class QLabel;
+class QWidget;
+class QListWidget;
+class QPushButton;
+class QGridLayout;
+class QComboBox;
+class QCheckBox;
+
+#include "plugin/PluginPathSetter.h"
+
+class PluginPathConfigurator : public QFrame
+{
+    Q_OBJECT
+
+public:
+    PluginPathConfigurator(QWidget *parent = 0);
+    ~PluginPathConfigurator();
+
+    void setPaths(PluginPathSetter::Paths paths);
+    PluginPathSetter::Paths getPaths() const { return m_paths; }
+
+signals:
+    void pathsChanged();
+
+private slots:
+    void upClicked();
+    void downClicked();
+    void addClicked();
+    void deleteClicked();
+    void resetClicked();
+    void currentTypeChanged(QString);
+    void currentLocationChanged(int);
+    void envOverrideChanged(int);
+    void seePluginsClicked();
+    
+private:
+    QGridLayout *m_layout;
+    QLabel *m_header;
+    QComboBox *m_pluginTypeSelector;
+    QListWidget *m_list;
+    QPushButton *m_seePlugins;
+    QPushButton *m_up;
+    QPushButton *m_down;
+    QPushButton *m_add;
+    QPushButton *m_delete;
+    QPushButton *m_reset;
+    QCheckBox *m_envOverride;
+
+    PluginPathSetter::Paths m_paths;
+    PluginPathSetter::Paths m_defaultPaths;
+    
+    void populate();
+    void populateFor(PluginPathSetter::TypeKey, int makeCurrent);
+
+    QString getLabelFor(PluginPathSetter::TypeKey);
+    PluginPathSetter::TypeKey getKeyForLabel(QString label);
+};
+
+#endif
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/PluginReviewDialog.cpp	Mon Jun 11 14:40:36 2018 +0100
@@ -0,0 +1,140 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "PluginReviewDialog.h"
+
+#include <QGridLayout>
+#include <QTableWidget>
+#include <QDialogButtonBox>
+#include <QFileInfo>
+#include <QHeaderView>
+#include <QDesktopWidget>
+#include <QApplication>
+
+#include "plugin/FeatureExtractionPluginFactory.h"
+#include "plugin/RealTimePluginFactory.h"
+
+PluginReviewDialog::PluginReviewDialog(QWidget *parent) :
+    QDialog(parent)
+{
+    setWindowTitle(tr("Plugins Loaded"));
+
+    QGridLayout *layout = new QGridLayout;
+    setLayout(layout);
+
+    m_table = new QTableWidget;
+    layout->addWidget(m_table, 0, 1);
+    
+    QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Close);
+    layout->addWidget(bb, 1, 1);
+    connect(bb, SIGNAL(rejected()), this, SLOT(close()));
+}
+
+PluginReviewDialog::~PluginReviewDialog()
+{
+}
+
+void
+PluginReviewDialog::populate()
+{
+    FeatureExtractionPluginFactory *feFactory =
+        FeatureExtractionPluginFactory::instance();
+    QString err;
+    std::vector<QString> feIds = feFactory->getPluginIdentifiers(err);
+
+    RealTimePluginFactory *dssiFactory =
+        RealTimePluginFactory::instance("dssi");
+    std::vector<QString> dssiIds = dssiFactory->getPluginIdentifiers();
+
+    RealTimePluginFactory *ladspaFactory =
+        RealTimePluginFactory::instance("ladspa");
+    std::vector<QString> ladspaIds = ladspaFactory->getPluginIdentifiers();
+
+    m_table->setRowCount(int(feIds.size() + dssiIds.size() + ladspaIds.size()));
+    m_table->setColumnCount(5);
+
+    QStringList headers;
+    int typeCol = 0, libCol = 1, idCol = 2, dirCol = 3, nameCol = 4;
+    headers << tr("Type") << tr("Library")
+            << tr("Identifier") << tr("Found in") << tr("Name");
+    m_table->setHorizontalHeaderLabels(headers);
+
+    int row = 0;
+
+    for (QString id: feIds) {
+        auto staticData = feFactory->getPluginStaticData(id);
+        m_table->setItem(row, typeCol, new QTableWidgetItem
+                         (tr("Vamp")));
+        m_table->setItem(row, idCol, new QTableWidgetItem
+                         (QString::fromStdString(staticData.basic.identifier)));
+        m_table->setItem(row, nameCol, new QTableWidgetItem
+                         (QString::fromStdString(staticData.basic.name)));
+        QString path = feFactory->getPluginLibraryPath(id);
+        m_table->setItem(row, libCol, new QTableWidgetItem
+                         (QFileInfo(path).fileName()));
+        m_table->setItem(row, dirCol, new QTableWidgetItem
+                         (QFileInfo(path).path()));
+        row++;
+    }
+
+    for (QString id: dssiIds) {
+        auto descriptor = dssiFactory->getPluginDescriptor(id);
+        if (!descriptor) continue;
+        m_table->setItem(row, typeCol, new QTableWidgetItem
+                         (tr("DSSI")));
+        m_table->setItem(row, idCol, new QTableWidgetItem
+                         (QString::fromStdString(descriptor->label)));
+        m_table->setItem(row, nameCol, new QTableWidgetItem
+                         (QString::fromStdString(descriptor->name)));
+        QString path = dssiFactory->getPluginLibraryPath(id);
+        m_table->setItem(row, libCol, new QTableWidgetItem
+                         (QFileInfo(path).fileName()));
+        m_table->setItem(row, dirCol, new QTableWidgetItem
+                         (QFileInfo(path).path()));
+        row++;
+    }
+
+    for (QString id: ladspaIds) {
+        auto descriptor = ladspaFactory->getPluginDescriptor(id);
+        if (!descriptor) continue;
+        m_table->setItem(row, typeCol, new QTableWidgetItem
+                         (tr("LADSPA")));
+        m_table->setItem(row, idCol, new QTableWidgetItem
+                         (QString::fromStdString(descriptor->label)));
+        m_table->setItem(row, nameCol, new QTableWidgetItem
+                         (QString::fromStdString(descriptor->name)));
+        QString path = ladspaFactory->getPluginLibraryPath(id);
+        m_table->setItem(row, libCol, new QTableWidgetItem
+                         (QFileInfo(path).fileName()));
+        m_table->setItem(row, dirCol, new QTableWidgetItem
+                         (QFileInfo(path).path()));
+        row++;
+    }
+
+    m_table->setSortingEnabled(true);
+    m_table->setSelectionMode(QAbstractItemView::NoSelection);
+    m_table->resizeColumnsToContents();
+
+    int twidth = m_table->horizontalHeader()->length();
+    int theight = m_table->verticalHeader()->length();
+    
+    QDesktopWidget *desktop = QApplication::desktop();
+    QRect available = desktop->availableGeometry();
+
+    int width = std::min(twidth + 30, (available.width() * 3) / 4);
+    int height = std::min(theight + 30, (available.height() * 3) / 4);
+
+    resize(width, height);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/PluginReviewDialog.h	Mon Jun 11 14:40:36 2018 +0100
@@ -0,0 +1,37 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef SV_PLUGIN_REVIEW_DIALOG_H
+#define SV_PLUGIN_REVIEW_DIALOG_H
+
+#include <QDialog>
+#include <QTableWidget>
+
+class QEvent;
+
+class PluginReviewDialog : public QDialog
+{
+    Q_OBJECT
+    
+public:
+    PluginReviewDialog(QWidget *parent = 0);
+    ~PluginReviewDialog();
+
+    void populate();
+    
+private:
+    QTableWidget *m_table;
+};
+
+#endif