diff options
author | Florian Jung <flo@windfisch.org> | 2011-08-02 19:24:08 +0000 |
---|---|---|
committer | Florian Jung <flo@windfisch.org> | 2011-08-02 19:24:08 +0000 |
commit | 0dbef158d3bf5a96bbd2b4432bcc23223783d3d4 (patch) | |
tree | 6384729649d27429a2d6873fe9361911504496d6 /muse2 | |
parent | 6b001296f40debda70c799e99808f23c74a1f2f1 (diff) | |
parent | 4a9ab9a27b2f70168547e54fc2e7d905e964e228 (diff) |
merged experimental into trunk (clone-part-behaviour)
Diffstat (limited to 'muse2')
-rw-r--r-- | muse2/ChangeLog | 3 | ||||
-rw-r--r-- | muse2/muse/arranger/pcanvas.cpp | 4 | ||||
-rw-r--r-- | muse2/muse/arranger/pcanvas.h | 2 | ||||
-rw-r--r-- | muse2/muse/functions.cpp | 95 | ||||
-rw-r--r-- | muse2/muse/functions.h | 2 | ||||
-rw-r--r-- | muse2/muse/midiedit/dcanvas.cpp | 175 | ||||
-rw-r--r-- | muse2/muse/midiedit/dcanvas.h | 2 | ||||
-rw-r--r-- | muse2/muse/midiedit/ecanvas.cpp | 7 | ||||
-rw-r--r-- | muse2/muse/midiedit/prcanvas.cpp | 209 | ||||
-rw-r--r-- | muse2/muse/midiedit/prcanvas.h | 2 | ||||
-rw-r--r-- | muse2/muse/midiedit/scoreedit.cpp | 78 | ||||
-rw-r--r-- | muse2/muse/midiedit/scoreedit.h | 4 | ||||
-rw-r--r-- | muse2/muse/part.cpp | 51 | ||||
-rw-r--r-- | muse2/muse/part.h | 2 | ||||
-rw-r--r-- | muse2/muse/song.h | 2 | ||||
-rw-r--r-- | muse2/muse/steprec.cpp | 248 | ||||
-rw-r--r-- | muse2/muse/undo.cpp | 34 | ||||
-rw-r--r-- | muse2/muse/widgets/canvas.cpp | 25 | ||||
-rw-r--r-- | muse2/muse/widgets/canvas.h | 2 |
19 files changed, 551 insertions, 396 deletions
diff --git a/muse2/ChangeLog b/muse2/ChangeLog index 9a3ff964..625ab3f4 100644 --- a/muse2/ChangeLog +++ b/muse2/ChangeLog @@ -1,3 +1,6 @@ +02.08.2011: + - Changed behaviour of part clones (flo93) + 31.07.2011: - Remodel the Knob (Orcan) diff --git a/muse2/muse/arranger/pcanvas.cpp b/muse2/muse/arranger/pcanvas.cpp index 1f25d6cc..7298194c 100644 --- a/muse2/muse/arranger/pcanvas.cpp +++ b/muse2/muse/arranger/pcanvas.cpp @@ -449,7 +449,7 @@ void PartCanvas::updateSelection() // resizeItem //--------------------------------------------------------- -void PartCanvas::resizeItem(CItem* i, bool noSnap) +void PartCanvas::resizeItem(CItem* i, bool noSnap, bool ctrl) { Track* t = ((NPart*)(i))->track(); Part* p = ((NPart*)(i))->part(); @@ -463,7 +463,7 @@ void PartCanvas::resizeItem(CItem* i, bool noSnap) if (newwidth == 0) newwidth = AL::sigmap.rasterStep(p->tick(), *_raster); - song->cmdResizePart(t, p, newwidth); + song->cmdResizePart(t, p, newwidth, !ctrl); } //--------------------------------------------------------- diff --git a/muse2/muse/arranger/pcanvas.h b/muse2/muse/arranger/pcanvas.h index acfe565e..ccfb1fcc 100644 --- a/muse2/muse/arranger/pcanvas.h +++ b/muse2/muse/arranger/pcanvas.h @@ -83,7 +83,7 @@ class PartCanvas : public Canvas { virtual int pitch2y(int p) const; virtual CItem* newItem(const QPoint&, int); - virtual void resizeItem(CItem*,bool); + virtual void resizeItem(CItem*,bool, bool ctrl); virtual void newItem(CItem*,bool); virtual bool deleteItem(CItem*); virtual void moveCanvasItems(CItemList&, int, int, DragType); diff --git a/muse2/muse/functions.cpp b/muse2/muse/functions.cpp index b47874fd..66257401 100644 --- a/muse2/muse/functions.cpp +++ b/muse2/muse/functions.cpp @@ -290,6 +290,7 @@ bool modify_notelen(const set<Part*>& parts, int range, int rate, int offset) { map<Event*, Part*> events = get_events(parts, range); Undo operations; + map<Part*, int> partlen; if ( (!events.empty()) && ((rate!=100) || (offset!=0)) ) { @@ -305,6 +306,9 @@ bool modify_notelen(const set<Part*>& parts, int range, int rate, int offset) if (len <= 0) len = 1; + + if ((event.tick()+len > part->lenTick()) && (!part->hasHiddenNotes())) + partlen[part]=event.tick()+len; // schedule auto-expanding if (event.lenTick() != len) { @@ -314,6 +318,9 @@ bool modify_notelen(const set<Part*>& parts, int range, int rate, int offset) } } + for (map<Part*, int>::iterator it=partlen.begin(); it!=partlen.end(); it++) + schedule_resize_all_same_len_clone_parts(it->first, it->second, operations); + return song->applyOperationGroup(operations); } else @@ -481,10 +488,11 @@ bool crescendo(const set<Part*>& parts, int range, int start_val, int end_val, b return false; } -bool move_notes(const set<Part*>& parts, int range, signed int ticks) //TODO: clipping +bool move_notes(const set<Part*>& parts, int range, signed int ticks) { map<Event*, Part*> events = get_events(parts, range); Undo operations; + map<Part*, int> partlen; if ( (!events.empty()) && (ticks!=0) ) { @@ -500,12 +508,17 @@ bool move_notes(const set<Part*>& parts, int range, signed int ticks) //TODO: cl else newEvent.setTick(event.tick()+ticks); - if (newEvent.endTick() > part->lenTick()) //if exceeding the part's end, clip + if (newEvent.endTick() > part->lenTick()) //if exceeding the part's end: { - if (part->lenTick() > newEvent.tick()) - newEvent.setLenTick(part->lenTick() - newEvent.tick()); + if (part->hasHiddenNotes()) // auto-expanding is forbidden, clip + { + if (part->lenTick() > newEvent.tick()) + newEvent.setLenTick(part->lenTick() - newEvent.tick()); + else + del=true; //if the new length would be <= 0, erase the note + } else - del=true; //if the new length would be <= 0, erase the note + partlen[part]=newEvent.endTick(); // schedule auto-expanding } if (del==false) @@ -514,6 +527,9 @@ bool move_notes(const set<Part*>& parts, int range, signed int ticks) //TODO: cl operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, false, false)); } + for (map<Part*, int>::iterator it=partlen.begin(); it!=partlen.end(); it++) + schedule_resize_all_same_len_clone_parts(it->first, it->second, operations); + return song->applyOperationGroup(operations); } else @@ -715,6 +731,7 @@ QMimeData* selected_events_to_mime(const set<Part*>& parts, int range) void paste_at(Part* dest_part, const QString& pt, int pos) { Undo operations; + unsigned newpartlen=dest_part->lenTick(); Xml xml(pt.toLatin1().constData()); for (;;) @@ -744,18 +761,29 @@ void paste_at(Part* dest_part, const QString& pt, int pos) e.setTick(tick); e.setSelected(true); - int diff = e.endTick()-dest_part->lenTick(); - if (diff > 0) // too short part? extend it + + if (e.endTick() > dest_part->lenTick()) // event exceeds part? { - Part* newPart = dest_part->clone(); - newPart->setLenTick(newPart->lenTick()+diff); - // Indicate no undo, and do port controller values but not clone parts. - operations.push_back(UndoOp(UndoOp::ModifyPart,dest_part, newPart, true, false)); - dest_part = newPart; // reassign TODO FINDME does this work, or has dest_part to be a nonconst reference? + if (dest_part->hasHiddenNotes()) // auto-expanding is forbidden? + { + if (e.tick() < dest_part->lenTick()) + e.setLenTick(dest_part->lenTick() - e.tick()); // clip + else + e.setLenTick(0); // don't insert that note at all + } + else + { + if (e.endTick() > newpartlen) + newpartlen=e.endTick(); + } } - // Indicate no undo, and do not do port controller values and clone parts. - operations.push_back(UndoOp(UndoOp::AddEvent,e, dest_part, false, false)); + + if (e.lenTick() != 0) operations.push_back(UndoOp(UndoOp::AddEvent,e, dest_part, false, false)); } + + if (newpartlen != dest_part->lenTick()) + schedule_resize_all_same_len_clone_parts(dest_part, newpartlen, operations); + song->applyOperationGroup(operations); goto end_of_paste_at; } @@ -866,6 +894,45 @@ void shrink_parts(int raster) song->applyOperationGroup(operations); } +void internal_schedule_expand_part(Part* part, int raster, Undo& operations) +{ + EventList* events=part->events(); + unsigned len=part->lenTick(); + + for (iEvent ev=events->begin(); ev!=events->end(); ev++) + if (ev->second.endTick() > len) + len=ev->second.endTick(); + + if (raster) len=ceil((float)len/raster)*raster; + + if (len > part->lenTick()) + { + MidiPart* new_part = new MidiPart(*(MidiPart*)part); + new_part->setLenTick(len); + operations.push_back(UndoOp(UndoOp::ModifyPart, part, new_part, true, false)); + } +} + +void schedule_resize_all_same_len_clone_parts(Part* part, unsigned new_len, Undo& operations) +{ + unsigned old_len=part->lenTick(); + if (old_len!=new_len) + { + Part* part_it=part; + do + { + if (part_it->lenTick()==old_len) + { + MidiPart* new_part = new MidiPart(*(MidiPart*)part_it); + new_part->setLenTick(new_len); + operations.push_back(UndoOp(UndoOp::ModifyPart, part_it, new_part, true, false)); + } + + part_it=part_it->nextClone(); + } while (part_it!=part); + } +} + void expand_parts(int raster) { Undo operations; diff --git a/muse2/muse/functions.h b/muse2/muse/functions.h index 633457a9..90fc64e9 100644 --- a/muse2/muse/functions.h +++ b/muse2/muse/functions.h @@ -24,6 +24,7 @@ class QString; class QMimeData; +class Undo; extern GateTime* gatetime_dialog; extern Velocity* velocity_dialog; @@ -87,6 +88,7 @@ void select_not_in_loop(const std::set<Part*>& parts); //functions for parts void shrink_parts(int raster=-1); //negative values mean "config.division" void expand_parts(int raster=-1); +void schedule_resize_all_same_len_clone_parts(Part* part, unsigned new_len, Undo& operations); void clean_parts(); //functions for reading and writing default values diff --git a/muse2/muse/midiedit/dcanvas.cpp b/muse2/muse/midiedit/dcanvas.cpp index 92e514af..d0a5ee31 100644 --- a/muse2/muse/midiedit/dcanvas.cpp +++ b/muse2/muse/midiedit/dcanvas.cpp @@ -104,9 +104,9 @@ Undo DrumCanvas::moveCanvasItems(CItemList& items, int dp, int dx, DragType dtyp { if(editor->parts()->empty()) return Undo(); //return empty list - + PartsToChangeMap parts2change; - Undo operations; + Undo operations; for(iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) { @@ -153,85 +153,72 @@ Undo DrumCanvas::moveCanvasItems(CItemList& items, int dp, int dx, DragType dtyp ip2c->second.xdiff = npartoffset; } } - + + bool forbidden=false; 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); - - // 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 (opart->hasHiddenNotes()) { - if(ip->second == opart) - { - editor->parts()->erase(ip); - break; - } - } - - editor->parts()->add(newPart); - // Do port controller values but not clone parts. - operations.push_back(UndoOp(UndoOp::ModifyPart, opart, newPart, true, false)); - - ip2c->second.npart = newPart; - + forbidden=true; + break; + } + } + + + if (!forbidden) + { + std::vector< CItem* > doneList; + typedef std::vector< CItem* >::iterator iDoneList; + + for(iCItem ici = items.begin(); ici != items.end(); ++ici) + { + CItem* ci = ici->second; + + 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 (idl == doneList.end()) + { + operations.push_back(moveItem(ci, newpos, dtype)); + doneList.push_back(ci); + } + ci->move(newpos); + + if(moving.size() == 1) + itemReleased(curItem, newpos); + + if(dtype == MOVE_COPY || dtype == MOVE_CLONE) + selectItem(ci, false); + } + + for(iPartToChange ip2c = parts2change.begin(); ip2c != parts2change.end(); ++ip2c) + { + Part* opart = ip2c->first; + int diff = ip2c->second.xdiff; + + schedule_resize_all_same_len_clone_parts(opart, opart->lenTick() + diff, operations); + } + + return operations; } - - 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) + else { - 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 (idl == doneList.end()) - { - operations.push_back(moveItem(ci, newpos, dtype)); - doneList.push_back(ci); - } - ci->move(newpos); - - if(moving.size() == 1) { - itemReleased(curItem, newpos); - } - if(dtype == MOVE_COPY || dtype == MOVE_CLONE) - selectItem(ci, false); - } - - return operations; + return Undo(); //return empty list + } } //--------------------------------------------------------- @@ -261,9 +248,10 @@ UndoOp DrumCanvas::moveItem(CItem* item, const QPoint& pos, DragType dtype) //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().toLatin1().constData()); + // Added by T356, removed by flo93: with operation groups, it happens that the + // part is too short right now, even if it's queued for being extended + //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().toLatin1().constData()); if (dtype == MOVE_COPY || dtype == MOVE_CLONE) return UndoOp(UndoOp::AddEvent, newEvent, part, false, false); @@ -308,7 +296,7 @@ CItem* DrumCanvas::newItem(int tick, int instrument, int velocity) // resizeItem //--------------------------------------------------------- -void DrumCanvas::resizeItem(CItem* item, bool) +void DrumCanvas::resizeItem(CItem* item, bool, bool) { DEvent* nevent = (DEvent*) item; Event ev = nevent->event(); @@ -361,20 +349,23 @@ void DrumCanvas::newItem(CItem* item, bool noSnap, bool replace) // Added by T356. Part* part = nevent->part(); - song->startUndo(); - int modified=SC_EVENT_MODIFIED; + Undo operations; int diff = event.endTick()-part->lenTick(); - if (diff > 0) {// too short part? extend it - 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, 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, false, false); - song->endUndo(modified); + + if (! ((diff > 0) && part->hasHiddenNotes()) ) //operation is allowed + { + operations.push_back(UndoOp(UndoOp::AddEvent,event, part, false, false)); + + if (diff > 0)// part must be extended? + { + schedule_resize_all_same_len_clone_parts(part, event.endTick(), operations); + printf("newItem: extending\n"); + } + } + //else forbid action by not applying it + song->applyOperationGroup(operations); + songChanged(SC_EVENT_INSERTED); //this forces an update of the itemlist, which is neccessary + //to remove "forbidden" events from the list again } //--------------------------------------------------------- @@ -746,7 +737,7 @@ void DrumCanvas::keyPressed(int index, int velocity) MidiPlayEvent e(0, port, channel, 0x90, pitch, velocity); audio->msgPlayMidiEvent(&e); - if (_steprec && pos[0] >= start_tick && pos[0] < end_tick && curPart) + if (_steprec && pos[0] >= start_tick /* && pos[0] < end_tick [removed by flo93: this is handled in steprec->record] */ && curPart) steprec->record(curPart,index,drumMap[index].len,editor->raster(),velocity,globalKeyState&Qt::ControlModifier,globalKeyState&Qt::ShiftModifier); } @@ -1135,7 +1126,7 @@ void DrumCanvas::midiNote(int pitch, int velo) if (_midiin && _steprec && curPart && !audio->isPlaying() && velo && pos[0] >= start_tick - && pos[0] < end_tick + /* && pos[0] < end_tick [removed by flo93: this is handled in steprec->record] */ && !(globalKeyState & Qt::AltModifier)) { steprec->record(curPart,drumInmap[pitch],drumMap[(int)drumInmap[pitch]].len,editor->raster(),velo,globalKeyState&Qt::ControlModifier,globalKeyState&Qt::ShiftModifier); } diff --git a/muse2/muse/midiedit/dcanvas.h b/muse2/muse/midiedit/dcanvas.h index 868113a6..a2d0f7de 100644 --- a/muse2/muse/midiedit/dcanvas.h +++ b/muse2/muse/midiedit/dcanvas.h @@ -57,7 +57,7 @@ class DrumCanvas : public EventCanvas { virtual Undo moveCanvasItems(CItemList&, int, int, DragType); virtual UndoOp moveItem(CItem*, const QPoint&, DragType); virtual CItem* newItem(const QPoint&, int); - virtual void resizeItem(CItem*, bool); + virtual void resizeItem(CItem*, bool, bool); virtual void newItem(CItem*, bool); virtual void newItem(CItem*, bool, bool replace ); virtual bool deleteItem(CItem*); diff --git a/muse2/muse/midiedit/ecanvas.cpp b/muse2/muse/midiedit/ecanvas.cpp index ef47e0d6..a829650c 100644 --- a/muse2/muse/midiedit/ecanvas.cpp +++ b/muse2/muse/midiedit/ecanvas.cpp @@ -416,7 +416,12 @@ void EventCanvas::endMoveItems(const QPoint& pos, DragType dragtype, int dir) Undo operations = moveCanvasItems(moving, dp, dx, dragtype); - song->applyOperationGroup(operations); + if (operations.empty()) + songChanged(SC_EVENT_MODIFIED); //this is a hack to force the canvas to repopulate + //itself. otherwise, if a moving operation was forbidden, + //the canvas would still show the movement + else + song->applyOperationGroup(operations); moving.clear(); updateSelection(); diff --git a/muse2/muse/midiedit/prcanvas.cpp b/muse2/muse/midiedit/prcanvas.cpp index 091582ef..5fb59099 100644 --- a/muse2/muse/midiedit/prcanvas.cpp +++ b/muse2/muse/midiedit/prcanvas.cpp @@ -66,7 +66,7 @@ void PianoCanvas::addItem(Part* part, Event& event) NEvent* ev = new NEvent(event, part, pitch2y(event.pitch())); items.add(ev); - int diff = event.endTick()-part->lenTick(); + int diff = event.tick()-part->lenTick(); if (diff > 0) {// too short part? extend it //printf("addItem - this code should not be run!\n"); //Part* newPart = part->clone(); @@ -261,8 +261,8 @@ Undo PianoCanvas::moveCanvasItems(CItemList& items, int dp, int dx, DragType dty if(editor->parts()->empty()) return Undo(); //return empty list - Undo operations; PartsToChangeMap parts2change; + Undo operations; for(iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) { @@ -310,84 +310,71 @@ Undo PianoCanvas::moveCanvasItems(CItemList& items, int dp, int dx, DragType dty } } + bool forbidden=false; 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); - - // 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 (opart->hasHiddenNotes()) { - if(ip->second == opart) - { - editor->parts()->erase(ip); - break; - } - } + forbidden=true; + break; + } + } + + + if (!forbidden) + { + std::vector< CItem* > doneList; + typedef std::vector< CItem* >::iterator iDoneList; + + for(iCItem ici = items.begin(); ici != items.end(); ++ici) + { + CItem* ci = ici->second; + + 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 (idl == doneList.end()) + { + operations.push_back(moveItem(ci, newpos, dtype)); + doneList.push_back(ci); + } + ci->move(newpos); + + if(moving.size() == 1) + itemReleased(curItem, newpos); + + if(dtype == MOVE_COPY || dtype == MOVE_CLONE) + selectItem(ci, false); + } + + for(iPartToChange ip2c = parts2change.begin(); ip2c != parts2change.end(); ++ip2c) + { + Part* opart = ip2c->first; + int diff = ip2c->second.xdiff; - editor->parts()->add(newPart); - // Do port controller values but not clone parts. - operations.push_back(UndoOp(UndoOp::ModifyPart, opart, newPart, true, false)); - - ip2c->second.npart = newPart; - + schedule_resize_all_same_len_clone_parts(opart, opart->lenTick() + diff, operations); + } + + return operations; } - - 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) + else { - 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 (idl == doneList.end()) - { - operations.push_back(moveItem(ci, newpos, dtype)); - doneList.push_back(ci); - } - ci->move(newpos); - - if(moving.size() == 1) - itemReleased(curItem, newpos); - if(dtype == MOVE_COPY || dtype == MOVE_CLONE) - selectItem(ci, false); - } - - return operations; + return Undo(); //return empty list + } } //--------------------------------------------------------- @@ -427,9 +414,10 @@ UndoOp PianoCanvas::moveItem(CItem* item, const QPoint& pos, DragType dtype) item->setEvent(newEvent); - // Added by T356. - if(((int)newEvent.endTick() - (int)part->lenTick()) > 0) - printf("PianoCanvas::moveItem Error! New event end:%d exceeds length:%d of part:%s\n", newEvent.endTick(), part->lenTick(), part->name().toLatin1().constData()); + // Added by T356, removed by flo93: with operation groups, it happens that the + // part is too short right now, even if it's queued for being extended + //if(((int)newEvent.endTick() - (int)part->lenTick()) > 0) + // printf("PianoCanvas::moveItem Error! New event end:%d exceeds length:%d of part:%s\n", newEvent.endTick(), part->lenTick(), part->name().toLatin1().constData()); if (dtype == MOVE_COPY || dtype == MOVE_CLONE) return UndoOp(UndoOp::AddEvent, newEvent, part, false, false); @@ -479,28 +467,32 @@ void PianoCanvas::newItem(CItem* item, bool noSnap) event.setLenTick(w); event.setPitch(y2pitch(item->y())); - song->startUndo(); - int modified=SC_EVENT_MODIFIED; + Undo operations; 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, 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, false, false); - song->endUndo(modified); + + if (! ((diff > 0) && part->hasHiddenNotes()) ) //operation is allowed + { + operations.push_back(UndoOp(UndoOp::AddEvent,event, part, false, false)); + + if (diff > 0)// part must be extended? + { + schedule_resize_all_same_len_clone_parts(part, event.endTick(), operations); + printf("newItem: extending\n"); + } + + song->applyOperationGroup(operations); + } + //else forbid action by not applying it + else + songChanged(SC_EVENT_INSERTED); //this forces an update of the itemlist, which is neccessary + //to remove "forbidden" events from the list again } //--------------------------------------------------------- // resizeItem //--------------------------------------------------------- -void PianoCanvas::resizeItem(CItem* item, bool noSnap) // experimental changes to try dynamically extending parts +void PianoCanvas::resizeItem(CItem* item, bool noSnap, bool) // experimental changes to try dynamically extending parts { //printf("resizeItem!\n"); NEvent* nevent = (NEvent*) item; @@ -519,23 +511,25 @@ void PianoCanvas::resizeItem(CItem* item, bool noSnap) // experimental c if (len <= 0) len = editor->raster(); } - song->startUndo(); - int modified=SC_EVENT_MODIFIED; - int diff = event.tick()+len-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, true, false); - modified=modified|SC_PART_MODIFIED; - part = newPart; // reassign - } - newEvent.setLenTick(len); - // Indicate no undo, and do not do port controller values and clone parts. - audio->msgChangeEvent(event, newEvent, nevent->part(), false, false, false); - song->endUndo(modified); + Undo operations; + int diff = event.tick()+len-part->lenTick(); + + if (! ((diff > 0) && part->hasHiddenNotes()) ) //operation is allowed + { + newEvent.setLenTick(len); + operations.push_back(UndoOp(UndoOp::ModifyEvent,newEvent, event, nevent->part(), false, false)); + + if (diff > 0)// part must be extended? + { + schedule_resize_all_same_len_clone_parts(part, event.tick()+len, operations); + printf("resizeItem: extending\n"); + } + } + //else forbid action by not performing it + song->applyOperationGroup(operations); + songChanged(SC_EVENT_MODIFIED); //this forces an update of the itemlist, which is neccessary + //to remove "forbidden" events from the list again } //--------------------------------------------------------- @@ -668,9 +662,8 @@ void PianoCanvas::pianoPressed(int pitch, int velocity, bool shift) MidiPlayEvent e(0, port, channel, 0x90, pitch, velocity); audio->msgPlayMidiEvent(&e); - if (_steprec && pos[0] >= start_tick && pos[0] < end_tick && curPart) { + if (_steprec && pos[0] >= start_tick /* && pos[0] < end_tick [removed by flo93: this is handled in steprec->record] */ && curPart) steprec->record(curPart,pitch,editor->raster(),editor->raster(),velocity,globalKeyState&Qt::ControlModifier,shift); - } } //--------------------------------------------------------- @@ -894,7 +887,7 @@ void PianoCanvas::midiNote(int pitch, int velo) if (_midiin && _steprec && curPart && !audio->isPlaying() && velo && pos[0] >= start_tick - && pos[0] < end_tick + /* && pos[0] < end_tick [removed by flo93: this is handled in steprec->record] */ && !(globalKeyState & Qt::AltModifier)) { steprec->record(curPart,pitch,editor->raster(),editor->raster(),velo,globalKeyState&Qt::ControlModifier,globalKeyState&Qt::ShiftModifier); } diff --git a/muse2/muse/midiedit/prcanvas.h b/muse2/muse/midiedit/prcanvas.h index c6e47c9e..d96dd4df 100644 --- a/muse2/muse/midiedit/prcanvas.h +++ b/muse2/muse/midiedit/prcanvas.h @@ -57,7 +57,7 @@ class PianoCanvas : public EventCanvas { virtual Undo moveCanvasItems(CItemList&, int, int, DragType); virtual UndoOp moveItem(CItem*, const QPoint&, DragType); virtual CItem* newItem(const QPoint&, int); - virtual void resizeItem(CItem*, bool noSnap); + virtual void resizeItem(CItem*, bool noSnap, bool); virtual void newItem(CItem*, bool noSnap); virtual bool deleteItem(CItem*); virtual void startDrag(CItem* item, bool copymode); diff --git a/muse2/muse/midiedit/scoreedit.cpp b/muse2/muse/midiedit/scoreedit.cpp index 1a885d99..785ac7a5 100644 --- a/muse2/muse/midiedit/scoreedit.cpp +++ b/muse2/muse/midiedit/scoreedit.cpp @@ -1189,6 +1189,7 @@ ScoreCanvas::ScoreCanvas(ScoreEdit* pr, QWidget* parent_widget) : View(parent_wi undo_started=false; selected_part=NULL; + dragged_event_part=NULL; last_len=384; new_len=-1; @@ -1682,7 +1683,8 @@ void staff_t::create_appropriate_eventlist() Event& event=it->second; if ( ( event.isNote() && !event.isNoteOff() && - (event.endTick() <= part->lenTick()) ) && + // (event.endTick() <= part->lenTick()) ) && + (event.tick() <= part->lenTick()) ) && // changed to accord to prcanvas.cpp and others (flo93) ( ((type==GRAND_TOP) && (event.pitch() >= SPLIT_NOTE)) || ((type==GRAND_BOTTOM) && (event.pitch() < SPLIT_NOTE)) || (type==NORMAL) ) ) @@ -3623,7 +3625,7 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) clicked_event_ptr=set_it->source_event; dragged_event=*set_it->source_event; original_dragged_event=dragged_event.clone(); - dragged_event_part=set_it->source_part; + set_dragged_event_part(set_it->source_part); if ((mouse_erases_notes) || (event->button()==Qt::MidButton)) //erase? { @@ -3664,7 +3666,6 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) signed int relative_tick=(signed) tick - curr_part->tick(); if (relative_tick<0) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: relative_tick is negative!" << endl; - song->startUndo(); if (!ctrl) deselect_all(); @@ -3690,7 +3691,7 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) newevent.setLenTick(curr_part->lenTick() - newevent.tick()); } - audio->msgAddEvent(newevent, curr_part, false, false, false); + audio->msgAddEvent(newevent, curr_part, true, false, false); dragged_event_part=curr_part; dragged_event=newevent; @@ -3708,7 +3709,6 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) drag_cursor_changed=true; setCursor(Qt::SizeAllCursor); - song->endUndo(SC_EVENT_INSERTED); song->update(SC_SELECTION); } } @@ -3737,15 +3737,13 @@ void ScoreCanvas::mouseReleaseEvent (QMouseEvent* event) if (flo_quantize(dragged_event.lenTick(), quant_ticks()) <= 0) { if (debugMsg) cout << "new length <= 0, erasing item" << endl; - audio->msgDeleteEvent(dragged_event, dragged_event_part, false, false, false); + if (undo_started) song->undo(); + audio->msgDeleteEvent(dragged_event, dragged_event_part, true, false, false); } else { last_len=flo_quantize(dragged_event.lenTick(), quant_ticks()); } - - if (undo_started) - song->endUndo(SC_EVENT_MODIFIED | SC_EVENT_REMOVED); } @@ -3863,6 +3861,7 @@ void ScoreCanvas::mouseMoveEvent (QMouseEvent* event) old_pitch=-1; old_dest_tick=MAXINT; + old_len=-1; } } @@ -3905,7 +3904,7 @@ void ScoreCanvas::mouseMoveEvent (QMouseEvent* event) if (dest_tick != old_dest_tick) { - if (undo_started) song->undo(); + if (undo_started) song->undo(); //FINDMICH EXTEND undo_started=move_notes(part_to_set(dragged_event_part),1, (signed)dest_tick-original_dragged_event.tick()); old_dest_tick=dest_tick; } @@ -3915,14 +3914,8 @@ void ScoreCanvas::mouseMoveEvent (QMouseEvent* event) case LENGTH: tick+=quant_ticks(); - if (dragged_event.tick()+dragged_event.lenTick() + dragged_event_part->tick() != unsigned(tick)) + if (dragged_event.tick()+old_len + dragged_event_part->tick() != unsigned(tick)) { - if (!undo_started) - { - song->startUndo(); - undo_started=true; - } - Event tmp=dragged_event.clone(); signed relative_tick=tick-signed(dragged_event_part->tick()); signed new_len=relative_tick-dragged_event.tick(); @@ -3935,16 +3928,29 @@ void ScoreCanvas::mouseMoveEvent (QMouseEvent* event) if (debugMsg) cout << "not setting len to a negative value. using 0 instead" << endl; } + unsigned newpartlen=dragged_event_part->lenTick(); if (tmp.endTick() > dragged_event_part->lenTick()) { - tmp.setLenTick(dragged_event_part->lenTick() - tmp.tick()); - if (debugMsg) cout << "resized note would exceed its part; limiting length to " << tmp.lenTick() << endl; + if (dragged_event_part->hasHiddenNotes()) // do not allow autoexpand + { + tmp.setLenTick(dragged_event_part->lenTick() - tmp.tick()); + if (debugMsg) cout << "resized note would exceed its part; limiting length to " << tmp.lenTick() << endl; + } + else + { + newpartlen=tmp.endTick(); + if (debugMsg) cout << "resized note would exceeds its part; expanding the part..." << endl; + } } - audio->msgChangeEvent(dragged_event, tmp, dragged_event_part, false, false, false); - dragged_event=tmp; + if (undo_started) song->undo(); + Undo operations; + operations.push_back(UndoOp(UndoOp::ModifyEvent, tmp, dragged_event, dragged_event_part, false, false)); + if (newpartlen != dragged_event_part->lenTick()) + schedule_resize_all_same_len_clone_parts(dragged_event_part, newpartlen, operations); + undo_started=song->applyOperationGroup(operations); - fully_recalculate(); + old_len=new_len; } break; @@ -4413,6 +4419,9 @@ void ScoreCanvas::update_parts() if (selected_part!=NULL) //if it's null, let it be null selected_part=partFromSerialNumber(selected_part_index); + if (dragged_event_part!=NULL) //same thing here + dragged_event_part=partFromSerialNumber(dragged_event_part_index); + for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++) it->update_parts(); } @@ -4452,20 +4461,34 @@ void staff_t::update_part_indices() /* BUGS and potential bugs - * o when the keymap is not used, this will probably lead to a bug * o tied notes don't work properly when there's a key-change in * between, for example, when a cis is tied to a des + * o schedule_all_same_len_parts: if there are two clones A and B, + * and both A and B get scheduled to be expanded (because we + * have one event from A and one event from B), this causes a bug, + * because after A (and B) got resized, the B-resize is invalid! * * CURRENT TODO - * o clones should have same size - * o insert empty measure should also work inside parts, that is, - * move notes _within_ parts + * o redo transport menu: offer "one beat" and "one bar" steps + * maybe also offer scrollbar + * o quick "set left/right marker", "select between markers" + * or even "set marker and select between immediately" + * o support partially selected parts. when moving, automatically split + * + * o speed up structural operations + * o maybe remove "insert empty measure"? + * o structural OPs: don't erase note which begins at "end of cut" + * o add "move other notes" or "overwrite notes" or "mix with notes" to paste * * IMPORTANT TODO + * o draw the edge of parts hiding notes "jagged" (hasHiddenNotes() is interesting for this) + * o shrink a part from its beginning as well! watch out for clones! + * o insert empty measure should also work inside parts, that is, + * move notes _within_ parts + * * o canvas editor: create clone via "alt+drag" moves window instead * o investigate with valgrind * o controller view in score editor - * o deal with expanding parts * o fix sigedit boxes * o solo button * o grand staff brace @@ -4476,6 +4499,7 @@ void staff_t::update_part_indices() * o transpose etc. must also transpose key-pressure events * o transpose: support in-key-transpose * o thin out: remove unneeded ctrl messages + * o make muse usable without the middle mouse button * * less important stuff * o quantize-templates (everything is forced into a specified diff --git a/muse2/muse/midiedit/scoreedit.h b/muse2/muse/midiedit/scoreedit.h index e7302a46..6a5dd63a 100644 --- a/muse2/muse/midiedit/scoreedit.h +++ b/muse2/muse/midiedit/scoreedit.h @@ -701,12 +701,14 @@ class ScoreCanvas : public View bool dragging; bool drag_cursor_changed; Part* dragged_event_part; + int dragged_event_part_index; Event dragged_event; Event original_dragged_event; Event* clicked_event_ptr; int old_pitch; unsigned old_dest_tick; + int old_len; bool have_lasso; QPoint lasso_start; @@ -811,6 +813,8 @@ class ScoreCanvas : public View Part* get_selected_part() {return selected_part;} void set_selected_part(Part* p) {selected_part=p; if (selected_part) selected_part_index=selected_part->sn();} + Part* get_dragged_event_part() {return dragged_event_part;} + void set_dragged_event_part(Part* p) {dragged_event_part=p; if (dragged_event_part) dragged_event_part_index=dragged_event_part->sn();} set<Part*> get_all_parts(); diff --git a/muse2/muse/part.cpp b/muse2/muse/part.cpp index 398720af..67cf441e 100644 --- a/muse2/muse/part.cpp +++ b/muse2/muse/part.cpp @@ -819,7 +819,7 @@ void Song::removePart(Part* part) // cmdResizePart //--------------------------------------------------------- -void Song::cmdResizePart(Track* track, Part* oPart, unsigned int len) +void Song::cmdResizePart(Track* track, Part* oPart, unsigned int len, bool doClones) { switch(track->type()) { case Track::WAVE: @@ -899,28 +899,21 @@ void Song::cmdResizePart(Track* track, Part* oPart, unsigned int len) case Track::DRUM: { Undo operations; - - MidiPart* nPart = new MidiPart(*(MidiPart*)oPart); - nPart->setLenTick(len); - // Do port controller values but not clone parts. - operations.push_back(UndoOp(UndoOp::ModifyPart, oPart, nPart, true, false)); - - // cut Events in nPart - // Changed by T356. Don't delete events if this is a clone part. - // The other clones might be longer than this one and need these events. - if(nPart->cevents()->arefCount() <= 1) - { - if(oPart->lenTick() > len) { - EventList* el = nPart->events(); - iEvent ie = el->lower_bound(len); - for (; ie != el->end();) { - iEvent i = ie; - ++ie; - // Do port controller values and clone parts. - operations.push_back(UndoOp(UndoOp::DeleteEvent, i->second, nPart, true, true)); - } - } - } + + unsigned orig_len=oPart->lenTick(); + MidiPart* part_it=(MidiPart*)oPart; + do + { + if (part_it->lenTick()==orig_len) + { + MidiPart* newPart = new MidiPart(*part_it); + newPart->setLenTick(len); + // Do port controller values but not clone parts. + operations.push_back(UndoOp(UndoOp::ModifyPart, part_it, newPart, true, false)); + } + + part_it=(MidiPart*)part_it->nextClone(); + } while (doClones && (part_it != (MidiPart*)oPart)); song->applyOperationGroup(operations); break; @@ -1176,3 +1169,15 @@ WavePart* WavePart::clone() const return new WavePart(*this); } + + +bool Part::hasHiddenNotes() +{ + unsigned lastNote=0; + + for (iEvent ev=events()->begin(); ev!=events()->end(); ev++) + if (ev->second.endTick() > lastNote) + lastNote=ev->second.endTick(); + + return lastNote > lenTick(); +} diff --git a/muse2/muse/part.h b/muse2/muse/part.h index 9b037edd..ff7091a6 100644 --- a/muse2/muse/part.h +++ b/muse2/muse/part.h @@ -82,6 +82,8 @@ class Part : public PosLen { void setPrevClone(Part* p) { _prevClone = p; } void setNextClone(Part* p) { _nextClone = p; } + bool hasHiddenNotes(); + iEvent addEvent(Event& p); virtual void write(int, Xml&, bool isCopy = false, bool forceWavePaths = false) const; diff --git a/muse2/muse/song.h b/muse2/muse/song.h index fd88b278..45751418 100644 --- a/muse2/muse/song.h +++ b/muse2/muse/song.h @@ -253,7 +253,7 @@ class Song : public QObject { // part manipulations //----------------------------------------- - void cmdResizePart(Track* t, Part* p, unsigned int size); + void cmdResizePart(Track* t, Part* p, unsigned int size, bool doClones=false); void cmdSplitPart(Track* t, Part* p, int tick); void cmdGluePart(Track* t, Part* p); diff --git a/muse2/muse/steprec.cpp b/muse2/muse/steprec.cpp index 29cb9540..c1fc23b1 100644 --- a/muse2/muse/steprec.cpp +++ b/muse2/muse/steprec.cpp @@ -9,6 +9,7 @@ #include "part.h" #include "event.h" #include "globals.h" +#include "functions.h" #include "song.h" #include "audio.h" @@ -39,121 +40,144 @@ void StepRec::timeout() void StepRec::record(Part* part, int pitch, int len, int step, int velo, bool ctrl, bool shift) { - unsigned tick = song->cpos(); - - if (pitch!=rcSteprecNote) { - chord_timer->stop(); - - - // - // extend len of last note? - // - EventList* events = part->events(); - if (ctrl) { - for (iEvent i = events->begin(); i != events->end(); ++i) { - Event ev = i->second; - if (!ev.isNote()) - continue; - if (ev.pitch() == pitch && ((ev.tick() + ev.lenTick()) == tick)) { - Event e = ev.clone(); - e.setLenTick(ev.lenTick() + len); - // Indicate do undo, and do not do port controller values and clone parts. - audio->msgChangeEvent(ev, e, part, true, false, false); - - if (!shift) { - chord_timer_set_to_tick = tick + step; - chord_timer->start(); - } - return; - } - } - } + unsigned tick = song->cpos(); + unsigned lasttick=0; + Undo operations; + + if (pitch!=rcSteprecNote) + { + chord_timer->stop(); + + // extend len of last note? + EventList* events = part->events(); + if (ctrl) + { + for (iEvent i = events->begin(); i != events->end(); ++i) + { + Event ev = i->second; + if (ev.isNote() && ev.pitch() == pitch && ((ev.tick() + ev.lenTick() + part->tick()) == tick)) + { + Event e = ev.clone(); + e.setLenTick(ev.lenTick() + len); + operations.push_back(UndoOp(UndoOp::ModifyEvent, e,ev, part, false, false)); + + if (!shift) + { + chord_timer_set_to_tick = tick + step; + chord_timer->start(); + } + + lasttick=tick+len - part->tick(); + goto steprec_record_foot; + } + } + } - // - // if we already entered the note, delete it - // - EventRange range = events->equal_range(tick); - for (iEvent i = range.first; i != range.second; ++i) { - Event ev = i->second; - if (ev.isNote() && ev.pitch() == pitch) { - // Indicate do undo, and do not do port controller values and clone parts. - //audio->msgDeleteEvent(ev, part); - audio->msgDeleteEvent(ev, part, true, false, false); + if (tick<=part->endTick()) + { + // if we already entered the note, delete it + // if we would find a note after part->lenTick(), the above "if" + // avoids this. this has to be avoided because then part->hasHiddenNotes() is true + // which results in forbidding any action beyond its end + EventRange range = events->equal_range(tick - part->tick()); + for (iEvent i = range.first; i != range.second; ++i) + { + Event ev = i->second; + if (ev.isNote() && ev.pitch() == pitch) + { + audio->msgDeleteEvent(ev, part, true, false, false); - if (!shift) { - chord_timer_set_to_tick = tick + step; - chord_timer->start(); - } - - return; - } - } - - Event e(Note); - e.setTick(tick - part->tick()); - e.setPitch(pitch); - e.setVelo(velo); - e.setLenTick(len); - // Indicate do undo, and do not do port controller values and clone parts. - //audio->msgAddEvent(e, part); - audio->msgAddEvent(e, part, true, false, false); - - if (! (globalKeyState & Qt::ShiftModifier)) { - chord_timer_set_to_tick = tick + step; - chord_timer->start(); - } - } - else { // equals if (pitch==rcSteprecNote) - bool held_notes=false; - if (note_held_down!=NULL) - { - for (int i=0;i<128;i++) - if (note_held_down[i]) { held_notes=true; break; } - } - else - held_notes=false; - - - if (held_notes) - { - chord_timer->stop(); - - // extend len of last note(s) - using std::set; - - set<Event*> extend_set; - EventList* events = part->events(); - for (iEvent i = events->begin(); i != events->end(); ++i) { - Event& ev = i->second; - if (!ev.isNote()) - continue; + if (!shift) + { + chord_timer_set_to_tick = tick + step; + chord_timer->start(); + } + + return; + } + } + } + + + Event e(Note); + e.setTick(tick - part->tick()); + e.setPitch(pitch); + e.setVelo(velo); + e.setLenTick(len); + operations.push_back(UndoOp(UndoOp::AddEvent, e, part, false, false)); + lasttick=e.endTick(); + + if (! (globalKeyState & Qt::ShiftModifier)) + { + chord_timer_set_to_tick = tick + step; + chord_timer->start(); + } + + goto steprec_record_foot; // this is actually unneccessary, but for clarity + } + else // equals if (pitch==rcSteprecNote) + { + bool held_notes=false; + if (note_held_down!=NULL) + { + for (int i=0;i<128;i++) + if (note_held_down[i]) { held_notes=true; break; } + } + else + held_notes=false; + - if (note_held_down[ev.pitch()] && ((ev.tick() + ev.lenTick()) == tick)) - extend_set.insert(&ev); - } - for (set<Event*>::iterator it=extend_set.begin(); it!=extend_set.end(); it++) - { - Event& ev=**it; - Event e = ev.clone(); - e.setLenTick(ev.lenTick() + len); - // Indicate do undo, and do not do port controller values and clone parts. - audio->msgChangeEvent(ev, e, part, true, false, false); - } + if (held_notes) + { + chord_timer->stop(); + + // extend len of last note(s) + using std::set; + + set<Event*> extend_set; + EventList* events = part->events(); + for (iEvent i = events->begin(); i != events->end(); ++i) + { + Event& ev = i->second; + if (ev.isNote() && note_held_down[ev.pitch()] && ((ev.tick() + ev.lenTick() + part->tick()) == tick)) + extend_set.insert(&ev); + } + + for (set<Event*>::iterator it=extend_set.begin(); it!=extend_set.end(); it++) + { + Event& ev=**it; + Event e = ev.clone(); + e.setLenTick(ev.lenTick() + len); + operations.push_back(UndoOp(UndoOp::ModifyEvent,e, ev, part, false, false)); + } - if (!shift) { - chord_timer_set_to_tick = tick + step; - chord_timer->start(); - } - return; - - } - else // equals if (!held_notes) - { - chord_timer->stop(); + if (!shift) + { + chord_timer_set_to_tick = tick + step; + chord_timer->start(); + } + + lasttick=tick+len - part->tick(); + goto steprec_record_foot; // this is actually unneccessary, but for clarity + } + else // equals if (!held_notes) + { + chord_timer->stop(); - //simply proceed, inserting a rest - Pos p(song->cpos() + step, true); - song->setPos(0, p, true, false, true); - } - } + // simply proceed, inserting a rest + Pos p(song->cpos() + step, true); + song->setPos(0, p, true, false, true); + + return; + } + } + + steprec_record_foot: + if (!((lasttick > part->lenTick()) && part->hasHiddenNotes())) // allowed? + { + if (lasttick > part->lenTick()) // we have to expand the part? + schedule_resize_all_same_len_clone_parts(part, lasttick, operations); + + song->applyOperationGroup(operations); + } } diff --git a/muse2/muse/undo.cpp b/muse2/muse/undo.cpp index 0c05ef08..d13b38d7 100644 --- a/muse2/muse/undo.cpp +++ b/muse2/muse/undo.cpp @@ -15,6 +15,7 @@ #include "globals.h" #include <QAction> +#include <set> // iundo points to last Undo() in Undo-list @@ -207,10 +208,43 @@ void Song::endUndo(int flags) } +void cleanOperationGroup(Undo& group) +{ + using std::set; + + set<Track*> processed_tracks; + set<Part*> processed_parts; + + for (iUndoOp op=group.begin(); op!=group.end();) + { + iUndoOp op_=op; + op_++; + + if ((op->type==UndoOp::ModifyTrack) || (op->type==UndoOp::DeleteTrack)) + { + if (processed_tracks.find(op->oTrack)!=processed_tracks.end()) + group.erase(op); + else + processed_tracks.insert(op->oTrack); + } + else if ((op->type==UndoOp::ModifyPart) || (op->type==UndoOp::DeletePart)) + { + if (processed_parts.find(op->oPart)!=processed_parts.end()) + group.erase(op); + else + processed_parts.insert(op->oPart); + } + + op=op_; + } +} + + bool Song::applyOperationGroup(Undo& group, bool doUndo) { if (!group.empty()) { + cleanOperationGroup(group); //this is a HACK! but it works :) (added by flo93) redoList->push_back(group); redo(); diff --git a/muse2/muse/widgets/canvas.cpp b/muse2/muse/widgets/canvas.cpp index 650b10b7..a74e8a8f 100644 --- a/muse2/muse/widgets/canvas.cpp +++ b/muse2/muse/widgets/canvas.cpp @@ -525,7 +525,6 @@ void Canvas::viewMousePressEvent(QMouseEvent* event) //printf("viewMousePressEvent ignoring buttons:%x mods:%x button:%x\n", (int)event->buttons(), (int)keyState, event->button()); return; } - bool shift = keyState & Qt::ShiftModifier; bool alt = keyState & Qt::AltModifier; bool ctrl = keyState & Qt::ControlModifier; start = event->pos(); @@ -624,15 +623,6 @@ void Canvas::viewMousePressEvent(QMouseEvent* event) else if (alt) { drag = DRAG_CLONE_START; } - else if (shift) { //Select all on the same pitch (e.g. same y-value) - deselectAll(); - for (iCItem i = items.begin(); i != items.end(); ++i) { - if (i->second->y() == curItem->y() ) - selectItem(i->second, true); - } - updateSelection(); - redraw(); - } else drag = DRAG_MOVE_START; } @@ -1088,6 +1078,7 @@ void Canvas::viewMouseReleaseEvent(QMouseEvent* event) QPoint pos = event->pos(); bool ctrl = ((QInputEvent*)event)->modifiers() & Qt::ControlModifier; + bool shift = ((QInputEvent*)event)->modifiers() & Qt::ShiftModifier; bool redrawFlag = false; switch (drag) { @@ -1096,7 +1087,17 @@ void Canvas::viewMouseReleaseEvent(QMouseEvent* event) case DRAG_CLONE_START: if (!ctrl) deselectAll(); - selectItem(curItem, !(ctrl && curItem->isSelected())); + + if (!shift) { //Select or deselect only the clicked item + selectItem(curItem, !(ctrl && curItem->isSelected())); + } + else { //Select or deselect all on the same pitch (e.g. same y-value) + bool selectionFlag = !(ctrl && curItem->isSelected()); + for (iCItem i = items.begin(); i != items.end(); ++i) + if (i->second->y() == curItem->y() ) + selectItem(i->second, selectionFlag); + } + updateSelection(); redrawFlag = true; itemReleased(curItem, curItem->pos()); @@ -1131,7 +1132,7 @@ void Canvas::viewMouseReleaseEvent(QMouseEvent* event) case DRAG_OFF: break; case DRAG_RESIZE: - resizeItem(curItem, false); + resizeItem(curItem, false, ctrl); break; case DRAG_NEW: newItem(curItem, false); diff --git a/muse2/muse/widgets/canvas.h b/muse2/muse/widgets/canvas.h index 2eae3d03..dbe13fcb 100644 --- a/muse2/muse/widgets/canvas.h +++ b/muse2/muse/widgets/canvas.h @@ -102,7 +102,7 @@ class Canvas : public View { virtual int pitch2y(int) const = 0; //CDW virtual CItem* newItem(const QPoint&, int state) = 0; - virtual void resizeItem(CItem*, bool noSnap=false) = 0; + virtual void resizeItem(CItem*, bool noSnap=false, bool ctrl=false) = 0; virtual void newItem(CItem*, bool noSnap=false) = 0; virtual bool deleteItem(CItem*) = 0; int getCurrentDrag(); |