//=========================================================
//  MusE
//  Linux Music Editor
//    $Id: tlist.cpp,v 1.31.2.7 2004/12/26 21:02:23 spamatica Exp $
//  (C) Copyright 1999 Werner Schweer (ws@seh.de)
//=========================================================

#include <cmath>

#include <qpainter.h>
#include <qlineedit.h>
#include <qpopupmenu.h>
#include <qmessagebox.h>
#include <qscrollbar.h>
#include <qtimer.h>
#include <qfileinfo.h>

#include "globals.h"
#include "icons.h"
#include "scrollscale.h"
#include "tlist.h"
#include "xml.h"
#include "mididev.h"
#include "midiport.h"
#include "comment.h"
#include "track.h"
#include "song.h"
#include "header.h"
#include "node.h"
#include "audio.h"
#include "instruments/minstrument.h"
#include "app.h"
#include "gconfig.h"
#include "event.h"
#include "midiedit/drummap.h"

static const int MIN_TRACKHEIGHT = 20;
static const int WHEEL_DELTA = 120;

//---------------------------------------------------------
//   THeaderTip::maybeTip
//---------------------------------------------------------

void THeaderTip::maybeTip(const QPoint &pos)
      {
      Header* w  = (Header*)parentWidget();
      int section = w->sectionAt(pos.x());
      if (section == -1)
            return;
      QRect r(w->sectionPos(section), 0, w->sectionSize(section),
         w->height());
      QString p;
      switch (section) {
            case COL_RECORD:   p = QHeader::tr("Enable Recording"); break;
            case COL_MUTE:     p = QHeader::tr("Mute Indicator"); break;
            case COL_SOLO:     p = QHeader::tr("Solo/Pre Fader Listening"); break;
            case COL_CLASS:    p = QHeader::tr("Track Type"); break;
            case COL_NAME:     p = QHeader::tr("Track Name"); break;
            case COL_OCHANNEL: p = QHeader::tr("Output Channel Number"); break;
            case COL_OPORT:    p = QHeader::tr("Output Port"); break;
            case COL_TIMELOCK: p = QHeader::tr("Time Lock"); break;
            default: return;
            }
      tip(r, p);
      }

//---------------------------------------------------------
//   TList
//---------------------------------------------------------

TList::TList(Header* hdr, QWidget* parent, const char* name)
   : QWidget(parent, name, WRepaintNoErase | WResizeNoErase)
      {
      ypos = 0;
      editMode = false;
      setFocusPolicy(QWidget::StrongFocus);
      setMouseTracking(true);
      header    = hdr;

      scroll    = 0;
      editTrack = 0;
      editor    = 0;
      mode      = NORMAL;

      setBackgroundMode(NoBackground);

      connect(song, SIGNAL(songChanged(int)), SLOT(songChanged(int)));
      connect(muse, SIGNAL(configChanged()), SLOT(redraw()));
      }

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

void TList::songChanged(int flags)
      {
      if (flags & (SC_MUTE | SC_SOLO | SC_RECFLAG | SC_TRACK_INSERTED
         | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_ROUTE | SC_CHANNELS))
            redraw();
      if (flags & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED))
            adjustScrollbar();
      }

//---------------------------------------------------------
//   drawCenteredPixmap
//    small helper function for "draw()" below
//---------------------------------------------------------

static void drawCenteredPixmap(QPainter& p, const QPixmap* pm, const QRect& r)
      {
      p.drawPixmap(r.x() + (r.width() - pm->width())/2, r.y() + (r.height() - pm->height())/2, *pm);
      }

//---------------------------------------------------------
//   paintEvent
//---------------------------------------------------------

void TList::paintEvent(QPaintEvent* ev)
      {
      if (!pmValid)
            paint(ev->rect());
      bitBlt(this, ev->rect().topLeft(), &pm, ev->rect(), CopyROP, true);
      }

//---------------------------------------------------------
//   redraw
//---------------------------------------------------------

void TList::redraw()
      {
      paint(QRect(0, 0, pm.width(), pm.height()));
      update();
      }

//---------------------------------------------------------
//   redraw
//---------------------------------------------------------

void TList::redraw(const QRect& r)
      {
      paint(r);
      update(r);
      }

//---------------------------------------------------------
//   paint
//---------------------------------------------------------

void TList::paint(const QRect& r)
      {
      if (!isVisible())
            return;
      QRect rect(r);
      if (!pmValid) {
            pmValid = true;
            rect = QRect(0, 0, pm.width(), pm.height());
            }
      QPainter p(&pm);

      if (bgPixmap.isNull())
            p.fillRect(rect, config.trackBg);
      else
            p.drawTiledPixmap(rect, bgPixmap, QPoint(rect.x(), ypos + rect.y()));
      p.setClipRegion(rect);

      int y  = rect.y();
      int w  = rect.width();
      int h  = rect.height();
      int x1 = rect.x();
      int x2 = rect.x() + w;

      //---------------------------------------------------
      //    Tracks
      //---------------------------------------------------

      TrackList* l = song->tracks();
      int idx = 0;
      int yy  = -ypos;
      for (iTrack i = l->begin(); i != l->end(); ++idx, yy += (*i)->height(), ++i) {
            Track* track = *i;
            Track::TrackType type = track->type();
            int trackHeight = track->height();
            if (yy >= (y + h))
                  break;
            if ((yy + trackHeight) < y)
                  continue;
            //
            // clear one row
            //
            QColor bg;
            if (track->selected()) {
                  bg = config.selectTrackBg;
                  p.setPen(config.selectTrackFg);
                  }
            else {
                  switch(type) {
                        case Track::MIDI:
                              bg = config.midiTrackBg;
                              break;
                        case Track::DRUM:
                              bg = config.drumTrackBg;
                              break;
                        case Track::WAVE:
                              bg = config.waveTrackBg;
                              break;
                        case Track::AUDIO_OUTPUT:
                              bg = config.outputTrackBg;
                              break;
                        case Track::AUDIO_INPUT:
                              bg = config.inputTrackBg;
                              break;
                        case Track::AUDIO_GROUP:
                              bg = config.groupTrackBg;
                              break;
                        case Track::AUDIO_AUX:
                              bg = config.auxTrackBg;
                              break;
                        case Track::AUDIO_SOFTSYNTH:
                              bg = config.synthTrackBg;
                              break;
                        }
                  p.setPen(palette().active().text());
                  }
            p.fillRect(x1, yy, w, trackHeight, bg);

            int x = 0;
            for (int index = 0; index < header->count(); ++index) {
                  int section = header->mapToSection(index);
                  int w   = header->sectionSize(section);
                  QRect r = p.xForm(QRect(x+2, yy, w-4, trackHeight));

                  switch (section) {
                        case COL_RECORD:
                              if (track->canRecord()) {
                                    drawCenteredPixmap(p,
                                       track->recordFlag() ? record_on_Icon : record_off_Icon, r);
                                    }
                              break;
                        case COL_CLASS:
                              {
                              const QPixmap* pm = 0;
                              switch(type) {
                                    case Track::MIDI:
                                          pm = addtrack_addmiditrackIcon;
                                          break;
                                    case Track::DRUM:
                                          pm = addtrack_drumtrackIcon;
                                          break;
                                    case Track::WAVE:
                                          pm = addtrack_wavetrackIcon;
                                          break;
                                    case Track::AUDIO_OUTPUT:
                                          pm = addtrack_audiooutputIcon;
                                          break;
                                    case Track::AUDIO_INPUT:
                                          pm = addtrack_audioinputIcon;
                                          break;
                                    case Track::AUDIO_GROUP:
                                          pm = addtrack_audiogroupIcon;
                                          break;
                                    case Track::AUDIO_AUX:
                                          pm = addtrack_auxsendIcon;
                                          break;
                                    case Track::AUDIO_SOFTSYNTH:
                                          pm = waveIcon;
                                          break;
                                    }
                              drawCenteredPixmap(p, pm, r);
                              }
                              break;
                        case COL_MUTE:
                              if (track->off())
                                    drawCenteredPixmap(p, offIcon, r);
                              else if (track->mute())
                                    drawCenteredPixmap(p, editmuteSIcon, r);
                              break;
                        case COL_SOLO:
                              if (track->solo())
                                    drawCenteredPixmap(p, bluedotIcon, r);
                              break;
                        case COL_TIMELOCK:
                              if (track->isMidiTrack()
                                 && track->locked()) {
                                    drawCenteredPixmap(p, lockIcon, r);
                                    }
                              break;
                        case COL_NAME:
                              p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, track->name());
                              break;
                        case COL_OCHANNEL:
                              {
                              QString s;
                              int n;
                              if (track->isMidiTrack()) {
                                    n = ((MidiTrack*)track)->outChannel() + 1;
                                    }
                              else {
                                    // show number of ports
                                    n = ((WaveTrack*)track)->channels();
                                    }
                              s.setNum(n);
                              p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter, s);
                              }
                              break;
                        case COL_OPORT:
                              {
                              QString s;
                              if (track->isMidiTrack()) {
                                    int outport = ((MidiTrack*)track)->outPort();
                                    MidiDevice* dev = midiPorts[outport].device();
                                    if (dev)
                                          s.sprintf("%d(%s)", outport + 1,
                                             dev->name().latin1());
                                    else
                                          s.sprintf("%d(none)", outport + 1);
                                    }
                              p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, s);
                              }
                              break;
                        default:
                              break;
                        }
                  x += header->sectionSize(section);
                  }
            p.setPen(gray);
            p.drawLine(x1, yy, x2, yy);
            }
      p.drawLine(x1, yy, x2, yy);

      if (mode == DRAG) {
            int yy = curY - dragYoff;
            p.setPen(green);
            p.drawLine(x1, yy, x2, yy);
            p.drawLine(x1, yy + dragHeight, x2, yy+dragHeight);
            }

      //---------------------------------------------------
      //    draw vertical lines
      //---------------------------------------------------

      int n = header->count();
      int xpos = 0;
      p.setPen(gray);
      for (int index = 0; index < n; index++) {
            int section = header->mapToSection(index);
            xpos += header->sectionSize(section);
            p.drawLine(xpos, 0, xpos, height());
            }
      }

//---------------------------------------------------------
//   returnPressed
//---------------------------------------------------------

void TList::returnPressed()
      {
      editor->hide();
      if (editor->text() != editTrack->name()) {
            TrackList* tl = song->tracks();
            for (iTrack i = tl->begin(); i != tl->end(); ++i) {
                  if ((*i)->name() == editor->text()) {
                        QMessageBox::critical(this,
                           tr("MusE: bad trackname"),
                           tr("please choose a unique track name"),
                           QMessageBox::Ok,
                           QMessageBox::NoButton,
                           QMessageBox::NoButton);
                        editTrack = 0;
                        setFocus();
                        return;
                        }
                  }
            Track* track = editTrack->clone();
            editTrack->setName(editor->text());
            audio->msgChangeTrack(track, editTrack);
            }
      editTrack = 0;
      editMode = false;
      setFocus();
      }

//---------------------------------------------------------
//   adjustScrollbar
//---------------------------------------------------------

void TList::adjustScrollbar()
      {
      int h = 0;
      TrackList* l = song->tracks();
      for (iTrack it = l->begin(); it != l->end(); ++it)
            h += (*it)->height();
      scroll->setMaxValue(h +30);
      redraw();
      }

//---------------------------------------------------------
//   y2Track
//---------------------------------------------------------

Track* TList::y2Track(int y) const
      {
      TrackList* l = song->tracks();
      int ty = 0;
      for (iTrack it = l->begin(); it != l->end(); ++it) {
            int h = (*it)->height();
            if (y >= ty && y < ty + h)
                  return *it;
            ty += h;
            }
      return 0;
      }

//---------------------------------------------------------
//   viewMouseDoubleClickEvent
//---------------------------------------------------------

void TList::mouseDoubleClickEvent(QMouseEvent* ev)
      {
      int x       = ev->x();
      int section = header->sectionAt(x);
      if (section == -1)
            return;

      Track* t = y2Track(ev->y() + ypos);

      if (t) {
            int colx = header->sectionPos(section);
            int colw = header->sectionSize(section);
            int coly = t->y() - ypos;
            int colh = t->height();

            if (section == COL_NAME) {
                  editTrack = t;
                  if (editor == 0) {
                        editor = new QLineEdit(this);
                        /*connect(editor, SIGNAL(returnPressed()),
                           SLOT(returnPressed()));*/
                        editor->setFrame(true);
                        }
                  editor->setText(editTrack->name());
                  editor->end(false);
                  editor->setGeometry(colx, coly, colw, colh);
                  editMode = true;
                  editor->show();
                  }
            else
                  mousePressEvent(ev);
            }
      }

//---------------------------------------------------------
//   portsPopupMenu
//---------------------------------------------------------

void TList::portsPopupMenu(Track* t, int x, int y)
      {
      switch(t->type()) {
            case Track::MIDI:
            case Track::DRUM:
                  {
                  MidiTrack* track = (MidiTrack*)t;
                  QPopupMenu* p = midiPortsPopup(0);
                  int n = p->exec(mapToGlobal(QPoint(x, y)), 0);
                  if (n != -1) {
                        track->setOutPort(n);
                        song->update();
                        if (t->type() == Track::DRUM) {
                              bool change = QMessageBox::question(this, tr("Update drummap?"),
                                             tr("Do you want to use same port for all instruments in the drummap?"),
                                             tr("&Yes"), tr("&No"), QString::null, 0, 1);
                              if (!change) {
                                    for (int i=0; i<DRUM_MAPSIZE; i++) //Remap all drum instruments to this port
                                          drumMap[i].port = track->outPort();
                                    }
                              }
                        }
                  delete p;
                  }
                  break;
            case Track::WAVE:
            case Track::AUDIO_OUTPUT:
            case Track::AUDIO_INPUT:
            case Track::AUDIO_GROUP:
            case Track::AUDIO_AUX:
            case Track::AUDIO_SOFTSYNTH: //TODO
                  break;
            }
      }

//---------------------------------------------------------
//   oportPropertyPopupMenu
//---------------------------------------------------------

void TList::oportPropertyPopupMenu(Track* t, int x, int y)
      {
      if (t->type() != Track::MIDI && t->type() != Track::DRUM)
            return;
      int oPort      = ((MidiTrack*)t)->outPort();
      MidiPort* port = &midiPorts[oPort];

      QPopupMenu* p = new QPopupMenu(this);
      p->setCheckable(true);
      p->insertItem(tr("Show Gui"), 0);

      p->setItemEnabled(0, port->hasGui());
      p->setItemChecked(0, port->guiVisible());

      int n = p->exec(mapToGlobal(QPoint(x, y)), 0);
      if (n == 0) {
            bool show = !port->guiVisible();
            audio->msgShowInstrumentGui(port->instrument(), show);
            }
      delete p;
      }

//---------------------------------------------------------
//   tracklistChanged
//---------------------------------------------------------

void TList::tracklistChanged()
      {
      redraw();
      }

//---------------------------------------------------------
//   keyPressEvent
//---------------------------------------------------------

void TList::keyPressEvent(QKeyEvent* e)
      {
      if (editMode) {
            // First time we get a keypress event when lineedit is open is on the return key:
            returnPressed();
            return;
            }
      emit keyPressExt(e); //redirect keypress events to main app
      e->ignore();
      /*
      int key = e->key();
      switch (key) {
            case Key_Up:
                  moveSelection(-1);
                  break;
            case Key_Down:
                  moveSelection(1);
                  break;
            default:

                  break;
            }
            */
      }

//---------------------------------------------------------
//   moveSelection
//---------------------------------------------------------

void TList::moveSelection(int n)
      {
      TrackList* tracks = song->tracks();

      // check for single selection
      int nselect = 0;
      for (iTrack t = tracks->begin(); t != tracks->end(); ++t)
            if ((*t)->selected())
                  ++nselect;
      if (nselect != 1)
            return;
      for (iTrack t = tracks->begin(); t != tracks->end(); ++t) {
            iTrack s = t;
            if ((*t)->selected()) {
                  if (n > 0) {
                        while (n--) {
                              ++t;
                              if (t == tracks->end()) {
                                    --t;
                                    break;
                                    }
                              }
                        }
                  else {
                        while (n++ != 0) {
                              if (t == tracks->begin())
                                    break;
                              --t;
                              }
                        }
                  (*s)->setSelected(false);
                  (*t)->setSelected(true);
                  if (editTrack && editTrack != *t)
                        returnPressed();
                  redraw();
                  break;
                  }
            }
      emit selectionChanged();
      }

//---------------------------------------------------------
//   mousePressEvent
//---------------------------------------------------------

void TList::mousePressEvent(QMouseEvent* ev)
      {
      int x       = ev->x();
      int y       = ev->y();
      int button  = ev->button();
      bool shift  = ev->state() & ShiftButton;

      Track* t    = y2Track(y + ypos);

      TrackColumn col = TrackColumn(header->sectionAt(x));
      if (t == 0) {
            if (button == QMouseEvent::RightButton) {
                  QPopupMenu* p = new QPopupMenu(this);
                  p->clear();
                  p->insertItem(*addtrack_addmiditrackIcon,
                     tr("Add Midi Track"), Track::MIDI);
                  p->insertItem(*addtrack_drumtrackIcon,
                     tr("Add Drum Track"),Track::DRUM);
                  p->insertItem(*addtrack_wavetrackIcon,
                     tr("Add Wave Track"), Track::WAVE);
                  p->insertItem(*addtrack_audiooutputIcon,
                     tr("Add Output"), Track::AUDIO_OUTPUT);
                  p->insertItem(*addtrack_audiogroupIcon,
                     tr("Add Group"), Track::AUDIO_GROUP);
                  p->insertItem(*addtrack_audioinputIcon,
                     tr("Add Input"), Track::AUDIO_INPUT);
                  p->insertItem(*addtrack_auxsendIcon,
                     tr("Add Aux Send"), Track::AUDIO_AUX);
                  int n = p->exec(ev->globalPos(), 0);

                  if (n != -1) {
                        t = song->addTrack((Track::TrackType)n);
                        song->deselectTracks();
                        t->setSelected(true);
                        emit selectionChanged();
                        adjustScrollbar();
                        }
                 delete p;
                 }
            return;
            }

      TrackList* tracks = song->tracks();
      dragYoff = y - (t->y() - ypos);
      startY   = y;

      if (resizeFlag) {
            mode = RESIZE;
            int y  = ev->y();
            int ty = -ypos;
            sTrack = 0;
            for (iTrack it = tracks->begin(); it != tracks->end(); ++it, ++sTrack) {
                  int h = (*it)->height();
                  ty += h;
                  if (y >= (ty-2)) {
                   
                        if ( (*it) == tracks->back() && y > ty ) {
                              //printf("tracks->back() && y > ty\n");
                        }
                        else if ( y > (ty+2) ) {
                              //printf(" y > (ty+2) \n");
                        }
                        else {
                              //printf("ogga ogga\n");
                        
                              break;
                              }
                   
                   
                   //&& y < (ty))
                   //     break;
                        }
                  }

            return;
            }
      mode = START_DRAG;

      switch (col) {
            case COL_RECORD:
                  {
                  bool val = !(t->recordFlag());
                  if (!t->isMidiTrack()) {
                        if (t->type() == Track::AUDIO_OUTPUT) {
                              if (val && t->recordFlag() == false) {
                                    muse->bounceToFile();
                                    audio->msgSetRecord((AudioOutput*)t, val);
                                    }
                              audio->msgSetRecord((AudioOutput*)t, val);
                              if (!((AudioOutput*)t)->recFile())
                                    val = false;
                              else
                                    return;
                              }
                        song->setRecordFlag(t, val);
                        }
                  else
                        song->setRecordFlag(t, val);
                  }
                  break;
            case COL_NONE:
                  break;
            case COL_CLASS:
                  if (t->isMidiTrack())
                        classesPopupMenu(t, x, t->y() - ypos);
                  break;
            case COL_OPORT:
                  if (button == QMouseEvent::LeftButton)
                        portsPopupMenu(t, x, t->y() - ypos);
                  else if (button == QMouseEvent::RightButton)
                        oportPropertyPopupMenu(t, x, t->y() - ypos);
                  break;
            case COL_MUTE:
                  if (t->off())
                        t->setOff(false);
                  else
                        t->setMute(!t->mute());
                  song->update(SC_MUTE);
                  break;

            case COL_SOLO:
                  t->setSolo(!t->solo());
                  song->update(SC_SOLO);
                  break;

            case COL_NAME:
                  if (button == QMouseEvent::LeftButton) {
                        if (!shift) {
                              song->deselectTracks();
                              t->setSelected(true);
                              }
                        else
                              t->setSelected(!t->selected());
                        if (editTrack && editTrack != t)
                              returnPressed();
                        emit selectionChanged();
                        }
                  else if (button == QMouseEvent::RightButton) {
                        mode = NORMAL;
                        QPopupMenu* p = new QPopupMenu(this);
                        p->clear();
                        p->insertItem(*deleteIcon, tr("Delete Track"), 0);
                        p->insertItem(tr("Track Comment"), 1);
                        int n = p->exec(ev->globalPos(), 0);
                        if (n != -1) {
                              switch (n) {
                                    case 0:     // delete track
                                          song->removeTrack0(t);
                                          break;

                                    case 1:     // show track comment
                                          {
                                          TrackComment* tc = new TrackComment(t, 0);
                                          tc->show();
                                          }
                                          break;

                                    default:
                                          printf("action %d\n", n);
                                          break;
                                    }

                              }
                        delete p;
                        }
                  break;

            case COL_TIMELOCK:
                  t->setLocked(!t->locked());
                  break;

            case COL_OCHANNEL:
                  {
                  MidiTrack* mt = dynamic_cast<MidiTrack*>(t);
                  if (mt == 0)
                        break;
                  int channel = mt->outChannel();
                  if (button == QMouseEvent::RightButton) {
                        if (channel < 15)
                              ++channel;
                        }
                  else if (button == QMouseEvent::MidButton) {
                        if (channel > 0)
                              --channel;
                        }
                  if (channel != ((MidiTrack*)t)->outChannel()) {
                        mt->setOutChannel(channel);
                        /* --- I really don't like this, you can mess up the whole map "as easy as dell"
                        if (mt->type() == MidiTrack::DRUM) {//Change channel on all drum instruments
                              for (int i=0; i<DRUM_MAPSIZE; i++)
                                    drumMap[i].channel = channel;
                              }*/
                        song->update(-1);
                        }
                  }
            }
      redraw();
      }

//---------------------------------------------------------
//   selectTrack
//---------------------------------------------------------
void TList::selectTrack(Track* tr)
      {
      song->deselectTracks();
      tr->setSelected(true);
      emit selectionChanged();
      }
//---------------------------------------------------------
//   mouseMoveEvent
//---------------------------------------------------------

void TList::mouseMoveEvent(QMouseEvent* ev)
      {
      if (ev->state() == 0) {
            int y = ev->y();
            int ty = -ypos;
            TrackList* tracks = song->tracks();
            iTrack it;
            for (it = tracks->begin(); it != tracks->end(); ++it) {
                  int h = (*it)->height();
                  ty += h;
                  if ( y >= (ty-2) ) {
                  
                        if ( (*it) == tracks->back() && y > ty ) {
                              //printf("tracks->back() && y > ty\n");
                        }
                        else if ( y > (ty+2) ) {
                              //printf(" y > (ty+2) \n");
                        }
                        else {
                              //printf("ogga ogga\n");
                        
                              if (!resizeFlag) {
                                    resizeFlag = true;
                                    setCursor(QCursor(splitVCursor));
                                    }
                              break;
                              }
                        }
                  }
            if (it == tracks->end() && resizeFlag) {
                  setCursor(QCursor(arrowCursor));
                  resizeFlag = false;
                  }
            return;
            }
      curY      = ev->y();
      int delta = curY - startY;
      switch (mode) {
            case START_DRAG:
                  if (delta < 0)
                        delta = -delta;
                  if (delta <= 2)
                        break;
                  {
                  Track* t = y2Track(startY + ypos);
                  if (t == 0)
                        mode = NORMAL;
                  else {
                        mode = DRAG;
                        dragHeight = t->height();
                        sTrack     = song->tracks()->index(t);
                        setCursor(QCursor(sizeVerCursor));
                        redraw();
                        }
                  }
                  break;
            case NORMAL:
                  break;
            case DRAG:
                  redraw();
                  break;
            case RESIZE:
                  {
                  Track* t = song->tracks()->index(sTrack);
                  int h  = t->height() + delta;
                  startY = curY;
                  if (h < MIN_TRACKHEIGHT)
                        h = MIN_TRACKHEIGHT;
                  t->setHeight(h);
                  song->update(SC_TRACK_MODIFIED);
                  }
                  break;
            }
      }

//---------------------------------------------------------
//   mouseReleaseEvent
//---------------------------------------------------------

void TList::mouseReleaseEvent(QMouseEvent* ev)
      {
      if (mode == DRAG) {
            Track* t = y2Track(ev->y() + ypos);
            if (t) {
                  int dTrack = song->tracks()->index(t);
                  audio->msgMoveTrack(sTrack, dTrack);
                  }
            }
      if (mode != NORMAL) {
            mode = NORMAL;
            setCursor(QCursor(arrowCursor));
            redraw();
            }
      if (editTrack)
            editor->setFocus();
      adjustScrollbar();
      }

//---------------------------------------------------------
//   wheelEvent
//---------------------------------------------------------

void TList::wheelEvent(QWheelEvent* ev)
      {
      int x           = ev->x();
      int y           = ev->y();
      Track* t        = y2Track(y + ypos);
      if (t == 0)
            return;
      TrackColumn col = TrackColumn(header->sectionAt(x));
      int delta       = ev->delta() / WHEEL_DELTA;
      ev->accept();

      switch (col) {
            case COL_RECORD:
            case COL_NONE:
            case COL_CLASS:
            case COL_NAME:
                  break;
            case COL_MUTE:
                  if (t->off())
                        t->setOff(false);
                  else
                        t->setMute(!t->mute());
                  song->update(SC_MUTE);
                  break;

            case COL_SOLO:
                  t->setSolo(!t->solo());
                  song->update(SC_SOLO);
                  break;

            case COL_TIMELOCK:
                  t->setLocked(!t->locked());
                  break;

            case COL_OPORT:
                  if (t->isMidiTrack()) {
                        MidiTrack* mt = (MidiTrack*)t;
                        int port = mt->outPort() + delta;

                        if (port >= MIDI_PORTS)
                              port = MIDI_PORTS-1;
                        else if (port < 0)
                              port = 0;
                        if (port != ((MidiTrack*)t)->outPort()) {
                              mt->setOutPort(port);
                              song->update(SC_ROUTE);
                              }
                        }
                  break;

            case COL_OCHANNEL:
                  if (t->isMidiTrack()) {
                        MidiTrack* mt = (MidiTrack*)t;
                        int channel = mt->outChannel() + delta;

                        if (channel >= MIDI_CHANNELS)
                              channel = MIDI_CHANNELS-1;
                        else if (channel < 0)
                              channel = 0;
                        if (channel != ((MidiTrack*)t)->outChannel()) {
                              mt->setOutChannel(channel);
                              // may result in adding/removing mixer strip:
                              song->update(-1);
                              }
                        }
                  else {
                        int n = t->channels() + delta;
                        if (n > MAX_CHANNELS)
                              n = MAX_CHANNELS;
                        else if (n < 1)
                              n = 1;
                        if (n != t->channels()) {
                              audio->msgSetChannels((AudioTrack*)t, n);
                              song->update(SC_CHANNELS);
                              }
                        }
                  break;
            }
      }

//---------------------------------------------------------
//   writeStatus
//---------------------------------------------------------

void TList::writeStatus(int level, Xml& xml, const char* name) const
      {
      xml.tag(level++, name);
      header->writeStatus(level, xml);
      xml.etag(level, name);
      }

//---------------------------------------------------------
//   readStatus
//---------------------------------------------------------

void TList::readStatus(Xml& xml, const char* name)
      {
      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 == header->name())
                              header->readStatus(xml);
                        else
                              xml.unknown("Tlist");
                        break;
                  case Xml::TagEnd:
                        if (tag == name)
                              return;
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   setYPos
//---------------------------------------------------------

void TList::setYPos(int y)
      {
      int delta  = ypos - y;         // -  -> shift up
      ypos  = y;
      if (pm.isNull())
            return;
      if (!pmValid) {
            redraw();
            return;
            }
      int w = width();
      int h = height();
      QRect r;
      if (delta >= h || delta <= -h)
            r = QRect(0, 0, w, h);
      else if (delta < 0) {   // shift up
            bitBlt(&pm,  0, 0, &pm, 0, -delta, w, h + delta, CopyROP, true);
            r = QRect(0, h + delta, w, -delta);
            }
      else {                  // shift down
            bitBlt(&pm,  0, delta, &pm, 0, 0, w, h-delta, CopyROP, true);
            r = QRect(0, 0, w, delta);
            }
      paint(r);
      update();
      }

//---------------------------------------------------------
//   resizeEvent
//---------------------------------------------------------

void TList::resizeEvent(QResizeEvent* ev)
      {
      pm.resize(ev->size());
      pmValid = false;
      }

//---------------------------------------------------------
//   classesPopupMenu
//---------------------------------------------------------

void TList::classesPopupMenu(Track* t, int x, int y)
      {
      QPopupMenu p(this);
      p.clear();
      p.insertItem(*addtrack_wavetrackIcon, tr("Midi"), Track::MIDI);
      p.insertItem(*addtrack_drumtrackIcon, tr("Drum"), Track::DRUM);
      int n = p.exec(mapToGlobal(QPoint(x, y)), 0);

      if (n == -1)
            return;

      if (Track::TrackType(n) == Track::MIDI && t->type() == Track::DRUM) {
            //
            //    Drum -> Midi
            //
            audio->msgIdle(true);
            PartList* pl = t->parts();
            for (iPart ip = pl->begin(); ip != pl->end(); ++ip) {
                  EventList* el = ip->second->events();
                  for (iEvent ie = el->begin(); ie != el->end(); ++ie) {
                        Event ev = ie->second;
                        if (ev.type() != Note)
                              continue;
                        int pitch = ev.pitch();
                        pitch = drumMap[pitch].anote;
                        ev.setPitch(pitch);
                        }
                  }
            t->setType(Track::MIDI);
            audio->msgIdle(false);
            }
      else if (Track::TrackType(n) == Track::DRUM && t->type() == Track::MIDI) {
            //
            //    Midi -> Drum
            //
            bool change = QMessageBox::question(this, tr("Update drummap?"),
                           tr("Do you want to use same port and channel for all instruments in the drummap?"),
                           tr("&Yes"), tr("&No"), QString::null, 0, 1);
            if (!change) {
                  MidiTrack* m = (MidiTrack*) t;
                  for (int i=0; i<DRUM_MAPSIZE; i++) {
                        drumMap[i].channel = m->outChannel();
                        drumMap[i].port    = m->outPort();
                        }
                  }

            audio->msgIdle(true);
            PartList* pl = t->parts();
            for (iPart ip = pl->begin(); ip != pl->end(); ++ip) {
                  EventList* el = ip->second->events();
                  for (iEvent ie = el->begin(); ie != el->end(); ++ie) {
                        Event ev = ie->second;
                        if (ev.type() != Note)
                              continue;
                        int pitch = ev.pitch();
                        pitch = drumInmap[pitch];
                        ev.setPitch(pitch);
                        }
                  }
            t->setType(Track::DRUM);
            audio->msgIdle(false);
            }
      }
