Chris@1285: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@1285: 
Chris@1285: /*
Chris@1285:     Sonic Visualiser
Chris@1285:     An audio file viewer and annotation editor.
Chris@1285:     Centre for Digital Music, Queen Mary, University of London.
Chris@1285:     
Chris@1285:     This program is free software; you can redistribute it and/or
Chris@1285:     modify it under the terms of the GNU General Public License as
Chris@1285:     published by the Free Software Foundation; either version 2 of the
Chris@1285:     License, or (at your option) any later version.  See the file
Chris@1285:     COPYING included with this distribution for more information.
Chris@1285: */
Chris@1285: 
Chris@1285: #include "PluginPathConfigurator.h"
Chris@1292: #include "PluginReviewDialog.h"
Chris@1285: 
Chris@1285: #include <QPushButton>
Chris@1287: #include <QListWidget>
Chris@1288: #include <QGridLayout>
Chris@1289: #include <QComboBox>
Chris@1285: #include <QLabel>
Chris@1290: #include <QCheckBox>
Chris@1291: #include <QFileDialog>
Chris@1285: 
Chris@1285: #include "IconLoader.h"
Chris@1285: #include "WidgetScale.h"
Chris@1285: 
Chris@1293: #include "plugin/PluginPathSetter.h"
Chris@1293: 
Chris@1285: PluginPathConfigurator::PluginPathConfigurator(QWidget *parent) :
Chris@1287:     QFrame(parent)
Chris@1285: {
Chris@1285:     m_layout = new QGridLayout;
Chris@1285:     setLayout(m_layout);
Chris@1292: 
Chris@1291:     QHBoxLayout *buttons = new QHBoxLayout;
Chris@1291: 
Chris@1291:     m_down = new QPushButton;
Chris@1291:     m_down->setIcon(IconLoader().load("down"));
Chris@1291:     m_down->setToolTip(tr("Move the selected location later in the list"));
Chris@1291:     connect(m_down, SIGNAL(clicked()), this, SLOT(downClicked()));
Chris@1291:     buttons->addWidget(m_down);
Chris@1291: 
Chris@1291:     m_up = new QPushButton;
Chris@1291:     m_up->setIcon(IconLoader().load("up"));
Chris@1291:     m_up->setToolTip(tr("Move the selected location earlier in the list"));
Chris@1291:     connect(m_up, SIGNAL(clicked()), this, SLOT(upClicked()));
Chris@1291:     buttons->addWidget(m_up);
Chris@1291: 
Chris@1291:     m_add = new QPushButton;
Chris@1291:     m_add->setIcon(IconLoader().load("plus"));
Chris@1295:     m_add->setToolTip(tr("Add a new location to the list"));
Chris@1291:     connect(m_add, SIGNAL(clicked()), this, SLOT(addClicked()));
Chris@1291:     buttons->addWidget(m_add);
Chris@1291:     
Chris@1291:     m_delete = new QPushButton;
Chris@1291:     m_delete->setIcon(IconLoader().load("datadelete"));
Chris@1291:     m_delete->setToolTip(tr("Remove the selected location from the list"));
Chris@1291:     connect(m_delete, SIGNAL(clicked()), this, SLOT(deleteClicked()));
Chris@1291:     buttons->addWidget(m_delete);
Chris@1291: 
Chris@1291:     m_reset = new QPushButton;
Chris@1293:     m_reset->setText(tr("Reset to Default"));
Chris@1291:     m_reset->setToolTip(tr("Reset the list for this plugin type to its default"));
Chris@1291:     connect(m_reset, SIGNAL(clicked()), this, SLOT(resetClicked()));
Chris@1291:     buttons->addWidget(m_reset);
Chris@1287: 
Chris@1297:     buttons->addStretch(50);
Chris@1297: 
Chris@1297:     m_seePlugins = new QPushButton;
Chris@1297:     m_seePlugins->setText(tr("Review plugins..."));
Chris@1297:     connect(m_seePlugins, SIGNAL(clicked()), this, SLOT(seePluginsClicked()));
Chris@1297:     buttons->addWidget(m_seePlugins);
Chris@1297: 
Chris@1287:     int row = 0;
Chris@1287:     
Chris@1289:     m_header = new QLabel;
Chris@1290:     m_header->setText(tr("Plugin locations for plugin type:"));
Chris@1289:     m_layout->addWidget(m_header, row, 0);
Chris@1289: 
Chris@1289:     m_pluginTypeSelector = new QComboBox;
Chris@1290:     m_layout->addWidget(m_pluginTypeSelector, row, 1, Qt::AlignLeft);
Chris@1289:     connect(m_pluginTypeSelector, SIGNAL(currentTextChanged(QString)),
Chris@1289:             this, SLOT(currentTypeChanged(QString)));
Chris@1289: 
Chris@1290:     m_layout->setColumnStretch(1, 10);
Chris@1287:     ++row;
Chris@1287:     
Chris@1287:     m_list = new QListWidget;
Chris@1290:     m_layout->addWidget(m_list, row, 0, 1, 3);
Chris@1290:     m_layout->setRowStretch(row, 20);
Chris@1288:     connect(m_list, SIGNAL(currentRowChanged(int)),
Chris@1288:             this, SLOT(currentLocationChanged(int)));
Chris@1287:     ++row;
Chris@1290: 
Chris@1297:     m_layout->addLayout(buttons, row, 0, 1, 3);
Chris@1292:     
Chris@1287:     ++row;
Chris@1291: 
Chris@1290:     m_envOverride = new QCheckBox;
Chris@1290:     connect(m_envOverride, SIGNAL(stateChanged(int)),
Chris@1290:             this, SLOT(envOverrideChanged(int)));
Chris@1290:     m_layout->addWidget(m_envOverride, row, 0, 1, 3);
Chris@1291:     ++row;
Chris@1285: }
Chris@1285: 
Chris@1285: PluginPathConfigurator::~PluginPathConfigurator()
Chris@1285: {
Chris@1285: }
Chris@1285: 
Chris@1296: QString
Chris@1296: PluginPathConfigurator::getLabelFor(PluginPathSetter::TypeKey key)
Chris@1296: {
Chris@1296:     if (key.second == KnownPlugins::FormatNative) {
Chris@1296:         switch (key.first) {
Chris@1296:         case KnownPlugins::VampPlugin:
Chris@1296:             return tr("Vamp");
Chris@1296:         case KnownPlugins::LADSPAPlugin:
Chris@1296:             return tr("LADSPA");
Chris@1296:         case KnownPlugins::DSSIPlugin:
Chris@1296:             return tr("DSSI");
Chris@1296:         }
Chris@1296:     } else if (key.second == KnownPlugins::FormatNonNative32Bit) {
Chris@1296:         switch (key.first) {
Chris@1296:         case KnownPlugins::VampPlugin:
Chris@1296:             return tr("Vamp (32-bit)");
Chris@1296:         case KnownPlugins::LADSPAPlugin:
Chris@1296:             return tr("LADSPA (32-bit)");
Chris@1296:         case KnownPlugins::DSSIPlugin:
Chris@1296:             return tr("DSSI (32-bit)");
Chris@1296:         }
Chris@1296:     }
Chris@1298:     SVCERR << "PluginPathConfigurator::getLabelFor: WARNING: "
Chris@1298:            << "Unknown format value " << key.second << endl;
Chris@1298:     return "<unknown>";
Chris@1296: }
Chris@1296: 
Chris@1296: PluginPathSetter::TypeKey
Chris@1296: PluginPathConfigurator::getKeyForLabel(QString label)
Chris@1296: {
Chris@1296:     for (const auto &p: m_paths) {
Chris@1296:         auto key = p.first;
Chris@1296:         if (getLabelFor(key) == label) {
Chris@1296:             return key;
Chris@1296:         }
Chris@1296:     }
Chris@1296:     SVCERR << "PluginPathConfigurator::getKeyForLabel: WARNING: "
Chris@1296:            << "Unrecognised label \"" << label << "\"" << endl;
Chris@1296:     return { KnownPlugins::VampPlugin, KnownPlugins::FormatNative };
Chris@1296: }
Chris@1296: 
Chris@1285: void
Chris@1293: PluginPathConfigurator::setPaths(PluginPathSetter::Paths paths)
Chris@1285: {
Chris@1289:     m_paths = paths;
Chris@1293: 
Chris@1293:     m_defaultPaths = PluginPathSetter::getDefaultPaths();
Chris@1289: 
Chris@1289:     m_pluginTypeSelector->clear();
Chris@1289:     for (const auto &p: paths) {
Chris@1296:         m_pluginTypeSelector->addItem(getLabelFor(p.first));
Chris@1289:     }
Chris@1289:     
Chris@1285:     populate();
Chris@1285: }
Chris@1285: 
Chris@1285: void
Chris@1289: PluginPathConfigurator::populate()
Chris@1285: {
Chris@1287:     m_list->clear();
Chris@1286: 
Chris@1289:     if (m_paths.empty()) return;
Chris@1289: 
Chris@1296:     populateFor(m_paths.begin()->first, -1);
Chris@1289: }
Chris@1289: 
Chris@1289: void
Chris@1296: PluginPathConfigurator::populateFor(PluginPathSetter::TypeKey key,
Chris@1296:                                     int makeCurrent)
Chris@1289: {
Chris@1296:     QString envVariable = m_paths.at(key).envVariable;
Chris@1296:     bool useEnvVariable = m_paths.at(key).useEnvVariable;
Chris@1294:     QString envVarValue =
Chris@1294:         PluginPathSetter::getOriginalEnvironmentValue(envVariable);
Chris@1294:     QString currentValueRubric;
Chris@1294:     if (envVarValue == QString()) {
Chris@1294:         currentValueRubric = tr("(Variable is currently unset)");
Chris@1294:     } else {
Chris@1294:         if (envVarValue.length() > 100) {
Chris@1294:             QString envVarStart = envVarValue.left(95);
Chris@1294:             currentValueRubric = tr("(Current value begins: \"%1 ...\")")
Chris@1294:                 .arg(envVarStart);
Chris@1294:         } else {
Chris@1294:             currentValueRubric = tr("(Currently set to: \"%1\")")
Chris@1294:                 .arg(envVarValue);
Chris@1294:         }
Chris@1294:     }        
Chris@1290:     m_envOverride->setText
Chris@1294:         (tr("Allow the %1 environment variable to take priority over this\n%2")
Chris@1294:          .arg(envVariable)
Chris@1294:          .arg(currentValueRubric));
Chris@1290:     m_envOverride->setCheckState(useEnvVariable ? Qt::Checked : Qt::Unchecked);
Chris@1294: 
Chris@1289:     m_list->clear();
Chris@1289: 
Chris@1289:     for (int i = 0; i < m_pluginTypeSelector->count(); ++i) {
Chris@1296:         if (getLabelFor(key) == m_pluginTypeSelector->itemText(i)) {
Chris@1294:             m_pluginTypeSelector->blockSignals(true);
Chris@1289:             m_pluginTypeSelector->setCurrentIndex(i);
Chris@1294:             m_pluginTypeSelector->blockSignals(false);
Chris@1289:         }
Chris@1289:     }
Chris@1289:     
Chris@1296:     QStringList path = m_paths.at(key).directories;
Chris@1289:     
Chris@1289:     for (int i = 0; i < path.size(); ++i) {
Chris@1289:         m_list->addItem(path[i]);
Chris@1287:     }
Chris@1285: 
Chris@1290:     if (makeCurrent < path.size()) {
Chris@1287:         m_list->setCurrentRow(makeCurrent);
Chris@1290:         currentLocationChanged(makeCurrent);
Chris@1286:     }
Chris@1285: }
Chris@1285: 
Chris@1285: void
Chris@1288: PluginPathConfigurator::currentLocationChanged(int i)
Chris@1288: {
Chris@1296:     QString label = m_pluginTypeSelector->currentText();
Chris@1296:     PluginPathSetter::TypeKey key = getKeyForLabel(label);
Chris@1296:     QStringList path = m_paths.at(key).directories;
Chris@1288:     m_up->setEnabled(i > 0);
Chris@1290:     m_down->setEnabled(i >= 0 && i + 1 < path.size());
Chris@1290:     m_delete->setEnabled(i >= 0 && i < path.size());
Chris@1296:     m_reset->setEnabled(path != m_defaultPaths.at(key).directories);
Chris@1289: }
Chris@1289: 
Chris@1289: void
Chris@1296: PluginPathConfigurator::currentTypeChanged(QString label)
Chris@1289: {
Chris@1296:     populateFor(getKeyForLabel(label), -1);
Chris@1290: }
Chris@1290: 
Chris@1290: void
Chris@1292: PluginPathConfigurator::envOverrideChanged(int state)
Chris@1290: {
Chris@1292:     bool useEnvVariable = (state == Qt::Checked);
Chris@1292:     
Chris@1296:     QString label = m_pluginTypeSelector->currentText();
Chris@1296:     PluginPathSetter::TypeKey key = getKeyForLabel(label);
Chris@1292: 
Chris@1296:     auto newEntry = m_paths.at(key);
Chris@1292:     newEntry.useEnvVariable = useEnvVariable;
Chris@1296:     m_paths[key] = newEntry;
Chris@1292: 
Chris@1293:     emit pathsChanged();
Chris@1288: }
Chris@1288: 
Chris@1288: void
Chris@1285: PluginPathConfigurator::upClicked()
Chris@1285: {
Chris@1296:     QString label = m_pluginTypeSelector->currentText();
Chris@1296:     PluginPathSetter::TypeKey key = getKeyForLabel(label);
Chris@1296:     QStringList path = m_paths.at(key).directories;
Chris@1289:         
Chris@1287:     int current = m_list->currentRow();
Chris@1287:     if (current <= 0) return;
Chris@1287:     
Chris@1286:     QStringList newPath;
Chris@1289:     for (int i = 0; i < path.size(); ++i) {
Chris@1287:         if (i + 1 == current) {
Chris@1289:             newPath.push_back(path[i+1]);
Chris@1289:             newPath.push_back(path[i]);
Chris@1286:             ++i;
Chris@1286:         } else {
Chris@1289:             newPath.push_back(path[i]);
Chris@1286:         }
Chris@1286:     }
Chris@1290: 
Chris@1296:     auto newEntry = m_paths.at(key);
Chris@1290:     newEntry.directories = newPath;
Chris@1296:     m_paths[key] = newEntry;
Chris@1287:     
Chris@1296:     populateFor(key, current - 1);
Chris@1292: 
Chris@1293:     emit pathsChanged();
Chris@1285: }
Chris@1285: 
Chris@1285: void
Chris@1285: PluginPathConfigurator::downClicked()
Chris@1285: {
Chris@1296:     QString label = m_pluginTypeSelector->currentText();
Chris@1296:     PluginPathSetter::TypeKey key = getKeyForLabel(label);
Chris@1296:     QStringList path = m_paths.at(key).directories;
Chris@1289: 
Chris@1287:     int current = m_list->currentRow();
Chris@1289:     if (current < 0 || current + 1 >= path.size()) return;
Chris@1289: 
Chris@1286:     QStringList newPath;
Chris@1289:     for (int i = 0; i < path.size(); ++i) {
Chris@1287:         if (i == current) {
Chris@1289:             newPath.push_back(path[i+1]);
Chris@1289:             newPath.push_back(path[i]);
Chris@1286:             ++i;
Chris@1286:         } else {
Chris@1289:             newPath.push_back(path[i]);
Chris@1286:         }
Chris@1286:     }
Chris@1290: 
Chris@1296:     auto newEntry = m_paths.at(key);
Chris@1290:     newEntry.directories = newPath;
Chris@1296:     m_paths[key] = newEntry;
Chris@1287:     
Chris@1296:     populateFor(key, current + 1);
Chris@1292: 
Chris@1293:     emit pathsChanged();
Chris@1285: }
Chris@1285: 
Chris@1285: void
Chris@1291: PluginPathConfigurator::addClicked()
Chris@1291: {
Chris@1296:     QString label = m_pluginTypeSelector->currentText();
Chris@1296:     PluginPathSetter::TypeKey key = getKeyForLabel(label);
Chris@1291: 
Chris@1291:     QString newDir = QFileDialog::getExistingDirectory
Chris@1291:         (this, tr("Choose directory to add"));
Chris@1291: 
Chris@1291:     if (newDir == QString()) return;
Chris@1291: 
Chris@1296:     auto newEntry = m_paths.at(key);
Chris@1291:     newEntry.directories.push_back(newDir);
Chris@1296:     m_paths[key] = newEntry;
Chris@1291:     
Chris@1296:     populateFor(key, newEntry.directories.size() - 1);
Chris@1292: 
Chris@1293:     emit pathsChanged();
Chris@1291: }
Chris@1291: 
Chris@1291: void
Chris@1285: PluginPathConfigurator::deleteClicked()
Chris@1285: {
Chris@1296:     QString label = m_pluginTypeSelector->currentText();
Chris@1296:     PluginPathSetter::TypeKey key = getKeyForLabel(label);
Chris@1296:     QStringList path = m_paths.at(key).directories;
Chris@1289:     
Chris@1287:     int current = m_list->currentRow();
Chris@1290:     if (current < 0) return;
Chris@1289: 
Chris@1286:     QStringList newPath;
Chris@1289:     for (int i = 0; i < path.size(); ++i) {
Chris@1287:         if (i != current) {
Chris@1289:             newPath.push_back(path[i]);
Chris@1286:         }
Chris@1286:     }
Chris@1290: 
Chris@1296:     auto newEntry = m_paths.at(key);
Chris@1290:     newEntry.directories = newPath;
Chris@1296:     m_paths[key] = newEntry;
Chris@1287:     
Chris@1296:     populateFor(key, current < newPath.size() ? current : current-1);
Chris@1292: 
Chris@1293:     emit pathsChanged();
Chris@1285: }
Chris@1290: 
Chris@1290: void
Chris@1290: PluginPathConfigurator::resetClicked()
Chris@1290: {
Chris@1296:     QString label = m_pluginTypeSelector->currentText();
Chris@1296:     PluginPathSetter::TypeKey key = getKeyForLabel(label);
Chris@1296:     m_paths[key] = m_defaultPaths[key];
Chris@1296:     populateFor(key, -1);
Chris@1292: 
Chris@1293:     emit pathsChanged();
Chris@1290: }
Chris@1292: 
Chris@1292: void
Chris@1292: PluginPathConfigurator::seePluginsClicked()
Chris@1292: {
Chris@1292:     PluginReviewDialog dialog(this);
Chris@1292:     dialog.populate();
Chris@1292:     dialog.exec();
Chris@1292: }
Chris@1292: