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

#include <stdio.h>

#include <qlayout.h>
#include <qlabel.h>
#include <qpushbutton.h>
#include <qmultilineedit.h>
#include <qmessagebox.h>
#include <qslider.h>
#include <qradiobutton.h>
#include <qspinbox.h>

#include "song.h"
#include "event.h"
#include "editevent.h"
#include "pitchlabel.h"
#include "pitchedit.h"
#include "intlabel.h"
#include "globals.h"
#include "posedit.h"

//---------------------------------------------------------
//   string2qhex
//---------------------------------------------------------

QString string2hex(const unsigned char* data, int len)
      {
      QString d;
      QString s;
      for (int i = 0; i < len; ++i) {
            if ((i > 0) && ((i % 8)==0)) {
                  d += "\n";
                  }
            else if (i)
                  d += " ";
            d += s.sprintf("%02x", data[i]);
            }
      return d;
      }

//---------------------------------------------------------
//   hex2string
//---------------------------------------------------------

char* hex2string(QWidget* parent, const char* src, int& len)
      {
      char buffer[2048];
      char* dst = buffer;

      while (*src) {
            while (*src == ' ' || *src == '\n')
                  ++src;
            char* ep;
            long val =  strtol(src, &ep, 16);
            if (ep == src) {
                  QMessageBox::information(parent,
                     QString("MusE"),
                     QWidget::tr("Cannot convert sysex string"));
                  return 0;
                  }
            src    = ep;
            *dst++ = val;
            if (dst - buffer >= 2048) {
                  QMessageBox::information(parent,
                     QString("MusE"),
                     QWidget::tr("Hex String too long (2048 bytes limit)"));
                  return 0;
                  }
            }
      len = dst - buffer;
      char* b = new char[len+1];
      memcpy(b, buffer, len);
      b[len] = 0;
      return b;
      }

//---------------------------------------------------------
//   getEvent
//---------------------------------------------------------

MidiEvent* EditNoteDialog::getEvent(int tick, const MidiEvent* event,
   QWidget* parent)
      {
      EditNoteDialog* dlg = new EditNoteDialog(tick, event, parent);
      MidiEvent* nevent = 0;
      if (dlg->exec() == QDialog::Accepted) {
            nevent = dlg->event();
            }
      delete dlg;
      return nevent;
      }

MidiEvent* EditSysexDialog::getEvent(int tick, const MidiEvent* event,
   QWidget* parent)
      {
      EditSysexDialog* dlg = new EditSysexDialog(tick, event, parent);
      MidiEvent* nevent = 0;
      if (dlg->exec() == QDialog::Accepted) {
            nevent = dlg->event();
            }
      delete dlg;
      return nevent;
      }

MidiEvent* EditCtrl7Dialog::getEvent(int tick, const MidiEvent* event, QWidget* parent)
      {
      EditEventDialog* dlg = new EditCtrl7Dialog(tick, event, parent);
      MidiEvent* nevent = 0;
      if (dlg->exec() == QDialog::Accepted) {
            nevent = dlg->event();
            }
      delete dlg;
      return nevent;
      }

MidiEvent* EditCtrl14Dialog::getEvent(int tick, const MidiEvent* event, QWidget* parent)
      {
      EditEventDialog* dlg = new EditCtrl14Dialog(tick, event, parent);
      MidiEvent* nevent = 0;
      if (dlg->exec() == QDialog::Accepted) {
            nevent = dlg->event();
            }
      delete dlg;
      return nevent;
      }

MidiEvent* EditMetaDialog::getEvent(int tick, const MidiEvent* event, QWidget* parent)
      {
      EditEventDialog* dlg = new EditMetaDialog(tick, event, parent);
      MidiEvent* nevent = 0;
      if (dlg->exec() == QDialog::Accepted) {
            nevent = dlg->event();
            }
      delete dlg;
      return nevent;
      }

MidiEvent* EditPitchDialog::getEvent(int tick, const MidiEvent* event, QWidget* parent)
      {
      EditEventDialog* dlg = new EditPitchDialog(tick, event, parent);
      MidiEvent* nevent = 0;
      if (dlg->exec() == QDialog::Accepted) {
            nevent = dlg->event();
            }
      delete dlg;
      return nevent;
      }

MidiEvent* EditCAfterDialog::getEvent(int tick, const MidiEvent* event, QWidget* parent)
      {
      EditEventDialog* dlg = new EditCAfterDialog(tick, event, parent);
      MidiEvent* nevent = 0;
      if (dlg->exec() == QDialog::Accepted) {
            nevent = dlg->event();
            }
      delete dlg;
      return nevent;
      }

MidiEvent* EditPAfterDialog::getEvent(int tick, const MidiEvent* event, QWidget* parent)
      {
      EditEventDialog* dlg = new EditPAfterDialog(tick, event, parent);
      MidiEvent* nevent = 0;
      if (dlg->exec() == QDialog::Accepted) {
            nevent = dlg->event();
            }
      delete dlg;
      return nevent;
      }

//---------------------------------------------------------
//   EditEventDialog
//---------------------------------------------------------

EditEventDialog::EditEventDialog(QWidget* parent,
   const char* name) : QDialog(parent, name, true)
      {
      QVBoxLayout* xlayout = new QVBoxLayout(this);
      layout = new QGridLayout;
      xlayout->addLayout(layout);

      //---------------------------------------------------
      //  Ok, Cancel
      //---------------------------------------------------

      QBoxLayout* w5 = new QHBoxLayout;
      xlayout->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(cancelB, SIGNAL(clicked()), SLOT(reject()));
      connect(okB, SIGNAL(clicked()), SLOT(accept()));
      }

//---------------------------------------------------------
//   EditNoteDialog
//---------------------------------------------------------

EditNoteDialog::EditNoteDialog(int tick, const MidiEvent* event,
   QWidget* parent, const char* name)
   : EditNoteDialogBase(parent, name)
      {
      if (event) {
            pos->setValue(tick);
            il1->setValue(event->lenTick());
            pl->setValue(event->pitch());
            il2->setValue(event->velo());
            il3->setValue(event->veloOff());
            }
      else {
            pos->setValue(0);
            il1->setValue(96);
            pl->setValue(64);
            il2->setValue(100);
            il3->setValue(0);
            }
      }

//---------------------------------------------------------
//   EditNoteDialog::event
//---------------------------------------------------------

MidiEvent* EditNoteDialog::event()
      {
      MidiEvent* event = new MidiEvent(
         pos->pos().posTick(), MidiEvent::Note,
         pl->value(), il2->value(), il3->value(), il1->value());
      return event;
      }

//---------------------------------------------------------
//   EditSysExDialog
//---------------------------------------------------------

EditSysexDialog::EditSysexDialog(int tick, const MidiEvent* event,
   QWidget* parent, const char* name)
   : EditSysexDialogBase(parent, name)
      {
      sysex = 0;
      if (event) {
            pos->setValue(tick);
            edit->setText(string2hex(event->data(), event->dataLen()));
            }
      else {
            pos->setValue(0);
            }
      }

//---------------------------------------------------------
//   ~EditSysexDialog
//---------------------------------------------------------

EditSysexDialog::~EditSysexDialog()
      {
      if (sysex)
            delete sysex;
      }

//---------------------------------------------------------
//   EditSysExDialog::event
//---------------------------------------------------------

MidiEvent* EditSysexDialog::event()
      {
      MidiEvent* event = new MidiEvent(
         pos->pos().posTick(), MidiEvent::Sysex, len, sysex);
      return event;
      }

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

void EditSysexDialog::accept()
      {
      QString qsrc = edit->text();
      const char* src = qsrc.latin1();

      sysex = (unsigned char*)hex2string(this, src, len);
      if (sysex)
            QDialog::accept();
      }

//---------------------------------------------------------
//   EditCtrl7Dialog
//---------------------------------------------------------

EditCtrl7Dialog::EditCtrl7Dialog(int tick, const MidiEvent* event,
   QWidget* parent, const char* name)
   : EditEventDialog(parent, name)
      {
      setCaption(tr("MusE: Enter Controller Event"));

      QLabel* l1 = new QLabel(tr("Time Position"), this);
      pos = new PosEdit(this);

      QLabel* l2     = new QLabel(tr("Controller"), this);
      il2  = new IntLabel(0, 0, 127, this);
      il2->setFrame(true);
      il2->setDark();

      QLabel* l3 = new QLabel(tr("Value"), this);
      il3  = new IntLabel(0, 0, 127, this);
      il3->setFrame(true);
      il3->setDark();

      QSlider* slider = new QSlider(0, 127, 1, 0, QSlider::Horizontal, this);
      connect(slider, SIGNAL(valueChanged(int)), il3, SLOT(setValue(int)));
      connect(il3, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));

      if (event) {
            pos->setValue(tick);
            il2->setValue(event->dataA());
            il3->setValue(event->dataB());
            slider->setValue(event->dataB());
            }
      else {
            pos->setValue(0);
            }

      layout->addWidget(l1,  0, 0);
      layout->addWidget(pos, 0, 1, AlignLeft);
      layout->addWidget(l2,  1, 0);
      layout->addWidget(il2, 1, 1, AlignLeft);
      layout->addWidget(l3,  2, 0);
      layout->addWidget(il3, 2, 1, AlignLeft);
      layout->addMultiCellWidget(slider, 3, 3, 0, 1);
      }

//---------------------------------------------------------
//   EditCtrl7Dialog::event
//---------------------------------------------------------

MidiEvent* EditCtrl7Dialog::event()
      {
      MidiEvent* event = new MidiEvent(
         pos->pos().posTick(), MidiEvent::Ctrl7,
         il2->value(), il3->value(), 0, 0);
      return event;
      }

//---------------------------------------------------------
//   EditCtrl14Dialog
//---------------------------------------------------------

EditCtrl14Dialog::EditCtrl14Dialog(int tick, const MidiEvent* event,
   QWidget* parent, const char* name)
   : EditEventDialog(parent, name)
      {
      setCaption(tr("MusE: Enter Controller14 Event"));

      QLabel* l1 = new QLabel(tr("Time Position"), this);
      pos = new PosEdit(this);

      QLabel* l2h     = new QLabel(tr("Controller-H"), this);
      il2h  = new IntLabel(0, 0, 127, this);
      il2h->setFrame(true);
      il2h->setDark();

      QLabel* l2l     = new QLabel(tr("Controller-L"), this);
      il2l  = new IntLabel(0, 0, 127, this);
      il2l->setFrame(true);
      il2l->setDark();

      QLabel* l3 = new QLabel(tr("Value"), this);
      il3  = new IntLabel(0, 0, 4095, this);
      il3->setFrame(true);
      il3->setDark();

      QSlider* slider = new QSlider(0, 4095, 1, 0, QSlider::Horizontal, this);
      connect(slider, SIGNAL(valueChanged(int)), il3, SLOT(setValue(int)));
      connect(il3, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));

      if (event) {
            pos->setValue(tick);
            il2h->setValue(event->dataA());
            il2l->setValue(event->dataC());
            il3->setValue(event->dataB());
            slider->setValue(event->dataB());
            }
      else {
            pos->setValue(0);
            }

      layout->addWidget(l1,  0, 0);
      layout->addWidget(pos, 0, 1, AlignLeft);
      layout->addWidget(l2h,  1, 0);
      layout->addWidget(il2h, 1, 1, AlignLeft);
      layout->addWidget(l2l,  2, 0);
      layout->addWidget(il2l, 2, 1, AlignLeft);
      layout->addWidget(l3,  3, 0);
      layout->addWidget(il3, 3, 1, AlignLeft);
      layout->addMultiCellWidget(slider, 4, 4, 0, 1);
      }

//---------------------------------------------------------
//   EditCtrl14Dialog::event
//---------------------------------------------------------

MidiEvent* EditCtrl14Dialog::event()
      {
      MidiEvent* event = new MidiEvent(
         pos->pos().posTick(), MidiEvent::Ctrl14,
         il2h->value(), il3->value(), il2l->value(), 0);
      return event;
      }

//---------------------------------------------------------
//   EditMetaDialog
//---------------------------------------------------------

EditMetaDialog::EditMetaDialog(int tick, const MidiEvent* ev,
   QWidget* parent, const char* name)
   : EditEventDialog(parent, name)
      {
      meta = 0;
      setCaption(tr("MusE: Enter Meta Event"));

      QLabel* l1 = new QLabel(tr("Time Position"), this);
      pos = new PosEdit(this);

      QLabel* l2 = new QLabel(tr("Meta Type"), this);
      il2 = new IntLabel(-1, 0, 127, this, -1);
      il2->setFrame(true);
      il2->setDark();

      hexButton = new QRadioButton(tr("Enter Hex"), this, "hextoggle");
      hexButton->setChecked(true);
      connect(hexButton, SIGNAL(toggled(bool)), SLOT(toggled(bool)));

      edit = new QMultiLineEdit(this);
      edit->setFont(font5);

      if (ev) {
            pos->setValue(tick);
            il2->setValue(ev->dataA());
            toggled(true);
            edit->setText(string2hex(ev->data(), ev->dataLen()));
            }
      else {
            pos->setValue(0);
            il2->setValue(0);
            }

      layout->addWidget(l1,  0, 0);
      layout->addWidget(pos, 0, 1, AlignLeft);
      layout->addWidget(l2,  1, 0);
      layout->addWidget(il2, 1, 1, AlignLeft);
      layout->addMultiCellWidget(hexButton, 2, 2, 0, 1);
      layout->addMultiCellWidget(edit, 3, 3, 0, 1);
      }

//---------------------------------------------------------
//   toggled
//---------------------------------------------------------

void EditMetaDialog::toggled(bool flag)
      {
      QString qsrc    = edit->text();
      const char* src = qsrc.latin1();
      edit->clear();

      QString dst;
      if (flag) {       // convert to hex
            dst = string2hex((unsigned char*)src, strlen(src));
            }
      else {            // convert to string
            int len;
            dst = hex2string(this, src, len);
            }
      edit->setText(dst);
      }

//---------------------------------------------------------
//   ~EditMetaDialog
//---------------------------------------------------------

EditMetaDialog::~EditMetaDialog()
      {
      if (meta)
            delete meta;
      }

//---------------------------------------------------------
//   EditMetaDialog::event
//---------------------------------------------------------

MidiEvent* EditMetaDialog::event()
      {
      MidiEvent* event = new MidiEvent(
         pos->pos().posTick(), MidiEvent::Meta, len,
         (const unsigned char*)meta);
      event->setA(il2->value());
      return event;
      }

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

void EditMetaDialog::accept()
      {
      QString qsrc = edit->text();
      const char* src = qsrc.latin1();
      if (!hexButton->isOn()) {
            meta = (unsigned char*)strdup(src);
            len  = strlen(src);
            QDialog::accept();
            return;
            }

      meta = (unsigned char*)hex2string(this, src, len);
      if (meta)
            QDialog::accept();
      }

//---------------------------------------------------------
//   EditPitchDialog
//---------------------------------------------------------

EditPitchDialog::EditPitchDialog(int tick, const MidiEvent* event,
   QWidget* parent, const char* name)
   : EditEventDialog(parent, name)
      {
      setCaption(tr("MusE: Enter Pitch Shift"));

      QLabel* l1 = new QLabel(tr("Time Position"), this);
      pos = new PosEdit(this);

      QLabel* l2 = new QLabel(tr("Pitch"), this);
      il2  = new IntLabel(0, -8192, 8191, this);

      QSlider* slider = new QSlider(-8192, 8191, 10, 0, QSlider::Horizontal, this);
      connect(slider, SIGNAL(valueChanged(int)), il2, SLOT(setValue(int)));
      connect(il2, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));

      if (event) {
            int pitch = event->dataA();
            il2->setValue(pitch);
            slider->setValue(pitch);
            pos->setValue(tick);
            }
      else {
            pos->setValue(0);
            }

      layout->addWidget(l1,  0, 0);
      layout->addWidget(pos, 0, 1, AlignLeft);
      layout->addWidget(l2,  1, 0);
      layout->addWidget(il2,  1, 1, AlignLeft);
      layout->addMultiCellWidget(slider, 2, 2, 0, 1);
      }

//---------------------------------------------------------
//   EditPitchDialog::event
//---------------------------------------------------------

MidiEvent* EditPitchDialog::event()
      {
      int val = il2->value();

      MidiEvent* event = new MidiEvent(
         pos->pos().posTick(), MidiEvent::Pitch, val, 0, 0, 0);
      return event;
      }

//---------------------------------------------------------
//   EditCAfterDialog
//---------------------------------------------------------

EditCAfterDialog::EditCAfterDialog(int tick, const MidiEvent* event,
   QWidget* parent, const char* name)
   : EditEventDialog(parent, name)
      {
      setCaption(tr("MusE: Enter Channel Aftertouch"));

      QLabel* l1 = new QLabel(tr("Time Position"), this);
      pos = new PosEdit(this);

      QLabel* l2 = new QLabel(tr("Pressure"), this);
      il2  = new IntLabel(-1, 0, 127, this, -1);
      il2->setFrame(true);
      il2->setDark();

      QSlider* slider = new QSlider(0, 127, 1, 0, QSlider::Horizontal, this);
      connect(slider, SIGNAL(valueChanged(int)), il2, SLOT(setValue(int)));
      connect(il2, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));

      if (event) {
            pos->setValue(tick);
            il2->setValue(event->dataA());
            slider->setValue(event->dataA());
            }
      else {
            pos->setValue(0);
            il2->setValue(64);
            slider->setValue(64);
            }

      layout->addWidget(l1,   0, 0);
      layout->addWidget(pos,  0, 1, AlignLeft);
      layout->addWidget(l2,   1, 0);
      layout->addWidget(il2,  1, 1, AlignLeft);
      layout->addMultiCellWidget(slider, 2, 2, 0, 1);
      }

//---------------------------------------------------------
//   EditCAfterDialog::event
//---------------------------------------------------------

MidiEvent* EditCAfterDialog::event()
      {
      MidiEvent* event = new MidiEvent(
         pos->pos().posTick(), MidiEvent::CAfter,
         il2->value(), 0, 0, 0);
      return event;
      }

//---------------------------------------------------------
//   EditPAfterDialog
//---------------------------------------------------------

EditPAfterDialog::EditPAfterDialog(int tick, const MidiEvent* event,
   QWidget* parent, const char* name)
   : EditEventDialog(parent, name)
      {
      setCaption(tr("MusE: Enter Poly Aftertouch"));

      QLabel* l1 = new QLabel(tr("Time Position"), this);
      pos = new PosEdit(this);

      QLabel* l2 = new QLabel(tr("Pitch"), this);
      pl = new PitchLabel(this);

      QLabel* l3 = new QLabel(tr("Pressure"), this);
      il2  = new IntLabel(-1, 0, 127, this, -1);
      il2->setFrame(true);
      il2->setDark();

      QSlider* slider = new QSlider(0, 127, 1, 0, QSlider::Horizontal, this);
      connect(slider, SIGNAL(valueChanged(int)), il2, SLOT(setValue(int)));
      connect(il2, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));

      if (event) {
            pos->setValue(tick);
            pl->setValue(event->pitch());
            il2->setValue(event->dataB());
            slider->setValue(event->dataB());
            }
      else {
            pos->setValue(0);
            pl->setValue(64);
            il2->setValue(64);
            slider->setValue(64);
            }

      layout->addWidget(l1,  0, 0);
      layout->addWidget(pos, 0, 1, AlignLeft);
      layout->addWidget(l2,  1, 0);
      layout->addWidget(pl,  1, 1, AlignLeft);
      layout->addWidget(l3,  2, 0);
      layout->addWidget(il2, 2, 1, AlignLeft);
      layout->addMultiCellWidget(slider, 3, 3, 0, 1);
      }

//---------------------------------------------------------
//   EditPAfterDialog::event
//---------------------------------------------------------

MidiEvent* EditPAfterDialog::event()
      {
      MidiEvent* event = new MidiEvent(
         pos->pos().posTick(), MidiEvent::CAfter,
         pl->value(), il2->value(), 0, 0);
      return event;
      }

