diff options
Diffstat (limited to 'muse2/muse/midiedit/dcanvas.cpp')
-rw-r--r-- | muse2/muse/midiedit/dcanvas.cpp | 1291 |
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()); + } + |