//=============================================================================
//  Awl
//  Audio Widget Library
//  $Id:$
//
//  Copyright (C) 1999-2011 by Werner Schweer and others
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License
//  as published by the Free Software Foundation; version 2 of
//  the License, or (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//=============================================================================

#include "tcanvas.h"
#include "al/al.h"
#include "al/sig.h"
#include "al/tempo.h"
#include "../muse/gconfig.h"
#include "../muse/icons.h"

#include "metronom.xpm"
#include "clock.xpm"

#ifdef __APPLE__
  inline double exp10(double a) { return pow(10.0, a); }
#endif

static QIcon* clockIcon;
static QIcon* metronomIcon;

FollowMode TimeCanvas::followMode = FOLLOW_JUMP;
QPixmap* TimeCanvas::octave;
QPixmap* TimeCanvas::mk1;
QPixmap* TimeCanvas::mk2;
QPixmap* TimeCanvas::mk3;
QPixmap* TimeCanvas::mk4;

enum DragType {
      DRAG_RULER,
      DRAG_CANVASA, DRAG_CANVAS_B,
      DRAG_PANELA, DRAG_PANELB,
      DRAG_OTHER
      };

//---------------------------------------------------------
//   TimeCanvas
//---------------------------------------------------------

TimeCanvas::TimeCanvas(TimeCanvasType t)
   : QFrame()
      {
      setAttribute(Qt::WA_NoSystemBackground, true);

      _yFit      = false;
      _tool      = PointerTool;
      type       = t;
      _timeType  = AL::TICKS;
      marker     = 0;
      showCursor = false;
      ctrlHeight = 0;
      curPitch   = -1;
      mouseInB   = false;
      dragType   = DRAG_OTHER;
      followPos  = true;

      // set default color
      canvasBackgroundColor = QColor(0x71, 0x8d, 0xbe);

      if (clockIcon == 0) {
            clockIcon = new QIcon(QPixmap(clock_xpm));
            metronomIcon = new QIcon(QPixmap(metronom_xpm));
            }
      button = Qt::NoButton;
      grid   = new QGridLayout;
      grid->setMargin(0);
      grid->setSpacing(1);
      setLayout(grid);

      _widget = new QWidget;
      _widget->setAttribute(Qt::WA_NoSystemBackground);
      _widget->setAttribute(Qt::WA_StaticContents);
      _widget->installEventFilter(this);
      _widget->setMouseTracking(true);
      _widget->setAcceptDrops(true);

      // allow to set slider position before slider range
      // is known:

      pos1.setTick(0);
      pos2.setTick(INT_MAX);

      hmag = new QSlider(Qt::Horizontal);
      hmag->setRange(0, 100);
      _xmagMin = 0.001;
      _xmagMax = 0.3;
      _xmag    = 0.04;
      hmag->setValue(xmag2s(_xmag));

      vmag = 0;
      if (type != TIME_CANVAS_DRUMEDIT) {
            vmag = new QSlider(Qt::Vertical);
            vmag->setRange(0, 100);
            vmag->setPageStep(1);
            }
      _ymag = 1.0;

      hbar = new QScrollBar(Qt::Horizontal);
      hbar->setRange(0, INT_MAX);
      vbar = new QScrollBar(Qt::Vertical);
      timeTypeButton = new QToolButton;
      timeTypeButton->setFixedSize(20, rulerHeight);
      setTimeType1(AL::TICKS);
      yRange = 0;

      switch(type) {
            case TIME_CANVAS_PIANOROLL:
                  _ymagMin = 0.5;
                  _ymagMax = 3.0;
                  vmag->setValue(lrint((_ymag-_ymagMin)*100.0/(_ymagMax-_ymagMin)));
                  initPianoroll();
                  break;
            case TIME_CANVAS_DRUMEDIT:
                  _ymagMin = 1.0;
                  _ymagMax = 1.0;
                  yRange   = drumHeight * 128;
                  break;
            case TIME_CANVAS_WAVEEDIT:
                  _xmagMin = 0.001;
                  _xmagMax = 100.0;
                  _xmag    = 0.04;
                  _ymagMin = 1.0;
                  _ymagMax = 10.0;
                  _ymag    = 1.0;
                  break;
            default:
                  _ymagMin = 1.0;
                  _ymagMax = 1.0;
                  break;
            }
      updateGeometry();
      if (type == TIME_CANVAS_PIANOROLL || type == TIME_CANVAS_DRUMEDIT
         || type == TIME_CANVAS_WAVEEDIT) {
            addCtrlButton = new QPushButton(tr("Ctrl"), _widget);
            addCtrlButton->setGeometry(1, 1, rPanelA.width()-4, rulerHeight-4);
            addCtrlButton->setToolTip(tr("Add Controller View"));
            connect(addCtrlButton, SIGNAL(clicked()), SLOT(addCtrlClicked()));
            }

      grid->addWidget(_widget,  0, 0, 3, 2);
      grid->addWidget(hbar,     3, 0, Qt::AlignVCenter);
      grid->addWidget(hmag,     3, 1, Qt::AlignVCenter);
      grid->addWidget(timeTypeButton, 0, 2);
      grid->addWidget(vbar,     1, 2, Qt::AlignHCenter);
      if (vmag)
            grid->addWidget(vmag,     2, 2, Qt::AlignHCenter);

      grid->setColumnStretch(0, 100);
      grid->setRowStretch(1, 100);

      _raster = 0;
      updateScrollBars();
      connect(hbar, SIGNAL(valueChanged(int)), SLOT(moveX(int)));
      connect(vbar, SIGNAL(valueChanged(int)), SLOT(moveY(int)));
      connect(hmag, SIGNAL(valueChanged(int)), SLOT(scaleX(int)));
      if (vmag)
            connect(vmag, SIGNAL(valueChanged(int)), SLOT(scaleY(int)));
      connect(timeTypeButton, SIGNAL(clicked()), SLOT(toggleTimeType()));
      }

//---------------------------------------------------------
//   resizeController
//---------------------------------------------------------

void TimeCanvas::resizeController(int h)
      {
      if (h == ctrlHeight)
            return;
      int updateH = h > ctrlHeight ? h : ctrlHeight;
      ctrlHeight = h;
      updateGeometry();
      updateScrollBars();
      widget()->update(0, widget()->height() - updateH, widget()->width(), updateH);
      }

//---------------------------------------------------------
//   eventFilter
//---------------------------------------------------------

bool TimeCanvas::eventFilter(QObject* obj, QEvent* event)
      {
      if (obj != _widget)
            return QFrame::eventFilter(obj, event);

      switch(event->type()) {
            case QEvent::Paint:
                  {
                  QPainter p(_widget);
                  canvasPaintEvent(((QPaintEvent*)event)->rect(), p);
                  }
                  return true;

            case QEvent::Resize:
                  updateGeometry();
                  updateScrollBars();
                  layout();
                  return false;

            case QEvent::MouseButtonDblClick:
                  {
                  QMouseEvent* me = (QMouseEvent*)event;
                  QPoint p(me->pos());
                  button   = me->button();
                  keyState = me->modifiers();
                  mouseDoubleClick(me);
                  }
                  return true;

            case QEvent::MouseButtonPress:
                  {
                  QMouseEvent* me = (QMouseEvent*)event;
                  keyState = me->modifiers();
                  button   = me->button();
                  QPoint p(me->pos());
                  int x = p.x() - rRuler.x();
                  bool shift = keyState & Qt::ShiftModifier;

                  if (rRuler.contains(p)) {
                        dragType = DRAG_RULER;
                        if (shift) {
                              AL::Pos pos(pix2pos(x));
                              if (button == Qt::LeftButton)
                                    emit addMarker(pos);
                              else if (button == Qt::RightButton)
                                    emit removeMarker(pos);
                              return true;
                              }
                        }
                  else {
                        dragType = DRAG_OTHER;
                        mousePress(me);
                        }
                  }
                  // go on with MouseMove

            case QEvent::MouseMove:
                  {
                  QMouseEvent* me = (QMouseEvent*)event;
                  keyState        = me->modifiers();
                  button          = me->buttons();
                  QPoint p(me->pos());
                  AL::Pos pos(pix2pos(p.x()-rCanvasA.x()));

                  if (dragType == DRAG_OTHER) {
                        if (button == 0 && (rPanelB.contains(p) || rCanvasB.contains(p))) {
                              if (!mouseInB) {
                                    mouseInB = true;
                                    enterB();
                                    }
                              }
                        else {
                              if (button == 0 && mouseInB) {
                                    mouseInB = false;
                                    leaveB();
                                    }
                              }

                        if (showCursor && p.x() < rCanvasA.x()) {
                              showCursor = false;
                              widget()->update(rRuler);
                              emit cursorPos(cursor, showCursor);
                              }

                        if (p.x() >= rCanvasA.x() && (cursor != pos)) {
					int x1 = pos2pix(cursor) + rCanvasA.x();
                              int x2 = pos2pix(pos)    + rCanvasA.x();
                              QRect r1(x1-1, 0, 2, rRuler.height());
                              QRect r2(x2-1, 0, 2, rRuler.height());
                              widget()->update(rRuler & (r1 | r2));
                              cursor     = pos;
                              showCursor = true;
                              emit cursorPos(cursor, showCursor);
                              }

                        if (rRuler.contains(p)) {
                              int b = me->buttons();
                              if (b == 0)
                                    return true;
                              int i = 0;
                              if (b & Qt::MidButton)
                                    i = 1;
                              //else if (b & Qt::RightButton)
                              //      i = 2;
                              else if (b & Qt::RightButton) {
                                    if ((MusEGlobal::config.rangeMarkerWithoutMMB) && (event->modifiers() & Qt::ControlModifier))
                                        i = 1;
                                    else
                                        i = 2;
                                    }
                              if (keyState & Qt::ShiftModifier)
                                    emit addMarker(i);
                              emit posChanged(i, pos);
                              }
                        else {
                              mouseMove(p);
                              }
                        }
                  else if (dragType == DRAG_RULER) {
                        int b = me->buttons();
                        if (b == 0)
                              return true;
                        int i = 0;
                        if (b & Qt::MidButton)
                              i = 1;
                        else if (b & Qt::RightButton)
                              i = 2;
                        if (keyState & Qt::ShiftModifier)
                              emit addMarker(i);
                        emit posChanged(i, pos);
                        }
                  }
                  return true;

            case QEvent::MouseButtonRelease:
                  {
                  QMouseEvent* me = (QMouseEvent*)event;
                  button   = Qt::NoButton;
                  keyState = me->modifiers();
                  mouseRelease(me);
      		dragType = DRAG_OTHER;
                  }
                  return true;

            case QEvent::DragEnter:
                  dragEnter((QDragEnterEvent*)event);
                  return true;

            case QEvent::Drop:
                  drop((QDropEvent*)event);
                  return true;

            case QEvent::DragMove:
                  dragMove((QDragMoveEvent*)event);
                  return true;

            case QEvent::DragLeave:
                  dragLeave((QDragLeaveEvent*)event);
                  return true;

            case QEvent::Leave:
            	{
                  emit cursorPos(cursor, false);
                  showCursor = false;
                  emit pitchChanged(-1);
                  curPitch = -1;
            	QRect r(rRuler);
            	if (!rPanelA.isEmpty())
                  	r |= rPanelA;
                  widget()->update(r);

                  if (mouseInB) {
                        mouseInB = false;
                        // button = ((QMouseEvent*)event)->buttons();
                        leaveB();
                        }
                  }
                  return false;

            case QEvent::Wheel:
            	{
                  QWheelEvent* e = (QWheelEvent*)event;
                  if (e->orientation() != Qt::Vertical)
            		return true;
      		if ((e->modifiers() & Qt::ControlModifier) || (e->modifiers() & Qt::ShiftModifier)) {
                        //
                        // xmag
                        //
                        int oldx = e->x() - rCanvasA.x();
                        AL::Pos pos(pix2pos(oldx));
                        int step = e->delta() / 120;
                        if (step > 0) {
                              for (int i = 0; i< step; ++i)
                                    _xmag *= 1.1;
                              }
                        else {
                              for (int i = 0; i < -step; ++i)
                                    _xmag *= 0.9;
                              }
                        if (_xmag < _xmagMin)
                              _xmag = _xmagMin;
                        else if (_xmag > _xmagMax)
                              _xmag = _xmagMax;
                        hmag->setValue(xmag2s(_xmag));
                        int newx = pos2pix(pos);
                        updateScrollBars();
                        hbar->setValue(wpos.x() + (newx - oldx));
                        updateRulerMag();
                        magChanged();
                        _widget->update();
                        }
                  else {
                        //
                        //   scroll
                        //
                        int step = qMin(QApplication::wheelScrollLines() * vbar->singleStep(), vbar->pageStep());
            		int offset = e->delta() * step / 120;
            		if (vbar->invertedControls())
                  		offset = -offset;
            		if (qAbs(offset) < 1)
                  		return true;
      	      	vbar->setValue(vbar->value() + offset);
                        }
                  }
            	return true;
            default:
// printf("event %d missed\n", event->type());
                  break;
            }
      return false;
      }



void TimeCanvas::keyPressEvent(QKeyEvent *e)
{
    if (e->key() == Qt::Key_Up   || e->key() == Qt::Key_Down ||
        e->key() == Qt::Key_Left || e->key() == Qt::Key_Right)
        keyboardNavigate(e);
}

//---------------------------------------------------------
//   moveX
//---------------------------------------------------------

void TimeCanvas::moveX(int x)
      {
      int dx = wpos.x() - x;
      wpos.setX(x);

      int wh = _widget->height();

      if (type == TIME_CANVAS_PIANOROLL || type == TIME_CANVAS_DRUMEDIT
         || TIME_CANVAS_WAVEEDIT) {
            _widget->scroll(dx, 0, QRect(rCanvasA.x(), 0, rCanvasA.width(), wh));

            //HACK:
            // update controller names
            int w = 100 + ((dx > 0) ? dx : 0);
            _widget->update(rCanvasB.x(), rCanvasB.y(), w, rCanvasB.height());

            //HACK:
            // repaint rounded line end (splitter handle for controller
            // canvas)
            int x = rCanvasB.x() + rCanvasB.width() - 1;
            w = 1;
            if (dx < 0) {
                  x += dx;
                  w -= dx;
                  }
            _widget->update(x, rCanvasB.y(), w, rCanvasB.height());
            }
      else
            _widget->scroll(dx, 0);
      emit contentsMoving(wpos.x(), wpos.y());
      }

//---------------------------------------------------------
//   moveY
//---------------------------------------------------------

void TimeCanvas::moveY(int y)
      {
      int dy = wpos.y() - y;
      if (dy == 0)
            return;
      wpos.setY(y);

      // dont move ruler:

      int ww = _widget->width();
      int wh = _widget->height();

      QRect r(0, rulerHeight, ww, wh - rulerHeight - ctrlHeight);

      _widget->scroll(0, dy, r);
      emit contentsMoving(wpos.x(), wpos.y());
      }

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

void TimeCanvas::setYPos(int y)
      {
      setWPos(QPoint(wpos.x(), y));
      }

//---------------------------------------------------------
//   setWPos
//---------------------------------------------------------

void TimeCanvas::setWPos(const QPoint& p)
      {
      if (wpos != p) {
            wpos = p;
            hbar->setValue(wpos.x());
            vbar->setValue(wpos.y());
            _widget->update();
//            QCoreApplication::flush();
            }
      }

//---------------------------------------------------------
//   paintClockRuler
//---------------------------------------------------------

void TimeCanvas::paintClockRuler(QPainter& p, const QRect& r)
      {
      int x1 = r.x();
      int x2 = x1 + r.width();

      int y1 = r.y();
      int rh = r.height();
      if (y1 < rulerHeight) {
            rh -= rulerHeight - y1;
            y1 = rulerHeight;
            }
      int y2 = y1 + rh;

      //---------------------------------------------------
      //    draw Marker
      //---------------------------------------------------

      int y = rulerHeight - 16;
      p.setPen(Qt::black);
      p.setFont(_font3);
      QRect tr(r);
      tr.setHeight(12);

      if (marker) {
            for (AL::iMarker m = marker->begin(); m != marker->end(); ++m) {
                  int xp = mapx(int(m->second.frame()));
                  if (xp > x2)
                        break;
                  AL::iMarker mm = m;
                  ++mm;
                  int xe = x2;
                  if (mm != marker->end()) {
                        xe = mapx(mm->first);
                        }
                  QRect tr(xp, 0, x2 - xp, 11);
                  if (m->second.current()) {
                        p.fillRect(tr, Qt::white);
                        }
                  if (r.intersects(tr)) {
                        int x2;
                        AL::iMarker mm = m;
                        ++mm;
                        if (mm != marker->end())
                              x2 = mapx(mm->first);
                        else
                              x2 = xp+200;
                        QRect r  = QRect(xp+10, 0, x2-xp, 12);
                        p.drawPixmap(xp, 0, *flagIconS);
                        p.drawText(r, Qt::AlignLeft|Qt::AlignVCenter, m->second.name());
                        }
                  }
            }

      p.setPen(Qt::black);
      if (showCursor) {
            int xp = pos2pix(cursor);
            if (xp >= x1 && xp < x2)
                  p.drawLine(xp, 0, xp, rulerHeight);
            }

      AL::Pos p1(pix2pos(x1));
      AL::Pos p2(pix2pos(x2));

      int sec1 = p1.frame() / AL::sampleRate;
      int sec2 = (p2.frame() + AL::sampleRate - 1) / AL::sampleRate;

      int sw = lrint(AL::sampleRate * _xmag);

      if (sw > 20) {
            for (int sec = sec1; sec < sec2; ++sec) {
                  int min = sec / 60;
                  int sr  = sec % 60;

                  int yy;
                  QString s;
                  if (sr == 0) {
                        p.setFont(_font2);
                        s.sprintf("%d:00", min);
                        yy = y;
                        }
                  else {
                        p.setFont(_font1);
                        s.sprintf("%02d", sr);
                        yy = y + 7;
                        }
                  int xp = pos2pix(AL::Pos(sec * AL::sampleRate, AL::FRAMES));
// printf("  sec %d min %d sr %d  xp %d\n", sec, min, sr, xp);
                  p.setPen(Qt::black);
                  p.drawLine(xp, yy, xp, rulerHeight);
                  p.drawText(xp + 2, rulerHeight - 4, s);
                  p.setPen(sr == 0 ? Qt::lightGray : Qt::gray);
                  p.drawLine(xp, y1, xp, y2);
                  }
            }
      else {
            int min1 = sec1/60;
            int min2 = (sec2+59)/60;
            for (int min = min1; min < min2; ++min) {
                  QString s;
                  p.setFont(_font2);
                  s.sprintf("%d", min);
                  int xp = pos2pix(AL::Pos(min * AL::sampleRate * 60, AL::FRAMES));
                  p.setPen(Qt::black);
                  p.drawLine(xp, y, xp, rulerHeight);
                  p.drawText(xp + 2, rulerHeight - 4, s);
                  p.setPen(Qt::lightGray);
                  p.drawLine(xp, y1, xp, y2);
                  }
            }
      }

//---------------------------------------------------------
//   updateRulerMag
//---------------------------------------------------------

void TimeCanvas::updateRulerMag()
      {
      int bar1, beat, tick;
      pos1.mbt(&bar1, &beat, &tick);
      AL::Pos stick(bar1, 0, 0);
      AL::Pos ntick = AL::Pos(bar1 + 1, 0, 0);
      int tpix  = pos2pix(ntick) - pos2pix(stick);
      metronomeRulerMag = 0;
      if (tpix < 64)
      	metronomeRulerMag = 1;
      if (tpix < 32)
      	metronomeRulerMag = 2;
	if (tpix <= 16)
      	metronomeRulerMag = 3;
	if (tpix < 8)
      	metronomeRulerMag = 4;
      if (tpix <= 4)
      	metronomeRulerMag = 5;
      if (tpix <= 2)
      	metronomeRulerMag = 6;
      }

//---------------------------------------------------------
//   paintMetronomRuler
//---------------------------------------------------------

void TimeCanvas::paintMetronomRuler(QPainter& p, const QRect& r)
      {
      static const int mag[7] = {
            1, 1, 2, 5, 10, 20, 50
            };

      int x = r.x();
      int w = r.width();
      int y = rulerHeight - 16;

      p.setFont(_font3);

      int h  = 14;
      int y1 = r.y();
      int rh = r.height();
      if (y1 < rulerHeight) {
            rh -= rulerHeight - y1;
            y1 = rulerHeight;
            }
      int y2 = y1 + rh;

      if (x < (MAP_OFFSET - wpos.x()))
            x = MAP_OFFSET - wpos.x();
      AL::Pos pos1 = pix2pos(x);
      AL::Pos pos2 = pix2pos(x+w);

      if (marker) {
            AL::iMarker start = marker->lower_bound(pos1.tick());
            if (start != marker->begin())
                  --start;
            AL::iMarker end = marker->lower_bound(pos2.tick());
            for (AL::iMarker m = start; m != end; ++m) {
                  AL::Pos pm1(m->second);
                  AL::iMarker m2 = m;
                  ++m2;
                  AL::Pos pm2(pos2);
                  if (m2 != marker->end())
                        pm2 = m2->second;

                  int x1 = pos2pix(pm1);
                  int x2 = pos2pix(pm2);

                  if (pos[0] >= pm1 && (m2 == marker->end() || pos[0] < pm2))
                        p.fillRect(x1, 0, x2 - x1, 11, Qt::white);

                  QRect r  = QRect(x1 + 10, 0, x2 - x1, 12);
                  p.drawPixmap(x1, 0, *flagIconS);
                  p.drawText(r, Qt::AlignLeft|Qt::AlignVCenter, m->second.name());
                  }
            }

      //---------------------------------------------------
      //    draw raster
      //---------------------------------------------------

      int bar1, bar2, beat, tick;
      pos1.mbt(&bar1, &beat, &tick);
      pos2.mbt(&bar2, &beat, &tick);

      int n = mag[metronomeRulerMag];

	bar1 = (bar1 / n) * n;		// round down
      if (bar1 && n >= 2)
            bar1 -= 1;
	bar2 = ((bar2 + n - 1) / n) * n; // round up

      for (int bar = bar1; bar <= bar2;) {
      	AL::Pos stick(bar, 0, 0);
            if (metronomeRulerMag) {
			p.setFont(_font2);
                  int x = pos2pix(stick);
                  QString s;
                  s.setNum(bar + 1);

                  p.setPen(Qt::black);
                  p.drawLine(x, y, x, y + h);
                  QRect r = QRect(x+2, y, 1000, h);
                  p.drawText(r, Qt::AlignLeft | Qt::AlignVCenter, s);
                  p.setPen(Qt::lightGray);
                  if (x > 0)
                        p.drawLine(x, y1, x, y2);
                  }
            else {
                  AL::TimeSignature sig = stick.timesig();
                  int z = sig.z;
                  for (int beat = 0; beat < z; beat++) {
                        AL::Pos xx(bar, beat, 0);
                        int xp = pos2pix(xx);
                        if (xp < 0)
                              continue;
                        QString s;
                        QRect r(xp+2, y + 1, 1000, h);
                        int y3;
                        int num;
                        if (beat == 0) {
                              num = bar + 1;
                              y3  = y + 2;
                              p.setFont(_font2);
                              }
                        else {
                              num = beat + 1;
                              y3  = y + 8;
                              p.setFont(_font1);
                              r.moveTop(r.top() + 1);
                              }
                        s.setNum(num);
                        p.setPen(Qt::black);
                        p.drawLine(xp, y3, xp, y+h);
                        p.drawText(r, Qt::AlignLeft | Qt::AlignVCenter, s);
                        p.setPen(beat == 0 ? Qt::lightGray : Qt::gray);
                        if (xp > 0)
                              p.drawLine(xp, y1, xp, y2);
                        }
                  }
            if (bar == 0 && n >= 2)
                  bar += (n-1);
            else
                  bar += n;
            }
      //
      //  draw mouse cursor marker
      //
      p.setPen(Qt::black);
      if (showCursor) {
            int xp = pos2pix(cursor);
            if (xp >= x && xp < x+w)
                  p.drawLine(xp, 0, xp, rulerHeight-1);
            }

      }

//---------------------------------------------------------
//   tempoChanged
//---------------------------------------------------------

void TimeCanvas::tempoChanged()
      {
      widget()->update(rCanvasA.x(), 0, rCanvasA.width(), widget()->height());
      }

//---------------------------------------------------------
//   canvasPaintEvent
//---------------------------------------------------------

void TimeCanvas::canvasPaintEvent(const QRect& r, QPainter& p)
      {
      if (r.intersects(rButton)) {
            p.fillRect(rButton, QColor(0xe0, 0xe0, 0xe0));
            p.setPen(QPen(Qt::black, 2));
            int y = rButton.y() + rButton.height() - 1;
            p.drawLine(rButton.x(), y, rButton.width(), y);
            }
      p.setRenderHint(QPainter::TextAntialiasing, true);

      QRect par = r & rPanelA;
      if (!(par.isEmpty() || rPanelA.isEmpty())) {
            if (type == TIME_CANVAS_DRUMEDIT) {
                  paintDrumList(p, par);
                  }
            else if (type == TIME_CANVAS_PIANOROLL) {
                  paintPiano(p, par);
                  }
            else if (type == TIME_CANVAS_WAVEEDIT) {
                  p.fillRect(par, QColor(0xe0, 0xe0, 0xe0));
                  }
            }

      QRect pbr(r & rPanelB);
      QRect hor(r & (rRuler | rCanvasA | rCanvasB));
      QRect car(r & rCanvasA);
      QRect cbr(r & rCanvasB);

      bool drawPanelB  = !(pbr.isEmpty() || rPanelB.isEmpty());
      bool drawRuler   = !(hor.isEmpty() || (rRuler.isEmpty() && rCanvasA.isEmpty() && rCanvasB.isEmpty()));
      bool drawCanvasA = !(car.isEmpty() || rCanvasA.isEmpty());
      bool drawCanvasB = !(cbr.isEmpty() || rCanvasB.isEmpty());

      //
      //  draw canvas background
      //

      p.setClipRect(r);
      p.setBrushOrigin(QPoint(car.x() + wpos.x(), car.y() + wpos.y()));
      if (drawCanvasA) {
            if (canvasBackgroundPixmap.isNull()) {
            	if (type == TIME_CANVAS_DRUMEDIT || type == TIME_CANVAS_PIANOROLL
                     || type == TIME_CANVAS_WAVEEDIT) {
                        QRect rr(car);
                        // paint inactive area different
                        // (darker)
                        QColor c = canvasBackgroundColor.darker(150);
                        int x1 = pos2pix(partPos1) + rCanvasA.x();
                        if (rr.x() < x1) {
                              QRect r(rr.x(), rr.y(), x1-rr.x(), rr.height());
                              p.fillRect(r, c);
                              rr.adjust(x1-rr.x(), 0, 0, 0);
                              }
                        int x2  = pos2pix(partPos2) + rCanvasA.x();
                        int xx2 = rr.x() + rr.width();
                        if (xx2 > x2) {
                              if (x2 < rr.x())
                                    x2 = rr.x();
                              QRect r(x2, rr.y(), xx2-x2, rr.height());
                              p.fillRect(r, c);
                              rr.adjust(0, 0, -(xx2-x2), 0);
                              }
                        if (!rr.isEmpty()) {
	            		p.fillRect(rr, canvasBackgroundColor);
                              }
                        }
                  else
              		p.fillRect(car, canvasBackgroundColor);
                  }
            else {
                  p.drawTiledPixmap(car, canvasBackgroundPixmap,
            	   car.topLeft() + QPoint(wpos));
                  }
            }

      if (drawCanvasB)
            p.fillRect(cbr, canvasBackgroundColor);

      //---------------------------------------------------
      //    draw Ruler
      //---------------------------------------------------

      if (drawRuler) {
            QRect rrr(r & rRuler);
            if (!rrr.isEmpty())
                  p.fillRect(rrr, QColor(0xe0, 0xe0, 0xe0));
            int x1 = hor.x();
            int x2 = x1 + hor.width();
            int y1 = rulerHeight - 17;
            int y2 = rulerHeight - 1;

            p.setPen(QPen(Qt::black, 1));
            p.drawLine(x1, y1, x2, y1);
            p.setPen(QPen(Qt::black, 2));
            p.drawLine(x1, y2, x2, y2);

            QPoint off(rRuler.topLeft());
            p.translate(off);
            if (_timeType == AL::TICKS)
                  paintMetronomRuler(p, hor.translated(-off));
            else
                  paintClockRuler(p, hor.translated(-off));
            p.translate(-off);
            }

      if (drawCanvasA) {
            p.setClipRect(car);
            paintCanvas(p, car);
            }
      p.setRenderHint(QPainter::Antialiasing, false);
      if (drawPanelB) {
            p.setClipRect(pbr);
            QPoint off(rPanelB.topLeft());
            p.translate(off);
            paintControllerPanel(p, pbr.translated(-off));
            p.translate(-off);
            }
      if (drawCanvasB) {
            p.setClipRect(cbr);
            QPoint off(rCanvasB.topLeft());
            p.translate(off);
            paintControllerCanvas(p, cbr.translated(-off));
            p.translate(-off);
            }
      //---------------------------------------------------
      // draw marker
      //---------------------------------------------------

      int y1 = r.y();
      int y2 = y1 + r.height();
      if (drawRuler) {
            p.setClipRect(hor);
            int w  = r.width();
            int x  = r.x();
            int y  = rulerHeight - 16;
            QColor lcColors[3] = { Qt::red, Qt::blue, Qt::blue };

            for (int i = 0; i < 3; ++i) {
                  p.setPen(lcColors[i]);
                  int xp      = pos2pix(pos[i]) + rRuler.x();
                  QPixmap* pm = markIcon[i];
                  int pw = (pm->width() + 1) / 2;
                  int x1 = x - pw;
                  int x2 = x + w + pw;
                  if (xp >= x1 && xp < x2) {
                        p.drawPixmap(xp - pw, y-2, *pm);
                        p.drawLine(xp, y1, xp, y2);
                        }
                  }
            }
      if (marker) {
            int yy1 = y1;
            if (yy1 < rCanvasA.x())
                  yy1 = rCanvasA.x();
            p.setPen(Qt::green);
            AL::iMarker start = marker->lower_bound(pos1.tick());
            if (start != marker->begin())
                  --start;
            AL::iMarker end = marker->lower_bound(pos2.tick());
            if (end != marker->end())
                  ++end;
            for (AL::iMarker m = start; m != end; ++m) {
                  AL::Pos pm(m->second);
                  int x = pos2pix(pm) + rRuler.x();
                  p.drawLine(x, yy1, x, y2);
                  }
            }
      }

//---------------------------------------------------------
//   paintCanvas
//---------------------------------------------------------

void TimeCanvas::paintCanvas(QPainter& p, const QRect& cr)
      {
      QPoint off(rCanvasA.topLeft());

      if (type == TIME_CANVAS_PIANOROLL) {
            paintPianorollHorizontalGrid(p, cr);
            p.setRenderHint(QPainter::Antialiasing, true);
            }
      else if (type == TIME_CANVAS_DRUMEDIT) {
            paintDrumeditHorizontalGrid(p, cr);
            p.setRenderHint(QPainter::Antialiasing, true);
            }
      else
            off = QPoint(rCanvasA.x(), rCanvasA.y() - wpos.y());
      p.translate(off);
      paint(p, cr.translated(-off));
      p.resetMatrix();
      }

//---------------------------------------------------------
//   setLocatorPos
//---------------------------------------------------------

void TimeCanvas::setLocatorPos(int idx, const AL::Pos& val, bool follow)
      {
      if (pos[idx] == val)
            return;
      QFontMetrics fm(_font2);
      int fw  = fm.width("123") + 2;
      int w   = qMax(markIcon[idx]->width() + 2, fw);
      int h   = widget()->height();

      int x = pos2pix(val);
      if (idx == 0 && follow && followPos && followMode != FOLLOW_NO) {
            int scroll = 0;
            if (followMode == FOLLOW_JUMP) {
                  int x2 = rRuler.width() - 20;
                  if (x2 < 0)
                        x2 = rRuler.width();
                  if (x > x2) {
                        int x1 = 20;
                        if (x1 >= rRuler.width())
                              x1 = 0;
                        scroll = x - x1;
                        }
                  else if (x < 0) {
                        scroll = x - MAP_OFFSET;
                        }
                  }
            else if (followMode == FOLLOW_CONTINUOUS) {
                  int x1 = rRuler.width() / 2;
                  if (x != x1) {
                        scroll = x - (rRuler.width() / 2);
                        }
                  }
            if (scroll) {
                  moveX(wpos.x() + scroll);
                  hbar->setValue(wpos.x());
                  }
            }

      int offset = rRuler.x() - (w/2);
      int x1     = pos2pix(pos[idx]);
      int x2     = pos2pix(val);
      QRect oR(x1 + offset, 0, w, h);
      QRect nR(x2 + offset, 0, w, h);
      pos[idx] = val;
      widget()->update(oR | nR);
      }

//---------------------------------------------------------
//   setMag
//---------------------------------------------------------

void TimeCanvas::setMag(double x, double y)
      {
      if (_xmag == x && _ymag == y)
            return;
      _xmag = x;
      _ymag = y;
      if (vmag)
            vmag->setValue(lrint((_ymag-_ymagMin)*100.0/(_ymagMax-_ymagMin)));
      hmag->setValue(xmag2s(_xmag));
      updateScrollBars();
      updateRulerMag();
      magChanged();
      _widget->update();
      }

//---------------------------------------------------------
//   scaleX
//---------------------------------------------------------

void TimeCanvas::scaleX(int val)
      {
      _xmag = s2xmag(val);
      updateScrollBars();
      updateRulerMag();
      magChanged();
      _widget->update();
      }

//---------------------------------------------------------
//   scaleY
//---------------------------------------------------------

void TimeCanvas::scaleY(int val)
      {
      int y = lrint(wpos.y() / _ymag);
      _ymag = (_ymagMax - _ymagMin) / 100.0 * val + _ymagMin;
      y = lrint(y * _ymag);
      wpos.setY(y);
      updateScrollBars();
      magChanged();
      _widget->update();
      }

//---------------------------------------------------------
//   setRaster
//    r = 1   - no raster
//        0   - measure raster
//        > 1 - tick raster
//---------------------------------------------------------

void TimeCanvas::setRaster(int r)
      {
      if (_raster != r) {
            _raster = r;
            _widget->update();
            }
      }

//---------------------------------------------------------
//   setTimeRange
//---------------------------------------------------------

void TimeCanvas::setTimeRange(const AL::Pos& p1, const AL::Pos& p2)
      {
      if (pos1 == p1 && pos2 == p2)
            return;
      pos1 = p1;
      pos2 = p2;
      updateScrollBars();
      widget()->update();
      }

//---------------------------------------------------------
//   setEndPos
//---------------------------------------------------------

void TimeCanvas::setEndPos(const AL::Pos& p2)
      {
      if (pos2 == p2)
            return;
      pos2 = p2;
      updateScrollBars();
      widget()->update();
      }

//---------------------------------------------------------
//   updateScrollBars
//---------------------------------------------------------

void TimeCanvas::updateScrollBars()
      {
      hbar->blockSignals(true);
      vbar->blockSignals(true);

      int ymax = lrint(yRange * _ymag) - rCanvasA.height();
      if (ymax < 0)
            ymax = 0;
      vbar->setRange(0, ymax);
      vbar->setPageStep(rCanvasA.height());

      int xmin    = lrint(pos1.time(_timeType) * _xmag);
      unsigned x2 = pos2.time(_timeType);
      int xmax    = lrint(x2 * _xmag) - rCanvasA.width();
      if (xmax - xmin < 0)
            xmax = xmin;
      hbar->setRange(xmin, xmax);
      hbar->setPageStep(rCanvasA.width());
      wpos.setX(hbar->value());
      wpos.setY(vbar->value());

      hbar->blockSignals(false);
      vbar->blockSignals(false);
      }

//---------------------------------------------------------
//   setTimeType1
//---------------------------------------------------------

void TimeCanvas::setTimeType1(AL::TType t)
      {
      double conv = 1.0;
      if (t == AL::TICKS) {
            timeTypeButton->setIcon(*metronomIcon);
            if (_timeType == AL::FRAMES)
                  conv = AL::sampleRate / double(AL::division * 120 / 60);
            }
      else {
            timeTypeButton->setIcon(*clockIcon);
            if (_timeType == AL::TICKS)
                  conv = double(AL::division * 120 / 60) / double(AL::sampleRate);
            }
      _timeType = t;
      _xmag    *= conv;
      _xmagMax *= conv;
      _xmagMin *= conv;

      updateRulerMag();
      magChanged();
      }

//---------------------------------------------------------
//   setTimeType
//---------------------------------------------------------

void TimeCanvas::setTimeType(AL::TType t)
      {
      setTimeType1(t);
      updateScrollBars();
      timeTypeChanged();
      widget()->update();
      }

//---------------------------------------------------------
//   toggleTimeType
//---------------------------------------------------------

void TimeCanvas::toggleTimeType()
      {
      if (_timeType == AL::TICKS)
            setTimeType(AL::FRAMES);
      else
            setTimeType(AL::TICKS);
      }

//---------------------------------------------------------
//   setMarkerList
//---------------------------------------------------------

void TimeCanvas::setMarkerList(AL::MarkerList* ml)
      {
      if (marker == ml)
            return;
      marker = ml;
      widget()->update();
      }

//---------------------------------------------------------
//   pix2pos
//---------------------------------------------------------

AL::Pos TimeCanvas::pix2pos(int x) const
      {
      int val = lrint((x + wpos.x() - MAP_OFFSET)/_xmag);
      if (val < 0)
           val = 0;
      return AL::Pos(val, _timeType);
      }

//---------------------------------------------------------
//   pos2pix
//---------------------------------------------------------

int TimeCanvas::pos2pix(const AL::Pos& p) const
      {
      return lrint(p.time(_timeType) * _xmag) + MAP_OFFSET - wpos.x();
      }

//---------------------------------------------------------
//   mapx
//---------------------------------------------------------

int TimeCanvas::mapx(int x)  const
      {
      return lrint(x * _xmag) + MAP_OFFSET - wpos.x();
      }

//---------------------------------------------------------
//   mapxDev
//---------------------------------------------------------

int TimeCanvas::mapxDev(int x) const
      {
      int val = lrint((x + wpos.x() - MAP_OFFSET)/_xmag);
      if (val < 0)
           val = 0;
      return val;
      }

//---------------------------------------------------------
//   setCorderWidget
//---------------------------------------------------------

void TimeCanvas::setCornerWidget(QWidget* w)
      {
      grid->addWidget(w, 3, 2);
      }

//---------------------------------------------------------
//   initPianoroll
//---------------------------------------------------------

/*
      0   1   2  3  4  5  6  7  8  9  10
      c-2 c-1 C0 C1 C2 C3 C4 C5 C6 C7 C8 - G8

      Grid ve:

           +------------+ ------------------------------
       11  |            |
           |         b  |         7
           +------+     |
       10  |  a#  +-----+ ..............................
           +------+  a  |
        9  |            |         6
           +------+     |
        8  |  g#  +-----+ ..............................
           +------+  g  |
        7  |            |         5
           +------+     |
        6  |  f#  +-----+ ..............................
           +------+  f  |
        5  |            |         4
           |            |
           +------------+ ------------------------------
        4  |            |
           |         e  |         3
           +------+     |
        3  |  d#  +-----+ ..............................
           +------+  d  |
        2  |            |         2
           +------+     |
        1  |  c#  +-----+ ..............................
           +------+  c  |
           |            |         1
        0  |            |
           +------------+ ------------------------------
 */

void TimeCanvas::initPianoroll()
      {
      static const char *oct_xpm[] = {
      // w h colors
            "40 91 2 1",
            ". c #f0f0f0",
            "# c #000000",
            //           x
            "####################################### ",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#", // 10
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#", //------------------------
            "#######################................#",
            "########################...............#",
            "########################...............#",
            "####################################### ",     // 7
            "########################...............#",
            "########################...............#",
            "#######################................#", //------------------------
            ".......................................#",
            ".......................................#",
            ".......................................#",     // 6
            ".......................................#",
            ".......................................#",
            ".......................................#", //------------------------
            "#######################................#",
            "########################...............#",
            "########################...............#",     // 7
            "####################################### ",
            "########################...............#",
            "########################...............#",
            "#######################................#", //------------------------
            ".......................................#",
            ".......................................#",
            ".......................................#",    // 6
            ".......................................#",
            ".......................................#",
            ".......................................#", //------------------------
            "#######################................#",
            "########################...............#",
            "########################...............#",    // 7
            "####################################### ",
            "########################...............#",
            "########################...............#",
            "#######################................#", //------------------------
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",    // 10
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            "####################################### ", //----------------------
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",    // 9
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#", //------------------------
            "#######################................#",
            "########################...............#",
            "########################...............#",
            "####################################### ",   // 7
            "########################...............#",
            "########################...............#",
            "#######################................#", //------------------------
            ".......................................#",
            ".......................................#",
            ".......................................#",     // 6
            ".......................................#",
            ".......................................#",
            ".......................................#", //--------------------------
            "#######################................#",
            "########################...............#",
            "########################...............#",     // 7
            "####################################### ",
            "########################...............#",
            "########################...............#",
            "#######################................#", //------------------------
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",     // 10
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            };

      static const char *mk1_xpm[] = {
            "40 13 2 1",
            ". c #ff0000",
            "# c none",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            "#######################................#",
            "########################...............#",
            "########################...............#",
            "####################################### ",
            };

      static const char *mk2_xpm[] = {
            "40 13 2 1",
            ". c #ff0000",
            "# c none",
            "########################...............#",
            "########################...............#",
            "#######################................#", //------------------------
            ".......................................#",
            ".......................................#",
            ".......................................#",     // 6
            ".......................................#",
            ".......................................#",
            ".......................................#", //--------------------------
            "#######################................#",
            "########################...............#",
            "########################...............#",     // 7
            "####################################### ",
            };

      static const char *mk3_xpm[] = {
            "40 13 2 1",
            ". c #ff0000",
            "# c none",
            "########################...............#",
            "########################...............#",
            "#######################................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            ".......................................#",
            "########################################",
            };

      static const char *mk4_xpm[] = {
            "40 13 2 1",
            "# c #ff0000",
            ". c none",
            "........................................",
            "........................................",
            "........................................",
            "#######################.................",
            "########################................",
            "########################................",
            "########################................",
            "########################................",
            "########################................",
            "#######################.................",
            "........................................",
            "........................................",
            "........................................",
            };

      if (octave == 0) {
            octave = new QPixmap(oct_xpm);
            mk1    = new QPixmap(mk1_xpm);
            mk2    = new QPixmap(mk2_xpm);
            mk3    = new QPixmap(mk3_xpm);
            mk4    = new QPixmap(mk4_xpm);
            }
      yRange = keyHeight * 75;
      }

//---------------------------------------------------------
//   pitch2y
//	y = 0 == origin of rCanvasA
//---------------------------------------------------------

int TimeCanvas::pitch2y(int pitch) const
      {
      int y;
      if (type == TIME_CANVAS_DRUMEDIT)
            y = pitch * drumHeight;
      else {
      	static int tt[] = {
	            12, 19, 25, 32, 38, 51, 58, 64, 71, 77, 84, 90
      	      };
	      y = (75 * keyHeight) - (tt[pitch % 12] + (7 * keyHeight) * (pitch / 12));
      	if (y < 0)
	            y = 0;
            }
      return lrint(y - wpos.y() / _ymag);
      }

//---------------------------------------------------------
//   y2pitch
//	y = 0 == origin of rCanvasA
//---------------------------------------------------------

int TimeCanvas::y2pitch(int y) const
      {
      y = lrint((y + wpos.y()) / _ymag);
      int pitch;
      if (type == TIME_CANVAS_DRUMEDIT)
            pitch = y / drumHeight;
      else {
      	const int total = (10 * 7 + 5) * keyHeight;       // 75 Ganztonschritte
            y = total - y;
            int oct = (y / (7 * keyHeight)) * 12;
            char kt[] = {
                  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                  1, 1, 1, 1, 1, 1, 1,
                  2, 2, 2, 2, 2, 2,
                  3, 3, 3, 3, 3, 3, 3,
                  4, 4, 4, 4, 4, 4, 4, 4, 4,
                  5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
                  6, 6, 6, 6, 6, 6, 6,
                  7, 7, 7, 7, 7, 7,
                  8, 8, 8, 8, 8, 8, 8,
                  9, 9, 9, 9, 9, 9,
                  10, 10, 10, 10, 10, 10, 10,
                  11, 11, 11, 11, 11, 11, 11, 11, 11, 11
                  };
            pitch = kt[y % 91] + oct;
            if (pitch < 0 || pitch > 127)
                  pitch = -1;
            }
      return pitch;
      }

//---------------------------------------------------------
//   paintPiano
//---------------------------------------------------------

void TimeCanvas::paintPiano(QPainter& p, QRect r)
      {
      int   d    = int(_ymag)+1;
      qreal x    = qreal(r.x());
      qreal y    = (r.y()-rulerHeight-d) / _ymag;
      if (y < 0.0)
            y = 0.0;
      qreal h = (r.height()+d) / _ymag;
      QPointF offset(x, wpos.y() / _ymag + keyHeight * 2 + y);

      p.translate(0.0, qreal(rulerHeight));
      p.scale(1.0, _ymag);
      p.drawTiledPixmap(QRectF(x, y, qreal(r.width()), h), *octave, offset);

      if (curPitch != -1) {
            int y = pitch2y(curPitch);
            QPixmap* pm;
            switch(curPitch % 12) {
                  case 0:
                  case 5:
                        pm = mk3;
                        break;
                  case 2:
                  case 7:
                  case 9:
                        pm = mk2;
                        break;
                  case 4:
                  case 11:
                        pm = mk1;
                        break;
                  default:
                        pm = mk4;
                        break;
                  }
            p.drawPixmap(0, y, *pm);
            }
      p.resetMatrix();
      }

//---------------------------------------------------------
//   paintPianorollHorizontalGrid
//---------------------------------------------------------

void TimeCanvas::paintPianorollHorizontalGrid(QPainter& p, QRect r)
      {
      qreal offset = rulerHeight - wpos.y();
      qreal kh = keyHeight * _ymag;

      int x1 = r.x();
      int x2 = x1 + r.width();
      if (x1 < pianoWidth)
            x1 = pianoWidth;
      qreal y  = kh + offset;
      qreal y1 = r.y() - _ymag;
      qreal y2 = y1 + r.height() + _ymag;
      for (int key = 1; key < 75; ++key, y += kh) {
            if (y < y1)
                  continue;
            if (y > y2)
                  break;
            switch (key % 7) {
                  case 2:
                  case 5:
                        p.setPen(QPen(Qt::lightGray));
                        break;
                  default:
                        p.setPen(QPen(Qt::gray));
                        break;
                  }
            p.drawLine(QLineF(x1, y, x2, y));
            }
      }

//---------------------------------------------------------
//   paintDrumeditHorizontalGrid
//---------------------------------------------------------

void TimeCanvas::paintDrumeditHorizontalGrid(QPainter& p, QRect r)
      {
      int offset = rulerHeight - wpos.y();

      p.setPen(QPen(Qt::lightGray));
      int x1 = r.x();
      int x2 = x1 + r.width();
      if (x1 < drumWidth)
            x1 = drumWidth;

      p.setPen(QPen(Qt::lightGray));

      int y  = offset;
      int y1 = r.y();
      int y2 = y1 + r.height();
      for (int i = 0; i < 128; ++i, y += drumHeight) {
            if (y < y1)
                  continue;
            if (y > y2)
                  break;
            p.drawLine(QLine(x1, y, x2, y));
            }
      }

//---------------------------------------------------------
//   addCtrlClicked
//---------------------------------------------------------

void TimeCanvas::addCtrlClicked()
      {
      addController();
      }

//---------------------------------------------------------
//   updateGeometry
//---------------------------------------------------------

void TimeCanvas::updateGeometry()
      {
      int wh = _widget->height();
      int ww = _widget->width();
      if (wh < ctrlHeight)
            ctrlHeight = wh;

      int x1 = 0;
      if (type == TIME_CANVAS_PIANOROLL)
            x1 = pianoWidth;
      else if (type == TIME_CANVAS_DRUMEDIT)
            x1 = drumWidth;
      else if (type == TIME_CANVAS_WAVEEDIT)
            x1 = waveWidth;
      int y2 = wh - ctrlHeight;

      rPanelA.setRect(0, rulerHeight, x1, wh - rulerHeight - ctrlHeight);
      rPanelB.setRect(0, y2,          x1, ctrlHeight);

      int cw = ww - x1;
      rRuler.setRect(  x1, 0,           cw, rulerHeight);
      rCanvasA.setRect(x1, rulerHeight, cw, wh - rulerHeight - ctrlHeight);
      rCanvasB.setRect(x1, y2,          cw, ctrlHeight);

      rButton.setRect(0, 0, rCanvasA.x(), rPanelA.y());

      if (yRange > 0 && _yFit) {
            _ymagMin = double(rCanvasA.height()) / double(yRange);
            if (_ymag < _ymagMin)
                  _ymag = _ymagMin;
            if (vmag)
                  vmag->setValue(lrint((_ymag-_ymagMin)*100.0/(_ymagMax-_ymagMin)));
            }
      }

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

void TimeCanvas::setTool(int t)
      {
      if (_tool == Tool(t))
            return;
      _tool = Tool(t);
      emit toolChanged(_tool);
      setCursor();
      }

//---------------------------------------------------------
//   setCursor
//---------------------------------------------------------

void TimeCanvas::setCursor()
      {
      switch(_tool) {
            case PencilTool:
                  widget()->setCursor(QCursor(QPixmap(":/xpm/pencil.xpm"), 4, 15));
                  break;
            case RubberTool:
                  widget()->setCursor(QCursor(QPixmap(":/xpm/delete.xpm"), 4, 15));
                  break;
            case GlueTool:
                  widget()->setCursor(QCursor(QPixmap(":/xpm/glue.xpm"), 4, 15));
                  break;
            case CutTool:
                  widget()->setCursor(QCursor(QPixmap(":/xpm/cut.xpm"), 4, 15));
                  break;
            case MuteTool:
                  widget()->setCursor(QCursor(QPixmap(":/xpm/editmute.xmp"), 4, 15));
                  break;
            default:
                  widget()->setCursor(QCursor(Qt::ArrowCursor));
                  break;
            }
      }

//---------------------------------------------------------
//   setCanvasBackground
//---------------------------------------------------------

void TimeCanvas::setCanvasBackground(const QColor& color)
      {
	canvasBackgroundPixmap = QPixmap();
      canvasBackgroundColor = color;
      widget()->update();
      }

//---------------------------------------------------------
//   setCanvasBackground
//---------------------------------------------------------

void TimeCanvas::setCanvasBackground(const QPixmap& pm)
      {
      canvasBackgroundPixmap = pm;
      widget()->update();
      }

//---------------------------------------------------------
//   setYMagRange
//---------------------------------------------------------

void TimeCanvas::setYMagRange(double min, double max)
      {
      _ymagMin = min;
      _ymagMax = max;
      if (vmag)
            vmag->setValue(lrint((_ymag-_ymagMin)*100.0/(_ymagMax-_ymagMin)));
      }

//---------------------------------------------------------
//   setVSize
//---------------------------------------------------------

void TimeCanvas::setVSize(int val)
      {
      if (yRange == val)
            return;
      yRange = val;
      if (_yFit) {
            _ymagMin = double(rCanvasA.height()) / double(yRange);
            if (_ymag < _ymagMin)
                  _ymag = _ymagMin;
            if (vmag)
                  vmag->setValue(lrint((_ymag-_ymagMin)*100.0/(_ymagMax-_ymagMin)));
            }
      updateScrollBars();
      }

//---------------------------------------------------------
//   s2xmag
//	nonlinear xmag behaviour, feels better
//---------------------------------------------------------

double TimeCanvas::s2xmag(int val)
	{
      val = 100 - val;
	double f = 1.0 - log10(val * val + 1) * 0.25;
      return (_xmagMax - _xmagMin) * f + _xmagMin;
      }

//---------------------------------------------------------
//   xmag2s
//---------------------------------------------------------

int TimeCanvas::xmag2s(double m)
	{
      m -= _xmagMin;
      m /= (_xmagMax - _xmagMin);
      double val = sqrt(exp10((1.0 - m) * 4.0)-1.0);
      return lrint(100.0 - val);
      }

//---------------------------------------------------------
//   setPart
//---------------------------------------------------------

void TimeCanvas::setPart(const AL::Pos& p1, const AL::Pos& p2)
	{
      partPos1 = p1;
      partPos2 = p2;
      widget()->update();
      }

//---------------------------------------------------------
//   setFont1
//---------------------------------------------------------

void TimeCanvas::setFont1(const QFont& f)
      {
      _font1 = f;
      printf("TimeCanvas::setFont1\n");
      }

//---------------------------------------------------------
//   setFont2
//---------------------------------------------------------

void TimeCanvas::setFont2(const QFont& f)
      {
      _font2 = f;
      }

//---------------------------------------------------------
//   setFont3
//---------------------------------------------------------

void TimeCanvas::setFont3(const QFont& f)
      {
      _font3 = f;
      }