summaryrefslogtreecommitdiff
path: root/muse2/muse/midiedit/dcanvas.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'muse2/muse/midiedit/dcanvas.cpp')
-rw-r--r--muse2/muse/midiedit/dcanvas.cpp1291
1 files changed, 1291 insertions, 0 deletions
diff --git a/muse2/muse/midiedit/dcanvas.cpp b/muse2/muse/midiedit/dcanvas.cpp
new file mode 100644
index 00000000..65138bb4
--- /dev/null
+++ b/muse2/muse/midiedit/dcanvas.cpp
@@ -0,0 +1,1291 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+// $Id: dcanvas.cpp,v 1.16.2.10 2009/10/15 22:45:50 terminator356 Exp $
+// (C) Copyright 1999 Werner Schweer (ws@seh.de)
+//=========================================================
+
+#include <qpainter.h>
+#include <qapplication.h>
+#include <qclipboard.h>
+#include <q3dragobject.h>
+//Added by qt3to4:
+#include <QDragLeaveEvent>
+#include <Q3PointArray>
+#include <Q3CString>
+#include <QDragEnterEvent>
+#include <QDragMoveEvent>
+#include <QDropEvent>
+#include <QResizeEvent>
+
+#include <stdio.h>
+#include <values.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include "dcanvas.h"
+#include "midieditor.h"
+#include "drummap.h"
+#include "event.h"
+#include "mpevent.h"
+#include "xml.h"
+#include "globals.h"
+#include "midiport.h"
+#include "audio.h"
+#include "velocity.h"
+
+#define CARET 10
+#define CARET2 5
+
+//---------------------------------------------------------
+// DEvent
+//---------------------------------------------------------
+
+DEvent::DEvent(Event e, Part* p)
+ : CItem(e, p)
+ {
+ int instr = e.pitch();
+ int y = instr * TH + TH/2;
+ int tick = e.tick() + p->tick();
+ setPos(QPoint(tick, y));
+ setBBox(QRect(-CARET2, -CARET2, CARET, CARET));
+ }
+
+//---------------------------------------------------------
+// addItem
+//---------------------------------------------------------
+
+void DrumCanvas::addItem(Part* part, Event& event)
+ {
+ if (signed(event.tick())<0) {
+ printf("ERROR: trying to add event before current part!\n");
+ return;
+ }
+
+ DEvent* ev = new DEvent(event, part);
+ items.add(ev);
+
+ int diff = event.endTick()-part->lenTick();
+ if (diff > 0) {// too short part? extend it
+ //printf("addItem - this code should not be run!\n");
+ //Part* newPart = part->clone();
+ //newPart->setLenTick(newPart->lenTick()+diff);
+ //audio->msgChangePart(part, newPart,false);
+ //part = newPart;
+ part->setLenTick(part->lenTick()+diff);
+ }
+ }
+
+//---------------------------------------------------------
+// DrumCanvas
+//---------------------------------------------------------
+
+DrumCanvas::DrumCanvas(MidiEditor* pr, QWidget* parent, int sx,
+ int sy, const char* name)
+ : EventCanvas(pr, parent, sx, sy, name)
+ {
+ setVirt(false);
+ songChanged(SC_TRACK_INSERTED);
+ }
+
+//---------------------------------------------------------
+// moveCanvasItems
+//---------------------------------------------------------
+
+void DrumCanvas::moveCanvasItems(CItemList& items, int dp, int dx, DragType dtype, int* pflags)
+{
+ if(editor->parts()->empty())
+ return;
+
+ PartsToChangeMap parts2change;
+
+ int modified = 0;
+ for(iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip)
+ {
+ Part* part = ip->second;
+ if(!part)
+ continue;
+
+ int npartoffset = 0;
+ for(iCItem ici = items.begin(); ici != items.end(); ++ici)
+ {
+ CItem* ci = ici->second;
+ if(ci->part() != part)
+ continue;
+
+ int x = ci->pos().x() + dx;
+ int y = pitch2y(y2pitch(ci->pos().y()) + dp);
+ QPoint newpos = raster(QPoint(x, y));
+
+ // Test moving the item...
+ DEvent* nevent = (DEvent*) ci;
+ Event event = nevent->event();
+ x = newpos.x();
+ if(x < 0)
+ x = 0;
+ int ntick = editor->rasterVal(x) - part->tick();
+ if(ntick < 0)
+ ntick = 0;
+ int diff = ntick + event.lenTick() - part->lenTick();
+
+ // If moving the item would require a new part size...
+ if(diff > npartoffset)
+ npartoffset = diff;
+ }
+
+ if(npartoffset > 0)
+ {
+ // Create new part...
+ // if there are several events that are moved outside the part, it will be recreated for each
+ // so the part _in_ the event will not be valid, ask the authority.
+// Part* newPart = part->clone();
+ //Part* newPart = Canvas::part()->clone();
+
+// newPart->setLenTick(newPart->lenTick() + npartoffset);
+ //audio->msgChangePart(part, newPart,false);
+
+// modified = SC_PART_MODIFIED;
+
+ // BUG FIX: #1650953
+ // Added by T356.
+ // Fixes posted "select and drag past end of part - crashing" bug
+// for(iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip)
+// {
+// if(ip->second == part)
+// {
+// editor->parts()->erase(ip);
+// break;
+// }
+// }
+
+// editor->parts()->add(newPart);
+// audio->msgChangePart(part, newPart,false);
+
+// if(parts2change.find(part) == parts2change.end())
+// parts2change.insert(std::pair<Part*, Part*> (part, newPart));
+ iPartToChange ip2c = parts2change.find(part);
+ if(ip2c == parts2change.end())
+ {
+ PartToChange p2c = {0, npartoffset};
+ parts2change.insert(std::pair<Part*, PartToChange> (part, p2c));
+ }
+ else
+ ip2c->second.xdiff = npartoffset;
+
+
+ //part = newPart; // reassign
+ //item->setPart(part);
+ //item->setEvent(newEvent);
+ //curPart = part;
+ //curPartId = curPart->sn();
+
+ }
+ }
+
+ for(iPartToChange ip2c = parts2change.begin(); ip2c != parts2change.end(); ++ip2c)
+ {
+ Part* opart = ip2c->first;
+ int diff = ip2c->second.xdiff;
+
+ Part* newPart = opart->clone();
+
+ newPart->setLenTick(newPart->lenTick() + diff);
+
+ modified = SC_PART_MODIFIED;
+
+ // BUG FIX: #1650953
+ // Added by T356.
+ // Fixes posted "select and drag past end of part - crashing" bug
+ for(iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip)
+ {
+ if(ip->second == opart)
+ {
+ editor->parts()->erase(ip);
+ break;
+ }
+ }
+
+ editor->parts()->add(newPart);
+ // Indicate no undo, and do port controller values but not clone parts.
+ //audio->msgChangePart(opart, newPart, false);
+ audio->msgChangePart(opart, newPart, false, true, false);
+
+ ip2c->second.npart = newPart;
+
+ }
+
+ iPartToChange icp = parts2change.find(curPart);
+ if(icp != parts2change.end())
+ {
+ curPart = icp->second.npart;
+ curPartId = curPart->sn();
+ }
+
+ std::vector< CItem* > doneList;
+ typedef std::vector< CItem* >::iterator iDoneList;
+
+ for(iCItem ici = items.begin(); ici != items.end(); ++ici)
+ {
+ CItem* ci = ici->second;
+
+ // If this item's part is in the parts2change list, change the item's part to the new part.
+ Part* pt = ci->part();
+ iPartToChange ip2c = parts2change.find(pt);
+ if(ip2c != parts2change.end())
+ ci->setPart(ip2c->second.npart);
+
+ int x = ci->pos().x();
+ int y = ci->pos().y();
+ int nx = x + dx;
+ int ny = pitch2y(y2pitch(y) + dp);
+ QPoint newpos = raster(QPoint(nx, ny));
+ selectItem(ci, true);
+
+ iDoneList idl;
+ for(idl = doneList.begin(); idl != doneList.end(); ++idl)
+ // This compares EventBase pointers to see if they're the same...
+ if((*idl)->event() == ci->event())
+ break;
+
+ // Do not process if the event has already been processed (meaning it's an event in a clone part)...
+ //if(moveItem(ci, newpos, dtype))
+ if(idl != doneList.end())
+ // Just move the canvas item.
+ ci->move(newpos);
+ else
+ {
+ // Currently moveItem always returns true.
+ if(moveItem(ci, newpos, dtype))
+ {
+ // Add the canvas item to the list of done items.
+ doneList.push_back(ci);
+ // Move the canvas item.
+ ci->move(newpos);
+ }
+ }
+
+ if(moving.size() == 1) {
+ itemReleased(curItem, newpos);
+ }
+ if(dtype == MOVE_COPY || dtype == MOVE_CLONE)
+ selectItem(ci, false);
+ }
+
+ if(pflags)
+ *pflags = modified;
+}
+
+//---------------------------------------------------------
+// moveItem
+//---------------------------------------------------------
+
+// Changed by T356.
+//bool DrumCanvas::moveItem(CItem* item, const QPoint& pos, DragType dtype, int* pflags)
+bool DrumCanvas::moveItem(CItem* item, const QPoint& pos, DragType dtype)
+ {
+ DEvent* nevent = (DEvent*) item;
+
+ // Changed by T356.
+ //MidiPart* part = (MidiPart*)Canvas::part(); // part can be dynamically recreated, ask the authority
+ MidiPart* part = (MidiPart*)nevent->part();
+
+ Event event = nevent->event();
+ int x = pos.x();
+ if (x < 0)
+ x = 0;
+ int ntick = editor->rasterVal(x) - part->tick();
+ if (ntick < 0)
+ ntick = 0;
+ int npitch = y2pitch(pos.y());
+ Event newEvent = event.clone();
+
+ newEvent.setPitch(npitch);
+ newEvent.setTick(ntick);
+
+ // Removed by T356.
+ /*
+ // Added by T356.
+ int modified = 0;
+ //song->startUndo();
+ int diff = newEvent.endTick()-part->lenTick();
+ if (diff > 0) // too short part? extend it
+ {
+ // if there are several events that are moved outside the part, it will be recreated for each
+ // so the part _in_ the event will not be valid, ask the authority.
+ //Part* newPart = part->clone();
+ MidiPart* newPart = (MidiPart*)Canvas::part()->clone();
+ newPart->setLenTick(newPart->lenTick()+diff);
+ audio->msgChangePart(Canvas::part(), newPart,false);
+
+ modified = SC_PART_MODIFIED;
+ part = newPart; // reassign
+ for(iPart i = editor->parts()->begin(); i != editor->parts()->end(); ++i)
+ {
+ if(i->second == Canvas::part())
+ {
+ editor->parts()->erase(i);
+ break;
+ }
+ }
+ editor->parts()->add(part);
+ item->setPart(part);
+ item->setEvent(newEvent);
+ curPart = part;
+ curPartId = curPart->sn();
+ }
+ */
+
+ // Added by T356.
+ // msgAddEvent and msgChangeEvent (below) will set these, but set them here first?
+ //item->setPart(part);
+ item->setEvent(newEvent);
+
+ // Added by T356.
+ if(((int)newEvent.endTick() - (int)part->lenTick()) > 0)
+ printf("DrumCanvas::moveItem Error! New event end:%d exceeds length:%d of part:%s\n", newEvent.endTick(), part->lenTick(), part->name().latin1());
+
+ if (dtype == MOVE_COPY || dtype == MOVE_CLONE) {
+ // Indicate no undo, and do not do port controller values and clone parts.
+ //audio->msgAddEvent(newEvent, part, false);
+ audio->msgAddEvent(newEvent, part, false, false, false);
+ }
+ else {
+ // Indicate no undo, and do not do port controller values and clone parts.
+ //audio->msgChangeEvent(event, newEvent, part, false);
+ audio->msgChangeEvent(event, newEvent, part, false, false, false);
+ }
+
+ // Removed by T356.
+ //if(pflags)
+ // *pflags = modified;
+
+ return true;
+ }
+
+//---------------------------------------------------------
+// newItem
+//---------------------------------------------------------
+
+CItem* DrumCanvas::newItem(const QPoint& p, int state)
+ {
+ int instr = y2pitch(p.y()); //drumInmap[y2pitch(p.y())];
+ int velo = drumMap[instr].lv4;
+ if (state == Qt::ShiftModifier)
+ velo = drumMap[instr].lv3;
+ else if (state == Qt::ControlModifier)
+ velo = drumMap[instr].lv2;
+ else if (state == (Qt::ControlModifier | Qt::ShiftModifier))
+ velo = drumMap[instr].lv1;
+ int tick = editor->rasterVal(p.x());
+ tick -= curPart->tick();
+ Event e(Note);
+ e.setTick(tick);
+ e.setPitch(instr);
+ e.setVelo(velo);
+ e.setLenTick(drumMap[instr].len);
+ return new DEvent(e, curPart);
+ }
+
+//---------------------------------------------------------
+// resizeItem
+//---------------------------------------------------------
+
+void DrumCanvas::resizeItem(CItem* item, bool)
+ {
+ DEvent* nevent = (DEvent*) item;
+ Event ev = nevent->event();
+ // Indicate do undo, and do not do port controller values and clone parts.
+ //audio->msgDeleteEvent(ev, nevent->part());
+ audio->msgDeleteEvent(ev, nevent->part(), true, false, false);
+ }
+
+//---------------------------------------------------------
+// newItem
+//---------------------------------------------------------
+
+void DrumCanvas::newItem(CItem* item, bool noSnap)
+ {
+ DEvent* nevent = (DEvent*) item;
+ Event event = nevent->event();
+ int x = item->x();
+ if (!noSnap)
+ x = editor->rasterVal(x);
+ event.setTick(x - nevent->part()->tick());
+ //int npitch = drumMap[y2pitch(item->y())].enote;
+ int npitch = event.pitch();
+ event.setPitch(npitch);
+
+ //
+ // check for existing event
+ // if found change command semantic from insert to delete
+ //
+ EventList* el = nevent->part()->events();
+ iEvent lower = el->lower_bound(event.tick());
+ iEvent upper = el->upper_bound(event.tick());
+
+ for (iEvent i = lower; i != upper; ++i) {
+ Event ev = i->second;
+ // Added by T356. Only do notes.
+ if(!ev.isNote())
+ continue;
+
+ if (ev.pitch() == npitch) {
+ // Indicate do undo, and do not do port controller values and clone parts.
+ //audio->msgDeleteEvent(ev, nevent->part());
+ audio->msgDeleteEvent(ev, nevent->part(), true, false, false);
+ return;
+ }
+ }
+
+ // Added by T356.
+ Part* part = nevent->part();
+ song->startUndo();
+ int modified=SC_EVENT_MODIFIED;
+ int diff = event.endTick()-part->lenTick();
+ if (diff > 0) {// too short part? extend it
+ //printf("extend Part!\n");
+ Part* newPart = part->clone();
+ newPart->setLenTick(newPart->lenTick()+diff);
+ // Indicate no undo, and do port controller values but not clone parts.
+ //audio->msgChangePart(part, newPart,false);
+ audio->msgChangePart(part, newPart, false, true, false);
+ modified=modified|SC_PART_MODIFIED;
+ part = newPart; // reassign
+ }
+ // Indicate no undo, and do not do port controller values and clone parts.
+ //audio->msgAddEvent(event, part,false);
+ audio->msgAddEvent(event, part, false, false, false);
+ song->endUndo(modified);
+
+ //audio->msgAddEvent(event, nevent->part());
+ }
+
+//---------------------------------------------------------
+// deleteItem
+//---------------------------------------------------------
+
+bool DrumCanvas::deleteItem(CItem* item)
+ {
+ Event ev = ((DEvent*)item)->event();
+ // Indicate do undo, and do not do port controller values and clone parts.
+ //audio->msgDeleteEvent(ev, ((DEvent*)item)->part());
+ audio->msgDeleteEvent(ev, ((DEvent*)item)->part(), true, false, false);
+ return false;
+ }
+
+//---------------------------------------------------------
+// drawItem
+//---------------------------------------------------------
+
+void DrumCanvas::drawItem(QPainter&p, const CItem*item, const QRect& rect)
+ {
+ DEvent* e = (DEvent*) item;
+ int x = 0, y = 0;
+ x = mapx(item->pos().x());
+ y = mapy(item->pos().y());
+ Q3PointArray pa(4);
+ pa.setPoint(0, x - CARET2, y);
+ pa.setPoint(1, x, y - CARET2);
+ pa.setPoint(2, x + CARET2, y);
+ pa.setPoint(3, x, y + CARET2);
+ QRect r(pa.boundingRect());
+ r = r.intersect(rect);
+ if(!r.isValid())
+ return;
+
+ p.setPen(Qt::black);
+
+ if (e->part() != curPart)
+ {
+ if(item->isMoving())
+ p.setBrush(Qt::gray);
+ else if(item->isSelected())
+ p.setBrush(Qt::black);
+ else
+ p.setBrush(Qt::lightGray);
+ }
+ else if (item->isMoving()) {
+ p.setBrush(Qt::gray);
+ }
+ else if (item->isSelected())
+ {
+ p.setBrush(Qt::black);
+ }
+ else
+ {
+ int velo = e->event().velo();
+ DrumMap* dm = &drumMap[y2pitch(y)]; //Get the drum item
+ QColor color;
+ if (velo < dm->lv1)
+ color.setRgb(240, 240, 255);
+ else if (velo < dm->lv2)
+ color.setRgb(200, 200, 255);
+ else if (velo < dm->lv3)
+ color.setRgb(170, 170, 255);
+ else
+ color.setRgb(0, 0, 255);
+ p.setBrush(color);
+ }
+
+ p.drawPolygon(pa);
+ }
+
+//---------------------------------------------------------
+// drawMoving
+// draws moving items
+//---------------------------------------------------------
+
+void DrumCanvas::drawMoving(QPainter& p, const CItem* item, const QRect& rect)
+ {
+ //if(((DEvent*)item)->part() != curPart)
+ // return;
+ //if(!item->isMoving())
+ // return;
+ Q3PointArray pa(4);
+ QPoint pt = map(item->mp());
+ int x = pt.x();
+ int y = pt.y();
+ pa.setPoint(0, x-CARET2, y + TH/2);
+ pa.setPoint(1, x, y + TH/2+CARET2);
+ pa.setPoint(2, x+CARET2, y + TH/2);
+ pa.setPoint(3, x, y + (TH-CARET)/2);
+ QRect mr(pa.boundingRect());
+ mr = mr.intersect(rect);
+ if(!mr.isValid())
+ return;
+ p.setPen(Qt::black);
+ p.setBrush(Qt::black);
+ p.drawPolygon(pa);
+ }
+
+//---------------------------------------------------------
+// drawCanvas
+//---------------------------------------------------------
+
+extern void drawTickRaster(QPainter& p, int, int, int, int, int);
+
+void DrumCanvas::drawCanvas(QPainter& p, const QRect& rect)
+ {
+ int x = rect.x();
+ int y = rect.y();
+ int w = rect.width();
+ int h = rect.height();
+
+ //---------------------------------------------------
+ // horizontal lines
+ //---------------------------------------------------
+
+ int yy = ((y-1) / TH) * TH + TH;
+ for (; yy < y + h; yy += TH) {
+ p.setPen(Qt::gray);
+ p.drawLine(x, yy, x + w, yy);
+ }
+
+ //---------------------------------------------------
+ // vertical lines
+ //---------------------------------------------------
+
+ drawTickRaster(p, x, y, w, h, editor->raster());
+ }
+
+//---------------------------------------------------------
+// y2pitch
+//---------------------------------------------------------
+
+int DrumCanvas::y2pitch(int y) const
+ {
+ int pitch = y/TH;
+ if (pitch >= DRUM_MAPSIZE)
+ pitch = DRUM_MAPSIZE-1;
+ return pitch;
+ }
+
+//---------------------------------------------------------
+// pitch2y
+//---------------------------------------------------------
+
+int DrumCanvas::pitch2y(int pitch) const
+ {
+ return pitch * TH;
+ }
+
+//---------------------------------------------------------
+// cmd
+//---------------------------------------------------------
+
+void DrumCanvas::cmd(int cmd)
+ {
+ switch(cmd) {
+ case CMD_CUT:
+ copy();
+ song->startUndo();
+ for (iCItem i = items.begin(); i != items.end(); ++i) {
+ if (!i->second->isSelected())
+ continue;
+ DEvent* e = (DEvent*)(i->second);
+ Event event = e->event();
+ // Indicate no undo, and do not do port controller values and clone parts.
+ //audio->msgDeleteEvent(event, e->part(), false);
+ audio->msgDeleteEvent(event, e->part(), false, false, false);
+ }
+ song->endUndo(SC_EVENT_REMOVED);
+ break;
+ case CMD_COPY:
+ copy();
+ break;
+ case CMD_PASTE:
+ paste();
+ break;
+ case CMD_SELECT_ALL: // select all
+ for (iCItem k = items.begin(); k != items.end(); ++k) {
+ if (!k->second->isSelected())
+ selectItem(k->second, true);
+ }
+ break;
+ case CMD_SELECT_NONE: // select none
+ deselectAll();
+ break;
+ case CMD_SELECT_INVERT: // invert selection
+ for (iCItem k = items.begin(); k != items.end(); ++k) {
+ selectItem(k->second, !k->second->isSelected());
+ }
+ break;
+ case CMD_SELECT_ILOOP: // select inside loop
+ for (iCItem k = items.begin(); k != items.end(); ++k) {
+ DEvent* nevent =(DEvent*)(k->second);
+ Part* part = nevent->part();
+ Event event = nevent->event();
+ unsigned tick = event.tick() + part->tick();
+ if (tick < song->lpos() || tick >= song->rpos())
+ selectItem(k->second, false);
+ else
+ selectItem(k->second, true);
+ }
+ break;
+ case CMD_SELECT_OLOOP: // select outside loop
+ for (iCItem k = items.begin(); k != items.end(); ++k) {
+ DEvent* nevent = (DEvent*)(k->second);
+ Part* part = nevent->part();
+ Event event = nevent->event();
+ unsigned tick = event.tick() + part->tick();
+ if (tick < song->lpos() || tick >= song->rpos())
+ selectItem(k->second, true);
+ else
+ selectItem(k->second, false);
+ }
+ break;
+ case CMD_SELECT_PREV_PART: // select previous part
+ {
+ Part* pt = editor->curCanvasPart();
+ Part* newpt = pt;
+ PartList* pl = editor->parts();
+ for(iPart ip = pl->begin(); ip != pl->end(); ++ip)
+ if(ip->second == pt)
+ {
+ if(ip == pl->begin())
+ ip = pl->end();
+ --ip;
+ newpt = ip->second;
+ break;
+ }
+ if(newpt != pt)
+ editor->setCurCanvasPart(newpt);
+ }
+ break;
+ case CMD_SELECT_NEXT_PART: // select next part
+ {
+ Part* pt = editor->curCanvasPart();
+ Part* newpt = pt;
+ PartList* pl = editor->parts();
+ for(iPart ip = pl->begin(); ip != pl->end(); ++ip)
+ if(ip->second == pt)
+ {
+ ++ip;
+ if(ip == pl->end())
+ ip = pl->begin();
+ newpt = ip->second;
+ break;
+ }
+ if(newpt != pt)
+ editor->setCurCanvasPart(newpt);
+ }
+ break;
+ case CMD_DEL:
+ if (selectionSize()) {
+ song->startUndo();
+ for (iCItem i = items.begin(); i != items.end(); ++i) {
+ if (!i->second->isSelected())
+ continue;
+ Event ev = i->second->event();
+ // Indicate no undo, and do not do port controller values and clone parts.
+ //audio->msgDeleteEvent(ev, i->second->part(), false);
+ audio->msgDeleteEvent(ev, i->second->part(), false, false, false);
+ }
+ song->endUndo(SC_EVENT_REMOVED);
+ }
+ return;
+
+ case CMD_SAVE:
+ case CMD_LOAD:
+ printf("DrumCanvas:: cmd not implemented %d\n", cmd);
+ break;
+
+ case CMD_FIXED_LEN: //Set notes to the length specified in the drummap
+ if (!selectionSize())
+ break;
+ song->startUndo();
+ for (iCItem k = items.begin(); k != items.end(); ++k) {
+ if (k->second->isSelected()) {
+ DEvent* devent = (DEvent*)(k->second);
+ Event event = devent->event();
+ Event newEvent = event.clone();
+ newEvent.setLenTick(drumMap[event.pitch()].len);
+ // Indicate no undo, and do not do port controller values and clone parts.
+ //audio->msgChangeEvent(event, newEvent, devent->part() , false);
+ audio->msgChangeEvent(event, newEvent, devent->part(), false, false, false);
+ }
+ }
+ song->endUndo(SC_EVENT_MODIFIED);
+ break;
+ case CMD_LEFT:
+ {
+ int frames = pos[0] - editor->rasterStep(pos[0]);
+ if (frames < 0)
+ frames = 0;
+ Pos p(frames,true);
+ song->setPos(0, p, true, true, true);
+ }
+ break;
+ case CMD_RIGHT:
+ {
+ Pos p(pos[0] + editor->rasterStep(pos[0]), true);
+ song->setPos(0, p, true, true, true);
+ }
+ break;
+ case CMD_MODIFY_VELOCITY:
+ {
+ Velocity w(this);
+ w.setRange(0); //TODO: Make this work! Probably put _to & _toInit in ecanvas instead
+ if (!w.exec())
+ break;
+ int range = w.range(); // all, selected, looped, sel+loop
+ int rate = w.rateVal();
+ int offset = w.offsetVal();
+
+ song->startUndo();
+ for (iCItem k = items.begin(); k != items.end(); ++k) {
+ DEvent* devent = (DEvent*)(k->second);
+ Event event = devent->event();
+ if (event.type() != Note)
+ continue;
+ unsigned tick = event.tick();
+ bool selected = k->second->isSelected();
+ bool inLoop = (tick >= song->lpos()) && (tick < song->rpos());
+
+ if ((range == 0)
+ || (range == 1 && selected)
+ || (range == 2 && inLoop)
+ || (range == 3 && selected && inLoop)) {
+ int velo = event.velo();
+
+ //velo = rate ? (velo * 100) / rate : 64;
+ velo = (velo * rate) / 100;
+ velo += offset;
+
+ if (velo <= 0)
+ velo = 1;
+ if (velo > 127)
+ velo = 127;
+ if (event.velo() != velo) {
+ Event newEvent = event.clone();
+ newEvent.setVelo(velo);
+ // Indicate no undo, and do not do port controller values and clone parts.
+ //audio->msgChangeEvent(event, newEvent, devent->part(), false);
+ audio->msgChangeEvent(event, newEvent, devent->part(), false, false, false);
+ }
+ }
+ }
+ song->endUndo(SC_EVENT_MODIFIED);
+ }
+ break;
+ }
+ updateSelection();
+ redraw();
+ }
+
+//---------------------------------------------------------
+// getTextDrag
+//---------------------------------------------------------
+
+Q3TextDrag* DrumCanvas::getTextDrag(QWidget* parent)
+ {
+ //---------------------------------------------------
+ // generate event list from selected events
+ //---------------------------------------------------
+
+ EventList el;
+ unsigned startTick = MAXINT;
+ for (iCItem i = items.begin(); i != items.end(); ++i) {
+ if (!i->second->isSelected())
+ continue;
+ DEvent* ne = (DEvent*)(i->second);
+ Event e = ne->event();
+ if (startTick == MAXINT)
+ startTick = e.tick();
+ el.add(e);
+ }
+
+ //---------------------------------------------------
+ // write events as XML into tmp file
+ //---------------------------------------------------
+
+ FILE* tmp = tmpfile();
+ if (tmp == 0) {
+ fprintf(stderr, "EventCanvas::copy() fopen failed: %s\n",
+ strerror(errno));
+ return 0;
+ }
+ Xml xml(tmp);
+
+ int level = 0;
+ for (ciEvent e = el.begin(); e != el.end(); ++e)
+ e->second.write(level, xml, -startTick);
+
+ //---------------------------------------------------
+ // read tmp file into QTextDrag Object
+ //---------------------------------------------------
+
+ fflush(tmp);
+ struct stat f_stat;
+ if (fstat(fileno(tmp), &f_stat) == -1) {
+ fprintf(stderr, "EventCanvas::copy() fstat failes:<%s>\n",
+ strerror(errno));
+ fclose(tmp);
+ return 0;
+ }
+ int n = f_stat.st_size;
+ char* fbuf = (char*)mmap(0, n+1, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE, fileno(tmp), 0);
+ fbuf[n] = 0;
+ Q3TextDrag* drag = new Q3TextDrag(QString(fbuf), parent);
+ drag->setSubtype("eventlist");
+ munmap(fbuf, n);
+ fclose(tmp);
+ return drag;
+ }
+
+//---------------------------------------------------------
+// copy
+// cut copy paste
+//---------------------------------------------------------
+
+void DrumCanvas::copy()
+ {
+ Q3TextDrag* drag = getTextDrag(0);
+ if (drag)
+ QApplication::clipboard()->setData(drag, QClipboard::Clipboard);
+ }
+
+//---------------------------------------------------------
+// paste
+//---------------------------------------------------------
+
+int DrumCanvas::pasteAt(const QString& pt, int pos)
+ {
+ const char* p = pt.latin1();
+ Xml xml(p);
+
+ // Added by T356.
+ int modified = SC_EVENT_INSERTED;
+
+ song->startUndo();
+ for (;;) {
+ Xml::Token token = xml.parse();
+ QString tag = xml.s1();
+ switch (token) {
+ case Xml::Error:
+ case Xml::End:
+ //song->endUndo(SC_EVENT_INSERTED); By T356
+ song->endUndo(modified);
+ return pos;
+ case Xml::TagStart:
+ if (tag == "event") {
+ Event e(Note);
+ e.read(xml);
+
+ // Added by T356.
+ int tick = e.tick() + pos - curPart->tick();
+ if (tick<0) {
+ printf("DrumCanvas::pasteAt ERROR: trying to add event before current part!\n");
+ song->endUndo(SC_EVENT_INSERTED);
+ //delete el;
+ return pos;
+ }
+ e.setTick(tick);
+ int diff = e.endTick() - curPart->lenTick();
+ if (diff > 0) {// too short part? extend it
+ Part* newPart = curPart->clone();
+ newPart->setLenTick(newPart->lenTick()+diff);
+ // Indicate no undo, and do port controller values but not clone parts.
+ //audio->msgChangePart(curPart, newPart,false);
+ audio->msgChangePart(curPart, newPart, false, true, false);
+
+ modified=modified|SC_PART_MODIFIED;
+ curPart = newPart; // reassign
+ }
+
+ // Indicate no undo, and do not do port controller values and clone parts.
+ //audio->msgAddEvent(e, curPart, false);
+ audio->msgAddEvent(e, curPart, false, false, false);
+ }
+ else
+ //xml.unknown("EventCanvas::paste"); By T356
+ xml.unknown("DCanvas::pasteAt");
+ break;
+ case Xml::TagEnd:
+ default:
+ break;
+ }
+ }
+ }
+
+//---------------------------------------------------------
+// paste
+// paste events
+//---------------------------------------------------------
+
+void DrumCanvas::paste()
+ {
+// Q3CString subtype("eventlist"); // ddskrjo
+ QString subtype("eventlist");
+ QMimeSource* ms = QApplication::clipboard()->data();
+ QString pt;
+ if (!Q3TextDrag::decode(ms, pt, subtype)) {
+ printf("cannot paste: bad data type\n");
+ return;
+ }
+ pasteAt(pt, song->cpos());
+ }
+
+//---------------------------------------------------------
+// startDrag
+//---------------------------------------------------------
+
+void DrumCanvas::startDrag(CItem* /* item*/, bool copymode)
+ {
+ Q3TextDrag* drag = getTextDrag(this);
+ if (drag) {
+// QApplication::clipboard()->setData(drag, QClipboard::Clipboard);
+
+ if (copymode)
+ drag->dragCopy();
+ else
+ drag->dragMove();
+ }
+ }
+
+//---------------------------------------------------------
+// dragEnterEvent
+//---------------------------------------------------------
+
+void DrumCanvas::dragEnterEvent(QDragEnterEvent* event)
+ {
+ event->accept(Q3TextDrag::canDecode(event));
+ }
+
+//---------------------------------------------------------
+// dragMoveEvent
+//---------------------------------------------------------
+
+void DrumCanvas::dragMoveEvent(QDragMoveEvent*)
+ {
+// printf("drag move %x\n", this);
+ }
+
+//---------------------------------------------------------
+// dragLeaveEvent
+//---------------------------------------------------------
+
+void DrumCanvas::dragLeaveEvent(QDragLeaveEvent*)
+ {
+// printf("drag leave\n");
+ }
+
+//---------------------------------------------------------
+// dropEvent
+//---------------------------------------------------------
+
+void DrumCanvas::viewDropEvent(QDropEvent* event)
+ {
+ QString text;
+ if (event->source() == this) {
+ printf("local DROP\n");
+ return;
+ }
+ if (Q3TextDrag::decode(event, text)) {
+// printf("drop <%s>\n", text.ascii());
+ int x = editor->rasterVal(event->pos().x());
+ if (x < 0)
+ x = 0;
+ pasteAt(text, x);
+ }
+ }
+
+//---------------------------------------------------------
+// keyPressed
+//---------------------------------------------------------
+
+void DrumCanvas::keyPressed(int index, bool)
+ {
+ int port = drumMap[index].port;
+ int channel = drumMap[index].channel;
+ int pitch = drumMap[index].anote;
+
+ // play note:
+ MidiPlayEvent e(0, port, channel, 0x90, pitch, 127);
+ audio->msgPlayMidiEvent(&e);
+ }
+
+//---------------------------------------------------------
+// keyReleased
+//---------------------------------------------------------
+
+void DrumCanvas::keyReleased(int index, bool)
+ {
+ int port = drumMap[index].port;
+ int channel = drumMap[index].channel;
+ int pitch = drumMap[index].anote;
+
+ // release note:
+ MidiPlayEvent e(0, port, channel, 0x90, pitch, 0);
+ audio->msgPlayMidiEvent(&e);
+ }
+
+//---------------------------------------------------------
+// mapChanged
+//---------------------------------------------------------
+
+void DrumCanvas::mapChanged(int spitch, int dpitch)
+ {
+ //TODO: Circumvent undo behaviour, since this isn't really a true change of the events,
+ // but merely a change in pitch because the pitch relates to the order of the dlist.
+ // Right now the sequencer spits out internalError: undoOp without startUndo() if start/stopundo is there, which is misleading
+ // If start/stopundo is there, undo misbehaves since it doesn't undo but messes things up
+ // Other solution: implement a specific undo-event for this (SC_DRUMMAP_MODIFIED or something) which undoes movement of
+ // dlist-items (ml)
+
+ std::vector< std::pair<Part*, Event*> > delete_events;
+ std::vector< std::pair<Part*, Event> > add_events;
+
+ typedef std::vector< std::pair<Part*, Event*> >::iterator idel_ev;
+ typedef std::vector< std::pair<Part*, Event> >::iterator iadd_ev;
+
+ /*
+ class delete_events : public std::vector< Part*, Event* >
+ {
+ public:
+ idel_ev find(Part* p, Event* e)
+ {
+
+ };
+ };
+ class add_events : public std::vector< Part*, Event >
+ {
+ public:
+ iadd_ev find(Part* p, Event& e)
+ {
+
+ };
+ };
+ */
+
+ MidiTrackList* tracks = song->midis();
+ for (ciMidiTrack t = tracks->begin(); t != tracks->end(); t++) {
+ MidiTrack* curTrack = *t;
+ if (curTrack->type() != Track::DRUM)
+ continue;
+
+ MidiPort* mp = &midiPorts[curTrack->outPort()];
+ PartList* parts= curTrack->parts();
+ for (iPart part = parts->begin(); part != parts->end(); ++part) {
+ EventList* events = part->second->events();
+ Part* thePart = part->second;
+ for (iEvent i = events->begin(); i != events->end(); ++i) {
+ Event event = i->second;
+ if(event.type() != Controller && event.type() != Note)
+ continue;
+ int pitch = event.pitch();
+ bool drc = false;
+ // Is it a drum controller event, according to the track port's instrument?
+ if(event.type() == Controller && mp->drumController(event.dataA()))
+ {
+ drc = true;
+ pitch = event.dataA() & 0x7f;
+ }
+
+ if (pitch == spitch) {
+ Event* spitch_event = &(i->second);
+ delete_events.push_back(std::pair<Part*, Event*>(thePart, spitch_event));
+ Event newEvent = spitch_event->clone();
+ if(drc)
+ newEvent.setA((newEvent.dataA() & ~0xff) | dpitch);
+ else
+ newEvent.setPitch(dpitch);
+ add_events.push_back(std::pair<Part*, Event>(thePart, newEvent));
+ }
+ else if (pitch == dpitch) {
+ Event* dpitch_event = &(i->second);
+ delete_events.push_back(std::pair<Part*, Event*>(thePart, dpitch_event));
+ Event newEvent = dpitch_event->clone();
+ if(drc)
+ newEvent.setA((newEvent.dataA() & ~0xff) | spitch);
+ else
+ newEvent.setPitch(spitch);
+ add_events.push_back(std::pair<Part*, Event>(thePart, newEvent));
+ }
+ }
+ }
+ }
+
+ song->startUndo();
+ for (idel_ev i = delete_events.begin(); i != delete_events.end(); i++) {
+ //std::pair<Part*, Event*> pair = *i;
+ //Part* thePart = pair.first;
+ //Event* theEvent = pair.second;
+ Part* thePart = (*i).first;
+ Event* theEvent = (*i).second;
+ // Indicate no undo, and do port controller values but not clone parts.
+ //audio->msgDeleteEvent(*theEvent, thePart, false);
+ audio->msgDeleteEvent(*theEvent, thePart, false, true, false);
+ }
+
+ DrumMap dm = drumMap[spitch];
+ drumMap[spitch] = drumMap[dpitch];
+ drumMap[dpitch] = dm;
+ drumInmap[int(drumMap[spitch].enote)] = spitch;
+ drumOutmap[int(drumMap[int(spitch)].anote)] = spitch;
+ drumInmap[int(drumMap[int(dpitch)].enote)] = dpitch;
+ drumOutmap[int(drumMap[int(dpitch)].anote)] = dpitch;
+
+ for (iadd_ev i = add_events.begin(); i != add_events.end(); i++) {
+ //std::pair<Part*, Event> pair = *i;
+ //Part* thePart = pair.first;
+ //Event& theEvent = pair.second;
+ Part* thePart = (*i).first;
+ Event& theEvent = (*i).second;
+ // Indicate no undo, and do port controller values but not clone parts.
+ //audio->msgAddEvent(theEvent, thePart, false);
+ audio->msgAddEvent(theEvent, thePart, false, true, false);
+ }
+
+ song->endUndo(SC_EVENT_MODIFIED);
+ song->update(SC_DRUMMAP);
+ }
+
+//---------------------------------------------------------
+// resizeEvent
+//---------------------------------------------------------
+
+void DrumCanvas::resizeEvent(QResizeEvent* ev)
+ {
+ if (ev->size().width() != ev->oldSize().width())
+ emit newWidth(ev->size().width());
+ EventCanvas::resizeEvent(ev);
+ }
+
+
+//---------------------------------------------------------
+// modifySelected
+//---------------------------------------------------------
+
+void DrumCanvas::modifySelected(NoteInfo::ValType type, int delta)
+ {
+ audio->msgIdle(true);
+ song->startUndo();
+ for (iCItem i = items.begin(); i != items.end(); ++i) {
+ if (!(i->second->isSelected()))
+ continue;
+ DEvent* e = (DEvent*)(i->second);
+ Event event = e->event();
+ if (event.type() != Note)
+ continue;
+
+ MidiPart* part = (MidiPart*)(e->part());
+ Event newEvent = event.clone();
+
+ switch (type) {
+ case NoteInfo::VAL_TIME:
+ {
+ int newTime = event.tick() + delta;
+ if (newTime < 0)
+ newTime = 0;
+ newEvent.setTick(newTime);
+ }
+ break;
+ case NoteInfo::VAL_LEN:
+ /*
+ {
+ int len = event.lenTick() + delta;
+ if (len < 1)
+ len = 1;
+ newEvent.setLenTick(len);
+ }
+ */
+ printf("DrumCanvas::modifySelected - NoteInfo::VAL_LEN not implemented\n");
+ break;
+ case NoteInfo::VAL_VELON:
+ /*
+ {
+ int velo = event->velo() + delta;
+ if (velo > 127)
+ velo = 127;
+ else if (velo < 0)
+ velo = 0;
+ newEvent.setVelo(velo);
+ }
+ */
+ printf("DrumCanvas::modifySelected - NoteInfo::VAL_VELON not implemented\n");
+ break;
+ case NoteInfo::VAL_VELOFF:
+ /*
+ {
+ int velo = event.veloOff() + delta;
+ if (velo > 127)
+ velo = 127;
+ else if (velo < 0)
+ velo = 0;
+ newEvent.setVeloOff(velo);
+ }
+ */
+ printf("DrumCanvas::modifySelected - NoteInfo::VAL_VELOFF not implemented\n");
+ break;
+ case NoteInfo::VAL_PITCH:
+ {
+ int pitch = event.pitch() - delta; // Reversing order since the drumlist is displayed in increasing order
+ if (pitch > 127)
+ pitch = 127;
+ else if (pitch < 0)
+ pitch = 0;
+ newEvent.setPitch(pitch);
+ }
+ break;
+ }
+ song->changeEvent(event, newEvent, part);
+ // Indicate do not do port controller values and clone parts.
+ //song->undoOp(UndoOp::ModifyEvent, newEvent, event, part);
+ song->undoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false);
+ }
+ song->endUndo(SC_EVENT_MODIFIED);
+ audio->msgIdle(false);
+ }
+
+//---------------------------------------------------------
+// curPartChanged
+//---------------------------------------------------------
+
+void DrumCanvas::curPartChanged()
+ {
+ editor->setCaption(getCaption());
+ }
+