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

#include <stdio.h>

#include <qlayout.h>
#include <qpainter.h>
#include <qtoolbutton.h>
#include <qpopupmenu.h>
#include <qlabel.h>
#include <qcursor.h>

#include "globals.h"
#include "ctrledit.h"
#include "midieditor.h"
#include "icons.h"
#include "event.h"
#include "midiport.h"
#include "midithread.h"

extern void drawTickRaster(QPainter& p, int x, int y,
   int w, int h, int quant);

//---------------------------------------------------------
//   CEvent
//---------------------------------------------------------

CEvent::CEvent(MidiEvent* e, MidiPart* pt, int v)
      {
      _event = e;
      _part  = pt;
      _val   = v;
      ex     = e ? e->posTick() : 0;
      }

//---------------------------------------------------------
//   contains
//---------------------------------------------------------

bool CEvent::contains(int x1, int x2) const
      {
      int tick1 = _event ? _event->posTick() + _part->posTick() : 0;
      int tick2 = ex + _part->posTick();
      return ((tick1 >= x1 && tick1 < x2)
         || (tick2 >= x1 && tick2 < x2)
         || (tick1 < x1 && tick2 >= x2));
      }

//---------------------------------------------------------
//   CtrlCanvas
//---------------------------------------------------------

CtrlCanvas::CtrlCanvas(MidiEditor* e, QWidget* parent, int xmag,
   const char* name) : View(parent, xmag, 1, name)
      {
      setBg(white);
      editor = e;
      drag   = DRAG_OFF;
      tool   = PointerTool;
      pos[0] = 0;
      pos[1] = 0;
      pos[2] = 0;
      keyFilter = 0; //Start with no key filter
      connect(song, SIGNAL(posChanged(int, int, bool)), this, SLOT(setPos(int, int, bool)));

      setMouseTracking(true);
      if (editor->parts()->empty()) {
            curPart = 0;
            curTrack = 0;
            }
      else {
            curPart  = (MidiPart*)(editor->parts()->begin()->second);
            curTrack = (MidiTrack*)(curPart->track());
            }
      connect(song, SIGNAL(songChanged(int)), SLOT(songChanged(int)));
      songChanged(SC_TRACK_INSERTED);
      }

//---------------------------------------------------------
//   setPos
//    set one of three markers
//    idx   - 0-cpos  1-lpos  2-rpos
//    flag  - emit followEvent()
//---------------------------------------------------------

void CtrlCanvas::setPos(int idx, int val, bool adjustScrollbar)
      {
#if 1
//      printf("ctrlCanvas: setPos %d %d\n", idx, val);
      if (pos[idx] == val)
            return;

      int opos = mapx(pos[idx]);
      int npos = mapx(val);

      if (adjustScrollbar && idx == 0) {
            switch (song->follow()) {
                  case  Song::NO:
                        break;
                  case Song::JUMP:
                        if (npos >= width()) {
                              int ppos =  val - rmapxDev(width()/4);
                              if (ppos < 0)
                                    ppos = 0;
                              emit followEvent(ppos);
                              opos = mapx(pos[idx]);
                              npos = mapx(val);
                              }
                        else if (npos < 0) {
                              int ppos =  val - rmapxDev(width()*3/4);
                              if (ppos < 0)
                                    ppos = 0;
                              emit followEvent(ppos);
                              opos = mapx(pos[idx]);
                              npos = mapx(val);
                              }
                        break;
                  case Song::CONTINUOUS:
                        if (npos > (width()*5)/8) {
                              int ppos =  pos[idx] - rmapxDev(width()*5/8);
                              if (ppos < 0)
                                    ppos = 0;
                              emit followEvent(ppos);
                              opos = mapx(pos[idx]);
                              npos = mapx(val);
                              }
                        else if (npos < (width()*3)/8) {
                              int ppos =  pos[idx] - rmapxDev(width()*3/8);
                              if (ppos < 0)
                                    ppos = 0;
                              emit followEvent(ppos);
                              opos = mapx(pos[idx]);
                              npos = mapx(val);
                              }
                        break;
                  }
            }

      int x;
      int w = 1;
      if (opos > npos) {
            w += opos - npos;
            x = npos;
            }
      else {
            w += npos - opos;
            x = opos;
            }
      pos[idx] = val;
//printf("    redraw %d %d %d %d\n", x, 0, w, height());
      redraw(QRect(x, 0, w, height()));

#else
      if (pos[idx] == val)
            return;

      int opos = mapx(pos[idx]);
      pos[idx] = val;

      if (!isVisible())
            return;
      val = mapx(val);
      if (adjustScrollbar && idx == 0) {
            switch (song->follow()) {
                  case  Song::NO:
                        break;
                  case Song::JUMP:
                        if (val >= width()) {
                              emit followEvent(val);
                              return;
                              }
                        break;
                  case Song::CONTINUOUS:
                        if (val > width()/2) {
                              int ppos =  pos[idx] - rmapxDev(width()/2);
                              if (ppos < 0)
                                    ppos = 0;
                              emit followEvent(ppos);
                              return;
                              }
                        break;
                  }
            }
      int x;
      int w = 1;
      if (opos > val) {
            w += opos - val;
            x = val;
            }
      else {
            w += val - opos;
            x = opos;
            }
      paint(QRect(x, 0, w, height()));
#endif
      }

//---------------------------------------------------------
//   setCtrlType
//---------------------------------------------------------

void CtrlCanvas::setController(const MidiController& c)
      {
      ctrl = c;
      songChanged(-1);
      }

//---------------------------------------------------------
//   setKeyfilter
//---------------------------------------------------------
void CtrlCanvas::setKeyfilter(char in_keyfilter)
   {
   keyFilter = in_keyfilter;
   songChanged(-1); //Correct?
   }

//---------------------------------------------------------
//   leaveEvent
//---------------------------------------------------------

void CtrlCanvas::leaveEvent(QEvent*)
      {
      emit xposChanged(-1);
      emit yposChanged(-1);
      }

QPoint CtrlCanvas::raster(const QPoint& p) const
      {
      return p;
      }

//---------------------------------------------------------
//   deselectAll
//---------------------------------------------------------

void CtrlCanvas::deselectAll()
      {
//    for (iCEvent i = selection.begin(); i != selection.end(); ++i)
//            (*i)->setState(CEvent::Normal);
//      selection.clear();
//      update();
      }

//---------------------------------------------------------
//   selectItem
//---------------------------------------------------------

void CtrlCanvas::selectItem(CEvent*)
      {
//      e->setState(CEvent::Selected);
//      selection.push_back(e);
//      update();
      }

//---------------------------------------------------------
//   deselectItem
//---------------------------------------------------------

void CtrlCanvas::deselectItem(CEvent*)
      {
/*      e->setState(CEvent::Normal);
      for (iCEvent i = selection.begin(); i != selection.end(); ++i) {
            if (*i == e) {
                  selection.erase(i);
                  break;
                  }
            }
      update();
      */
      }

//---------------------------------------------------------
//   songChanged
//    alle markierten Parts werden hier
//    in die interne Eventliste aufgenommen
//---------------------------------------------------------

void CtrlCanvas::songChanged(int)
      {
      if (editor->parts()->empty())
            return;
      items.clear();

      MidiPort* port = &midiPorts[curTrack->outPort()];

      //---------------------------------------------------
      //    get initial controller value
      //---------------------------------------------------

      int val = 0;
      ChannelState* ics = port->iState(curTrack->outChannel());
      switch(ctrl.type()) {
            case MidiController::Controller7:
                  val = ics->controller[ctrl.hnum()];
                  break;
            case MidiController::Controller14:
                  {
                  int hval = ics->controller[ctrl.hnum()];
                  int lval = ics->controller[ctrl.lnum()];
                  if (hval == -1 && lval == -1)
                        val = -1;
                  else {
                        if (hval == -1)
                              hval = 0;
                        if (lval == -1)
                              lval = 0;
                        val = (hval << 7) + lval;
                        }
                  }
                  break;
            case MidiController::RPN:
            case MidiController::NRPN:
                  // TODO
                  break;
            case MidiController::Pitch:
                  val = ics->pitch;
                  break;
            case MidiController::XgSysex:
            case MidiController::Sysex:
                  // TODO
                  break;
            case MidiController::Velo:
                  break;
            }

      MidiEvent* last = 0;
      CEvent* lastce  = 0;

      for (iPart p = editor->parts()->begin(); p != editor->parts()->end(); ++p) {
            MidiPart* part = (MidiPart*)(p->second);
            EventList* el = part->events();
            for (iEvent i = el->begin(); i != el->end(); ++i) {
                  MidiEvent* e = (MidiEvent*)i->second;
                  switch(ctrl.type()) {
                        case MidiController::Velo:
                              if (e->type() != MidiEvent::Note)
                                    break;
                              //If it's velocity and keyFilter is activated, only add items with the same key
                              if (keyFilter == 0) //If no keyFilter, just add it:
                                 items.add(new CEvent(e, part, e->velo()));
                              else if (e->dataA() == keyFilter) //If keyFilter matches, add it
                                 items.add(new CEvent(e, part, e->velo()));

                              break;
                        case MidiController::Controller7:
                              if (e->type() != MidiEvent::Ctrl7)
                                    break;
                              if (e->cntrl() != ctrl.hnum())
                                    break;
                              if (last == 0) {
                                    lastce = new CEvent(0, part, val);
                                    items.add(lastce);
                                    }
                              if (lastce)
                                    lastce->setEX(e->posTick());
                              lastce = new CEvent(e, part, e->cntrlVal());
                              items.add(lastce);
                              last = e;
                              break;
                        case MidiController::Controller14:
                              if (e->type() != MidiEvent::Ctrl14)
                                    break;
                              if ((e->dataA() != ctrl.hnum()) || (e->dataC() != ctrl.lnum()))
                                    break;
                              if (last == 0) {
                                    lastce = new CEvent(0, part, val);
                                    items.add(lastce);
                                    }
                              if (lastce)
                                    lastce->setEX(e->posTick());
                              lastce = new CEvent(e, part, e->dataB());
                              items.add(lastce);
                              last = e;
                              break;
                        case MidiController::Pitch:
                              if (e->type() != MidiEvent::Pitch)
                                    break;
                              if (last == 0) {
                                    lastce = new CEvent(0, part, val);
                                    items.add(lastce);
                                    }
                              if (lastce)
                                    lastce->setEX(e->posTick());
                              lastce = new CEvent(e, part, e->dataA());
                              items.add(lastce);
                              last = e;
                              break;
                        default:
                              break;
                        }
                  }
            }
      redraw();
      }

//---------------------------------------------------------
//   viewMousePressEvent
//---------------------------------------------------------

void CtrlCanvas::viewMousePressEvent(QMouseEvent* event)
      {
      start = event->pos();
      Tool activeTool = tool;
      bool shift = event->state() & ShiftButton;

      int xpos = start.x();
      int ypos = start.y();

      switch (activeTool) {
            case PointerTool:
                  drag = DRAG_LASSO_START;
                  break;

            case PencilTool:
                  if (shift) {
                        if (ctrl.type() != MidiController::Velo) {
                              drag = DRAG_NEW;
                              song->startUndo();
                              newVal(xpos, xpos, ypos);
                              }
                        }
                  else {
                        drag = DRAG_RESIZE;
                        song->startUndo();
                        changeVal(xpos, xpos, ypos);
                        }
                  break;

            case RubberTool:
                  if (ctrl.type() != MidiController::Velo) {
                        drag = DRAG_DELETE;
                        song->startUndo();
                        deleteVal(xpos, xpos, ypos);
                        }
                  break;

            case DrawTool:
                  if (drawLineMode) {
                        line2x = xpos;
                        line2y = ypos;
                        if (shift)
                              newValRamp(line1x, line1y, line2x, line2y);
                        else
                              changeValRamp(line1x, line1y, line2x, line2y);
                        drawLineMode = false;
                        }
                  else {
                        line2x = line1x = xpos;
                        line2y = line1y = ypos;
                        drawLineMode = true;
                        }
                  redraw();
                  break;

            default:
                  break;
            }
      }

//---------------------------------------------------------
//   newValRamp
//---------------------------------------------------------

void CtrlCanvas::newValRamp(int x1, int y1, int x2, int y2)
      {
      int xx1 = editor->rasterVal1(x1);
      int xx2 = editor->rasterVal2(x2);
      int min = ctrl.minVal();
      int max = ctrl.maxVal();
      int h   = height();

      int raster = editor->raster();
      if (raster == 1)          // set reasonable raster
            raster = division/4;

      song->startUndo();

      // delete existing events

      for (ciCEvent i = items.begin(); i != items.end(); ++i) {
            CEvent* ev = *i;
            MidiEvent* event = ev->event();
            if (event == 0)
                  continue;
            int x = event->posTick();
            if (x < xx1)
                  continue;
            if (x >= xx2)
                  break;
            midiThread->msgDeleteEvent(event, ev->part(), false);
            }

      // insert new events
      for (int x = xx1; x < xx2; x += raster) {
            int y = (x2==x1) ? y1 : (((y2-y1)*(x-x1))/(x2-x1))+y1;
            int nval = (((h - y) *  (max-min)) / h) + min;
            if (nval > max)
                  nval = max;

            int tick = x - curPart->posTick();
            MidiEvent* event = 0;
            switch(ctrl.type()) {
                  case MidiController::Controller7:
                        event = new MidiEvent(
                           tick, MidiEvent::Ctrl7, ctrl.hnum(), nval, 0, 0);
                        break;
                  case MidiController::Controller14:
                        event = new MidiEvent(
                           tick, MidiEvent::Ctrl14, ctrl.hnum(), nval, ctrl.lnum(), 0);
                        break;
                  case MidiController::RPN:
                        event = new MidiEvent(
                           tick, MidiEvent::RPN, ctrl.hnum()*128+ctrl.lnum(), nval, 0, 0);
                        break;
                  case MidiController::NRPN:
                        event = new MidiEvent(
                           tick, MidiEvent::RPN, ctrl.hnum()*128+ctrl.lnum(), nval, 0, 0);
                        break;
                  case MidiController::XgSysex:
                  case MidiController::Sysex:
                        break;
                  case MidiController::Pitch:
                        event = new MidiEvent(
                           tick, MidiEvent::Pitch, nval, 0, 0, 0);
                        break;
                  case MidiController::Velo:
                        break;
                  }
            if (event) {
                  midiThread->msgAddEvent(event, curPart, false);
                  }
            }
      song->update(0);
      redraw();
      song->endUndo(SC_EVENT_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_REMOVED);
      }

//---------------------------------------------------------
//   changeValRamp
//---------------------------------------------------------

void CtrlCanvas::changeValRamp(int x1, int y1, int x2, int y2)
      {
      int h   = height();
      int min = ctrl.minVal();
      int max = ctrl.maxVal();
      bool changed = false;

      song->startUndo();
      for (ciCEvent i = items.begin(); i != items.end(); ++i) {
            if ((*i)->contains(x1, x2)) {
                  CEvent* ev       = *i;
                  MidiEvent* event = ev->event();
                  if (event == 0)
                        continue;

                  MidiPart* part   = ev->part();

                  int x = event->posTick() + ev->part()->posTick();
                  int y = (x2==x1) ? y1 : (((y2-y1)*(x-x1))/(x2-x1))+y1;
                  int nval = (((h - y) *  (max-min)) / h) + min;
                  if (nval > max)
                        nval = max;

                  ev->setVal(nval);
                  if (ctrl.type() == MidiController::Velo) {
                        if ((event->velo() != nval)) {
                              MidiEvent* newEvent = new MidiEvent(*event);
                              newEvent->setVelo(nval);
                              midiThread->msgChangeEvent(event, newEvent, part, false);
                              ev->setEvent(newEvent);
                              changed = true;
                              }
                        }
                  else if (ctrl.type() == MidiController::Controller7) {
                        if (event) {
                              if ((event->cntrlVal() != nval)) {
                                    MidiEvent* newEvent = new MidiEvent(*event);
                                    newEvent->setB(nval);
                                    midiThread->msgChangeEvent(event, newEvent, part, false);
                                    ev->setEvent(newEvent);
                                    changed = true;
                                    }
                              }
                        else {
                              MidiPort* port = &midiPorts[curTrack->outPort()];
                              int channel = curTrack->outChannel();
                              int oval = port->iState(channel)->controller[ctrl.hnum()];
                              if (oval != nval) {
                                    port->iState(channel)->controller[ctrl.hnum()] = nval;
                                    changed = true;
                                    }
                              }
                        }
                  }
            }
      if (changed)
            redraw();
      song->endUndo(SC_EVENT_MODIFIED);
      }

//---------------------------------------------------------
//   viewMouseMoveEvent
//---------------------------------------------------------

void CtrlCanvas::viewMouseMoveEvent(QMouseEvent* event)
      {
      QPoint pos  = event->pos();
      QPoint dist = pos - start;
      bool moving = dist.y() >= 3 || dist.y() <= 3 || dist.x() >= 3 || dist.x() <= 3;
      switch (drag) {
            case DRAG_LASSO_START:
                  if (!moving)
                        break;
                  drag = DRAG_LASSO;
                  // weiter mit DRAG_LASSO:
            case DRAG_LASSO:
                  lasso.setRect(start.x(), start.y(), dist.x(), dist.y());
                  redraw();
                  break;
            case DRAG_RESIZE:
                  changeVal(start.x(), pos.x(), pos.y());
                  start = pos;
                  break;

            case DRAG_NEW:
                  newVal(start.x(), pos.x(), pos.y());
                  start = pos;
                  break;

            case DRAG_DELETE:
                  deleteVal(start.x(), pos.x(), pos.y());
                  start = pos;
                  break;

            default:
                  break;
            }
      if (tool == DrawTool && drawLineMode) {
            line2x = pos.x();
            line2y = pos.y();
            redraw();
            }
      emit xposChanged(pos.x());
      int min = ctrl.minVal();
      int max = ctrl.maxVal();
      int val = max - (pos.y() * (max-min) / height());
      if (val < min)
            val = min;
      if (val > max)
            val = max;
      emit yposChanged(val);
      }

//---------------------------------------------------------
//   viewMouseReleaseEvent
//---------------------------------------------------------

void CtrlCanvas::viewMouseReleaseEvent(QMouseEvent* event)
      {
      bool shift = event->state() & ShiftButton;

      switch (drag) {
            case DRAG_RESIZE:
            case DRAG_NEW:
            case DRAG_DELETE:
                  song->endUndo(SC_EVENT_MODIFIED | SC_EVENT_INSERTED);
                  break;

            case DRAG_LASSO_START:
                  lasso.setRect(-1, -1, -1, -1);

            case DRAG_LASSO:
                  if (!shift)
                        deselectAll();
                  lasso = lasso.normalize();
                  for (iCEvent i = items.begin(); i != items.end(); ++i) {
#if 0
                        if ((*i)->intersects(lasso)) {
                              if (shift && (*i)->isSelected())
                                    deselectItem(*i);
                              else
                                    selectItem(*i);
                              }
#endif
                        }
                  drag = DRAG_OFF;
                  redraw();
                  break;

            default:
                  break;
            }
      drag = DRAG_OFF;
      }

//---------------------------------------------------------
//   changeVal
//---------------------------------------------------------

void CtrlCanvas::changeVal(int x1, int x2, int y)
      {
      int h   = height();
      int min = ctrl.minVal();
      int max = ctrl.maxVal();

      bool changed = false;
      int nval = (((h - y) *  (max-min)) / h) + min;
      if (nval > max)
            nval = max;

      for (ciCEvent i = items.begin(); i != items.end(); ++i) {
            if (!(*i)->contains(x1, x2))
                  continue;

            CEvent* ev       = *i;
            MidiEvent* event = ev->event();
            MidiPart* part   = ev->part();
            ev->setVal(nval);
            if (ctrl.type() == MidiController::Velo) {
                  if ((event->velo() != nval)) {
                        MidiEvent* newEvent = new MidiEvent(*event);
                        newEvent->setVelo(nval);
                        midiThread->msgChangeEvent(event, newEvent, part, false);
                        ev->setEvent(newEvent);
                        changed = true;
                        }
                  }
            else if (ctrl.type() == MidiController::Controller7) {
                  if (event) {
                        if ((event->cntrlVal() != nval)) {
                              MidiEvent* newEvent = new MidiEvent(*event);
                              newEvent->setB(nval);
                              midiThread->msgChangeEvent(event, newEvent, part, false);
                              ev->setEvent(newEvent);
                              changed = true;
                              }
                        }
                  else {
                        MidiPort* port = &midiPorts[curTrack->outPort()];
                        int channel = curTrack->outChannel();
                        int oval = port->iState(channel)->controller[ctrl.hnum()];
                        if (oval != nval) {
                              port->iState(channel)->controller[ctrl.hnum()] = nval;
                              changed = true;
                              }
                        }
                  }
            else if (ctrl.type() == MidiController::Pitch) {
                  if (event) {
                        if ((event->pitch() != nval)) {
                              MidiEvent* newEvent = new MidiEvent(
                                 event->posTick(), MidiEvent::Pitch, nval, 0, 0, 0);
                              midiThread->msgChangeEvent(event, newEvent, part, false);
                              ev->setEvent(newEvent);
                              changed = true;
                              }
                        }
                  else {
                        MidiPort* port = &midiPorts[curTrack->outPort()];
                        int channel = curTrack->outChannel();
                        int oval = port->iState(channel)->pitch;
                        if (oval != nval) {
                              port->iState(channel)->pitch = nval;
                              changed = true;
                              }
                        }
                  }
            }
      if (changed)
            redraw();
      }

//---------------------------------------------------------
//   newVal
//---------------------------------------------------------

void CtrlCanvas::newVal(int x1, int x2, int y)
      {
      int xx1 = editor->rasterVal1(x1);
      int xx2 = editor->rasterVal2(x2);
      int min = ctrl.minVal();
      int max = ctrl.maxVal();

      int h = height();
      int nval = (((h - y) *  (max-min)) / h) + min;
      if (nval > max)
            nval = max;

      bool found = false;
      bool song_changed = false;
      for (ciCEvent i = items.begin(); i != items.end(); ++i) {
            CEvent* ev = *i;
            int partTick = ev->part()->posTick();
            MidiEvent* event = ev->event();
            if (event == 0)
                  continue;
            int ax = event->posTick() + partTick;
            if (ax < xx1)
                  continue;
            if (ax >= xx2)
                  break;
            if (ax == xx1) {
                  // change event
                  found = true;
                  ev->setVal(nval);
                  switch(ctrl.type()) {
                        case MidiController::Controller7:
                              if ((event->cntrlVal() != nval)) {
                                    MidiEvent* newEvent = new MidiEvent(*event);
                                    newEvent->setB(nval);
                                    midiThread->msgChangeEvent(event, newEvent, ev->part(), false);
                                    ev->setEvent(newEvent);
                                    song_changed = true;
                                    }
                              break;
                        case MidiController::Pitch:
                              {
                              if (event->pitch() != nval) {
                                    MidiEvent* newEvent = new MidiEvent(
                                       xx1-partTick, MidiEvent::Pitch, nval, 0, 0, 0);
                                    midiThread->msgChangeEvent(event, newEvent, ev->part(), false);
                                    ev->setEvent(newEvent);
                                    song_changed = true;
                                    }
                              }
                              break;
                        default:
                              // TODO
                              break;
                        }
                  }
            else if (ax < xx2) {
                  // delete event
                  midiThread->msgDeleteEvent(event, ev->part(), false);
                  song_changed = true;
                  }
            }
      if (!found) {
            // new event
            MidiEvent* event = 0;
            int tick = xx1 - curPart->posTick();

            switch(ctrl.type()) {
                  case MidiController::Controller7:
                        event = new MidiEvent(
                           tick, MidiEvent::Ctrl7, ctrl.hnum(), nval, 0, 0);
                        break;
                  case MidiController::Controller14:
                        event = new MidiEvent(
                           tick, MidiEvent::Ctrl14, ctrl.hnum(), nval, ctrl.lnum(), 0);
                        break;
                  case MidiController::RPN:
                        event = new MidiEvent(
                           tick, MidiEvent::RPN, ctrl.hnum()*128+ctrl.lnum(), nval, 0, 0);
                        break;
                  case MidiController::NRPN:
                        event = new MidiEvent(
                           tick, MidiEvent::RPN, ctrl.hnum()*128+ctrl.lnum(), nval, 0, 0);
                        break;
                  case MidiController::XgSysex:
                  case MidiController::Sysex:
                        break;
                  case MidiController::Pitch:
                        event = new MidiEvent(
                           tick, MidiEvent::Pitch, nval, 0, 0, 0);
                        break;
                  case MidiController::Velo:
                        break;
                  }
            if (event) {
                  midiThread->msgAddEvent(event, curPart, false);
                  song_changed = true;
                  }
            }
      if (song_changed) {
            songChanged(0);
            return;
            }
      redraw();
      }

//---------------------------------------------------------
//   deleteVal
//---------------------------------------------------------

void CtrlCanvas::deleteVal(int x1, int x2, int)
      {
      int xx1 = editor->rasterVal1(x1);
      int xx2 = editor->rasterVal2(x2);

      int partTick = curPart->posTick();
      xx1 -= partTick;
      xx2 -= partTick;

      bool song_changed = false;
      for (ciCEvent i = items.begin(); i != items.end(); ++i) {
            CEvent* ev = *i;
            MidiEvent* event = ev->event();
            if (event == 0)
                  continue;
            int x = event->posTick();
            if (x < xx1)
                  continue;
            if (x >= xx2)
                  break;
            if (event) {
                  midiThread->msgDeleteEvent(event, ev->part(), false);
                  song_changed = true;
                  }
            }
      if (song_changed) {
            songChanged(0);
            return;
            }
      }

//---------------------------------------------------------
//   setTool
//---------------------------------------------------------

void CtrlCanvas::setTool(int t)
      {
      if (tool == Tool(t))
            return;
      tool = Tool(t);
      switch(tool) {
            case PencilTool:
                  setCursor(QCursor(*pencilIcon, 4, 15));
                  break;
            case DrawTool:
                  drawLineMode = false;
                  break;
            default:
                  setCursor(QCursor(arrowCursor));
                  break;
            }
      }

//---------------------------------------------------------
//   pdraw
//---------------------------------------------------------

void CtrlCanvas::pdraw(QPainter& p, const QRect& rect)
      {
      int x = rect.x() - 1;   // compensate for 3 pixel line width
      int y = rect.y();
      int w = rect.width() + 2;
      int h = rect.height();

      int wh = height();

      //---------------------------------------------------
      // draw Canvas Items
      //---------------------------------------------------

      if (ctrl.type() == MidiController::Velo) {
            p.setPen(blue);
            for (iCEvent i = items.begin(); i != items.end(); ++i) {
                  CEvent* e = *i;
                  int tick = mapx(e->event()->posTick() + e->part()->posTick());
                  if (tick <= x)
                        continue;
                  if (tick > x+w)
                        break;
                  int y1 = wh - (e->val() * wh / 128);

                  p.setPen(QPen(blue, 3));
                  p.drawLine(tick, wh, tick, y1);
                  }
            }
      else {
            int min  = ctrl.minVal();
            int max  = ctrl.maxVal();
            int x1   = rect.x();
            int lval = -1;
            for (iCEvent i = items.begin(); i != items.end(); ++i) {
                  CEvent* e = *i;
                  int tick = mapx(e->event() ? e->event()->posTick() + e->part()->posTick() : 0);
                  if (tick <= x) {
                        if (e->val() == -1)
                              lval = -1;
                        else
                              lval = wh - ((e->val() - min) * wh / (max - min));
                        continue;
                        }
                  if (tick > x+w)
                        break;
                  if (lval == -1)
                        p.fillRect(x1, 0, tick - x1, wh, gray);
                  else
                        p.fillRect(x1, lval, tick - x1, wh - lval, blue);
                  x1 = tick;
                  if (e->val() == -1)
                        lval = -1;
                  else
                        lval = wh - ((e->val() - min) * wh / (max - min));
                  }
            if (lval == -1)
                  p.fillRect(x1, 0, (x+w) - x1, wh, gray);
            else
                  p.fillRect(x1, lval, (x+w) - x1, wh - lval, blue);
            }

      p.save();
      View::pdraw(p, rect);
      p.restore();

      //---------------------------------------------------
      //    draw marker
      //---------------------------------------------------

      int xp = mapx(pos[0]);
      if (xp >= x && xp < x+w) {
            p.setPen(red);
            p.drawLine(xp, y, xp, y+h);
            }
      xp = mapx(pos[1]);
      if (xp >= x && xp < x+w) {
            p.setPen(blue);
            p.drawLine(xp, y, xp, y+h);
            }
      xp = mapx(pos[2]);
      if (xp >= x && xp < x+w) {
            p.setPen(blue);
            p.drawLine(xp, y, xp, y+h);
            }

      //---------------------------------------------------
      //    draw lasso
      //---------------------------------------------------

      if (drag == DRAG_LASSO) {
            setPainter(p);
            p.setPen(blue);
            p.setBrush(NoBrush);
            p.drawRect(lasso);
            }
//      View::pdraw(p, rect);
      }

//---------------------------------------------------------
//   drawOverlay
//---------------------------------------------------------

void CtrlCanvas::drawOverlay(QPainter& p)
      {
      QString s(ctrl.name());
//printf("draw overlay %s\n", ctrl.name().latin1());
      p.setFont(font3);
      p.setPen(black);
      QFontMetrics fm(font3);
      int y = fm.lineSpacing() + 2;
      p.drawText(2, y, s);
      }

//---------------------------------------------------------
//   overlayRect
//    returns geometry of overlay rectangle
//---------------------------------------------------------

QRect CtrlCanvas::overlayRect() const
      {
      QFontMetrics fm(font3);
      QRect r(fm.boundingRect(ctrl.name()));
      r.moveBy(2, 2);   // top/left margin
      return r;
      }

//---------------------------------------------------------
//   draw
//---------------------------------------------------------

void CtrlCanvas::draw(QPainter& p, const QRect& rect)
      {
      drawTickRaster(p, rect.x(), rect.y(),
         rect.width(), rect.height(), editor->quant());

      //---------------------------------------------------
      //    draw line tool
      //---------------------------------------------------

      if (drawLineMode && (tool == DrawTool)) {
            p.setPen(black);
            p.drawLine(line1x, line1y, line2x, line2y);
            }
      }

