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

#include <stdio.h>
#include <qpainter.h>
#include <cmath>
#include <qinputdialog.h>
#include <qtimer.h>
#include <qcursor.h>
#include <assert.h>

#include "layout.h"

#include "scrollscale.h"
#include "symbols.h"
#include "globals.h"
#include "quant.h"
#include "event.h"
#include "action.h"
#include "midithread.h"

#define A_LYRICS 0
// TODO: dynamic_castīs sind mist!

//---------------------------------------------------------
//   ScoreCanvas
//---------------------------------------------------------

ScoreCanvas::ScoreCanvas(MidiEditor* pr, QWidget* parent,
   int sx, int sy) : View(parent, sx, sy)
      {
      timer = new QTimer(this, "timer");
      connect(timer, SIGNAL(timeout()), SLOT(blink()));

      setFocusPolicy(StrongFocus);
      setBg(white);

      lyricsNote  = 0;
      lyricsSr    = 0;
      quantConfig = 0;
      editor      = pr;
      tool        = PointerTool;
      paletteItem = -1;       // none selected
      page        = 0;
      oldTick     = -1;
      oldPitch    = -1;
      pos[0]      = song->cpos();
      pos[1]      = song->lpos();
      pos[2]      = song->rpos();
      curVelo     = 70;

      //  collect list of track serial numbers
      TrackList* tl = song->tracks();
      for (ciTrack t = tl->begin(); t != tl->end(); ++t) {
            MidiTrack* track =  dynamic_cast<MidiTrack*>(*t);
            if (track && track->selected())
                  tracks.push_back(track->sn());
            }
      system = new SystemLayout();
      system->updateTracks(tracks);
      system->layout1();
      system->layout2();
      setMouseTracking(true);
      connect(song, SIGNAL(posChanged(int,int,bool)), SLOT(setPos(int,int,bool)));
      }

//---------------------------------------------------------
//   ~ScoreCanvas
//---------------------------------------------------------

ScoreCanvas::~ScoreCanvas()
      {
      delete system;
      }

//---------------------------------------------------------
//   ScoreCanvas::getCaption
//---------------------------------------------------------

QString ScoreCanvas::getCaption() const
      {
      return QString("MusE Score");
      }

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

void ScoreCanvas::songChanged(int flags)
      {
      if (flags & ~SC_SELECTION) {
            system->updateTracks(tracks);
            if (system->tracks()->size() == 0)
                  return;

            system->layout1();
            system->layout2();
            }
      select.clear();
      system->updateSelection(this);
#if 0
      //
      // apply selections
      //
      for (iScore i = select.begin(); i != select.end(); ++i) {
            MidiEvent* ev = i->second->event();
            if (ev) {
                  system->collect(&tmp, ev);
                  }
            }
      for (iScore i = tmp.begin(); i != tmp.end(); ++i)
            selectItem(i->second);
#endif
      redraw();
      }

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

void ScoreCanvas::setTool(int t)
      {
      tool = Tool(t);
      QCursor cursor;

      switch (tool) {
            case ScoreTool:
                  switch(editor->quant()) {
                        case 1024:              // 1/1 note head
                        case 1536:
                        case 2304:
                              cursor = QCursor(QPixmap(*noteBM[0]), 6, 16);
                              break;
                        case 512:              // 1/2 note head
                        case 768:
                        case 1152:
                              cursor = QCursor(QPixmap(*noteBM[1]), 6, 16);
                              break;
                        case 256:               // 1/4 note head
                        case 384:
                        case 576:
                              cursor = QCursor(QPixmap(*noteBM[2]), 6, 16);
                              break;
                        case 128:               // 1/8 note head
                        case 192:
                        case 288:
                              cursor = QCursor(QPixmap(*noteBM[3]), 6, 16);
                              break;
                        case 64:                // 1/16 note head
                        case 96:
                        case 144:
                              cursor = QCursor(QPixmap(*noteBM[4]), 6, 16);
                              break;
                        case 32:                // 1/32 note head
                        case 48:
                        case 72:
                              cursor = QCursor(QPixmap(*noteBM[5]), 6, 16);
                              break;
                        case 16:                // 1/64 note head
                        case 24:
                        case 36:
                              cursor = QCursor(QPixmap(*noteBM[6]), 6, 16);
                              break;
                        }
                  break;
            default:
                  cursor = QCursor(arrowCursor);
                  break;
            }
      setCursor(cursor);
      }

//---------------------------------------------------------
//   setPos
//---------------------------------------------------------

void ScoreCanvas::setPos(int idx, int val, bool)
      {
      if (pos[idx] == val)
            return;
      pos[idx] = val;

      if (!isVisible())
            return;
      redraw();
      }

//       0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
//-------------------------------------------------------
// 1/16  x  x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
// 1/8   x     x     x     x     x     x     x     x
// 1/4   x           x           x           x
// 1/2   x                       x
// 1/1   x

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

void ScoreCanvas::selectItem(ScoreItem* item)
      {
      if (!item->isSelected()) {
            item->setState(ScoreItem::Selected);
            select.add(item);
            }

      //---------------------------------------------------
      //   marke split notes
      //---------------------------------------------------

      NoteItem* i = dynamic_cast<NoteItem*>(item);
      if (i) {
            for (;;) {
                  Tie* tie = i->tieFrom();
                  if (tie == 0)
                        break;
                  i = tie->startNote();
                  }
            for (;;) {
                  if (!i->isSelected()) {
                        i->setState(ScoreItem::Selected);
                        select.add(i);
                        }
                  Tie* tie = i->tieTo();
                  if (tie == 0)
                        break;
                  i = tie->endNote();
                  }
            }
      }

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

void ScoreCanvas::deselectItem(ScoreItem* item)
      {
      item->setState(ScoreItem::Normal);
      for (iScore i = select.begin(); i != select.end(); ++i) {
            if (i->second == item) {
                  select.erase(i);
                  break;
                  }
            }
      }

//---------------------------------------------------------
//   updateSelection
//---------------------------------------------------------

void ScoreCanvas::updateSelection()
      {
      if (select.size() != 1) {
            emit selectionChanged(0, 0, 0);
            }
      else {
            NoteItem* ni = dynamic_cast<NoteItem*>(select.begin()->second);
            if (ni == 0) {
                  emit selectionChanged(0, 0, 0);
                  return;
                  }
            emit selectionChanged(ni->event()->posTick(), ni->event(), ni->part());
            }
      }

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

void ScoreCanvas::deselectAll()
      {
      for (iScore i = select.begin(); i != select.end(); ++i)
            i->second->setState(ScoreItem::Normal);
      select.clear();
      }

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

void ScoreCanvas::leaveEvent(QEvent*)
      {
      oldTick  = -1;
      oldPitch = -1;
      emit timeChanged(-1);
      emit pitchChanged(-1);
      }

//---------------------------------------------------------
//   setPaletteItem
//---------------------------------------------------------

void ScoreCanvas::setPaletteItem(int item)
      {
      paletteItem = item;
      setTool(PencilTool);
      }

//---------------------------------------------------------
//   startMoving
//    copy selection list to moving list
//---------------------------------------------------------

void ScoreCanvas::startMoving(const QPoint& pos, bool)
      {
      for (iScore i = select.begin(); i != select.end(); ++i) {
            i->second->setState(ScoreItem::Moving);
            moving.add(i->second);
            }
      moveItems(pos);
      }

//---------------------------------------------------------
//   moveItems
//    move all items in moving list
//---------------------------------------------------------

void ScoreCanvas::moveItems(const QPoint& pos)
      {
      QPoint delta(pos - start);
      for (iScore i = moving.begin(); i != moving.end(); ++i) {
            ScoreItem* it = i->second;
            it->setMoving(it->pos() + delta);
            }
      redraw();
      }

//---------------------------------------------------------
//   setPage
//---------------------------------------------------------

void ScoreCanvas::setPage(int val)
      {
      page = val;
      system->setPage(page);
      redraw();
      }

//---------------------------------------------------------
//   pages
//---------------------------------------------------------

int ScoreCanvas::pages() const
      {
      return system->getPages();
      }

//---------------------------------------------------------
//   selectAll
//---------------------------------------------------------

void ScoreCanvas::selectAll(bool shift, QRect r)
      {
      Page* page = system->page();
      ScoreList* l = &(page->items);
      for (ciScore s = l->begin(); s != l->end(); ++s) {
            ScoreItem* i = s->second;
            if (i->intersects(r)) {
                  if (shift && i->state()==ScoreItem::Selected)
                        deselectItem(i);
                  else
                        selectItem(i);
                  }
            }
      SystemList* sl = &(page->systems);
      for (iSystem i = sl->begin(); i != sl->end(); ++i) {
            System* sys = &*i;
            StaveRowList* stl = &(sys->staves);
            for (iStaveRow k = stl->begin(); k != stl->end(); ++k) {
                  l = &(k->items);
                  QRect rr = r;
                  rr.moveBy(-k->offset.x(), -k->offset.y());
                  for (ciScore s = l->begin(); s != l->end(); ++s) {
                        ScoreItem* i = s->second;
                        if (i->intersects(rr)) {
                              if (shift && i->state()==ScoreItem::Selected)
                                    deselectItem(i);
                              else
                                    selectItem(i);
                              }
                        }
                  }
            }
      }

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

void ScoreCanvas::viewMousePressEvent(QMouseEvent* ev)
      {
      if (lyricsNote)
            endEnterLyrics();
      start        = ev->pos();
      bool shift   = ev->state() & ShiftButton;
      curItem      = system->searchItem(start);

      int ny;
      MidiTrack* track = 0;
      MidiPart*  part  = 0;
      int tick         = -1;

      StaveRow* sr = 0;
      if (curItem && (sr = curItem->staveRow())) {
            tick = system->xy2tick(start, &ny, sr);
            }
      else {
            tick = system->xy2tick(start, &ny, &sr);
            }

      if (tick != -1) {
            track = sr->stave->track;
            part  = (MidiPart*)(track->findPart(tick));
            }

      switch (tool) {
            case QuantTool:
                  if (tick != -1) {
                        int ntick = tick;
                        int nq = sr->stave->nq;
                        int rq = sr->stave->rq;
                        if (showQuantDialog(&ntick, &nq, &rq)) {
                              MidiEvent* e = new MidiEvent(
                                 ntick, MidiEvent::Quantize, nq, rq, 0,
                                 0);
                              switch(sr->stave->mode) {
                                    case NOSPLIT:
                                    case UPPER:
                                          e->setVoice(1);
                                          break;
                                    case LOWER:
                                          e->setVoice(2);
                                          break;
                                    }
                              midiThread->msgAddEvent(e, part);
                              }
                        }
                  break;

            case PointerTool:
                  if (curItem) {
                        if (ev->button() == QMouseEvent::LeftButton) {
                              drag = shift ? DRAG_COPY_START : DRAG_MOVE_START;
                              }
                        if (ev->button() == QMouseEvent::RightButton) {
                              rbClicked();
                              }
                        }
                  else {
                        drag = DRAG_LASSO_START;
                        }
                  break;

            case RubberTool:
                  if (curItem)
                        curItem->remove();
                  break;

            case ScoreTool:
                  if (curItem == 0 && tick != -1 && part) {
                        int pitch = system->y2pitch(ev->y());
                        tick      = (tick / editor->raster()) * editor->raster();
                        MidiEvent* e = new MidiEvent(
                           tick, MidiEvent::Note, pitch, curVelo, 0,
                           editor->quant());
                        curItem = new NoteItem(tick, e, part);
                        selectItem(curItem);
                        midiThread->msgAddEvent(e, part);
                        }
                  break;

            case PencilTool:
                  if (curItem && paletteItem != -2) {
                        selectItem(curItem);
                        drag = shift ? DRAG_COPY_START : DRAG_MOVE_START;
                        updateSelection();
                        redraw();
                        break;
                        }
                  if (paletteItem == -2) {
                        enterLyrics(start, tick, sr);
                        break;
                        }
                  if (paletteItem == -3) {
                        enterText(tick, sr);
                        break;
                        }
                  if (paletteItem == -4) {
                        enterChord(tick, sr);
                        break;
                        }
                  if ((paletteItem >= 0) && (tick != -1)) {
                        drag = DRAG_NEW_START;
                        int xoff = int(mm2dot(leftMargin)) + system->bracketWidth;
                        QPoint p(start.x() - xoff, ny);
                        MidiEvent* e = new MidiEvent(
                           tick, MidiEvent::Symbol,
                           paletteItem, ny, 0, 0);
                        curItem = new Symbol(p, tick, e, part);
                        sr->add(curItem);
                        }
                  deselectAll();
                  if (curItem)
                        selectItem(curItem);
                  updateSelection();
                  redraw();
                  break;
            case GlueTool:
            case CutTool:
            case DrawTool:
                  break;
            }
      }

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

void ScoreCanvas::viewMouseMoveEvent(QMouseEvent* ev)
      {
      const QPoint pos  = ev->pos();

      QPoint dist = pos - start;
      bool moving = dist.y() >= 3 || dist.y() <= -3 || dist.x() >= 3 || dist.x() <= -3;

      if (tool==RubberTool && (ev->state() & LeftButton)) {
            curItem  = system->searchItem(pos);
            if (curItem)
                   curItem->remove();
            }
      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_MOVE_START:
            case DRAG_COPY_START:
                  if (!moving)
                        break;
                  drag = drag == DRAG_MOVE_START ? DRAG_MOVE : DRAG_COPY;
                  if (!curItem->isSelected()) {
                        if (drag == DRAG_MOVE)
                              deselectAll();
                        selectItem(curItem);
                        updateSelection();
                        }
                  startMoving(pos, drag == DRAG_COPY);
                  break;
            case DRAG_NEW_START:
                  drag = DRAG_NEW;
                  startMoving(pos, false);
                  break;

            case DRAG_MOVE:
            case DRAG_COPY:
            case DRAG_NEW:
                  moveItems(pos);
                  break;

            case DRAG_DELETE:
//                  deleteItem(pos);
                  break;
            default:
                  break;
            }

      //
      // update cursor position display (tick/pitch)
      //
      int pitch = -1;
      int tick = system->xy2tick(ev->pos());
      if (tick >= 0) {
            tick  = (tick / editor->raster()) * editor->raster();
            pitch = system->y2pitch(ev->y());
            }
      if (tick != oldTick) {
            emit timeChanged(tick);
            oldTick = tick;
            }
      if (oldPitch != pitch) {
            emit pitchChanged(pitch);
            oldPitch = pitch;
            }
      }

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

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

      switch (drag) {
            case DRAG_MOVE_START:
            case DRAG_COPY_START:
                  if (shift && curItem->state()==ScoreItem::Selected) {
                        deselectItem(curItem);
                        }
                  else {
                        if (!shift)
                              deselectAll();
                        selectItem(curItem);
                        }
                  updateSelection();
                  redraw();
                  break;
            case DRAG_COPY:
                  endMoveItems(pos, true);
                  break;
            case DRAG_MOVE:
                  endMoveItems(pos, false);
                  break;
            case DRAG_OFF:
                  break;
            case DRAG_NEW_START:
            case DRAG_NEW:
                  {
                  Symbol* item = dynamic_cast<Symbol*>(curItem);
                  if (item) {
                        item->setState(ScoreItem::Selected);
                        MidiEvent* event = item->event();
                        int ny;
                        StaveRow* sr;
                        int ntick = system->xy2tick(pos, &ny, &sr);

                        if (sr->stave->mode == LOWER)
                              ny += system->lineHeight1;
                        if (ntick) {
                              event->setPosTick(ntick);
                              event->setB(ny);
                              event->setC(0);
                              midiThread->msgAddEvent(event, item->part());
                              moving.clear();
                              }
                        else {
                              delete event;
                              delete item;
                              redraw();
                              }
                        }
                  }
                  break;
            case DRAG_LASSO_START:
                  lasso.setRect(-1, -1, -1, -1);

            case DRAG_LASSO:
                  if (!shift)
                        deselectAll();
                  lasso = lasso.normalize();
                  selectAll(shift, lasso);
                  updateSelection();
                  drag = DRAG_OFF;
                  redraw();
                  break;

            case DRAG_DELETE:
                  break;

            default:
                  break;
            }
      drag = DRAG_OFF;
      }

//---------------------------------------------------------
//   endMoveItems
//---------------------------------------------------------

void ScoreCanvas::endMoveItems(const QPoint&, bool copyflag)
      {
      select.clear();
      song->startUndo();
      for (iScore i = moving.begin(); i != moving.end(); ++i) {
            selectItem(i->second);
            i->second->move(editor, system, copyflag);
            }

      song->endUndo(copyflag ? SC_EVENT_INSERTED : SC_EVENT_REMOVED|SC_EVENT_INSERTED);
      moving.clear();
      updateSelection();
      }

//---------------------------------------------------------
//   rbClicked
//    curItem clicked with right Button
//---------------------------------------------------------

void ScoreCanvas::rbClicked()
      {
      QuantItem* i = dynamic_cast<QuantItem*>(curItem);
      if (i == 0)
            return;
      int tick = i->tick();
      int nq   = i->noteQuant();
      int rq   = i->restQuant();

      if (showQuantDialog(&tick, &nq, &rq)) {
            MidiEvent* event    = i->event();
            MidiEvent* newEvent = new MidiEvent(*event);
            newEvent->setPosTick(tick);
            newEvent->setA(nq);
            newEvent->setB(rq);
            midiThread->msgChangeEvent(event, newEvent, i->part());
            }
      }

//---------------------------------------------------------
//   print
//---------------------------------------------------------

void ScoreCanvas::print()
      {
      system->print();
      }

//---------------------------------------------------------
//   preview
//---------------------------------------------------------

void ScoreCanvas::preview()
      {
      system->preview();
      }

//---------------------------------------------------------
//   voiceUp
//    TODO: undo
//---------------------------------------------------------

void ScoreCanvas::voiceUp()
      {
      for (iScore s = select.begin(); s != select.end(); ++s) {
            NoteItem* item = dynamic_cast<NoteItem*>(s->second);
            if (item == 0)
                  continue;
            MidiEvent* e = item->event();
            int v = e->voice();
            if (v == 0 || v == 2)
                  e->setVoice(1);
            }
      songChanged(0);
      }

//---------------------------------------------------------
//   voiceDown
//    TODO: undo
//---------------------------------------------------------

void ScoreCanvas::voiceDown()
      {
      for (iScore s = select.begin(); s != select.end(); ++s) {
            NoteItem* item = dynamic_cast<NoteItem*>(s->second);
            if (item == 0)
                  continue;
            MidiEvent* e = item->event();
            int v = e->voice();
            if (v == 0 || v == 1)
                  e->setVoice(2);
            }
      songChanged(0);
      }

//---------------------------------------------------------
//   flipStems
//    TODO: undo
//---------------------------------------------------------

void ScoreCanvas::flipStems()
      {
      bool changed = false;
      for (iScore s = select.begin(); s != select.end(); ++s) {
            NoteItem* item = dynamic_cast<NoteItem*>(s->second);
            if (item == 0)
                  continue;
            MidiEvent* e = item->event();
            int v = e->stem();
            if (item->isStemDown()) {
                  if (v == 0 || v == 2) {
                        e->setStem(1);
                        changed = true;
                        }
                  }
            else {
                  if (v == 0 || v == 1) {
                        changed = true;
                        e->setStem(2);
                        }
                  }
            }
      if (changed)
            songChanged(0);
      }

//---------------------------------------------------------
//   setEnh
//    setzt Wert für enharmonische Verwechslung in
//    markierten Noten-Event's
//---------------------------------------------------------

void ScoreCanvas::setEnh(QAction* action)
      {
      int n = ((Action*)action)->id();
      switch (n) {
            case 0:  n = 4; break;     // bb
            case 1:  n = 2; break;     // b
            case 2:  n = 0; break;
            case 3:  n = 1; break;     // #
            case 4:  n = 3; break;     // ##
            default:
                  assert(false);
                  break;
            }
      if (select.empty())
            return;
      song->startUndo();
      for (iScore s = select.begin(); s != select.end(); ++s) {
            NoteItem* item = dynamic_cast<NoteItem*>(s->second);
            if (item == 0)
                  continue;
            MidiEvent* event = item->event();
            MidiEvent* newEvent = new MidiEvent(*event);
            newEvent->setEnh(n);
            midiThread->msgChangeEvent(event, newEvent, item->part(), false);
            }
      song->endUndo(SC_EVENT_MODIFIED);
      }

//---------------------------------------------------------
//   draw
//    draw a page
//---------------------------------------------------------

void ScoreCanvas::draw(QPainter& p, const QRect& r)
      {
      system->draw(p, r);

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

      if (drag == DRAG_LASSO) {
            p.setPen(blue);
            p.setBrush(NoBrush);
            p.drawRect(lasso);
            }

      //---------------------------------------------------
      //    show cursor
      //---------------------------------------------------

      int x, y, h;
      if (!system->tick2pos(pos[0], &x, &y, &h))
            return;
      p.save();
      p.drawPixmap(x-7, y-8, *notenposBM);
      p.setPen(QPen(Qt::red,2));
      p.drawLine(x, y, x, y + h);
      p.restore();
      }

//---------------------------------------------------------
//   nextNote
//---------------------------------------------------------

NoteItem* ScoreCanvas::nextNote(NoteItem* ni) const
      {
      ScoreList* sl;
      iScore i;
      if (!system->searchItem(ni, i, sl))
            return 0;
      StaveRow* sr = i->second->staveRow();
      ++i;
      ni = 0;
      for (; i != sl->end(); ++i) {
            ni = dynamic_cast<NoteItem*>(i->second);
            if (ni)
                  break;
            }
      if (ni == 0) {
            // goto next stave
            StaveRowList* srl;
            iStaveRow isr;
            if (!system->searchItem(sr, isr, srl))
                  return 0;
            System* sys = isr->system;
            ++isr;
            for (; isr != srl->end(); ++i) {
                  sl = &isr->items;
                  for (i = sl->begin(); i != sl->end(); ++i) {
                        ni = dynamic_cast<NoteItem*>(i->second);
                        if (ni)
                              break;
                        }
                  if (ni)
                        break;
                  }
            if (ni == 0) {
                  // goto next system
                  SystemList* syl;
                  iSystem isl;
                  if (!system->searchItem(sys, isl, syl))
                        return 0;
                  ++isl;
                  for (; isl != syl->end(); ++isl) {
                        srl = &isl->staves;
                        iStaveRow isr = srl->begin();
                        for (; isr != srl->end(); ++isr) {
                              sl = &isr->items;
                              for (i = sl->begin(); i != sl->end(); ++i) {
                                    ni = dynamic_cast<NoteItem*>(i->second);
                                    if (ni)
                                          break;
                                    }
                              if (ni)
                                    break;
                              }
                        if (ni)
                              break;
                        }
                  }
            }
      return ni;
      }

//---------------------------------------------------------
//   prevNote
//---------------------------------------------------------

NoteItem* ScoreCanvas::prevNote(NoteItem* ni) const
      {
      ScoreList* sl;
      iScore i;
      if (!system->searchItem(ni, i, sl))
            return 0;
      ni = 0;
      while (i != sl->begin()) {
            --i;
            ni = dynamic_cast<NoteItem*>(i->second);
            if (ni)
                  break;
            }
      return ni;
      }

//---------------------------------------------------------
//   enterText
//---------------------------------------------------------

void ScoreCanvas::enterText(int, StaveRow*)
      {
      printf("enterText: not implemented\n");
      }

//---------------------------------------------------------
//   enterChord
//---------------------------------------------------------

void ScoreCanvas::enterChord(int, StaveRow*)
      {
      printf("enterChord: not implemented\n");
      }

void ScoreCanvas::blink()
      {
      ly->blink();
      redraw();
      }

//---------------------------------------------------------
//   viewKeyPressEvent
//---------------------------------------------------------


void ScoreCanvas::viewKeyPressEvent(QKeyEvent* event)
      {
      int key = event->key();
      NoteItem* ni;
      MidiEvent* ev;

      if (lyricsNote) {
            switch(key) {
                  case Key_Right:
                        ni = nextNote(lyricsNote);
                        ev = ni ? ni->event() : 0;
                        endEnterLyrics();
                        if (ev) {
                              NoteItem* si = system->searchItem(ev);
                              lyricsSr = si->staveRow();
                              enterLyrics(si, lyricsSr);
                              }
                        else {
                              lyricsNote = 0;
                              redraw();
                              }
                        return;
                  case Key_Left:
                        ni = prevNote(lyricsNote);
                        ev = ni ? ni->event() : 0;
                        endEnterLyrics();
                        if (ev) {
                              NoteItem* si = system->searchItem(ev);
                              lyricsSr = si->staveRow();
                              enterLyrics(si, lyricsSr);
                              }
                        else {
                              lyricsNote = 0;
                              }
                        redraw();
                        return;
                  case Key_Escape:
                        lyricsNote = 0;
                        printf("escape\n");
                        ly->resetEditState();
                        timer->stop();
                        return;

                  case Key_Backspace:
                  case Key_Delete:
                        ly->del();
                        redraw();
                        return;

                  case Key_Return:
                        endEnterLyrics();
                        return;
                  }
            ly->add(event->text());
            redraw();
            return;
            }
      if (select.size() != 1)
            return;
      ni = dynamic_cast<NoteItem*>(select.begin()->second);
      if (ni == 0)
            return;
      switch(key) {
            case Key_Right:
                  ni = nextNote(ni);
                  if (ni) {
                        deselectAll();
                        selectItem(ni);
                        updateSelection();
                        redraw();
                        }
                  break;
            case Key_Left:
                  ni = prevNote(ni);
                  if (ni) {
                        deselectAll();
                        selectItem(ni);
                        updateSelection();
                        redraw();
                        }
                  break;
            default:
                  break;
            }
      }

//---------------------------------------------------------
//   enterLyrics
//---------------------------------------------------------

void ScoreCanvas::enterLyrics(const QPoint&, int tick, StaveRow* sr)
      {
      //
      //  donīt allow lyrics for the lower stave in a split system
      //  for now:
      //
      if (sr == 0)
            return;
      if (sr->stave->mode == LOWER) {
            printf("no lyrics for lower stave\n");
            return;
            }
      ScoreList* sl = &(sr->items);
      tick = (tick / editor->raster()) * editor->raster();

      //---------------------------------------------------
      // search for nearest note
      //---------------------------------------------------

      NoteItem* note = 0;
      for (iScore i = sl->begin(); i != sl->end(); ++i) {
            NoteItem* ni = dynamic_cast<NoteItem*>(i->second);
            if (ni == 0)
                  continue;
            if (note == 0)
                  note = ni;
            else {
                  int d1 = note->tick() - tick;
                  int d2 = ni->tick()   - tick;
                  if (d1 < 0)
                        d1 = -d1;
                  if (d2 < 0)
                        d2 = -d2;
                  if (d1 > d2) {
                        note = ni;
                        }
                  }
            }
      if (note)
            enterLyrics(note, sr);
      else
            printf("no note for lyrics found\n");
      }

//---------------------------------------------------------
//   enterLyrics
//---------------------------------------------------------

void ScoreCanvas::enterLyrics(NoteItem* note, StaveRow* sr)
      {
      MidiEvent* event = note->event();
      iAttribute i = event->beginAttr();
      Attribute* at = 0;
      ly = 0;
      for (; i != event->endAttr(); ++i) {
            if (i->type() == A_LYRICS) {
                  at = &*i;
                  for (iScore is = sr->items.begin(); is != sr->items.end(); ++is) {
                        ly = dynamic_cast<LyricsItem*>(is->second);
                        if (ly && ly->event() == event)
                              break;
                        }
                  assert(ly);
                  break;
                  }
            }
      if (at == 0) {
            QString s;
            at = event->addAttr(Attribute(A_LYRICS, 0, 50, s));
            QPoint pos(0, 50);
            ly = new LyricsItem(pos, at, note, song->lyricsFont());
            sr->add(ly);
            }
      lyricsNote = note;
      lyricsSr   = sr;
      ly->setEditState();
      timer->start(400);
      redraw();
      }

//---------------------------------------------------------
//   endEnterLyrics
//---------------------------------------------------------

void ScoreCanvas::endEnterLyrics()
      {
      timer->stop();
      MidiEvent* event    = lyricsNote->event();
      MidiEvent* newEvent = new MidiEvent(*event);
      MidiPart* part      = lyricsNote->part();
      ly->resetEditState();
      lyricsNote = 0;

      iAttribute i = newEvent->beginAttr();
      for (; i != newEvent->endAttr(); ++i) {
            if (i->type() == A_LYRICS) {
                  if (!ly->text().isEmpty() && (ly->text() == i->text())) {
                        delete newEvent;
                        redraw();
                        return;
                        }
                  if (ly->text().isEmpty())     // TODO: remove Attribute
                        newEvent->eraseAttr(i);
                  else
                        i->setText(ly->text());
                  break;
                  }
            }
      midiThread->msgChangeEvent(event, newEvent, part);
      }

