summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Jung <flo@windfisch.org>2011-08-02 19:24:08 +0000
committerFlorian Jung <flo@windfisch.org>2011-08-02 19:24:08 +0000
commit0dbef158d3bf5a96bbd2b4432bcc23223783d3d4 (patch)
tree6384729649d27429a2d6873fe9361911504496d6
parent6b001296f40debda70c799e99808f23c74a1f2f1 (diff)
parent4a9ab9a27b2f70168547e54fc2e7d905e964e228 (diff)
merged experimental into trunk (clone-part-behaviour)
-rw-r--r--muse2/ChangeLog3
-rw-r--r--muse2/muse/arranger/pcanvas.cpp4
-rw-r--r--muse2/muse/arranger/pcanvas.h2
-rw-r--r--muse2/muse/functions.cpp95
-rw-r--r--muse2/muse/functions.h2
-rw-r--r--muse2/muse/midiedit/dcanvas.cpp175
-rw-r--r--muse2/muse/midiedit/dcanvas.h2
-rw-r--r--muse2/muse/midiedit/ecanvas.cpp7
-rw-r--r--muse2/muse/midiedit/prcanvas.cpp209
-rw-r--r--muse2/muse/midiedit/prcanvas.h2
-rw-r--r--muse2/muse/midiedit/scoreedit.cpp78
-rw-r--r--muse2/muse/midiedit/scoreedit.h4
-rw-r--r--muse2/muse/part.cpp51
-rw-r--r--muse2/muse/part.h2
-rw-r--r--muse2/muse/song.h2
-rw-r--r--muse2/muse/steprec.cpp248
-rw-r--r--muse2/muse/undo.cpp34
-rw-r--r--muse2/muse/widgets/canvas.cpp25
-rw-r--r--muse2/muse/widgets/canvas.h2
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();