//=============================================================================
// MusE
// Linux Music Editor
// $Id:$
//
// Copyright (C) 2002-2006 by Werner Schweer and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "song.h"
#include "fastlog.h"
#include "widgets/filedialog.h"
#include "plugin.h"
#include "plugingui.h"
#include "icons.h"
#include "al/xml.h"
#include "gui.h"
#include "awl/floatentry.h"
#include "awl/slider.h"
#include "awl/checkbox.h"
#include "awl/combobox.h"
using Awl::FloatEntry;
using Awl::Slider;
using Awl::CheckBox;
using Awl::ComboBox;
static const char* preset_file_pattern[] = {
QT_TR_NOOP("presets (*.pre *.pre.gz *.pre.bz2)"),
QT_TR_NOOP("All Files (*)"),
0
};
int PluginDialog::selectedPlugType = SEL_SM;
QStringList PluginDialog::sortItems = QStringList();
//---------------------------------------------------------
// PluginDialog
// select Plugin dialog
//---------------------------------------------------------
PluginDialog::PluginDialog(QWidget* parent)
: QDialog(parent)
{
setWindowTitle(tr("MusE: select plugin"));
QVBoxLayout* layout = new QVBoxLayout(this);
pList = new QTreeWidget(this);
pList->setColumnCount(11);
pList->setSortingEnabled(true);
QStringList headerLabels;
headerLabels << tr("Lib");
headerLabels << tr("Label");
headerLabels << tr("Name");
headerLabels << tr("AI");
headerLabels << tr("AO");
headerLabels << tr("CI");
headerLabels << tr("CO");
headerLabels << tr("IP");
headerLabels << tr("id");
headerLabels << tr("Maker");
headerLabels << tr("Copyright");
int sizes[] = { 110, 110, 0, 30, 30, 30, 30, 30, 40, 110, 110 };
for (int i = 0; i < 11; ++i) {
if (sizes[i] == 0) {
pList->header()->setResizeMode(i, QHeaderView::Stretch);
}
else {
if (sizes[i] <= 40) // hack alert!
pList->header()->setResizeMode(i, QHeaderView::Custom);
pList->header()->resizeSection(i, sizes[i]);
}
}
pList->setHeaderLabels(headerLabels);
pList->setSelectionBehavior(QAbstractItemView::SelectRows);
pList->setSelectionMode(QAbstractItemView::SingleSelection);
pList->setAlternatingRowColors(true);
fillPlugs(selectedPlugType);
layout->addWidget(pList);
//---------------------------------------------------
// Ok/Cancel Buttons
//---------------------------------------------------
QBoxLayout* w5 = new QHBoxLayout;
layout->addLayout(w5);
QPushButton* okB = new QPushButton(tr("Ok"), this);
okB->setDefault(true);
QPushButton* cancelB = new QPushButton(tr("Cancel"), this);
okB->setFixedWidth(80);
cancelB->setFixedWidth(80);
w5->addWidget(okB);
w5->addSpacing(12);
w5->addWidget(cancelB);
QGroupBox* plugSelGroup = new QGroupBox;
plugSelGroup->setTitle("Show plugs:");
QHBoxLayout* psl = new QHBoxLayout;
plugSelGroup->setLayout(psl);
QButtonGroup* plugSel = new QButtonGroup(plugSelGroup);
onlySM = new QRadioButton;
onlySM->setText(tr("Mono and Stereo"));
onlySM->setCheckable(true);
plugSel->addButton(onlySM);
psl->addWidget(onlySM);
onlyS = new QRadioButton;
onlyS->setText(tr("Stereo"));
onlyS->setCheckable(true);
plugSel->addButton(onlyS);
psl->addWidget(onlyS);
onlyM = new QRadioButton;
onlyM->setText(tr("Mono"));
onlyM->setCheckable(true);
plugSel->addButton(onlyM);
psl->addWidget(onlyM);
allPlug = new QRadioButton;
allPlug->setText(tr("Show All"));
allPlug->setCheckable(true);
plugSel->addButton(allPlug);
psl->addWidget(allPlug);
plugSel->setExclusive(true);
switch(selectedPlugType) {
case SEL_SM: onlySM->setChecked(true); break;
case SEL_S: onlyS->setChecked(true); break;
case SEL_M: onlyM->setChecked(true); break;
case SEL_ALL: allPlug->setChecked(true); break;
}
plugSelGroup->setToolTip(tr("Select which types of plugins should be visible in the list.
"
"Note that using mono plugins on stereo tracks is not a problem, two will be used in parallell.
"
"Also beware that the 'all' alternative includes plugins that probably not are usable by MusE."));
w5->addSpacing(12);
w5->addWidget(plugSelGroup);
w5->addSpacing(12);
QLabel *sortLabel = new QLabel;
sortLabel->setText(tr("Search in 'Label' and 'Name':"));
w5->addWidget(sortLabel);
w5->addSpacing(2);
sortBox = new QComboBox(this);
sortBox->setEditable(true);
if (!sortItems.empty())
sortBox->addItems(sortItems);
sortBox->setMinimumSize(100, 10);
w5->addWidget(sortBox);
w5->addStretch(-1);
if (!sortBox->currentText().isEmpty())
fillPlugs(sortBox->currentText());
else
fillPlugs(selectedPlugType);
connect(pList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(accept()));
connect(cancelB, SIGNAL(clicked()), SLOT(reject()));
connect(okB, SIGNAL(clicked()), SLOT(accept()));
connect(plugSel, SIGNAL(buttonClicked(QAbstractButton*)), SLOT(fillPlugs(QAbstractButton*)));
connect(sortBox, SIGNAL(editTextChanged(const QString&)),SLOT(fillPlugs(const QString&)));
}
//---------------------------------------------------------
// accept
//---------------------------------------------------------
void PluginDialog::accept()
{
if (!sortBox->currentText().isEmpty()) {
foreach (QString item, sortItems)
if(item == sortBox->currentText()) {
QDialog::accept();
return;
}
sortItems.push_front(sortBox->currentText());
}
QDialog::accept();
}
//---------------------------------------------------------
// value
//---------------------------------------------------------
Plugin* PluginDialog::value()
{
QTreeWidgetItem* item = pList->selectedItems().at(0);
if (item)
return plugins.find(item->text(0), item->text(1));
printf("plugin not found\n");
return 0;
}
//---------------------------------------------------------
// fillPlugs
//---------------------------------------------------------
void PluginDialog::fillPlugs(QAbstractButton* ab)
{
if (ab == allPlug)
fillPlugs(SEL_ALL);
else if (ab == onlyM)
fillPlugs(SEL_M);
else if (ab == onlyS)
fillPlugs(SEL_S);
else if (ab == onlySM)
fillPlugs(SEL_SM);
}
//---------------------------------------------------------
// fillPlugs int
//---------------------------------------------------------
void PluginDialog::fillPlugs(int nbr)
{
pList->clear();
for (iPlugin i = plugins.begin(); i != plugins.end(); ++i) {
int ai = (*i)->inports();
int ao = (*i)->outports();
int ci = (*i)->parameter();
int co = 0;
bool addFlag = false;
switch (nbr) {
case SEL_SM: // stereo & mono
if ((ai == 1 || ai == 2) && (ao == 1 || ao ==2)) {
addFlag = true;
}
break;
case SEL_S: // stereo
if ((ai == 1 || ai == 2) && ao ==2) {
addFlag = true;
}
break;
case SEL_M: // mono
if (ai == 1 && ao == 1) {
addFlag = true;
}
break;
case SEL_ALL: // all
addFlag = true;
break;
}
if (addFlag) {
QTreeWidgetItem* item = new QTreeWidgetItem;
item->setText(0, (*i)->lib());
item->setText(1, (*i)->label());
item->setText(2, (*i)->name());
item->setText(3, QString().setNum(ai));
item->setText(4, QString().setNum(ao));
item->setText(5, QString().setNum(ci));
item->setText(6, QString().setNum(co));
item->setText(7, QString().setNum((*i)->inPlaceCapable()));
item->setText(8, QString().setNum((*i)->id()));
item->setText(9, (*i)->maker());
item->setText(10, (*i)->copyright());
pList->addTopLevelItem(item);
}
}
selectedPlugType = nbr;
}
//---------------------------------------------------------
// fillPlugs QString
//---------------------------------------------------------
void PluginDialog::fillPlugs(const QString &sortValue)
{
pList->clear();
for (iPlugin i = plugins.begin(); i != plugins.end(); ++i) {
int ai = (*i)->inports();
int ao = (*i)->outports();
int ci = (*i)->parameter();
int co = 0;
bool addFlag = false;
if ((*i)->label().toLower().contains(sortValue.toLower()))
addFlag = true;
else if ((*i)->name().toLower().contains(sortValue.toLower()))
addFlag = true;
if (addFlag) {
QTreeWidgetItem* item = new QTreeWidgetItem;
item->setText(0, (*i)->lib());
item->setText(1, (*i)->label());
item->setText(2, (*i)->name());
item->setText(3, QString().setNum(ai));
item->setText(4, QString().setNum(ao));
item->setText(5, QString().setNum(ci));
item->setText(6, QString().setNum(co));
item->setText(7, QString().setNum((*i)->inPlaceCapable()));
item->setText(8, QString().setNum((*i)->id()));
item->setText(9, (*i)->maker());
item->setText(10, (*i)->copyright());
pList->addTopLevelItem(item);
}
}
}
//---------------------------------------------------------
// getPlugin
//---------------------------------------------------------
Plugin* PluginDialog::getPlugin(QWidget* parent)
{
PluginDialog* dialog = new PluginDialog(parent);
if (dialog->exec())
return dialog->value();
return 0;
}
static const char* presetOpenText = " "
"Click this button to load a saved preset.";
static const char* presetSaveText = "Click this button to save curent parameter "
"settings as a preset. You will be prompted for a file name.";
static const char* presetBypassText = "Click this button to bypass effect unit";
//---------------------------------------------------------
// PluginGui
//---------------------------------------------------------
PluginGui::PluginGui(PluginI* p)
: QMainWindow(0)
{
setIconSize(ICON_SIZE);
plugin = p;
setWindowTitle(plugin->name());
QToolBar* tools = addToolBar(tr("File Buttons"));
QAction* fileOpen = tools->addAction(QIcon(*openIcon), tr("Load Preset"),
this, SLOT(load()));
fileOpen->setWhatsThis(tr(presetOpenText));
QAction* fileSave = tools->addAction(QIcon(*saveIcon), tr("Save Preset"),
this, SLOT(save()));
fileSave->setWhatsThis(tr(presetSaveText));
tools->addAction(QWhatsThis::createAction(this));
onOff = tools->addAction(*onOffIcon, tr("bypass plugin"));
onOff->setCheckable(true);
onOff->setChecked(plugin->on());
onOff->setWhatsThis(tr(presetBypassText));
connect(onOff, SIGNAL(triggered(bool)), SLOT(bypassToggled(bool)));
QString id;
id.setNum(plugin->plugin()->id());
QString name(museGlobalShare + QString("/plugins/") + id + QString(".ui"));
QWidget* mw; // main widget
QFile uifile(name);
if (uifile.exists()) {
//
// construct GUI from *.ui file
//
QFormBuilder builder;
//
// HACK:
//
QString path(museGlobalLib + "/designer");
printf("build gui from ui <%s>\n", path.toLatin1().data());
builder.addPluginPath(path);
uifile.open(QFile::ReadOnly);
mw = builder.load(&uifile, this);
uifile.close();
setCentralWidget(mw);
connectPrebuiltGui(mw);
}
else {
mw = new QWidget(this);
setCentralWidget(mw);
QGridLayout* grid = new QGridLayout;
mw->setLayout(grid);
grid->setSpacing(2);
int n = plugin->plugin()->parameter();
resize(280, n*20+30);
QFontMetrics fm = fontMetrics();
int h = fm.height() + 4;
for (int i = 0; i < n; ++i) {
double lower;
double upper;
double dlower;
double dupper;
double val = plugin->param(i);
double dval = val;
plugin->range(i, &lower, &upper);
dlower = lower;
dupper = upper;
if (plugin->isLog(i)) {
if (lower == 0.0)
lower = 0.001;
dlower = fast_log10(lower)*20.0;
dupper = fast_log10(upper)*20.0;
if (val == 0.0f)
dval = dlower;
else
dval = fast_log10(val) * 20.0;
}
if (plugin->isBool(i)) {
CheckBox* cb = new CheckBox(mw);
cb->setId(i);
cb->setText(QString(plugin->getParameterName(i)));
cb->setChecked(plugin->param(i) > 0.0);
cb->setFixedHeight(h);
cb->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum));
GuiWidget w;
w.widget = cb;
w.parameter = i;
w.type = GuiWidget::CHECKBOX;
gw.push_back(w);
grid->addWidget(cb, i, 0, 1, 3);
connect(cb, SIGNAL(valueChanged(double,int)), SLOT(setController(double, int)));
}
else {
QLabel* label = new QLabel(QString(plugin->getParameterName(i)), mw);
label->setFixedHeight(20);
label->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum));
FloatEntry* e = new FloatEntry(mw);
e->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum));
e->setRange(lower, upper);
e->setId(i);
e->setFixedHeight(h);
e->setFrame(true);
GuiWidget w;
w.widget = e;
w.parameter = i;
w.type = GuiWidget::FLOAT_ENTRY;
gw.push_back(w);
Slider* s = new Slider(mw);
s->setId(i);
s->setLog(plugin->isLog(i));
s->setOrientation(Qt::Horizontal);
s->setFixedHeight(h);
s->setRange(dlower, dupper);
s->setLineStep((dupper-dlower)/100.0);
s->setPageStep((dupper-dlower)/10.0);
s->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum));
w.widget = s;
w.parameter = i;
w.type = GuiWidget::SLIDER;
gw.push_back(w);
grid->addWidget(label, i, 0);
grid->addWidget(e, i, 1);
const char* p = plugin->getParameterLabel(i);
if (p) {
QLabel* l = new QLabel(mw);
l->setFixedHeight(h);
l->setText(p);
grid->addWidget(l, i, 2);
}
grid->addWidget(s, i, 3);
connect(s, SIGNAL(valueChanged(double,int)), SLOT(setController(double,int)));
connect(e, SIGNAL(valueChanged(double,int)), SLOT(setController(double,int)));
}
updateValue(i, val);
}
grid->setColumnStretch(3, 10);
}
connect(plugin->track(), SIGNAL(autoReadChanged(bool)), SLOT(autoChanged()));
connect(plugin->track(), SIGNAL(autoWriteChanged(bool)), SLOT(autoChanged()));
connect(plugin->track(), SIGNAL(controllerChanged(int)), SLOT(controllerChanged(int)));
autoChanged();
}
void PluginGui::connectPrebuiltGui(QWidget* wContainer)
{
QObjectList l = wContainer->children();
for (int i = 0; i < l.size(); ++i) {
QObject* obj = l.at(i);
const char* name = obj->objectName().toLatin1().data();
if (strcmp(obj->metaObject()->className(), "QFrame") == 0) {
connectPrebuiltGui((QWidget *)obj);
}
if (*name !='P')
continue;
GuiWidget w;
w.widget = (QWidget*)obj;
if (strcmp(obj->metaObject()->className(), "Awl::Slider") == 0) {
connect((Slider*)obj, SIGNAL(valueChanged(double,int)), SLOT(setController(double,int)));
w.type = GuiWidget::SLIDER;
w.parameter = ((Slider*)obj)->id();
}
else if (strcmp(obj->metaObject()->className(), "Awl::FloatEntry") == 0) {
connect((FloatEntry*)obj, SIGNAL(valueChanged(double,int)), SLOT(setController(double,int)));
w.type = GuiWidget::FLOAT_ENTRY;
w.parameter = ((FloatEntry*)obj)->id();
}
else if (strcmp(obj->metaObject()->className(), "Awl::CheckBox") == 0) {
w.type = GuiWidget::CHECKBOX;
w.parameter = ((CheckBox*)obj)->id();
connect(obj, SIGNAL(valueChanged(double, int)), SLOT(setController(double, int)));
}
else if (strcmp(obj->metaObject()->className(), "Awl::ComboBox") == 0) {
w.type = GuiWidget::COMBOBOX;
w.parameter = ((ComboBox*)obj)->id();
connect(obj, SIGNAL(valueChanged(double, int)), SLOT(setController(double,int)));
}
else {
printf("PluginGui::unknown widget class %s\n", obj->metaObject()->className());
continue;
}
gw.push_back(w);
}
}
//---------------------------------------------------------
// PluginGui
//---------------------------------------------------------
PluginGui::~PluginGui()
{
}
//---------------------------------------------------------
// setController
//---------------------------------------------------------
void PluginGui::setController(double val, int param)
{
if (plugin->isInt(param))
val = rint(val);
CVal cval;
cval.f = val;
song->setControllerVal(plugin->track(), plugin->controller(param), cval);
}
//---------------------------------------------------------
// load
//---------------------------------------------------------
void PluginGui::load()
{
QString s("presets/plugins/");
s += plugin->plugin()->label();
s += "/";
QStringList pattern;
const char** p = preset_file_pattern;
while (*p)
pattern << *p++;
QString fn = getOpenFileName(s, pattern, this, tr("MusE: load preset"));
if (fn.isEmpty())
return;
QFile* qf = fileOpen(this, fn, QString(".pre"), QIODevice::ReadOnly, true);
if (qf == 0)
return;
QDomDocument doc;
int line, column;
QString err;
if (!doc.setContent(qf, false, &err, &line, &column)) {
QString col, ln, error;
col.setNum(column);
ln.setNum(line);
error = err + "\n at line: " + ln + " col: " + col;
printf("error reading med file: %s\n", error.toLatin1().data());
delete qf;
return;
}
QDomNode node = doc.documentElement();
while (!node.isNull()) {
QDomElement e = node.toElement();
if (e.isNull())
continue;
if (e.tagName() == "muse") {
// QString version = e.attribute(QString("version"));
node = node.firstChild();
while (!node.isNull()) {
QDomElement e = node.toElement();
bool prefader;
if (e.tagName() == "plugin")
plugin->readConfiguration(node.firstChild(), &prefader);
else
printf("MusE:PluginGui: unknown tag %s\n", e.tagName().toLatin1().data());
node = node.nextSibling();
}
}
else
printf("MusE: %s not supported\n", e.tagName().toLatin1().data());
node = node.nextSibling();
}
qf->close();
delete qf;
}
//---------------------------------------------------------
// save
//---------------------------------------------------------
void PluginGui::save()
{
QString s("presets/plugins/");
s += plugin->plugin()->label();
s += "/";
QStringList pattern;
const char** p = preset_file_pattern;
while (*p)
pattern << *p++;
QString fn = getSaveFileName(s, pattern, this,
tr("MusE: save preset"));
if (fn.isEmpty())
return;
QFile* f = fileOpen(this, fn, QString(".pre"), QIODevice::WriteOnly, true);
if (f == 0)
return;
Xml xml(f);
xml.header();
xml.stag("muse version=\"1.0\"");
plugin->writeConfiguration(xml, true);
xml.etag("muse");
f->close();
delete f;
}
//---------------------------------------------------------
// bypassToggled
//---------------------------------------------------------
void PluginGui::bypassToggled(bool val)
{
plugin->setOn(val);
song->update(SC_ROUTE);
}
//---------------------------------------------------------
// setOn
//---------------------------------------------------------
void PluginGui::setOn(bool val)
{
// onOff->blockSignals(true);
onOff->setChecked(val);
// onOff->blockSignals(false);
}
//---------------------------------------------------------
// updateValue
//---------------------------------------------------------
void PluginGui::updateValue(int parameter, double value)
{
for (std::vector::iterator i = gw.begin(); i != gw.end(); ++i) {
int idx = i->parameter;
if (idx != parameter)
continue;
switch (i->type) {
case GuiWidget::SLIDER:
((Slider*)(i->widget))->setValue(value);
break;
case GuiWidget::FLOAT_ENTRY:
{
const char* p = plugin->getParameterDisplay(idx, value);
if (p)
((FloatEntry*)(i->widget))->setText(QString(p));
else
((FloatEntry*)(i->widget))->setValue(value);
}
break;
case GuiWidget::CHECKBOX:
((CheckBox*)(i->widget))->setValue(value);
break;
case GuiWidget::COMBOBOX:
((ComboBox*)(i->widget))->setValue(value);
break;
}
}
}
//---------------------------------------------------------
// updateValues
//---------------------------------------------------------
void PluginGui::updateValues()
{
int n = plugin->plugin()->parameter();
for (int i = 0; i < n; ++i) {
double val = plugin->param(i);
updateValue(i, val);
}
}
//---------------------------------------------------------
// controllerChanged
//---------------------------------------------------------
void PluginGui::controllerChanged(int id)
{
double value = plugin->track()->ctrlVal(id).f;
for (std::vector::iterator i = gw.begin(); i != gw.end(); ++i) {
int idx = i->parameter;
if (plugin->controller(idx)->id() != id)
continue;
switch (i->type) {
case GuiWidget::SLIDER:
((Slider*)(i->widget))->setValue(value);
break;
case GuiWidget::FLOAT_ENTRY:
{
const char* p = plugin->getParameterDisplay(idx, value);
if (p)
((FloatEntry*)(i->widget))->setText(QString(p));
else
((FloatEntry*)(i->widget))->setValue(value);
}
break;
case GuiWidget::CHECKBOX:
((CheckBox*)(i->widget))->setValue(value);
break;
case GuiWidget::COMBOBOX:
((ComboBox*)(i->widget))->setValue(value);
break;
}
}
}
//---------------------------------------------------------
// autoChanged
//---------------------------------------------------------
void PluginGui::autoChanged()
{
bool ar = plugin->track()->autoRead();
bool aw = plugin->track()->autoWrite();
// controller are enabled if
// autoRead is off
// autoRead and autoWrite are on (touch mode)
bool ec = !ar || (ar && aw);
for (std::vector::iterator i = gw.begin(); i != gw.end(); ++i)
i->widget->setEnabled(ec);
}