//=========================================================
//  MusE
//  Linux Music Editor
//    $Id: ecanvas.cpp,v 1.8 2004/05/31 14:25:59 wschweer Exp $
//  (C) Copyright 2001 Werner Schweer (ws@seh.de)
//=========================================================

#include <values.h>

#include "midieditor.h"
#include "ecanvas.h"
#include "song.h"
#include "event.h"
#include "shortcuts.h"

//---------------------------------------------------------
//   EventCanvas
//---------------------------------------------------------

EventCanvas::EventCanvas(MidiEditor* pr, QWidget* parent, int sx,
   int sy, const char* name)
   : Canvas(parent, sx, sy, name)
      {
      editor      = pr;
      _steprec    = false;
      _midiin     = false;
      _playEvents = false;
      curVelo     = 70;

      setBg(white);
      setAcceptDrops(true);
      setFocusPolicy(StrongFocus);
      setMouseTracking(true);

      curPart   = (MidiPart*)(editor->parts()->begin()->second);
      curPartId = curPart->sn();
      }

//---------------------------------------------------------
//   getCaption
//---------------------------------------------------------

QString EventCanvas::getCaption() const
      {
      int bar1, bar2, xx;
      unsigned x;
      sigmap.tickValues(curPart->tick(), &bar1, &xx, &x);
      sigmap.tickValues(curPart->tick() + curPart->lenTick(), &bar2, &xx, &x);

      return QString("MusE: Part <") + curPart->name()
         + QString("> %1-%2").arg(bar1+1).arg(bar2+1);
      }

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

void EventCanvas::leaveEvent(QEvent*)
      {
      emit pitchChanged(-1);
      emit timeChanged(MAXINT);
      }

//---------------------------------------------------------
//   enterEvent
//---------------------------------------------------------

void EventCanvas::enterEvent(QEvent*)
      {
      emit enterCanvas();
      }

//---------------------------------------------------------
//   raster
//---------------------------------------------------------

QPoint EventCanvas::raster(const QPoint& p) const
      {
      int x = p.x();
      if (x < 0)
            x = 0;
      x = editor->rasterVal(x);
      int pitch = y2pitch(p.y());
      int y = pitch2y(pitch);
      return QPoint(x, y);
      }

//---------------------------------------------------------
//   startUndo
//---------------------------------------------------------

void EventCanvas::startUndo(DragType)
      {
      song->startUndo();
      }

//---------------------------------------------------------
//   endUndo
//---------------------------------------------------------

void EventCanvas::endUndo(DragType dtype)
      {
      song->endUndo((dtype == MOVE_COPY || dtype == MOVE_CLONE)
         ? SC_EVENT_INSERTED : SC_EVENT_MODIFIED);
      }

//---------------------------------------------------------
//   mouseMove
//---------------------------------------------------------

void EventCanvas::mouseMove(const QPoint& pos)
      {
      emit pitchChanged(y2pitch(pos.y()));
      int x = pos.x();
      emit timeChanged(editor->rasterVal(x));
      }

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

void EventCanvas::updateSelection()
      {
      song->update(SC_SELECTION);
      }

//---------------------------------------------------------
//   songChanged(type)
//---------------------------------------------------------

void EventCanvas::songChanged(int flags)
      {
      if (flags & ~SC_SELECTION) {
            items.clear();
            start_tick  = MAXINT;
            end_tick    = 0;
            curPart = 0;
            for (iPart p = editor->parts()->begin(); p != editor->parts()->end(); ++p) {
                  MidiPart* part = (MidiPart*)(p->second);
                  if (part->sn() == curPartId)
                        curPart = part;
                  unsigned stick = part->tick();
                  unsigned etick = stick + part->lenTick();
                  if (stick < start_tick)
                        start_tick = stick;
                  if (etick > end_tick)
                        end_tick = etick;

                  EventList* el = part->events();
                  for (iEvent i = el->begin(); i != el->end(); ++i) {
                        Event e = i->second;
                        if (e.isNote()) {
                              addItem(part, e);
                              }
                        }
                  }
            }

      Event event;
      MidiPart* part   = 0;
      int x            = 0;
      CItem*   nevent  = 0;

      int n  = 0;       // count selections
      for (iCItem k = items.begin(); k != items.end(); ++k) {
            Event ev = k->second->event();
            bool selected = ev.selected();
            if (selected) {
                  k->second->setSelected(true);
                  ++n;
                  if (!nevent) {
                        nevent   =  k->second;
                        Event mi = nevent->event();
                        curVelo  = mi.velo();
                        }
                  }
            }
      start_tick = song->roundDownBar(start_tick);
      end_tick   = song->roundUpBar(end_tick);

      if (n == 1) {
            x     = nevent->x();
            event = nevent->event();
            part  = (MidiPart*)nevent->part();
            if (curPart != part) {
                  curPart = part;
                  curPartId = curPart->sn();
                  curPartChanged();
                  }
            }
      emit selectionChanged(x, event, part);
      if (curPart == 0)
            curPart = (MidiPart*)(editor->parts()->begin()->second);
      redraw();
      }

//---------------------------------------------------------
//   selectFirst
//---------------------------------------------------------

void EventCanvas::selectFirst()
      {
      //Select leftmost note if none selected and there are any
      if (!items.empty() && selectionSize() == 0) {
            iCItem i = items.begin();
            if (!i->second->isSelected()) {
                  selectItem(i->second, true);
                  songChanged(SC_SELECTION);
                  }
            }
      }

//---------------------------------------------------------
//   track
//---------------------------------------------------------

MidiTrack* EventCanvas::track() const
      {
      return ((MidiPart*)curPart)->track();
      }


//---------------------------------------------------------
//   keyPress
//---------------------------------------------------------

void EventCanvas::keyPress(QKeyEvent* event)
      {
      int key = event->key();
      if (event->state() & ShiftButton)
            key += SHIFT;
      if (event->state() & AltButton)
            key += ALT;
      if (event->state() & ControlButton)
            key+= CTRL;

      //
      //  Shortcut for DrumEditor & PianoRoll
      //  Sets locators to selected events
      //
      if (key == shortcuts[SHRT_LOCATORS_TO_SELECTION].key) {
            int tick_max = 0;
            int tick_min = INT_MAX;
            bool found = false;

            for (iCItem i= items.begin(); i != items.end(); i++) {
                  if (!i->second->isSelected())
                        continue;

                  int tick = i->second->x();
                  int len = i->second->event().lenTick();
                  found = true;
                  if (tick + len > tick_max)
                        tick_max = tick + len;
                  if (tick < tick_min)
                        tick_min = tick;
                  }
            if (found) {
                  Pos p1(tick_min, true);
                  Pos p2(tick_max, true);
                  song->setPos(1, p1);
                  song->setPos(2, p2);
                  }
            }
      // Select items by key (PianoRoll & DrumEditor)
      else if (key == shortcuts[SHRT_SEL_RIGHT].key || key == shortcuts[SHRT_SEL_RIGHT_ADD].key) {
            iCItem i, iRightmost;
            CItem* rightmost = NULL;
            //Get the rightmost selected note (if any)
            for (i = items.begin(); i != items.end(); ++i) {
                  if (i->second->isSelected()) {
                        iRightmost = i; rightmost = i->second;
                        }
                  }
               if (rightmost) {
                     iCItem temp = iRightmost; temp++;
                     //If so, deselect current note and select the one to the right
                     if (temp != items.end()) {
                           if (key != shortcuts[SHRT_SEL_RIGHT_ADD].key)
                                 deselectAll();

                           iRightmost++;
                           iRightmost->second->setSelected(true);
                           updateSelection();
                           }
                     }
            }
      //Select items by key: (PianoRoll & DrumEditor)
      else if (key == shortcuts[SHRT_SEL_LEFT].key || key == shortcuts[SHRT_SEL_LEFT_ADD].key) {
            iCItem i, iLeftmost;
            CItem* leftmost = NULL;
            for (i = items.end(), i--; i != items.begin(); i--) {
                  if (i->second->isSelected()) {
                        iLeftmost = i; leftmost = i->second;
                        }
                  }
               if (leftmost) {
                     if (iLeftmost != items.begin()) {
                           //Add item
                           if (key != shortcuts[SHRT_SEL_LEFT_ADD].key)
                                 deselectAll();

                           iLeftmost--;
                           iLeftmost->second->setSelected(true);
                           updateSelection();
                           }
                     }
            }
      else if (key == shortcuts[SHRT_INC_PITCH].key) {
            modifySelected(NoteInfo::VAL_PITCH, 1);
            }
      else if (key == shortcuts[SHRT_DEC_PITCH].key) {
            modifySelected(NoteInfo::VAL_PITCH, -1);
            }
      else
            event->ignore();
      }
