diff options
Diffstat (limited to 'muse2')
| -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 | 68 | ||||
| -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 | 
18 files changed, 538 insertions, 396 deletions
| diff --git a/muse2/muse/arranger/pcanvas.cpp b/muse2/muse/arranger/pcanvas.cpp index 96c30fdb..edf60802 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..811aaa2a 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,24 @@ 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 draw the edge of parts hiding notes "jagged" (hasHiddenNotes() is interesting for this)   *    * IMPORTANT TODO + *   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 +4489,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(); | 
