//=========================================================
//  MusE
//  Linux Music Editor
//  $Id: plugin.cpp,v 1.1.1.1 2003/10/29 10:06:36 wschweer Exp $
//
//  (C) Copyright 2000 Werner Schweer (ws@seh.de)
//=========================================================

#include <qdir.h>
#include <stdio.h>
#include <dlfcn.h>
#include <cmath>

#include <qwidget.h>
#include <qlayout.h>
#include <qlabel.h>
#include <qsignalmapper.h>
#include <qpushbutton.h>
#include <qlistview.h>
#include <qtoolbar.h>
#include <qtoolbutton.h>
#include <qwhatsthis.h>
#include <qcheckbox.h>
#include <qtooltip.h>
#include <qwidgetfactory.h>
#include <qfile.h>
#include <qobjectlist.h>

#include "filedialog.h"
#include "slider.h"
#include "plugin.h"
#include "xml.h"
#include "icons.h"
#include "song.h"
#include "doublelabel.h"

PluginList plugins;

static const char* preset_file_pattern[] = {
      QT_TR_NOOP("presets (*.pre *.pre.gz *.pre.bz2)"),
      QT_TR_NOOP("All Files (*)"),
      0
      };

//---------------------------------------------------------
//   Plugin
//---------------------------------------------------------

Plugin::Plugin(QFileInfo* f,
   LADSPA_Descriptor_Function df, const LADSPA_Descriptor* d, bool ip)
   : fi(*f), ladspa(df), plugin(d)
      {
      _inPlaceCapable = ip;
      _inports = 0;
      _outports = 0;
      for (unsigned k = 0; k < d->PortCount; ++k) {
            LADSPA_PortDescriptor pd = d->PortDescriptors[k];
            if (pd &  LADSPA_PORT_CONTROL)
                  continue;
            if (pd &  LADSPA_PORT_INPUT)
                  ++_inports;
            else
                  ++_outports;
            }
      if (_inports != _outports)
            _inPlaceCapable = false;
      _references = 0;
      _instNo     = 0;
      }

//---------------------------------------------------------
//   loadPluginLib
//---------------------------------------------------------

static void loadPluginLib(QFileInfo* fi)
      {
      void* handle = dlopen(fi->filePath().ascii(), RTLD_NOW);
      if (handle == 0) {
            fprintf(stderr, "dlopen(%s) failed: %s\n",
              fi->filePath().ascii(), dlerror());
            return;
            }
      LADSPA_Descriptor_Function ladspa = (LADSPA_Descriptor_Function)dlsym(handle, "ladspa_descriptor");

      if (!ladspa) {
            const char *txt = dlerror();
            if (txt) {
                  fprintf(stderr,
                        "Unable to find ladspa_descriptor() function in plugin "
                        "library file \"%s\": %s.\n"
                        "Are you sure this is a LADSPA plugin file?\n",
                        fi->filePath().ascii(),
                        txt);
                  exit(1);
                  }
            }
      const LADSPA_Descriptor* descr;
      for (int i = 0;; ++i) {
            descr = ladspa(i);
            if (descr == NULL)
                  break;
            LADSPA_Properties properties = descr->Properties;
            int ai = 0;
            int ao = 0;
            for (unsigned k = 0; k < descr->PortCount; ++k) {
                  LADSPA_PortDescriptor pd = descr->PortDescriptors[k];
                  if (pd &  LADSPA_PORT_CONTROL)
                        continue;
                  if (pd &  LADSPA_PORT_INPUT)
                        ++ai;
                  else
                        ++ao;
                  }
            bool inPlaceBroken = LADSPA_IS_INPLACE_BROKEN(properties);
            plugins.add(fi, ladspa, descr, !inPlaceBroken);
            }
      }

//---------------------------------------------------------
//   loadPluginDir
//---------------------------------------------------------

static void loadPluginDir(const QString& s)
      {
      if (debugMsg)
            printf("load plugin dir <%s>\n", s.latin1());
      QDir pluginDir(s, QString("*.so"), QDir::Files);
      if (pluginDir.exists()) {
            const QFileInfoList* list = pluginDir.entryInfoList();
            QFileInfoListIterator it(*list);
            QFileInfo* fi;
            while((fi = it.current())) {
                  loadPluginLib(fi);
                  ++it;
                  }
            }
      }

//---------------------------------------------------------
//   initPlugins
//---------------------------------------------------------

void initPlugins()
      {
      loadPluginDir(museGlobalLib + QString("/plugins"));

      char* ladspaPath = getenv("LADSPA_PATH");
      if (ladspaPath == 0)
            ladspaPath = "/usr/lib/ladspa:/usr/local/lib/ladspa";

      char* p = ladspaPath;
      while (*p != '\0') {
            char* pe = p;
            while (*pe != ':' && *pe != '\0')
                  pe++;

            int n = pe - p;
            if (n) {
                  char* buffer = new char[n + 1];
                  strncpy(buffer, p, n);
                  buffer[n] = '\0';
                  loadPluginDir(QString(buffer));
                  delete[] buffer;
                  }
            p = pe;
            if (*p == ':')
                  p++;
            }
      }

//---------------------------------------------------------
//   find
//---------------------------------------------------------

Plugin* PluginList::find(const QString& file, const QString& name)
      {
      for (iPlugin i = begin(); i != end(); ++i) {
            if ((file == i->lib()) && (name == i->label()))
                  return &*i;
            }
      printf("Plugin <%s> not found\n", name.ascii());
      return 0;
      }

//---------------------------------------------------------
//   Pipeline
//---------------------------------------------------------

Pipeline::Pipeline()
   : std::vector<PluginI*>()
      {
      for (int i = 0; i < PipelineDepth; ++i)
            push_back(0);
      }

//---------------------------------------------------------
//   insert
//    give ownership of object plugin to Pipeline
//---------------------------------------------------------

void Pipeline::insert(PluginI* plugin, int index)
      {
      (*this)[index] = plugin;
      }

//---------------------------------------------------------
//   remove
//---------------------------------------------------------

void Pipeline::remove(int index)
      {
      PluginI* plugin = (*this)[index];
      if (plugin)
            delete plugin;
      (*this)[index] = 0;
      }

//---------------------------------------------------------
//   removeAll
//---------------------------------------------------------

void Pipeline::removeAll()
      {
      for (int i = 0; i < PipelineDepth; ++i)
            remove(i);
      }

//---------------------------------------------------------
//   isOn
//---------------------------------------------------------

bool Pipeline::isOn(int idx) const
      {
      PluginI* p = (*this)[idx];
      if (p)
            return p->on();
      return false;
      }

//---------------------------------------------------------
//   setOn
//---------------------------------------------------------

void Pipeline::setOn(int idx, bool flag)
      {
      PluginI* p = (*this)[idx];
      if (p) {
            p->setOn(flag);
            if (p->gui())
                  p->gui()->setOn(flag);
            }
      }

//---------------------------------------------------------
//   label
//---------------------------------------------------------

QString Pipeline::label(int idx) const
      {
      PluginI* p = (*this)[idx];
      if (p)
            return p->label();
      return QString("");
      }

//---------------------------------------------------------
//   name
//---------------------------------------------------------

QString Pipeline::name(int idx) const
      {
      PluginI* p = (*this)[idx];
      if (p)
            return p->name();
      return QString("empty");
      }

//---------------------------------------------------------
//   empty
//---------------------------------------------------------

bool Pipeline::empty(int idx) const
      {
      PluginI* p = (*this)[idx];
      return p == 0;
      }

//---------------------------------------------------------
//   move
//---------------------------------------------------------

void Pipeline::move(int idx, bool up)
      {
      PluginI* p1 = (*this)[idx];
      if (up) {
            (*this)[idx]   = (*this)[idx-1];
            (*this)[idx-1] = p1;
            }
      else {
            (*this)[idx]   = (*this)[idx+1];
            (*this)[idx+1] = p1;
            }
      }

//---------------------------------------------------------
//   showGui
//---------------------------------------------------------

void Pipeline::showGui(int idx, bool flag)
      {
      PluginI* p = (*this)[idx];
      if (p)
            p->showGui(flag);
      }

//---------------------------------------------------------
//   guiVisible
//---------------------------------------------------------

bool Pipeline::guiVisible(int idx)
      {
      PluginI* p = (*this)[idx];
      if (p)
            return p->guiVisible();
      return false;
      }

//---------------------------------------------------------
//   apply
//---------------------------------------------------------

void Pipeline::apply(int ports, unsigned long nframes, float** buffer1)
      {
      // prepare a second set of buffers in case a plugin is not
      // capable of inPlace processing

      float* buffer2[ports];
      float data[nframes * ports];
      for (int i = 0; i < ports; ++i)
            buffer2[i] = data + i * nframes;

      bool swap = false;

      for (iPluginI ip = begin(); ip != end(); ++ip) {
            PluginI* p = *ip;
            if (p && p->on()) {
                  if (p->inPlaceCapable()) {
                        if (swap)
                              p->connect(ports, buffer2, buffer2);
                        else
                              p->connect(ports, buffer1, buffer1);
                        }
                  else {
                        if (swap)
                              p->connect(ports, buffer2, buffer1);
                        else
                              p->connect(ports, buffer1, buffer2);
                        swap = !swap;
                        }
                  p->apply(nframes);
                  }
            }
      if (swap) {
            for (int i = 0; i < ports; ++i)
                  memcpy(buffer1[i], buffer2[i], sizeof(float) * nframes);
            }
      }

//---------------------------------------------------------
//   PluginI
//---------------------------------------------------------

void PluginI::init()
      {
      _plugin           = 0;
      instances         = 0;
      handle            = 0;
      controls          = 0;
      controlPorts      = 0;
      _gui              = 0;
      _on               = true;
      initControlValues = false;
      }

PluginI::PluginI()
      {
      init();
      }

//---------------------------------------------------------
//   PluginI
//---------------------------------------------------------

PluginI::~PluginI()
      {
      if (_plugin)
            _plugin->incReferences(-1);
      if (_gui)
            delete _gui;
      if (controls)
            delete controls;
      if (handle)
            delete handle;
      }

//---------------------------------------------------------
//   initPluginInstance
//    return true on error
//---------------------------------------------------------

bool PluginI::initPluginInstance(Plugin* plug, int channel)
      {
      if (plug == 0) {
            printf("initPluginInstance: zero plugin\n");
            return true;
            }
      _plugin = plug;
      _plugin->incReferences(1);

      QString inst("-" + QString::number(_plugin->instNo()));
      _name  = _plugin->name() + inst;
      _label = _plugin->label() + inst;

      instances = channel/plug->outports();
      if (instances < 1)
            instances = 1;
      handle    = new LADSPA_Handle[instances];
      for (int i = 0; i < instances; ++i) {
            handle[i] = _plugin->instantiate();
            if (handle[i] == 0)
                  return true;
            }

      controlPorts = 0;
      int ports    = _plugin->ports();

      for (int k = 0; k < ports; ++k) {
            LADSPA_PortDescriptor pd = _plugin->portd(k);
            if (pd & LADSPA_PORT_CONTROL)
                  ++controlPorts;
            }
      controls = new Port[controlPorts];
      int i = 0;
      for (int k = 0; k < ports; ++k) {
            LADSPA_PortDescriptor pd = _plugin->portd(k);
            if (pd & LADSPA_PORT_CONTROL) {
                  LADSPA_PortRangeHint range = _plugin->range(k);
                  LADSPA_PortRangeHintDescriptor rh = range.HintDescriptor;
                  double val = 1.0;
                  if (LADSPA_IS_HINT_DEFAULT_MINIMUM(rh))
                        val = range.LowerBound;
                  else if (LADSPA_IS_HINT_DEFAULT_LOW(rh))
                        if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor))
                              val = exp(log10(range.LowerBound) * .75 +
                                 log(range.UpperBound) * .25);
                        else
                              val = range.LowerBound*.75 + range.UpperBound*.25;
                  else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(rh))
                        if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor))
                              val = exp(log(range.LowerBound) * .5 +
                                 log(range.UpperBound) * .5);
                        else
                              val = range.LowerBound*.5 + range.UpperBound*.5;
                  else if (LADSPA_IS_HINT_DEFAULT_HIGH(rh))
                        if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor))
                              val = exp(log(range.LowerBound) * .25 +
                                 log(range.UpperBound) * .75);
                        else
                              val = range.LowerBound*.25 + range.UpperBound*.75;
                  else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(rh))
                        val = range.UpperBound;
                  else if (LADSPA_IS_HINT_DEFAULT_0(rh))
                        val = 0.0;
                  else if (LADSPA_IS_HINT_DEFAULT_1(rh))
                        val = 1.0;
                  else if (LADSPA_IS_HINT_DEFAULT_100(rh))
                        val = 100.0;
                  else if (LADSPA_IS_HINT_DEFAULT_440(rh))
                        val = 440.0;
                  controls[i].val    = val;
                  controls[i].tmpVal = val;
                  ++i;
                  }
            }
      int curPort = 0;
      for (int k = 0; k < ports; ++k) {
            LADSPA_PortDescriptor pd = _plugin->portd(k);
            if (pd & LADSPA_PORT_CONTROL) {
                  for (int i = 0; i < instances; ++i)
                        _plugin->connectPort(handle[i], k, &controls[curPort].val);
                  controls[curPort].idx = k;
                  ++curPort;
                  }
            }
      return false;
      }

//---------------------------------------------------------
//   connect
//---------------------------------------------------------

void PluginI::connect(int ports, float** src, float** dst)
      {
      int port = 0;
      for (int i = 0; i < instances; ++i) {
            for (int k = 0; k < _plugin->ports(); ++k) {
                  if (isAudioIn(k)) {
                        _plugin->connectPort(handle[i], k, src[port]);
                        port = (port + 1) % ports;
                        }
                  }
            }
      port = 0;
      for (int i = 0; i < instances; ++i) {
            for (int k = 0; k < _plugin->ports(); ++k) {
                  if (isAudioOut(k)) {
                        _plugin->connectPort(handle[i], k, dst[port]);
                        ++port;
                        if (port >= ports) {
                              return;
                              }
                        }
                  }
            }
      }

//---------------------------------------------------------
//   setControl
//---------------------------------------------------------

bool PluginI::setControl(const QString& s, double val)
      {
      for (int i = 0; i < controlPorts; ++i) {
            if (_plugin->portName(controls[i].idx) == s) {
                  controls[i].val = controls[i].tmpVal = val;
                  return false;
                  }
            }
      return true;
      }

//---------------------------------------------------------
//   saveConfiguration
//---------------------------------------------------------

void PluginI::writeConfiguration(int level, Xml& xml)
      {
      xml.tag(level++, "plugin file=\"%s\" label=\"%s\" channel=\"%d\"",
         _plugin->lib().latin1(), _plugin->label().latin1(), instances * _plugin->inports());
      for (int i = 0; i < controlPorts; ++i) {
            int idx = controls[i].idx;
            xml.tag(level, "control name=\"%s\" val=\"%f\" /",
               _plugin->portName(idx), controls[i].tmpVal);
            }
      if (_on == false)
            xml.intTag(level, "on", _on);
      if (guiVisible())
            xml.intTag(level, "gui", 1);
      xml.tag(level--, "/plugin");
      }

//---------------------------------------------------------
//   loadControl
//---------------------------------------------------------

bool PluginI::loadControl(Xml& xml)
      {
      QString file;
      QString label;
      QString name("mops");
      double val = 0.0;

      for (;;) {
            Xml::Token token = xml.parse();
            const QString& tag = xml.s1();

            switch (token) {
                  case Xml::Error:
                  case Xml::End:
                        return true;
                  case Xml::TagStart:
                        xml.unknown("PluginI-Control");
                        break;
                  case Xml::Attribut:
                        if (tag == "name")
                              name = xml.s2();
                        else if (tag == "val")
                              val = xml.s2().toDouble();
                        break;
                  case Xml::TagEnd:
                        if (tag == "control") {
                              if (setControl(name, val)) {
                                    printf("config plugin: val <%s>=%f not found\n",
                                       name.latin1(), val);
                                    return false;
                                    }
                              initControlValues = true;
                              }
                        return true;
                  default:
                        break;
                  }
            }
      return true;
      }

//---------------------------------------------------------
//   readConfiguration
//    return true on error
//---------------------------------------------------------

bool PluginI::readConfiguration(Xml& xml, bool readPreset)
      {
      QString file;
      QString label;
      if (!readPreset)
            instances = 1;

      for (;;) {
            Xml::Token token(xml.parse());
            const QString& tag(xml.s1());
            switch (token) {
                  case Xml::Error:
                  case Xml::End:
                        return true;
                  case Xml::TagStart:
                        if (!readPreset && _plugin == 0) {
                              _plugin = plugins.find(file, label);
                              if (_plugin && initPluginInstance(_plugin, instances)) {
                                    _plugin = 0;
                                    xml.parse1();
                                    break;
                                    }
                              }
                        if (tag == "control")
                              loadControl(xml);
                        else if (tag == "on") {
                              bool flag = xml.parseInt();
                              if (!readPreset)
                                    _on = flag;
                              }
                        else if (tag == "gui") {
                              bool flag = xml.parseInt();
                              showGui(flag);
                              }
                        else
                              xml.unknown("PluginI");
                        break;
                  case Xml::Attribut:
                        if (tag == "file") {
                              QString s = xml.s2();
                              if (readPreset) {
                                    if (s != plugin()->lib()) {
                                          printf("reading %s preset for %s\n",
                                             s.latin1(), plugin()->lib().latin1());
                                          return false;
                                          }
                                    }
                              else {
                                    file = s;
                                    }
                              }
                        else if (tag == "label") {
                              if (!readPreset)
                                    label = xml.s2();
                              }
                        else if (tag == "channel") {
                              if (!readPreset)
                                    instances = xml.s2().toInt();
                              }
                        break;
                  case Xml::TagEnd:
                        if (tag == "plugin") {
                              if (!readPreset && _plugin == 0) {
                                    _plugin = plugins.find(file, label);
                                    if (_plugin && initPluginInstance(_plugin, instances))
                                          return true;
                                    }
                              return false;
                              }
                        return true;
                  default:
                        break;
                  }
            }
      return true;
      }

//---------------------------------------------------------
//   showGui
//---------------------------------------------------------

void PluginI::showGui()
      {
      if (_gui == 0)
            makeGui();
      if (_gui->isVisible())
            _gui->hide();
      else
            _gui->show();
      }

void PluginI::showGui(bool flag)
      {
      if (flag) {
            if (_gui == 0)
                  makeGui();
            _gui->show();
            }
      else {
            if (_gui)
                  _gui->hide();
            }
      }

//---------------------------------------------------------
//   guiVisible
//---------------------------------------------------------

bool PluginI::guiVisible()
      {
      return _gui && _gui->isVisible();
      }

//---------------------------------------------------------
//   makeGui
//---------------------------------------------------------

void PluginI::makeGui()
      {
      _gui = new PluginGui(this);
      }

//---------------------------------------------------------
//   apply
//---------------------------------------------------------

void PluginI::apply(int n)
      {
      for (int i = 0; i < controlPorts; ++i)
            controls[i].val = controls[i].tmpVal;
      for (int i = 0; i < instances; ++i)
            _plugin->apply(handle[i], n);
      }

//---------------------------------------------------------
//   PluginDialog
//    select Plugin dialog
//---------------------------------------------------------

PluginDialog::PluginDialog(QWidget* parent, const char* name, bool modal)
  : QDialog(parent, name, modal)
      {
      setCaption(tr("MusE: select plugin"));
      QVBoxLayout* layout = new QVBoxLayout(this);

      pList  = new QListView(this);
      pList->setAllColumnsShowFocus(true);
      pList->addColumn(tr("Lib"),   110);
      pList->addColumn(tr("Label"), 110);
      pList->addColumn(tr("Name"),  200);
      pList->addColumn(tr("AI"),    30);
      pList->addColumn(tr("AO"),    30);
      pList->addColumn(tr("CI"),    30);
      pList->addColumn(tr("CO"),    30);
      pList->addColumn(tr("IP"),    30);
      pList->addColumn(tr("id"),    40);
      pList->addColumn(tr("Maker"), 110);
      pList->addColumn(tr("Copyright"), 110);
      pList->setColumnWidthMode(1, QListView::Maximum);

      for (iPlugin i = plugins.begin(); i != plugins.end(); ++i) {
            int ai = 0;
            int ao = 0;
            int ci = 0;
            int co = 0;
            for (int k = 0; k < i->ports(); ++k) {
                  LADSPA_PortDescriptor pd = i->portd(k);
                  if (pd &  LADSPA_PORT_CONTROL) {
                        if (pd &  LADSPA_PORT_INPUT)
                              ++ci;
                        else
                              ++co;
                        }
                  else {
                        if (pd &  LADSPA_PORT_INPUT)
                              ++ai;
                        else
                              ++ao;
                        }
                  }
            QListViewItem* item = new QListViewItem(pList,
               i->lib(),
               i->label(),
               i->name(),
               QString().setNum(ai),
               QString().setNum(ao),
               QString().setNum(ci),
               QString().setNum(co),
               QString().setNum(i->inPlaceCapable())
               );
            item->setText(8, QString().setNum(i->id()));
            item->setText(9, i->maker());
            item->setText(10, i->copyright());

            }

      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);
      w5->addStretch(1);

      connect(pList,   SIGNAL(doubleClicked(QListViewItem*)), SLOT(accept()));
      connect(cancelB, SIGNAL(clicked()), SLOT(reject()));
      connect(okB,     SIGNAL(clicked()), SLOT(accept()));
      }

//---------------------------------------------------------
//   value
//---------------------------------------------------------

Plugin* PluginDialog::value()
      {
      QListViewItem* item = pList->selectedItem();
      if (item)
            return plugins.find(item->text(0), item->text(1));
      return 0;
      }

//---------------------------------------------------------
//   getPlugin
//---------------------------------------------------------

Plugin* PluginDialog::getPlugin(QWidget* parent)
      {
      PluginDialog* dialog = new PluginDialog(parent);
      if (dialog->exec())
            return dialog->value();
      return 0;
      }

//---------------------------------------------------------
//   activate
//---------------------------------------------------------

void PluginI::activate()
      {
      for (int i = 0; i < instances; ++i)
            _plugin->activate(handle[i]);
      if (initControlValues) {
            for (int i = 0; i < controlPorts; ++i) {
                  controls[i].val = controls[i].tmpVal;
                  }
            }
      else {
            //
            // get initial control values from plugin
            //
            for (int i = 0; i < controlPorts; ++i) {
                  controls[i].tmpVal = controls[i].val;
                  }
            }
      }

const char* presetOpenText = "<img source=\"fileopen\"> "
      "Click this button to load a saved <em>preset</em>.";
const char* presetSaveText = "Click this button to save curent parameter "
      "settings as a <em>preset</em>.  You will be prompted for a file name.";
const char* presetBypassText = "Click this button to bypass effect unit";

//---------------------------------------------------------
//   PluginGui
//---------------------------------------------------------

PluginGui::PluginGui(PluginI* p)
   : QMainWindow(0)
      {
      plugin = p;
      setCaption(plugin->name());

      QToolBar* tools = new QToolBar(tr("File Buttons"), this);
      QToolButton* fileOpen = new QToolButton(*openIcon, tr("Load Preset"),
					    QString::null, this, SLOT(load()),
					    tools, "load preset" );

      QToolButton* fileSave = new QToolButton(*saveIcon, tr("Save Preset"),
					    QString::null,
					    this, SLOT(save()),
					    tools, "save preset");

      QWhatsThis::whatsThisButton(tools);

      onOff = new QToolButton(tools, "bypass");
      onOff->setIconSet(*exitIconS);
      onOff->setToggleButton(true);
      onOff->setOn(plugin->on());
      QToolTip::add(onOff, tr("bypass plugin"));
      connect(onOff, SIGNAL(toggled(bool)), SLOT(bypassToggled(bool)));

      QWhatsThis::add(fileOpen, tr(presetOpenText));
      QWhatsThis::add(onOff, tr(presetBypassText));
      QMimeSourceFactory::defaultFactory()->setPixmap(QString("fileopen"), *openIcon );
      QWhatsThis::add(fileSave, tr(presetSaveText));

      QString id;
      id.setNum(plugin->plugin()->id());
      QString name(museGlobalShare + QString("/plugins/") + id + QString(".ui"));
      QFile uifile(name);
      if (uifile.exists()) {
            QWidget* mw = QWidgetFactory::create(uifile.name(), 0, this);
            setCentralWidget(mw);
            QObjectList* l = mw->queryList(0, "^val[0-9]+", true, true);

            QObjectListIt it(*l);
            QObject *obj;

            while ( (obj = it.current()) != 0 ) {
                  ++it;
                  // ((QButton*)obj)->setEnabled( FALSE );
                  connect(obj, SIGNAL(valueChanged(double,int)), SLOT(parameterChanged(double,int)));
                  }
            delete l;
            }
      else {
            QWidget* mw = new QWidget(this);
            setCentralWidget(mw);
            QGridLayout* grid = new QGridLayout(mw);
            grid->setSpacing(2);

            QSignalMapper* mapper = new QSignalMapper(mw);
            connect(mapper, SIGNAL(mapped(int)), SLOT(parameterChanged(int)));

            int n     = plugin->parameters();
            params    = new GuiParam[n];

            resize(280, n*20+30);

            int style       = Slider::BgTrough | Slider::BgSlot;
            QFontMetrics fm = fontMetrics();
            int h           = fm.height() + 4;
            int lw          = fm.width(QString("-0.00"));

            for (int i = 0; i < n; ++i) {
                  QLabel* label = 0;
                  LADSPA_PortRangeHint range = plugin->range(i);
                  double lower = 0.0;     // default values
                  double upper = 1.0;
                  double val   = plugin->param(i);
                  params[i].hint = range.HintDescriptor;

                  if (LADSPA_IS_HINT_BOUNDED_BELOW(range.HintDescriptor)) {
                        lower = range.LowerBound;
                        }
                  if (LADSPA_IS_HINT_BOUNDED_ABOVE(range.HintDescriptor)) {
                        upper = range.UpperBound;
                        }
                  if (LADSPA_IS_HINT_SAMPLE_RATE(range.HintDescriptor)) {
                        lower *= sampleRate;
                        upper *= sampleRate;
                        }
                  if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) {
                        if (lower == 0.0)
                              lower = 0.001;
                        lower = log10(lower)*20.0;
                        upper = log10(upper)*20.0;
                        val = log10(val) * 20.0;
                        }
                  if (LADSPA_IS_HINT_TOGGLED(range.HintDescriptor)) {
                        params[i].type = GuiParam::GUI_SWITCH;
                        QCheckBox* cb = new QCheckBox(mw, "param");
                        cb->setText(QString(plugin->paramName(i)));
                        cb->setChecked(plugin->param(i) != 0.0);
                        cb->setFixedHeight(h);
                        params[i].actuator = cb;
                        }
                  else {
                        label           = new QLabel(QString(plugin->paramName(i)), mw);
                        params[i].type  = GuiParam::GUI_SLIDER;
//                        params[i].label = new QLabel(mw);
                        params[i].label = new DoubleLabel(val, lower, upper, mw);
                        params[i].label->setFrame(true);
                        params[i].label->setPrecision(2);

//                        params[i].label->setFixedWidth(lw + 12);
                        params[i].label->setMargin(2);
//                        params[i].label->setBackgroundMode(PaletteBase);
//                        params[i].label->setFrameStyle(QFrame::Panel | QFrame::Sunken);
//                        params[i].label->setLineWidth(2);
                        params[i].label->setFixedHeight(h);
//                        char buffer[64];
//                        sprintf(buffer, "%3.2f", val);
//                        params[i].label->setText(QString(buffer));

                        Slider* s = new Slider(mw, "param", Slider::Horizontal,
                           Slider::None, style);
                        s->setFixedHeight(h);
                        s->setRange(lower, upper);
                        s->setValue(val);
                        params[i].actuator = s;
                        }
                  if (params[i].type == GuiParam::GUI_SLIDER) {
                        label->setFixedHeight(20);
                        label->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum));
                        params[i].label->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum));
                        grid->addWidget(label, i, 0);
                        }
                  params[i].actuator->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum));
                  if (params[i].type == GuiParam::GUI_SLIDER) {
                        grid->addWidget(params[i].label,    i, 1);
                        grid->addWidget(params[i].actuator, i, 2);
                        }
                  if (params[i].type == GuiParam::GUI_SWITCH) {
                        grid->addMultiCellWidget(params[i].actuator, i, i, 0, 2);
                        }
                  mapper->setMapping(params[i].actuator, i);
                  if (params[i].type == GuiParam::GUI_SLIDER) {
                        connect(params[i].actuator, SIGNAL(valueChanged(double)), mapper, SLOT(map()));
                        connect(params[i].label, SIGNAL(valueChanged(double,int)), params[i].actuator, SLOT(setValue(double)));
                        }
                  if (params[i].type == GuiParam::GUI_SWITCH)
                        connect(params[i].actuator, SIGNAL(toggled(bool)), mapper, SLOT(map()));
                  }
            grid->setColStretch(2, 10);
            }
      }

//---------------------------------------------------------
//   parameterChanged
//---------------------------------------------------------

void PluginGui::parameterChanged(int param)
      {
      double val;
      if (params[param].type == GuiParam::GUI_SLIDER) {
            val = ((Slider*)params[param].actuator)->value();
            if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) {
                  val = pow(10.0, val/20.0);
                  }
            else if (LADSPA_IS_HINT_INTEGER(params[param].hint))
                  val = rint(val);
            char buffer[64];
            sprintf(buffer, "%3.2f", val);
            params[param].label->setText(QString(buffer));
            }
      else {
            val = ((QCheckBox*)params[param].actuator)->isChecked() ? 1.0 : 0.0;
            }
      plugin->setParam(param, val);
      }

void PluginGui::parameterChanged(double val, int param)
      {
      plugin->setParam(param, val);
      }

//---------------------------------------------------------
//   load
//---------------------------------------------------------

void PluginGui::load()
      {
      QString s("presets/plugins/");
      s += plugin->plugin()->label();
      s += "/";

      QString fn = getOpenFileName(s, preset_file_pattern,
         this, tr("MusE: load preset"));
      if (fn.isEmpty())
            return;
      bool popenFlag;
      FILE* f = fileOpen(this, fn, QString(".pre"), "r", popenFlag, true);
      if (f == 0)
            return;

      Xml xml(f);
      int mode = 0;
      for (;;) {
            Xml::Token token = xml.parse();
            QString tag = xml.s1();
            switch (token) {
                  case Xml::Error:
                  case Xml::End:
                        return;
                  case Xml::TagStart:
                        if (mode == 0 && tag == "muse")
                              mode = 1;
                        else if (mode == 1 && tag == "plugin") {
                              plugin->readConfiguration(xml, true);
                              mode = 0;
                              }
                        else
                              xml.unknown("PluginGui");
                        break;
                  case Xml::Attribut:
                        break;
                  case Xml::TagEnd:
                        if (!mode && tag == "muse")
                              goto ende;
                  default:
                        break;
                  }
            }
ende:
      if (popenFlag)
            pclose(f);
      else
            fclose(f);

      int n = plugin->parameters();
      for (int i = 0; i < n; ++i) {
            if (params[i].type == GuiParam::GUI_SLIDER) {
                  double val = plugin->param(i);
                  params[i].label->setValue(i);
                  if (LADSPA_IS_HINT_LOGARITHMIC(params[i].hint))
                        val = log10(val)*20.0;
                  ((Slider*)params[i].actuator)->setValue(val);
                  }
            else {
                  ((QCheckBox*)params[i].actuator)->setChecked(plugin->param(i) != 0.0);
                  }
            }
      }

//---------------------------------------------------------
//   save
//---------------------------------------------------------

void PluginGui::save()
      {
      QString s("presets/plugins/");
      s += plugin->plugin()->label();
      s += "/";

      QString fn = getSaveFileName(s, preset_file_pattern, this,
        tr("MusE: save preset"));
      if (fn.isEmpty())
            return;
      bool popenFlag;
      FILE* f = fileOpen(this, fn, QString(".pre"), "w", popenFlag, false, true);
      if (f == 0)
            return;
      Xml xml(f);
      xml.header();
      xml.tag(0, "muse version=\"1.0\"");
      plugin->writeConfiguration(1, xml);
      xml.tag(1, "/muse");

      if (popenFlag)
            pclose(f);
      else
            fclose(f);
      }

//---------------------------------------------------------
//   bypassToggled
//---------------------------------------------------------

void PluginGui::bypassToggled(bool val)
      {
      plugin->setOn(val);
      song->update(SC_ROUTE);
      }

//---------------------------------------------------------
//   songChanged
//---------------------------------------------------------

void PluginGui::setOn(bool val)
      {
      onOff->blockSignals(true);
      onOff->setOn(val);
      onOff->blockSignals(false);
      }

