#include #include #include #include #include #include #include #include #include #include "qmpsettingswindow.hpp" #include "qmpdeviceprioritydialog.hpp" #include "ui_qmpsettingswindow.h" #include "qmpmainwindow.hpp" qmpSettingsWindow::qmpSettingsWindow(qmpSettings *qmpsettings, QWidget *parent) : QDialog(parent), ui(new Ui::qmpSettingsWindow) { ui->setupUi(this); customOptions.clear(); customOptPages.clear(); connect(this, &qmpSettingsWindow::dialogClosing, (qmpMainWindow *)parent, &qmpMainWindow::dialogClosed); settings = qmpsettings; cwt = new qmpCustomizeWindow(this); cwa = new qmpCustomizeWindow(this); dps = new qmpDevPropDialog(this); devpriod = new qmpDevicePriorityDialog(this); } qmpSettingsWindow::~qmpSettingsWindow() { delete ui; } void qmpSettingsWindow::closeEvent(QCloseEvent *event) { setVisible(false); loadOption(); emit dialogClosing(); event->accept(); } void qmpSettingsWindow::hideEvent(QHideEvent *event) { emit dialogClosing(); event->accept(); } void qmpSettingsWindow::on_buttonBox_accepted() { saveOption(); qmpMainWindow::getInstance()->setupWidget(); emit dialogClosing(); } void qmpSettingsWindow::on_buttonBox_rejected() { loadOption(); emit dialogClosing(); } void qmpSettingsWindow::updatePluginList(qmpPluginManager *pmgr) { std::vector *plugins = pmgr->getPlugins(); QVariant *data = static_cast(settings->getOptionCustom("DisabledPlugins")); QList disabled_plugins_l = static_cast(data)->toList(); delete data; std::set disabled_plugins_s; for (auto &i : disabled_plugins_l) disabled_plugins_s.insert(i.toString().toStdString()); for (unsigned i = 0; i < plugins->size(); ++i) { bool enabled = disabled_plugins_s.find(plugins->at(i).name) == disabled_plugins_s.end(); plugins->at(i).enabled = enabled; } } void qmpSettingsWindow::postInit() { setupWidgets(); int sf = 0; QVariant *data = static_cast(settings->getOptionCustom("FluidSynth/SoundFonts")); for (auto i : data->toList()) if (!i.toString().startsWith('#')) { sf = 1; break; } delete data; std::string selecteddev; std::vector devs = qmpMainWindow::getInstance()->getPlayer()->getMidiOutDevices(); std::set devset; for (auto dev : devs) devset.insert(dev); QVariant *devpriov = static_cast(qmpMainWindow::getInstance()->getSettings()->getOptionCustom("Midi/DevicePriority")); QList devprio = devpriov->toList(); delete devpriov; for (auto &setdev : devprio) if (devset.find(setdev.toString().toStdString()) != devset.end()) { selecteddev = setdev.toString().toStdString(); break; } if (selecteddev == "Internal FluidSynth" && !sf) { if (QMessageBox::question(this, tr("No soundfont loaded"), tr("Internal fluidsynth is the only available MIDI output but it has no soundfont set. " "Would you like to setup soundfonts now? You may have to reload the internal synth afterwards.")) == QMessageBox::Yes) { show(); ui->tabWidget->setCurrentWidget(qobject_cast(pageForTab("SoundFonts")->parent())); } } } void qmpSettingsWindow::registerCustomizeWidgetOptions() { QPushButton *pbCustomizeToolbar = new QPushButton(tr("Customize...")); QPushButton *pbCustomizeActions = new QPushButton(tr("Customize...")); QVariant toolbar_def_val = QList({"Channel", "Playlist", "Effects", "Visualization"}); QVariant actions_def_val = QList({"FileInfo", "Render", "Panic", "ReloadSynth"}); settings->registerOptionCustom("Behavior", "Customize toolbar", "Behavior/Toolbar", pbCustomizeToolbar, &toolbar_def_val, std::bind(&qmpCustomizeWindow::save, cwt), std::bind(&qmpCustomizeWindow::load, cwt, std::placeholders::_1)); settings->registerOptionCustom("Behavior", "Customize actions", "Behavior/Actions", pbCustomizeActions, &actions_def_val, std::bind(&qmpCustomizeWindow::save, cwa), std::bind(&qmpCustomizeWindow::load, cwa, std::placeholders::_1)); connect(pbCustomizeToolbar, &QPushButton::clicked, [this] {loadOption("Behavior/Toolbar"); cwt->show();}); connect(pbCustomizeActions, &QPushButton::clicked, [this] {loadOption("Behavior/Actions"); cwa->show();}); connect(cwt, &QDialog::accepted, [this] {saveOption("Behavior/Toolbar"); qmpMainWindow::getInstance()->setupWidget();}); connect(cwa, &QDialog::accepted, [this] {saveOption("Behavior/Actions"); qmpMainWindow::getInstance()->setupWidget();}); connect(cwt, &QDialog::rejected, [this] {loadOption("Behavior/Toolbar");}); connect(cwa, &QDialog::rejected, [this] {loadOption("Behavior/Actions");}); qmpMainWindow::getInstance()->setupWidget(); } void qmpSettingsWindow::registerSoundFontOption() { QWidget *sfpanel = new QWidget(); sfpanel->setLayout(new QVBoxLayout); sfpanel->layout()->setContentsMargins(0, 0, 0, 0); QTableWidget *twsf = new QTableWidget(); twsf->setColumnCount(2); twsf->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::ResizeToContents); twsf->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection); twsf->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows); twsf->setHorizontalHeaderLabels({tr("E"), tr("Path")}); twsf->setHorizontalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel); twsf->setVerticalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel); sfpanel->layout()->addWidget(twsf); QWidget *controls = new QWidget(); controls->setLayout(new QHBoxLayout); controls->layout()->setContentsMargins(0, 0, 0, 0); QPushButton *pbsfadd = new QPushButton(style()->standardIcon(QStyle::StandardPixmap::SP_DialogOpenButton), QString()); QPushButton *pbsfrem = new QPushButton(style()->standardIcon(QStyle::StandardPixmap::SP_DialogDiscardButton), QString()); QPushButton *pbsfmup = new QPushButton(style()->standardIcon(QStyle::StandardPixmap::SP_ArrowUp), QString()); QPushButton *pbsfmdn = new QPushButton(style()->standardIcon(QStyle::StandardPixmap::SP_ArrowDown), QString()); controls->layout()->addWidget(pbsfadd); controls->layout()->addWidget(pbsfrem); controls->layout()->addWidget(pbsfmup); controls->layout()->addWidget(pbsfmdn); sfpanel->layout()->addWidget(controls); connect(pbsfadd, &QPushButton::clicked, [twsf, this] { QStringList sl = QFileDialog::getOpenFileNames(this, "Add File", "", "SoundFont files (*.sf2)"); for (int i = 0; i < sl.size(); ++i) { twsf->insertRow(twsf->rowCount()); QTableWidgetItem *sfn, *sfe; twsf->setItem(twsf->rowCount() - 1, 1, sfn = new QTableWidgetItem(sl[i])); twsf->setItem(twsf->rowCount() - 1, 0, sfe = new QTableWidgetItem()); sfe->setCheckState(Qt::CheckState::Unchecked); sfn->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable); sfe->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsUserCheckable); } }); connect(pbsfrem, &QPushButton::clicked, [twsf] { QList sl = twsf->selectedItems(); for (int i = 0; i < sl.size(); ++i) twsf->removeRow(twsf->row(sl[i])); }); connect(pbsfmup, &QPushButton::clicked, [twsf] { int cid = twsf->currentRow(); if (!cid) return; QTableWidgetItem *ci = twsf->takeItem(cid, 1); QTableWidgetItem *ce = twsf->takeItem(cid, 0); twsf->removeRow(cid); twsf->insertRow(cid - 1); twsf->setItem(cid - 1, 0, ce); twsf->setItem(cid - 1, 1, ci); twsf->setCurrentCell(cid - 1, 1); }); connect(pbsfmdn, &QPushButton::clicked, [twsf] { int cid = twsf->currentRow(); if (cid == twsf->rowCount() - 1) return; QTableWidgetItem *ci = twsf->takeItem(cid, 1); QTableWidgetItem *ce = twsf->takeItem(cid, 0); twsf->removeRow(cid); twsf->insertRow(cid + 1); twsf->setItem(cid + 1, 0, ce); twsf->setItem(cid + 1, 1, ci); twsf->setCurrentCell(cid + 1, 1); }); QVariant sf_def_val = QList(); auto save_func = [twsf]()->void * { QList sflist; for (int i = 0; i < twsf->rowCount(); ++i) { QString sfs = twsf->item(i, 1)->text(); if (twsf->item(i, 0)->checkState() == Qt::CheckState::Unchecked) sfs = "#" + sfs; sflist.push_back(sfs); } return new QVariant(sflist); }; auto load_func = [twsf](void *data) { QList sflist = static_cast(data)->toList(); twsf->clearContents(); twsf->setRowCount(0); for (int i = 0; i < sflist.size(); ++i) { twsf->insertRow(i); QTableWidgetItem *sfn, *sfe; QString sf = sflist[i].toString(); bool enabled = !sf.startsWith('#'); if (!enabled) sf = sf.mid(1); twsf->setItem(i, 1, sfn = new QTableWidgetItem(sf)); twsf->setItem(i, 0, sfe = new QTableWidgetItem()); sfn->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable); sfe->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsUserCheckable); sfe->setCheckState(enabled ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); } }; settings->registerOptionCustom("SoundFonts", "", "FluidSynth/SoundFonts", sfpanel, &sf_def_val, save_func, load_func); } void qmpSettingsWindow::registerPluginOption(qmpPluginManager *pmgr) { QTableWidget *twplugins = new QTableWidget(); twplugins->setColumnCount(4); twplugins->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::ResizeToContents); twplugins->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection); twplugins->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows); twplugins->setHorizontalHeaderLabels({tr("E"), tr("Plugin Name"), tr("Version"), tr("Path")}); twplugins->setHorizontalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel); twplugins->setVerticalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel); QVariant ep_def_val = QList(); auto save_func = [twplugins, this]()->void * { QVariant *data = static_cast(settings->getOptionCustom("DisabledPlugins")); QList disabled_plugins_ol = static_cast(data)->toList(); delete data; std::set disabled_plugins_s; for (auto &i : disabled_plugins_ol) disabled_plugins_s.insert(i.toString().toStdString()); for (int i = 0; i < twplugins->rowCount(); ++i) { QString pn = twplugins->item(i, 1)->text(); if (twplugins->item(i, 0)->checkState() == Qt::CheckState::Unchecked) disabled_plugins_s.insert(pn.toStdString()); else disabled_plugins_s.erase(pn.toStdString()); } QList disabled_plugins; for (auto &i : disabled_plugins_s) disabled_plugins.push_back(QString(i.c_str())); return new QVariant(disabled_plugins); }; auto load_func = [twplugins, pmgr](void *data) { QList disabled_plugins_l = static_cast(data)->toList(); std::set disabled_plugins; for (auto i : disabled_plugins_l) disabled_plugins.insert(i.toString().toStdString()); twplugins->clearContents(); twplugins->setRowCount(0); std::vector *plugins = pmgr->getPlugins(); for (int i = 0; static_cast(i) < plugins->size(); ++i) { twplugins->insertRow(i); qmpPlugin &p = plugins->at(static_cast(i)); QTableWidgetItem *icb; twplugins->setItem(i, 0, icb = new QTableWidgetItem()); bool enabled = disabled_plugins.find(p.name) == disabled_plugins.end(); icb->setCheckState(enabled ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); icb->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsUserCheckable); twplugins->setItem(i, 1, new QTableWidgetItem(p.name.c_str())); twplugins->setItem(i, 2, new QTableWidgetItem(p.version.c_str())); twplugins->setItem(i, 3, new QTableWidgetItem(p.path.c_str())); for (int j = 1; j <= 3; ++j) twplugins->item(i, j)->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable); } }; settings->registerOptionCustom("Plugins", "", "DisabledPlugins", twplugins, &ep_def_val, save_func, load_func); } void qmpSettingsWindow::registerExtraMidiOptions() { QPushButton *pbDevPrio = new QPushButton("..."); connect(pbDevPrio, &QPushButton::clicked, [this] {loadOption("Midi/DevicePriority"); devpriod->show();}); connect(devpriod, &QDialog::accepted, [this] {saveOption("Midi/DevicePriority");}); connect(devpriod, &QDialog::rejected, [this] {loadOption("Midi/DevicePriority");}); QVariant devprio_def_val = QList({"Internal FluidSynth"}); settings->registerOptionCustom("MIDI", "Select MIDI output devices", "Midi/DevicePriority", pbDevPrio, &devprio_def_val, std::bind(&qmpDevicePriorityDialog::save, devpriod), std::bind(&qmpDevicePriorityDialog::load, devpriod, std::placeholders::_1)); QPushButton *pbDevProp = new QPushButton("..."); connect(pbDevProp, &QPushButton::clicked, [this] {loadOption("Midi/DeviceInitializationFiles"); dps->show();}); connect(dps, &QDialog::accepted, [this] {saveOption("Midi/DeviceInitializationFiles");}); connect(dps, &QDialog::rejected, [this] {loadOption("Midi/DeviceInitializationFiles");}); QVariant devprop_def_val = QList({}); settings->registerOptionCustom("MIDI", "External MIDI device setup", "Midi/DeviceInitializationFiles", pbDevProp, &devprop_def_val, std::bind(&qmpDevPropDialog::save, dps), std::bind(&qmpDevPropDialog::load, dps, std::placeholders::_1)); } void qmpSettingsWindow::saveOption(std::string key) { auto save_opt = [this](std::string & key)->QVariant { qmpOption &o = settings->options[key]; QVariant ret; switch (o.type) { case qmpOption::ParameterType::parameter_int: { QSpinBox *sb = qobject_cast(o.widget); if (sb) ret = sb->value(); } break; case qmpOption::ParameterType::parameter_uint: { QHexSpinBox *sb = qobject_cast(o.widget); if (sb) { int val = sb->value(); ret = reinterpret_cast(val); } } break; case qmpOption::ParameterType::parameter_bool: { QCheckBox *cb = qobject_cast(o.widget); if (cb) ret = cb->isChecked(); } break; case qmpOption::ParameterType::parameter_double: { QDoubleSpinBox *sb = qobject_cast(o.widget); if (sb) ret = sb->value(); } break; case qmpOption::ParameterType::parameter_str: { QLineEdit *le = qobject_cast(o.widget); if (le) ret = le->text(); } break; case qmpOption::ParameterType::parameter_enum: { QComboBox *cb = qobject_cast(o.widget); if (cb) ret = cb->currentText(); } break; case qmpOption::ParameterType::parameter_url: { QFileEdit *fe = qobject_cast(o.widget); if (fe) ret = fe->text(); } break; default: if (o.save_func) { QVariant *var = static_cast(o.save_func()); ret = QVariant(*var); delete var; } break; } return ret; }; if (key.length()) { QVariant r = save_opt(key); if (r.isValid()) settings->settings->setValue(QString(key.c_str()), r); } else for (std::string &key : settings->optionlist) { QVariant r = save_opt(key); if (r.isValid()) settings->settings->setValue(QString(key.c_str()), r); } settings->settings->sync(); } void qmpSettingsWindow::loadOption(std::string key) { auto load_opt = [this](std::string & key) { qmpOption &o = settings->options[key]; switch (o.type) { case qmpOption::ParameterType::parameter_int: { QSpinBox *sb = qobject_cast(o.widget); if (sb) sb->setValue(settings->getOptionInt(key)); } break; case qmpOption::ParameterType::parameter_uint: { QHexSpinBox *sb = qobject_cast(o.widget); if (sb) sb->setValue(settings->getOptionUint(key)); } break; case qmpOption::ParameterType::parameter_bool: { QCheckBox *cb = qobject_cast(o.widget); if (cb) cb->setChecked(settings->getOptionBool(key)); } break; case qmpOption::ParameterType::parameter_double: { QDoubleSpinBox *sb = qobject_cast(o.widget); if (sb) sb->setValue(settings->getOptionDouble(key)); } break; case qmpOption::ParameterType::parameter_str: { QLineEdit *le = qobject_cast(o.widget); if (le) le->setText(QString(settings->getOptionString(key).c_str())); } break; case qmpOption::ParameterType::parameter_enum: { QComboBox *cb = qobject_cast(o.widget); if (cb) cb->setCurrentIndex(settings->getOptionEnumInt(key)); } break; case qmpOption::ParameterType::parameter_url: { QFileEdit *fe = qobject_cast(o.widget); if (fe) fe->setText(QString(settings->getOptionString(key).c_str())); } break; default: if (o.load_func) { void *var = settings->getOptionCustom(key); o.load_func(var); delete static_cast(var); } break; } }; if (key.length()) load_opt(key); else for (std::string &key : settings->optionlist) load_opt(key); } void qmpSettingsWindow::setupWidgets() { for (std::string &key : settings->optionlist) { if (!settings->options[key].desc.length() && settings->options[key].type != qmpOption::ParameterType::parameter_custom) continue; QWidget *optw = nullptr; qmpOption &o = settings->options[key]; switch (o.type) { case qmpOption::ParameterType::parameter_int: { QSpinBox *sb = new QSpinBox; sb->setMinimum(o.minv.toInt()); sb->setMaximum(o.maxv.toInt()); sb->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); optw = sb; } break; case qmpOption::ParameterType::parameter_uint: { QHexSpinBox *sb = new QHexSpinBox; sb->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); optw = sb; } break; case qmpOption::ParameterType::parameter_bool: { QCheckBox *cb = new QCheckBox(QString(o.desc.c_str())); cb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); optw = cb; } break; case qmpOption::ParameterType::parameter_double: { QDoubleSpinBox *sb = new QDoubleSpinBox; sb->setMinimum(o.minv.toDouble()); sb->setMaximum(o.maxv.toDouble()); sb->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); optw = sb; } break; case qmpOption::ParameterType::parameter_str: { QLineEdit *te = new QLineEdit(); te->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); optw = te; } break; case qmpOption::ParameterType::parameter_enum: { QComboBox *cb = new QComboBox(); for (std::string &item : o.enumlist) cb->addItem(QString(item.c_str())); cb->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); optw = cb; } break; case qmpOption::ParameterType::parameter_url: { QFileEdit *fe = new QFileEdit(); fe->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); optw = fe; } break; default: optw = o.widget; break; } o.widget = optw; QGridLayout *page = pageForTab(o.tab); if (o.type == qmpOption::ParameterType::parameter_bool || (o.type == qmpOption::parameter_custom && !o.desc.length())) { int row = page->rowCount(); page->addWidget(o.widget, row, 0, 1, 2); } else { QLabel *lb = new QLabel(o.desc.c_str(), page->parentWidget()); lb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); int row = page->rowCount(); page->addWidget(lb, row, 0); page->addWidget(o.widget, row, 1); } } loadOption(); } QGridLayout *qmpSettingsWindow::pageForTab(std::string tab) { if (customOptPages.find(tab) != customOptPages.end()) return customOptPages[tab]; QWidget *w = new QWidget; QGridLayout *page = new QGridLayout(w); w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); ui->tabWidget->addTab(w, QString(tab.c_str())); customOptPages[tab] = page; return page; } QFileEdit::QFileEdit(QWidget *par): QWidget(par) { QHBoxLayout *layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); le = new QLineEdit(this); layout->addWidget(le); tb = new QToolButton(this); tb->setText("..."); layout->addWidget(tb); connect(tb, &QToolButton::clicked, this, &QFileEdit::chooseFile); } QString QFileEdit::text() { return le->text(); } void QFileEdit::setText(const QString &s) { le->setText(s); } void QFileEdit::chooseFile() { QString s = QFileDialog::getOpenFileName(nullptr, tr("Select a file"), QFileInfo(text()).dir().absolutePath()); if (s.length()) setText(s); }