//=========================================================
//  MusE
//  Linux Music Editor
//  $Id: miditransform.cpp,v 1.1.1.1 2003/10/29 10:05:04 wschweer Exp $
//
//  (C) Copyright 2001 Werner Schweer (ws@seh.de)
//=========================================================

#include <stdio.h>
#include <list>
#include <qpushbutton.h>
#include <qlineedit.h>
#include <qcombobox.h>
#include <qlistbox.h>
#include <qspinbox.h>
#include <qmultilineedit.h>
#include <qcheckbox.h>

#include "spinboxFP.h"
#include "event.h"
#include "miditransform.h"
#include "track.h"
#include "song.h"
#include "xml.h"
#include "globals.h"
#include "comboQuant.h"
#include "pitchedit.h"
#include "midithread.h"

static int eventTypeTable[] = {
      1, 6, 4, 7, 8, 10, 11
      };
static int procVal2Map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 10, 11 };

//---------------------------------------------------------
//   MidiTransformation
//---------------------------------------------------------

class MidiTransformation {
   public:
      QString name;
      QString comment;

      ValOp selEventOp;
      MidiEvent::EventType selType;

      ValOp selVal1;
      int selVal1a, selVal1b;
      ValOp selVal2;
      int selVal2a, selVal2b;
      ValOp selLen;
      int selLenA, selLenB;
      ValOp selRange;
      int selRangeA, selRangeB;

      TransformOperator procEvent;
      MidiEvent::EventType eventType;
      TransformOperator procVal1;
      int procVal1a, procVal1b;
      TransformOperator procVal2;
      int procVal2a, procVal2b;
      TransformOperator procLen;
      int procLenA;
      TransformOperator procPos;
      int procPosA;

      TransformFunction funcOp;
      int quantVal;
      bool selectedTracks;
      bool insideLoop;

      MidiTransformation(const QString& s) {
            name       = s;
            selEventOp = All;
            selType    = MidiEvent::Note;
            selVal1    = Ignore;
            selVal1a   = 0;
            selVal1b   = 0;
            selVal2    = Ignore;
            selVal2a   = 0;
            selVal2b   = 0;
            selLen     = Ignore;
            selLenA    = 0;
            selLenB    = 0;
            selRange   = Ignore;
            selRangeA  = 0;
            selRangeB  = 0;
            procEvent  = Keep;
            eventType  = MidiEvent::Note;
            procVal1   = Keep;
            procVal1a  = 0;
            procVal1b  = 0;
            procVal2   = Keep;
            procVal2a  = 0;
            procVal2b  = 0;
            procLen    = Keep;
            procLenA   = 0;
            procPos    = Keep;
            procPosA   = 0;
            funcOp     = Select;
            quantVal   = division;
            selectedTracks = false;
            insideLoop = false;
            }
      void write(int level, Xml& xml);
      };

class MidiTransformPrivate {
   public:
      MidiTransformation* cmt;
      int cindex;                   // current index in preset list
      };

typedef std::list<MidiTransformation* > MidiTransformationList;
typedef std::list<MidiTransformation* >::iterator iMidiTransformation;
typedef std::list<MidiTransformation* >::const_iterator ciMidiTransformation;

static MidiTransformationList mtlist;

//---------------------------------------------------------
//   MidiTransformDialog
//    Widgets:
//    presetList nameEntry commentEntry
//    selEventOp   selType
//    selVal1Op    selVal1a selVal1b
//    selVal2Op    selVal2a selVal2b
//    selLenOp     selLenA  selLenB
//    selRangeOp   selBarA  selBarB
//
//    procEventOp  procType
//    procVal1Op   procVal1a procVal1b
//    procVal2Op   procVal2a procVal2b
//    procLenOp    procLenA
//    procPosOp    procPosA
//    funcOp       funcQuantVal
//    processAll   selectedTracks   insideLoop
//    buttonNew buttonDelete buttonApply buttonOk
//---------------------------------------------------------

MidiTransformerDialog::MidiTransformerDialog(QWidget* parent,
   const char* name, bool modal, WFlags fl)
   : MidiTransformDialogBase(parent, name, modal, fl)
      {
      data         = new MidiTransformPrivate;
      data->cmt    = 0;
      data->cindex = -1;
      connect(buttonApply,  SIGNAL(clicked()),      SLOT(apply()));
      connect(buttonNew,    SIGNAL(clicked()),      SLOT(presetNew()));
      connect(buttonDelete, SIGNAL(clicked()),      SLOT(presetDelete()));
      connect(selEventOp,   SIGNAL(activated(int)), SLOT(selEventOpSel(int)));
      connect(selType,      SIGNAL(activated(int)), SLOT(selTypeSel(int)));
      connect(selVal1Op,    SIGNAL(activated(int)), SLOT(selVal1OpSel(int)));
      connect(selVal2Op,    SIGNAL(activated(int)), SLOT(selVal2OpSel(int)));
      connect(selLenOp,     SIGNAL(activated(int)), SLOT(selLenOpSel(int)));
      connect(selRangeOp,   SIGNAL(activated(int)), SLOT(selRangeOpSel(int)));
      connect(procEventOp,  SIGNAL(activated(int)), SLOT(procEventOpSel(int)));
      connect(procType,     SIGNAL(activated(int)), SLOT(procEventTypeSel(int)));
      connect(procVal1Op,   SIGNAL(activated(int)), SLOT(procVal1OpSel(int)));
      connect(procVal2Op,   SIGNAL(activated(int)), SLOT(procVal2OpSel(int)));
      connect(procLenOp,    SIGNAL(activated(int)), SLOT(procLenOpSel(int)));
      connect(procPosOp,    SIGNAL(activated(int)), SLOT(procPosOpSel(int)));
      connect(funcOp,       SIGNAL(activated(int)), SLOT(funcOpSel(int)));
      connect(funcQuantVal, SIGNAL(valueChanged(int)), SLOT(funcQuantValSel(int)));
      connect(presetList,   SIGNAL(highlighted(QListBoxItem*)),
         SLOT(presetChanged(QListBoxItem*)));
      connect(nameEntry,    SIGNAL(textChanged(const QString&)),
         SLOT(nameChanged(const QString&)));
      connect(commentEntry,    SIGNAL(textChanged()), SLOT(commentChanged()));

      connect(selVal1a,  SIGNAL(valueChanged(int)), SLOT(selVal1aChanged(int)));
      connect(selVal1b,  SIGNAL(valueChanged(int)), SLOT(selVal1bChanged(int)));
      connect(selVal2a,  SIGNAL(valueChanged(int)), SLOT(selVal2aChanged(int)));
      connect(selVal2b,  SIGNAL(valueChanged(int)), SLOT(selVal2bChanged(int)));
      connect(selLenA,   SIGNAL(valueChanged(int)), SLOT(selLenAChanged(int)));
      connect(selLenB,   SIGNAL(valueChanged(int)), SLOT(selLenBChanged(int)));
      connect(selBarA,   SIGNAL(valueChanged(int)), SLOT(selBarAChanged(int)));
      connect(selBarB,   SIGNAL(valueChanged(int)), SLOT(selBarBChanged(int)));
      connect(procVal1a, SIGNAL(valueChanged(int)), SLOT(procVal1aChanged(int)));
      connect(procVal1b, SIGNAL(valueChanged(int)), SLOT(procVal1bChanged(int)));
      connect(procVal2a, SIGNAL(valueChanged(int)), SLOT(procVal2aChanged(int)));
      connect(procVal2b, SIGNAL(valueChanged(int)), SLOT(procVal2bChanged(int)));
      connect(procLenA,  SIGNAL(valueChanged(int)), SLOT(procLenAChanged(int)));
      connect(procPosA,  SIGNAL(valueChanged(int)), SLOT(procPosAChanged(int)));

      connect(processAll, SIGNAL(toggled(bool)), SLOT(processAllChanged(bool)));
      connect(selectedTracks, SIGNAL(toggled(bool)), SLOT(selectedTracksChanged(bool)));
      connect(insideLoop, SIGNAL(toggled(bool)), SLOT(insideLoopChanged(bool)));

      //---------------------------------------------------
      //  populate preset list
      //---------------------------------------------------

      for (iMidiTransformation i = mtlist.begin(); i != mtlist.end(); ++i) {
            presetList->insertItem((*i)->name);
            if (data->cmt == 0)
                  data->cmt = *i;
            }
      if (data->cmt == 0) {
            data->cmt = new MidiTransformation(tr("New"));
            mtlist.push_back(data->cmt);
            presetList->insertItem(tr("New"));
            }
      data->cindex = 0;
      presetList->setCurrentItem(0);
      }

//---------------------------------------------------------
//   ~MidiTransformDialog
//---------------------------------------------------------

MidiTransformerDialog::~MidiTransformerDialog()
      {
      delete data;
      }

//---------------------------------------------------------
//   writeMidiTransforms
//---------------------------------------------------------

void writeMidiTransforms(int level, Xml& xml)
      {
      for (iMidiTransformation i = mtlist.begin(); i != mtlist.end(); ++i) {
            (*i)->write(level, xml);
            }
      }

//---------------------------------------------------------
//   write
//---------------------------------------------------------

void MidiTransformation::write(int level, Xml& xml)
      {
      xml.tag(level++, "midiTransform");
      xml.strTag(level, "name", name);
      xml.strTag(level, "comment", comment);
      xml.intTag(level, "function", int(funcOp));
      if (funcOp == Quantize) {
            xml.intTag(level, "quantVal", quantVal);
            }
      if (funcOp == Transform || funcOp == Insert) {
            if (procEvent != Keep) {
                  xml.intTag(level, "procEventOp", int(procEvent));
                  xml.intTag(level, "eventType", int(eventType));
                  }
            if (procVal1 != Keep) {
                  xml.intTag(level, "procVal1Op", int(procVal1));
                  xml.intTag(level, "procVal1a", procVal1a);
                  xml.intTag(level, "procVal1b", procVal1b);
                  }
            if (procVal2 != Keep) {
                  xml.intTag(level, "procVal2Op", int(procVal2));
                  xml.intTag(level, "procVal2a", procVal2a);
                  xml.intTag(level, "procVal2b", procVal2b);
                  }
            if (procLen != Keep) {
                  xml.intTag(level, "procLenOp", int(procLen));
                  xml.intTag(level, "procLen",  procLenA);
                  }
            if (procPos != Keep) {
                  xml.intTag(level, "procPosOp", int(procPos));
                  xml.intTag(level, "procPos", procPosA);
                  }
            }
      if (selEventOp != Ignore) {
            xml.intTag(level, "selEventOp", int(selEventOp));
            xml.intTag(level, "selEventType", int(selType));
            }
      if (selVal1 != Ignore) {
            xml.intTag(level, "selVal1Op", int(selVal1));
            xml.intTag(level, "selVal1a", selVal1a);
            xml.intTag(level, "selVal1b", selVal1b);
            }
      if (selVal2 != Ignore) {
            xml.intTag(level, "selVal2Op", int(selVal2));
            xml.intTag(level, "selVal2a", selVal2a);
            xml.intTag(level, "selVal2b", selVal2b);
            }
      if (selLen != Ignore) {
            xml.intTag(level, "selLenOp", int(selLen));
            xml.intTag(level, "selLenA", selLenA);
            xml.intTag(level, "selLenB", selLenB);
            }
      if (selRange != Ignore) {
            xml.intTag(level, "selRangeOp", int(selRange));
            xml.intTag(level, "selRangeA", selRangeA);
            xml.intTag(level, "selRangeB", selRangeB);
            }
      xml.etag(level, "midiTransform");
      }

//---------------------------------------------------------
//   readMidiTransform
//---------------------------------------------------------

void readMidiTransform(Xml& xml)
      {
      MidiTransformation trans(QWidget::tr("new"));

      for (;;) {
            Xml::Token token = xml.parse();
            const QString& tag = xml.s1();
            switch (token) {
                  case Xml::Error:
                  case Xml::End:
                        return;
                  case Xml::TagStart:
                        if (tag == "name")
                              trans.name = xml.parse1();
                        else if (tag == "comment")
                              trans.comment = xml.parse1();
                        else if (tag == "function")
                              trans.funcOp = TransformFunction(xml.parseInt());
                        else if (tag == "quantVal")
                              trans.quantVal = xml.parseInt();
                        else if (tag == "procEventOp")
                              trans.procEvent = TransformOperator(xml.parseInt());
                        else if (tag == "eventType")
                              trans.eventType = MidiEvent::EventType(xml.parseInt());
                        else if (tag == "procVal1Op")
                              trans.procVal1 = TransformOperator(xml.parseInt());
                        else if (tag == "procVal1a")
                              trans.procVal1a = xml.parseInt();
                        else if (tag == "procVal1b")
                              trans.procVal1b = xml.parseInt();
                        else if (tag == "procVal2Op")
                              trans.procVal2 = TransformOperator(xml.parseInt());
                        else if (tag == "procVal2a")
                              trans.procVal2a = xml.parseInt();
                        else if (tag == "procVal2b")
                              trans.procVal2b = xml.parseInt();
                        else if (tag == "procLenOp")
                              trans.procLen = TransformOperator(xml.parseInt());
                        else if (tag == "procLen")
                              trans.procLenA = xml.parseInt();
                        else if (tag == "procPosOp")
                              trans.procPos = TransformOperator(xml.parseInt());
                        else if (tag == "procPos")
                              trans.procPosA = xml.parseInt();
                        else if (tag == "selEventOp")
                              trans.selEventOp = ValOp(xml.parseInt());
                        else if (tag == "selEventType")
                              trans.selType = MidiEvent::EventType(xml.parseInt());
                        else if (tag == "selVal1Op")
                              trans.selVal1 = ValOp(xml.parseInt());
                        else if (tag == "selVal1a")
                              trans.selVal1a = xml.parseInt();
                        else if (tag == "selVal1b")
                              trans.selVal1b = xml.parseInt();
                        else if (tag == "selVal2Op")
                              trans.selVal2 = ValOp(xml.parseInt());
                        else if (tag == "selVal2a")
                              trans.selVal2a = xml.parseInt();
                        else if (tag == "selVal2b")
                              trans.selVal2b = xml.parseInt();
                        else if (tag == "selLenOp")
                              trans.selLen = ValOp(xml.parseInt());
                        else if (tag == "selLenA")
                              trans.selLenA = xml.parseInt();
                        else if (tag == "selLenB")
                              trans.selLenB = xml.parseInt();
                        else if (tag == "selRangeOp")
                              trans.selRange = ValOp(xml.parseInt());
                        else if (tag == "selRangeA")
                              trans.selRangeA = xml.parseInt();
                        else if (tag == "selRangeB")
                              trans.selRangeB = xml.parseInt();
                        else
                              xml.unknown("midiTransform");
                        break;
                  case Xml::TagEnd:
                        if (xml.s1() == "midiTransform") {
                              MidiTransformation* t = new MidiTransformation(trans);
                              mtlist.push_back(t);
                              return;
                              }
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   accept
//---------------------------------------------------------

void MidiTransformerDialog::accept()
      {
      apply();
      reject();
      }
#if 0
//---------------------------------------------------------
//   reject
//---------------------------------------------------------

void MidiTransformerDialog::reject()
      {
      close(false);
      }
#endif

//---------------------------------------------------------
//   transformEvent
//    subfunction of processEvent()
//---------------------------------------------------------

void MidiTransformerDialog::transformEvent(MidiEvent* event, MidiPart* part,
   MidiPart* newPart)
      {
      MidiTransformation* cmt = data->cmt;
      MidiEvent* newEvent = new MidiEvent(*event);

      if (cmt->procEvent != Keep)
            newEvent->setType(cmt->eventType);

      //---------------------------------------------------
      //    transform value A
      //---------------------------------------------------

      int val = newEvent->dataA();
      switch (cmt->procVal1) {
            case Keep:
                  break;
            case Plus:
                  val += cmt->procVal1a;
                  break;
            case Minus:
                  val -= cmt->procVal1a;
                  break;
            case Multiply:
                  val = int(val * (cmt->procVal1a/100.0) + .5);
                  break;
            case Divide:
                  val = int(val / (cmt->procVal1a/100.0) + .5);
                  break;
            case Fix:
                  val = cmt->procVal1a;
                  break;
            case Value:
                  val = cmt->procVal2a;
                  break;
            case Invert:
                  val = 128 - val;
                  break;
            case ScaleMap:
                  printf("scale map not implemented\n");
                  break;
            case Flip:
                  val = cmt->procVal1a - val;
                  break;
            case Dynamic:           // "crescendo"
                  val = (((cmt->procVal2b-cmt->procVal2a)
                        * (newEvent->posTick() - song->lpos()))
                        / (song->rpos() - song->lpos())) + cmt->procVal2a;
                  break;
            case Random:
                  {
                  int range = cmt->procVal1b - cmt->procVal1a;
                  if (range > 0)
                        val = (rand() % range) + cmt->procVal1a;
                  else if (range < 0)
                        val = (rand() % -range) + cmt->procVal1b;
                  else
                        val = cmt->procVal1a;
                  }
                  break;
            }
      if (val < 0)
            val = 0;
      if (val > 127)
            val = 127;
      newEvent->setA(val);

      //---------------------------------------------------
      //    transform value B
      //---------------------------------------------------

      val = newEvent->dataB();
      switch (cmt->procVal2) {
            case Plus:
                  val += cmt->procVal2a;
                  break;
            case Minus:
                  val -= cmt->procVal2a;
                  break;
            case Multiply:
                  val = int(val * (cmt->procVal2a/100.0) + .5);
                  break;
            case Divide:
                  val = int(val / (cmt->procVal2a/100.0) + .5);
                  break;
            case Fix:
                  val = cmt->procVal2a;
                  break;
            case Value:
                  val = cmt->procVal1a;
                  break;
            case Invert:
                  val = 128 - val;
                  break;
            case Dynamic:
                  val = (((cmt->procVal2b-cmt->procVal2a)
                        * (newEvent->posTick() - song->lpos()))
                        / (song->rpos() - song->lpos())) + cmt->procVal2a;
                  break;
            case Random:
                  {
                  int range = cmt->procVal2b - cmt->procVal2a;
                  if (range > 0)
                        val = (rand() % range) + cmt->procVal2a;
                  else if (range < 0)
                        val = (rand() % -range) + cmt->procVal2b;
                  else
                        val = cmt->procVal1a;
                  }
                  break;
            case ScaleMap:
            case Keep:
            case Flip:
                  break;
            }
      if (val < 0)
            val = 0;
      if (val > 127)
            val = 127;
      newEvent->setB(val);

      //---------------------------------------------------
      //    transform len
      //---------------------------------------------------

      int len = newEvent->lenTick();
      switch (cmt->procLen) {
            case Plus:
                  len += cmt->procLenA;
                  break;
            case Minus:
                  len -= cmt->procLenA;
                  break;
            case Multiply:
                  len = int(val * (cmt->procLenA/100.0) + .5);
                  break;
            case Divide:
                  len = int(val / (cmt->procLenA/100.0) + .5);
                  break;
            case Fix:
                  len = cmt->procLenA;
                  break;
            case Invert:
            case ScaleMap:
            case Dynamic:
            case Random:
            case Keep:
            case Flip:
            case Value:
                  break;
            }
      if (len < 0)
            len = 0;
      newEvent->setLenTick(len);

      //---------------------------------------------------
      //    transform pos
      //---------------------------------------------------

      int pos = newEvent->posTick();
      switch (cmt->procPos) {
            case Plus:
                  pos += cmt->procPosA;
                  break;
            case Minus:
                  pos -= cmt->procPosA;
                  break;
            case Multiply:
                  pos = int(val * (cmt->procPosA/100.0) + .5);
                  break;
            case Divide:
                  pos = int(val / (cmt->procPosA/100.0) + .5);
                  break;
            case Fix:
            case Invert:
            case ScaleMap:
            case Dynamic:
            case Random:
            case Keep:
            case Flip:
            case Value:
                  break;
            }
      if (pos < 0)
            pos = 0;
      newEvent->setPosTick(pos);

      switch(data->cmt->funcOp) {
            case Transform:
                  midiThread->msgChangeEvent(event, newEvent, part, false);
                  break;
            case Insert:
                  midiThread->msgAddEvent(newEvent, part, false);
                  break;
            case Extract:
                  midiThread->msgDeleteEvent(event, part, false);
            case Copy:
                  newPart->addEvent(newEvent);
                  break;
            default:
                  break;
            }
      }

//---------------------------------------------------------
//   processEvent
//---------------------------------------------------------

void MidiTransformerDialog::processEvent(MidiEvent* event, MidiPart* part, MidiPart* newPart)
      {
      switch(data->cmt->funcOp) {
            case Select:
                  break;
            case Quantize:
                  {
                  int tick = event->posTick();
                  int rt = sigmap.raster(tick, data->cmt->quantVal) - tick;
                  if (tick != rt) {
                        MidiEvent* newEvent = new MidiEvent(*event);
                        newEvent->setPosTick(rt);
                        midiThread->msgChangeEvent(event, newEvent, part, false);
                        }
                  }
                  break;
            case Delete:
                  midiThread->msgDeleteEvent(event, part, false);
                  break;
            case Transform:
            case Insert:
            case Copy:
            case Extract:
                  transformEvent(event, part, newPart);
                  break;
            }
      }

//---------------------------------------------------------
//   isSelected
//    apply Select filter
//    return true if event is selected
//---------------------------------------------------------

bool MidiTransformerDialog::isSelected(MidiEvent* event, MidiPart*)
      {
      MidiTransformation* cmt = data->cmt;

      switch (cmt->selEventOp) {
            case Equal:
                  if (event->type() != cmt->selType) {
//                        printf("event type %d, selType %d\n",
//                           event->type(), cmt->selType);
                        return false;
                        }
                  break;
            case Unequal:
                  if (event->type() == cmt->selType)
                        return false;
                  break;
            default:
                  break;
            }
      switch (cmt->selVal1) {
            case Ignore:
                  break;
            case Equal:
                  if (event->dataA() != cmt->selVal1a)
                        return false;
                  break;
            case Unequal:
                  if (event->dataA() == cmt->selVal1a)
                        return false;
                  break;
            case Higher:
                  if (event->dataA() <= cmt->selVal1a)
                        return false;
                  break;
            case Lower:
                  if (event->dataA() >= cmt->selVal1a)
                        return false;
                  break;
            case Inside:
                  if ((event->dataA() < cmt->selVal1a)
                     || (event->dataA() >= cmt->selVal1b))
                        return false;
                  break;
            case Outside:
                  if ((event->dataA() >= cmt->selVal1a)
                     && (event->dataA() < cmt->selVal1b))
                        return false;
                  break;
            }
      switch (cmt->selVal2) {
            case Ignore:
                  break;
            case Equal:
                  if (event->dataB() != cmt->selVal2a)
                        return false;
                  break;
            case Unequal:
                  if (event->dataB() == cmt->selVal2a)
                        return false;
                  break;
            case Higher:
                  if (event->dataB() <= cmt->selVal2a)
                        return false;
                  break;
            case Lower:
                  if (event->dataB() >= cmt->selVal2a)
                        return false;
                  break;
            case Inside:
                  if ((event->dataB() < cmt->selVal2a)
                     || (event->dataB() >= cmt->selVal2b))
                        return false;
                  break;
            case Outside:
                  if ((event->dataB() >= cmt->selVal2a)
                     && (event->dataB() < cmt->selVal2b))
                        return false;
                  break;
            }
      int len = event->lenTick();
      switch (cmt->selLen) {
            case Ignore:
                  break;
            case Equal:
                  if (len != cmt->selLenA)
                        return false;
                  break;
            case Unequal:
                  if (len == cmt->selLenA)
                        return false;
                  break;
            case Higher:
                  if (len <= cmt->selLenA)
                        return false;
                  break;
            case Lower:
                  if (len >= cmt->selLenA)
                        return false;
                  break;
            case Inside:
                  if ((len < cmt->selLenA) || (len >= cmt->selLenB))
                        return false;
                  break;
            case Outside:
                  if ((len >= cmt->selLenA) && (len < cmt->selLenB))
                        return false;
                  break;
            }
      int bar, beat, tick;
      sigmap.tickValues(event->posTick(), &bar, &beat, &tick);
      int beat1 = cmt->selRangeA / 1000;
      int tick1 = cmt->selRangeA % 1000;
      int beat2 = cmt->selRangeB / 1000;
      int tick2 = cmt->selRangeB % 1000;
      switch (cmt->selRange) {
            case Ignore:
                  break;
            case Equal:
                  if (beat != beat1 || tick != tick1)
                        return false;
                  break;
            case Unequal:
                  if (beat == beat1 && tick == tick1)
                        return false;
                  break;
            case Higher:
                  if (beat <= beat1)
                        return false;
                  if (beat == beat1 && tick <= tick1)
                        return false;
                  break;
            case Lower:
                  if (beat >= beat1)
                        return false;
                  if (beat == beat1 && tick >= tick1)
                        return false;
                  break;
            case Inside:
                  if ((beat < beat1) || (beat >= beat2))
                        return false;
                  if (beat == beat1 && tick < tick1)
                        return false;
                  if (beat == beat2 && tick >= tick2)
                        return false;
                  break;
            case Outside:
                  if ((beat >= beat1) || (beat < beat2))
                        return false;
                  if (beat == beat1 && tick >= tick1)
                        return false;
                  if (beat == beat2 && tick < tick2)
                        return false;
                  break;
            }
      return true;
      }

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

void MidiTransformerDialog::apply()
      {
      TrackList* tracks = song->tracks();
      int flags = 0;
      song->startUndo();
      bool copyExtract = (data->cmt->funcOp == Copy)
                         || (data->cmt->funcOp == Extract);

      TrackList tl;
      for (iTrack t = tracks->begin(); t != tracks->end(); ++t) {
            if (((*t)->type() != Track::MIDI) && ((*t)->type() != Track::DRUM))
                  continue;
            if (data->cmt->selectedTracks && !(*t)->selected())
                  continue;
            MidiTrack* newTrack = 0;
            PartList *pl = (*t)->parts();
            if (copyExtract) {
                  // check wether we must generate a new track
                  for (iPart p = pl->begin(); p != pl->end(); ++p) {
                        MidiPart* part = (MidiPart *) p->second;
                        EventList* el = part->events();
                        for (iEvent i = el->begin(); i != el->end(); ++i) {
                              MidiEvent* event = (MidiEvent *) i->second;
                              int tick = event->posTick();
                              if (data->cmt->insideLoop && (tick < song->lpos() || tick >= song->rpos()))
                                    continue;
                              if (isSelected(event, part)) {
                                    newTrack = new MidiTrack();
                                    tl.push_back(newTrack);
                                    break;
                                    }
                              }
                        if (newTrack)
                              break;
                        }
                  }

            for (iPart p = pl->begin(); p != pl->end(); ++p) {
                  MidiPart* part = (MidiPart *) p->second;
                  MidiPart* newPart = 0;
                  EventList* el = part->events();
                  if (copyExtract) {
                        // check wether we must generate a new part
                        for (iEvent i = el->begin(); i != el->end(); ++i) {
                              MidiEvent* event = (MidiEvent *) i->second;
                              int tick = event->posTick();
                              if (data->cmt->insideLoop && (tick < song->lpos() || tick >= song->rpos()))
                                    continue;
                              if (isSelected(event, part)) {
                                    newPart = new MidiPart(newTrack);
                                    newPart->setName(part->name());
                                    newPart->setColorIndex(part->colorIndex());
                                    newPart->setPosTick(part->posTick());
                                    newPart->setLenTick(part->lenTick());
                                    song->addPart(newPart);
                                    flags |= SC_PART_INSERTED;
                                    break;
                                    }
                              }
                        }
                  EventList pel;
                  for (iEvent i = el->begin(); i != el->end(); ++i) {
                        MidiEvent* event = (MidiEvent *) i->second;
                        int tick = event->posTick();
                        if (data->cmt->insideLoop && (tick < song->lpos() || tick >= song->rpos()))
                              continue;
                        int flag = isSelected(event, part);
                        if (data->cmt->funcOp == Select)
                              event->setSelected(flag);
                        else if (flag)
                              pel.add(event);
                        }
                  for (iEvent i = pel.begin(); i != pel.end(); ++i) {
                        MidiEvent* event = (MidiEvent *) i->second;
                        processEvent(event, part, newPart);
                        }
                  }
            }
      if (!tl.empty()) {
            flags |= SC_TRACK_INSERTED;
            for (iTrack t = tl.begin(); t != tl.end(); ++t)
                  midiThread->msgAddTrack(*t, false);
            }

      switch(data->cmt->funcOp) {
            case Select:
                  flags |= SC_SELECTION;
                  break;
            case Quantize:
                  flags |= SC_EVENT_MODIFIED;
                  break;
            case Delete:
                  flags |= SC_EVENT_REMOVED;
                  break;
            case Transform:
                  flags |= SC_EVENT_MODIFIED;
                  break;
            case Insert:
                  flags |= SC_EVENT_INSERTED;
                  break;
            case Copy:
                  flags |= SC_EVENT_INSERTED;
            case Extract:
                  break;
            }
      song->endUndo(flags);
      }

//---------------------------------------------------------
//   setValOp
//---------------------------------------------------------

void MidiTransformerDialog::setValOp(QWidget* a, QWidget* b, ValOp op)
      {
      switch (op) {
            case Ignore:
                  a->setEnabled(false);
                  b->setEnabled(false);
                  break;
            case Equal:
            case Unequal:
            case Higher:
            case Lower:
                  a->setEnabled(true);
                  b->setEnabled(false);
                  break;
            case Inside:
            case Outside:
                  a->setEnabled(true);
                  b->setEnabled(true);
                  break;
            }
      }

//---------------------------------------------------------
//   selEventOpSel
//---------------------------------------------------------

void MidiTransformerDialog::selEventOpSel(int val)
      {
      selType->setEnabled(val != All);
      data->cmt->selEventOp = ValOp(val);
      selVal1aChanged(data->cmt->selVal1a);
      selVal1bChanged(data->cmt->selVal1b);
      }

//---------------------------------------------------------
//   selTypeSel
//---------------------------------------------------------

void MidiTransformerDialog::selTypeSel(int val)
      {
      data->cmt->selType = MidiEvent::EventType(eventTypeTable[val]);
      selVal1aChanged(data->cmt->selVal1a);
      selVal1bChanged(data->cmt->selVal1b);
      }

//---------------------------------------------------------
//   selVal1OpSel
//---------------------------------------------------------

void MidiTransformerDialog::selVal1OpSel(int val)
      {
      setValOp(selVal1a, selVal1b, ValOp(val));
      data->cmt->selVal1 = ValOp(val);
      }

//---------------------------------------------------------
//   selVal2OpSel
//---------------------------------------------------------

void MidiTransformerDialog::selVal2OpSel(int val)
      {
      setValOp(selVal2a, selVal2b, ValOp(val));
      data->cmt->selVal2 = ValOp(val);
      }

//---------------------------------------------------------
//   selLenOpSel
//---------------------------------------------------------

void MidiTransformerDialog::selLenOpSel(int val)
      {
      setValOp(selLenA, selLenB, ValOp(val));
      data->cmt->selLen = ValOp(val);
      }

//---------------------------------------------------------
//   selRangeOpSel
//---------------------------------------------------------

void MidiTransformerDialog::selRangeOpSel(int val)
      {
      setValOp(selBarA, selBarB, ValOp(val));
      data->cmt->selRange = ValOp(val);
      }

//---------------------------------------------------------
//   procEventOpSel
//---------------------------------------------------------

void MidiTransformerDialog::procEventOpSel(int val)
      {
      TransformOperator op = val == 0 ? Keep : Fix;
      procType->setEnabled(op == Fix);
      data->cmt->procEvent = op;
      }

//---------------------------------------------------------
//   procEventTypeSel
//---------------------------------------------------------

void MidiTransformerDialog::procEventTypeSel(int val)
      {
      data->cmt->eventType = MidiEvent::EventType(eventTypeTable[val]);
      }

//---------------------------------------------------------
//   procVal1OpSel
//---------------------------------------------------------

void MidiTransformerDialog::procVal1OpSel(int val)
      {
      data->cmt->procVal1 = TransformOperator(val);
      switch(TransformOperator(val)) {
            case Keep:
            case Invert:
                  procVal1a->setEnabled(false);
                  procVal1b->setEnabled(false);
                  break;
            case Multiply:
            case Divide:
                  procVal1a->setEnabled(true);
                  procVal1a->setPrecision(2);
                  procVal1b->setEnabled(false);
                  break;
            case Plus:
            case Minus:
            case Fix:
            case Value:
            case Flip:
                  procVal1a->setPrecision(0);
                  procVal1a->setEnabled(true);
                  procVal1b->setEnabled(false);
                  break;
            case Random:
            case ScaleMap:
            case Dynamic:
                  procVal1a->setPrecision(0);
                  procVal1a->setEnabled(true);
                  procVal1b->setEnabled(true);
                  break;
            }
      }

//---------------------------------------------------------
//   procVal2OpSel
//---------------------------------------------------------

void MidiTransformerDialog::procVal2OpSel(int val)
      {
      TransformOperator op = TransformOperator(procVal2Map[val]);
      data->cmt->procVal2 = op;

      switch (op) {
            case Keep:
            case Invert:
                  procVal2a->setEnabled(false);
                  procVal2b->setEnabled(false);
                  break;
            case Multiply:
            case Divide:
                  procVal2a->setEnabled(true);
                  procVal2a->setPrecision(2);
                  procVal2b->setEnabled(false);
                  break;
            case Plus:
            case Minus:
            case Fix:
            case Value:
                  procVal2a->setPrecision(0);
                  procVal2a->setEnabled(true);
                  procVal2b->setEnabled(false);
                  break;
            case Random:
            case Dynamic:
                  procVal2a->setPrecision(0);
                  procVal2a->setEnabled(true);
                  procVal2b->setEnabled(true);
                  break;
            default:
                  break;
            }
      }

//---------------------------------------------------------
//   procLenOpSel
//---------------------------------------------------------

void MidiTransformerDialog::procLenOpSel(int val)
      {
      TransformOperator op = TransformOperator(val);
      data->cmt->procLen = op;

      switch (op) {
            case Keep:
            case Invert:
                  procLenA->setEnabled(false);
                  break;
            case Plus:
            case Minus:
            case Fix:
                  procLenA->setPrecision(0);
                  procLenA->setEnabled(true);
                  break;
            case Multiply:
            case Divide:
                  procLenA->setPrecision(2);
                  procLenA->setEnabled(true);
                  break;
            default:
                  break;
            }
      }

//---------------------------------------------------------
//   procPosOpSel
//---------------------------------------------------------

void MidiTransformerDialog::procPosOpSel(int val)
      {
      TransformOperator op = TransformOperator(val);
      data->cmt->procPos = op;

      switch (op) {
            case Keep:
            case Invert:
                  procPosA->setEnabled(false);
                  break;
            case Multiply:
            case Divide:
                  procPosA->setPrecision(2);
                  procPosA->setEnabled(true);
                  break;
            case Plus:
            case Minus:
                  procPosA->setPrecision(0);
                  procPosA->setEnabled(true);
                  break;
            default:
                  break;
            }
      }

//---------------------------------------------------------
//   funcOpSel
//---------------------------------------------------------

void MidiTransformerDialog::funcOpSel(int val)
      {
      funcQuantVal->setEnabled(val == Quantize);
      bool isFuncOp = val == Transform || val == Insert;

      procEventOp->setEnabled(isFuncOp);
      procType->setEnabled(isFuncOp);
      procVal1Op->setEnabled(isFuncOp);
      procVal1a->setEnabled(isFuncOp);
      procVal1b->setEnabled(isFuncOp);
      procVal2Op->setEnabled(isFuncOp);
      procVal2a->setEnabled(isFuncOp);
      procVal2b->setEnabled(isFuncOp);
      procLenOp->setEnabled(isFuncOp);
      procLenA->setEnabled(isFuncOp);
      procPosOp->setEnabled(isFuncOp);
      procPosA->setEnabled(isFuncOp);
      if (isFuncOp) {
            procEventOpSel(data->cmt->procEvent);
            procVal1OpSel(data->cmt->procVal1);
            procVal2OpSel(data->cmt->procVal2);
            procLenOpSel(data->cmt->procLen);
            procPosOpSel(data->cmt->procPos);
            }
      data->cmt->funcOp = TransformFunction(val);
      }

//---------------------------------------------------------
//   presetNew
//---------------------------------------------------------

void MidiTransformerDialog::presetNew()
      {
      QString name;
      for (int i = 0;; ++i) {
            name.sprintf("New-%d", i);
            iMidiTransformation imt;
            for (imt = mtlist.begin(); imt != mtlist.end(); ++imt) {
                  if (name == (*imt)->name)
                        break;
                  }
            if (imt == mtlist.end())
                  break;
            }
      MidiTransformation* mt = new MidiTransformation(name);
      QListBoxText* lbi      = new QListBoxText(presetList, name);
      mtlist.push_back(mt);
      presetList->setCurrentItem(lbi);
      }

//---------------------------------------------------------
//   presetDelete
//---------------------------------------------------------

void MidiTransformerDialog::presetDelete()
      {
      if (data->cindex != -1) {
            iMidiTransformation mt = mtlist.begin();
            for (int i = 0; i < data->cindex; ++i, ++mt) {
                  mtlist.erase(mt);
                  presetList->removeItem(data->cindex);
                  break;
                  }
            }
      }

//---------------------------------------------------------
//   presetChanged
//---------------------------------------------------------

void MidiTransformerDialog::presetChanged(QListBoxItem* item)
      {
      data->cindex = presetList->index(item);
      iMidiTransformation i;
      for (i = mtlist.begin(); i != mtlist.end(); ++i) {
            if (item->text() == (*i)->name) {
                  data->cmt = *i;
                  break;
                  }
            }
      if (i == mtlist.end()) {
            printf("MidiTransformerDialog::presetChanged: not found\n");
            return;
            }
      nameEntry->setText(data->cmt->name);
      commentEntry->setText(data->cmt->comment);

      selEventOp->setCurrentItem(data->cmt->selEventOp);
      selEventOpSel(data->cmt->selEventOp);

      for (unsigned i = 0; i < sizeof(eventTypeTable)/sizeof(*eventTypeTable); ++i) {
            if (eventTypeTable[i] == data->cmt->selType) {
                  selType->setCurrentItem(i);
                  break;
                  }
            }

      selVal1Op->setCurrentItem(data->cmt->selVal1);
      selVal1OpSel(data->cmt->selVal1);

      selVal2Op->setCurrentItem(data->cmt->selVal2);
      selVal2OpSel(data->cmt->selVal2);

      selLenOp->setCurrentItem(data->cmt->selLen);
      selLenOpSel(data->cmt->selLen);

      selRangeOp->setCurrentItem(data->cmt->selRange);
      selRangeOpSel(data->cmt->selRange);

      funcOp->setCurrentItem(data->cmt->funcOp);
      funcOpSel(data->cmt->funcOp);

      procEventOp->setCurrentItem(data->cmt->procEvent);
      procEventOpSel(data->cmt->procEvent);

      procVal1Op->setCurrentItem(data->cmt->procVal1);
      procVal1OpSel(data->cmt->procVal1);

      for (unsigned i = 0; i < sizeof(procVal2Map)/sizeof(*procVal2Map); ++i) {
            if (procVal2Map[i] == data->cmt->procVal2) {
                  procVal2Op->setCurrentItem(i);
                  break;
                  }
            }
      procLenOp->setCurrentItem(data->cmt->procLen);
      procLenOpSel(data->cmt->procLen);

      procPosOp->setCurrentItem(data->cmt->procPos);
      procPosOpSel(data->cmt->procPos);

      selVal1aChanged(data->cmt->selVal1a);
      selVal1bChanged(data->cmt->selVal1b);
      selVal2a->setValue(data->cmt->selVal2a);
      selVal2b->setValue(data->cmt->selVal2b);
      selLenA->setValue(data->cmt->selLenA);
      selLenB->setValue(data->cmt->selLenB);
      selBarA->setValue(data->cmt->selRangeA);
      selBarB->setValue(data->cmt->selRangeB);
      procVal1a->setValue(data->cmt->procVal1a);
      procVal1b->setValue(data->cmt->procVal1b);
      procVal2a->setValue(data->cmt->procVal2a);
      procVal2b->setValue(data->cmt->procVal2b);
      procLenA->setValue(data->cmt->procLenA);
      procPosA->setValue(data->cmt->procPosA);
      funcQuantVal->setValue(data->cmt->quantVal);

      selectedTracks->setChecked(data->cmt->selectedTracks);
      selectedTracksChanged(data->cmt->selectedTracks);
      insideLoop->setChecked(data->cmt->insideLoop);
      insideLoopChanged(data->cmt->insideLoop);
      }

//---------------------------------------------------------
//   nameChanged
//---------------------------------------------------------

void MidiTransformerDialog::nameChanged(const QString& s)
      {
      data->cmt->name = s;
      QListBoxItem* item = presetList->item(data->cindex);
      if (s != item->text()) {
            disconnect(presetList,   SIGNAL(highlighted(QListBoxItem*)),
               this, SLOT(presetChanged(QListBoxItem*)));
            presetList->changeItem(s, data->cindex);
            connect(presetList,   SIGNAL(highlighted(QListBoxItem*)),
               SLOT(presetChanged(QListBoxItem*)));
            }
      }

//---------------------------------------------------------
//   commentChanged
//---------------------------------------------------------

void MidiTransformerDialog::commentChanged()
      {
      data->cmt->comment = commentEntry->text();
      }

//---------------------------------------------------------
//   selVal1aChanged
//---------------------------------------------------------

void MidiTransformerDialog::selVal1aChanged(int val)
      {
      data->cmt->selVal1a = val;
      if ((data->cmt->selEventOp != All)
         && (data->cmt->selType == MidiEvent::Note)) {
            selVal1a->setSuffix(" - " + pitch2string(val));
            }
      else
            selVal1a->setSuffix(QString(""));
      }

//---------------------------------------------------------
//   selVal1bChanged
//---------------------------------------------------------

void MidiTransformerDialog::selVal1bChanged(int val)
      {
      data->cmt->selVal1b = val;
      if ((data->cmt->selEventOp != All)
         && (data->cmt->selType == MidiEvent::Note)) {
            selVal1b->setSuffix(" - " + pitch2string(val));
            }
      else
            selVal1b->setSuffix(QString(""));
      }

//---------------------------------------------------------
//   selVal2aChanged
//---------------------------------------------------------

void MidiTransformerDialog::selVal2aChanged(int val)
      {
      data->cmt->selVal2a = val;
      }

//---------------------------------------------------------
//   selVal2bChanged
//---------------------------------------------------------

void MidiTransformerDialog::selVal2bChanged(int val)
      {
      data->cmt->selVal2b = val;
      }

//---------------------------------------------------------
//   selLenAChanged
//---------------------------------------------------------

void MidiTransformerDialog::selLenAChanged(int val)
      {
      data->cmt->selLenA = val;
      }

//---------------------------------------------------------
//   selLenBChanged
//---------------------------------------------------------

void MidiTransformerDialog::selLenBChanged(int val)
      {
      data->cmt->selLenB = val;
      }

//---------------------------------------------------------
//   selBarAChanged
//---------------------------------------------------------

void MidiTransformerDialog::selBarAChanged(int val)
      {
      data->cmt->selRangeA = val;
      }

//---------------------------------------------------------
//   selBarBChanged
//---------------------------------------------------------

void MidiTransformerDialog::selBarBChanged(int val)
      {
      data->cmt->selRangeB = val;
      }

//---------------------------------------------------------
//   procVal1aChanged
//---------------------------------------------------------

void MidiTransformerDialog::procVal1aChanged(int val)
      {
      data->cmt->procVal1a = val;
      }

//---------------------------------------------------------
//   procVal1bChanged
//---------------------------------------------------------

void MidiTransformerDialog::procVal1bChanged(int val)
      {
      data->cmt->procVal1b = val;
      }

//---------------------------------------------------------
//   procVal2aChanged
//---------------------------------------------------------

void MidiTransformerDialog::procVal2aChanged(int val)
      {
      data->cmt->procVal2a = val;
      }

//---------------------------------------------------------
//   procVal2bChanged
//---------------------------------------------------------

void MidiTransformerDialog::procVal2bChanged(int val)
      {
      data->cmt->procVal2b = val;
      }

//---------------------------------------------------------
//   procLenAChanged
//---------------------------------------------------------

void MidiTransformerDialog::procLenAChanged(int val)
      {
      data->cmt->procLenA = val;
      }

//---------------------------------------------------------
//   procPosAChanged
//---------------------------------------------------------

void MidiTransformerDialog::procPosAChanged(int val)
      {
      data->cmt->procPosA = val;
      }

//---------------------------------------------------------
//   funcQuantValSel
//---------------------------------------------------------

void MidiTransformerDialog::funcQuantValSel(int val)
      {
      data->cmt->quantVal = val;
      }

//---------------------------------------------------------
//   processAllChanged
//---------------------------------------------------------

void MidiTransformerDialog::processAllChanged(bool val)
      {
      if (val == true) {
            selectedTracks->setChecked(false);
            insideLoop->setChecked(false);
            data->cmt->selectedTracks = false;
            data->cmt->insideLoop = false;
            }
      }

//---------------------------------------------------------
//   selectedTracksChanged
//---------------------------------------------------------

void MidiTransformerDialog::selectedTracksChanged(bool val)
      {
      data->cmt->selectedTracks = val;
      processAll->setChecked(!val && !data->cmt->insideLoop);
      }

//---------------------------------------------------------
//   insideLoopChanged
//---------------------------------------------------------

void MidiTransformerDialog::insideLoopChanged(bool val)
      {
      data->cmt->insideLoop = val;
      processAll->setChecked(!data->cmt->selectedTracks && !val);
      }

