1 //=========================================================
2 //  MusE
3 //  Linux Music Editor
4 //  $Id: undo.cpp,v 1.12.2.9 2009/05/24 21:43:44 terminator356 Exp $
5 //
6 //  (C) Copyright 1999/2000 Werner Schweer (ws@seh.de)
7 //
8 //  This program is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU General Public License
10 //  as published by the Free Software Foundation; version 2 of
11 //  the License, or (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //  GNU General Public License for more details.
17 //
18 //  You should have received a copy of the GNU General Public License
19 //  along with this program; if not, write to the Free Software
20 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 //=========================================================
23 
24 #include "assert.h"
25 
26 #include "sig.h"
27 #include "keyevent.h"
28 
29 #include "undo.h"
30 #include "song.h"
31 #include "globals.h"
32 #include "audio.h"
33 #include "midiport.h"
34 #include "operations.h"
35 #include "tempo.h"
36 #include "audiodev.h"
37 #include "wave_helper.h"
38 #include "gconfig.h"
39 #include "al/al.h"
40 
41 #include <string.h>
42 #include <QAction>
43 #include <set>
44 
45 // Forwards from header:
46 #include "track.h"
47 #include "part.h"
48 #include "ctrl.h"
49 
50 // Enable for debugging:
51 //#define _UNDO_DEBUG_
52 
53 namespace MusECore {
54 
55 // iundo points to last Undo() in Undo-list
56 
57 static bool undoMode = false;  // for debugging
58 
59 std::list<QString> temporaryWavFiles;
60 
61 //---------------------------------------------------------
62 //   typeName
63 //---------------------------------------------------------
64 
typeName()65 const char* UndoOp::typeName()
66       {
67       static const char* name[] = {
68             "AddRoute", "DeleteRoute",
69             "AddTrack", "DeleteTrack",
70             "AddPart",  "DeletePart", "MovePart", "ModifyPartStart", "ModifyPartLength", "ModifyPartName", "SelectPart",
71             "AddEvent", "DeleteEvent", "ModifyEvent", "SelectEvent",
72             "AddAudioCtrlVal", "DeleteAudioCtrlVal", "ModifyAudioCtrlVal", "ModifyAudioCtrlValList",
73             "AddTempo", "DeleteTempo", "ModifyTempo", "SetTempo", "SetStaticTempo", "SetGlobalTempo", "EnableMasterTrack",
74             "AddSig",   "DeleteSig",   "ModifySig",
75             "AddKey",   "DeleteKey",   "ModifyKey",
76             "ModifyTrackName", "ModifyTrackChannel",
77             "SetTrackRecord", "SetTrackMute", "SetTrackSolo", "SetTrackRecMonitor", "SetTrackOff",
78             "MoveTrack",
79             "ModifyClip", "AddMarker", "DeleteMarker", "ModifyMarker", "SetMarkerPos",
80             "ModifySongLen", "SetInstrument", "DoNothing",
81             "ModifyMidiDivision",
82             "EnableAllAudioControllers",
83             "GlobalSelectAllEvents",
84             "NormalizeMidiDivision"
85             };
86       return name[type];
87       }
88 
89 //---------------------------------------------------------
90 //   dump
91 //---------------------------------------------------------
92 
dump()93 void UndoOp::dump()
94       {
95       printf("UndoOp: %s\n   ", typeName());
96       switch(type) {
97             case AddTrack:
98             case DeleteTrack:
99                   printf("%d %s\n", trackno, track->name().toLatin1().constData());
100                   break;
101             case AddEvent:
102             case DeleteEvent:
103                   printf("old event:\n");
104                   oEvent.dump(5);
105                   printf("   new event:\n");
106                   nEvent.dump(5);
107                   printf("   Part:\n");
108                   if (part)
109                         part->dump(5);
110                   break;
111             case ModifyTrackName:
112                   printf("<%s>-<%s>\n", _oldName->toLocal8Bit().data(), _newName->toLocal8Bit().data());
113                   break;
114             case ModifyTrackChannel:
115                   printf("%s <%d>-<%d>\n", track->name().toLatin1().constData(), _oldPropValue, _newPropValue);
116                   break;
117             case SetTrackRecord:
118                   printf("%s %d\n", track->name().toLatin1().constData(), a);
119                   break;
120             case SetTrackMute:
121                   printf("%s %d\n", track->name().toLatin1().constData(), a);
122                   break;
123             case SetTrackSolo:
124                   printf("%s %d\n", track->name().toLatin1().constData(), a);
125                   break;
126             case SetTrackRecMonitor:
127                   printf("%s %d\n", track->name().toLatin1().constData(), a);
128                   break;
129             case SetTrackOff:
130                   printf("%s %d\n", track->name().toLatin1().constData(), a);
131                   break;
132             default:
133                   break;
134             }
135       }
136 
137 //---------------------------------------------------------
138 //    clearDelete
139 //---------------------------------------------------------
140 
clearDelete()141 void UndoList::clearDelete()
142 {
143   if(!empty())
144   {
145     if (this->isUndo)
146     {
147       for(iUndo iu = begin(); iu != end(); ++iu)
148       {
149         Undo& u = *iu;
150         for(iUndoOp i = u.begin(); i != u.end(); ++i)
151         {
152           switch(i->type)
153           {
154             case UndoOp::DeleteTrack:
155                   if(i->track)
156                     delete const_cast<Track*>(i->track);
157                   break;
158 
159             case UndoOp::DeletePart:
160                   delete const_cast<Part*>(i->part);
161                   break;
162 
163             case UndoOp::ModifyMarker:
164             case UndoOp::SetMarkerPos:
165             case UndoOp::AddMarker:
166             case UndoOp::DeleteMarker:
167                   if (i->oldMarker)
168                     delete i->oldMarker;
169                   if (i->newMarker)
170                     delete i->newMarker;
171                   break;
172 
173             case UndoOp::ModifyPartName:
174             case UndoOp::ModifyTrackName:
175                   if (i->_oldName)
176                     delete i->_oldName;
177                   if (i->_newName)
178                     delete i->_newName;
179                   break;
180 
181             case UndoOp::ModifyAudioCtrlValList:
182                   if (i->_eraseCtrlList)
183                     delete i->_eraseCtrlList;
184                   if (i->_addCtrlList)
185                     delete i->_addCtrlList;
186                   break;
187 
188             default:
189                   break;
190           }
191         }
192         u.clear();
193       }
194     }
195     else
196     {
197       for(riUndo iu = rbegin(); iu != rend(); ++iu)
198       {
199         Undo& u = *iu;
200         for(riUndoOp i = u.rbegin(); i != u.rend(); ++i)
201         {
202           switch(i->type)
203           {
204             case UndoOp::AddTrack:
205                   delete i->track;
206                   break;
207 
208             case UndoOp::AddPart:
209                   delete i->part;
210                   break;
211 
212             case UndoOp::ModifyMarker:
213             case UndoOp::SetMarkerPos:
214             case UndoOp::AddMarker:
215             case UndoOp::DeleteMarker:
216                   if (i->oldMarker)
217                     delete i->oldMarker;
218                   if (i->newMarker)
219                     delete i->newMarker;
220                   break;
221 
222             case UndoOp::ModifyPartName:
223             case UndoOp::ModifyTrackName:
224                   if (i->_oldName)
225                     delete i->_oldName;
226                   if (i->_newName)
227                     delete i->_newName;
228                   break;
229 
230             case UndoOp::ModifyAudioCtrlValList:
231                   if (i->_eraseCtrlList)
232                     delete i->_eraseCtrlList;
233                   if (i->_addCtrlList)
234                     delete i->_addCtrlList;
235                   break;
236 
237             default:
238                   break;
239           }
240         }
241         u.clear();
242       }
243     }
244   }
245 
246   clear();
247 }
248 
249 //---------------------------------------------------------
250 //    startUndo
251 //---------------------------------------------------------
252 
startUndo(void * sender)253 void Song::startUndo(void* sender)
254       {
255       redoList->clearDelete(); // redo must be invalidated when a new undo is started
256       MusEGlobal::redoAction->setEnabled(false);
257       setUndoRedoText();
258 
259       undoList->push_back(Undo());
260       updateFlags = SongChangedStruct_t(0, 0, sender);
261       undoMode = true;
262       }
263 
264 //---------------------------------------------------------
265 //   endUndo
266 //---------------------------------------------------------
267 
endUndo(SongChangedStruct_t flags)268 void Song::endUndo(SongChangedStruct_t flags)
269       {
270       // It is possible the current list may be empty after our optimizations during appending
271       //  of given operations to the current list. (Or if no operations were pushed between startUndo and endUndo).
272       // Get rid of an empty current list now.
273       if(undoList->back().empty())
274         undoList->pop_back();
275       else
276       {
277         riUndo prev_undo = undoList->rbegin();
278         prev_undo++;
279         if (prev_undo!=undoList->rend())
280         {
281               // try to merge the current Undo with the last one
282               if (prev_undo->merge_combo(undoList->back()))
283                     undoList->pop_back();
284         }
285       }
286 
287       // Even if the current list was empty, or emptied during appending of given operations to the current list,
288       //  the given operations were executed so we still need to inform that something may have changed.
289 
290       updateFlags |= flags;
291       endMsgCmd();
292       undoMode = false;
293       }
294 
295 //---------------------------------------------------------
296 //   setUndoRedoText
297 //---------------------------------------------------------
298 
setUndoRedoText()299 void Song::setUndoRedoText()
300 {
301   if(MusEGlobal::undoAction)
302   {
303     QString s = tr("Und&o");
304     if(MusEGlobal::undoAction->isEnabled())
305     {
306       if(!undoList->empty() && !undoList->back().empty())
307       {
308         int sz = undoList->back().size();
309         //if(sz >= 2)
310         //  s += QString(" (%1)").arg(sz);
311         s += QString(" ") + undoList->back().front().typeName();
312         if(sz >= 2)
313           s += ", ..";  // Hm, the tooltip will not show three dots "..."
314       }
315     }
316     MusEGlobal::undoAction->setText(s);
317   }
318 
319   if(MusEGlobal::redoAction)
320   {
321     QString s = tr("Re&do");
322     if(MusEGlobal::redoAction->isEnabled())
323     {
324       if(!redoList->empty() && !redoList->back().empty())
325       {
326         int sz = redoList->back().size();
327         //if(sz >= 2)
328         //  s += QString(" (%1)").arg(sz);
329         s += QString(" ") + redoList->back().front().typeName();
330         if(sz >= 2)
331           s += ", ..";
332       }
333     }
334     MusEGlobal::redoAction->setText(s);
335   }
336 }
337 
push_front(const UndoOp & op)338 void Undo::push_front(const UndoOp& op)
339 {
340   insert(begin(), op);
341 }
342 
push_back(const UndoOp & op)343 void Undo::push_back(const UndoOp& op)
344 {
345   insert(end(), op);
346 }
347 
insert(Undo::iterator position,Undo::const_iterator first,Undo::const_iterator last)348 void Undo::insert(Undo::iterator position, Undo::const_iterator first, Undo::const_iterator last)
349 {
350   for(Undo::const_iterator iuo = first; iuo != last; ++iuo)
351     insert(position, *iuo);
352 }
353 
insert(Undo::iterator position,Undo::size_type n,const UndoOp & op)354 void Undo::insert(Undo::iterator position, Undo::size_type n, const UndoOp& op)
355 {
356   for(Undo::size_type i = 0; i != n; ++i)
357     insert(position, op);
358 }
359 
insert(Undo::iterator position,const UndoOp & op)360 void Undo::insert(Undo::iterator position, const UndoOp& op)
361 {
362   UndoOp n_op = op;
363 
364 #ifdef _UNDO_DEBUG_
365   switch(n_op.type)
366   {
367     case UndoOp::AddRoute:
368       fprintf(stderr, "Undo::insert: AddRoute\n");
369     break;
370     case UndoOp::DeleteRoute:
371       fprintf(stderr, "Undo::insert: DeleteRoute\n");
372     break;
373 
374 
375     case UndoOp::AddTrack:
376       fprintf(stderr, "Undo::insert: AddTrack\n");
377     break;
378     case UndoOp::DeleteTrack:
379       fprintf(stderr, "Undo::insert: DeleteTrack\n");
380     break;
381     case UndoOp::MoveTrack:
382       fprintf(stderr, "Undo::insert: MoveTrack\n");
383     break;
384     case UndoOp::ModifyTrackName:
385       fprintf(stderr, "Undo::insert: ModifyTrackName\n");
386     break;
387     case UndoOp::ModifyTrackChannel:
388       fprintf(stderr, "Undo::insert: ModifyTrackChannel\n");
389     break;
390     case UndoOp::SetTrackRecord:
391       fprintf(stderr, "Undo::insert: SetTrackRecord\n");
392     break;
393     case UndoOp::SetTrackMute:
394       fprintf(stderr, "Undo::insert: SetTrackMute\n");
395     break;
396     case UndoOp::SetTrackSolo:
397       fprintf(stderr, "Undo::insert: SetTrackSolo\n");
398     break;
399     case UndoOp::SetTrackRecMonitor:
400       fprintf(stderr, "Undo::insert: SetTrackRecMonitor\n");
401     break;
402     case UndoOp::SetTrackOff:
403       fprintf(stderr, "Undo::insert: SetTrackOff\n");
404     break;
405 
406 
407     case UndoOp::AddPart:
408       fprintf(stderr, "Undo::insert: AddPart\n");
409     break;
410     case UndoOp::DeletePart:
411       fprintf(stderr, "Undo::insert: DeletePart\n");
412     break;
413     case UndoOp::MovePart:
414       fprintf(stderr, "Undo::insert: MovePart\n");
415     break;
416     case UndoOp::SelectPart:
417       fprintf(stderr, "Undo::insert: SelectPart\n");
418     break;
419     case UndoOp::ModifyPartName:
420       fprintf(stderr, "Undo::insert: ModifyPartName\n");
421     break;
422     case UndoOp::ModifyPartStart:
423       fprintf(stderr, "Undo::insert: ModifyPartStart\n");
424     break;
425     case UndoOp::ModifyPartLength:
426       fprintf(stderr, "Undo::insert: ModifyPartLength\n");
427     break;
428 
429 
430     case UndoOp::AddEvent:
431       fprintf(stderr, "Undo::insert: AddEvent\n");
432     break;
433     case UndoOp::DeleteEvent:
434       fprintf(stderr, "Undo::insert: DeleteEvent\n");
435     break;
436     case UndoOp::ModifyEvent:
437       fprintf(stderr, "Undo::insert: ModifyEvent\n");
438     break;
439     case UndoOp::SelectEvent:
440       fprintf(stderr, "Undo::insert: SelectEvent\n");
441     break;
442 
443 
444     case UndoOp::AddAudioCtrlVal:
445       fprintf(stderr, "Undo::insert: AddAudioCtrlVal\n");
446     break;
447     case UndoOp::DeleteAudioCtrlVal:
448       fprintf(stderr, "Undo::insert: DeleteAudioCtrlVal\n");
449     break;
450     case UndoOp::ModifyAudioCtrlVal:
451       fprintf(stderr, "Undo::insert: ModifyAudioCtrlVal\n");
452     break;
453     case UndoOp::ModifyAudioCtrlValList:
454       fprintf(stderr, "Undo::insert: ModifyAudioCtrlValList\n");
455     break;
456 
457 
458     case UndoOp::AddTempo:
459       fprintf(stderr, "Undo::insert: AddTempo tempo:%d tick:%d\n", n_op.b, n_op.a);
460     break;
461     case UndoOp::DeleteTempo:
462       fprintf(stderr, "Undo::insert: DeleteTempo old val:%d tick:%d\n", n_op.b, n_op.a);
463     break;
464     case UndoOp::ModifyTempo:
465       fprintf(stderr, "Undo::insert: ModifyTempo old:%d new:%d tick:%d\n", n_op.b, n_op.c, n_op.a);
466     break;
467     case UndoOp::SetTempo:
468       fprintf(stderr, "Undo::insert: SetTempo tempo:%d tick:%d\n", n_op.b, n_op.a);
469     break;
470     case UndoOp::SetStaticTempo:
471       fprintf(stderr, "Undo::insert: SetStaticTempo\n");
472     break;
473     case UndoOp::SetGlobalTempo:
474       fprintf(stderr, "Undo::insert: SetGlobalTempo\n");
475     break;
476     case UndoOp::EnableMasterTrack:
477       fprintf(stderr, "Undo::insert: EnableMasterTrack\n");
478     break;
479 
480 
481     case UndoOp::AddSig:
482       fprintf(stderr, "Undo::insert: AddSig\n");
483     break;
484     case UndoOp::DeleteSig:
485       fprintf(stderr, "Undo::insert: DeleteSig\n");
486     break;
487     case UndoOp::ModifySig:
488       fprintf(stderr, "Undo::insert: ModifySig\n");
489     break;
490 
491 
492     case UndoOp::AddKey:
493       fprintf(stderr, "Undo::insert: AddKey\n");
494     break;
495     case UndoOp::DeleteKey:
496       fprintf(stderr, "Undo::insert: DeleteKey\n");
497     break;
498     case UndoOp::ModifyKey:
499       fprintf(stderr, "Undo::insert: ModifyKey\n");
500     break;
501 
502 
503     case UndoOp::ModifyClip:
504       fprintf(stderr, "Undo::insert: ModifyClip\n");
505     break;
506 
507 
508     case UndoOp::AddMarker:
509       fprintf(stderr, "Undo::insert: AddMarker\n");
510     break;
511 
512     case UndoOp::DeleteMarker:
513       fprintf(stderr, "Undo::insert: DeleteMarker\n");
514     break;
515 
516     case UndoOp::ModifyMarker:
517       fprintf(stderr, "Undo::insert: ModifyMarker\n");
518     break;
519 
520     case UndoOp::SetMarkerPos:
521       fprintf(stderr, "Undo::insert: SetMarkerPos\n");
522     break;
523 
524 
525     case UndoOp::ModifySongLen:
526       fprintf(stderr, "Undo::insert: ModifySongLen\n");
527     break;
528 
529     case UndoOp::SetInstrument:
530       fprintf(stderr, "Undo::insert: SetInstrument\n");
531     break;
532 
533 
534     case UndoOp::DoNothing:
535       fprintf(stderr, "Undo::insert: DoNothing\n");
536     break;
537 
538     case UndoOp::ModifyMidiDivision:
539       fprintf(stderr, "Undo::insert: ModifyMidiDivision\n");
540     break;
541 
542     case UndoOp::EnableAllAudioControllers:
543       fprintf(stderr, "Undo::insert: EnableAllAudioControllers\n");
544     break;
545 
546     case UndoOp::GlobalSelectAllEvents:
547       fprintf(stderr, "Undo::insert: GlobalSelectAllEvents\n");
548     break;
549 
550     case UndoOp::NormalizeMidiDivision:
551       fprintf(stderr, "Undo::insert: NormalizeMidiDivision\n");
552     break;
553 
554     default:
555     break;
556   }
557 #endif
558 
559   // (NOTE: Use this handy speed-up 'if' line to exclude unhandled operation types)
560   if(n_op.type != UndoOp::ModifyTrackChannel && n_op.type != UndoOp::ModifyClip && n_op.type != UndoOp::DoNothing)
561   {
562     // TODO FIXME: Must look beyond position and optimize in that direction too !
563     //for(Undo::iterator iuo = begin(); iuo != position; ++iuo)
564     iterator iuo = position;
565     while(iuo != begin())
566     {
567       --iuo;
568       UndoOp& uo = *iuo;
569 
570       switch(n_op.type)
571       {
572         case UndoOp::AddRoute:
573           if(uo.type == UndoOp::AddRoute && uo.routeFrom == n_op.routeFrom && uo.routeTo == n_op.routeTo)
574           {
575             fprintf(stderr, "MusE error: Undo::insert(): Double AddRoute. Ignoring.\n");
576             return;
577           }
578           else if(uo.type == UndoOp::DeleteRoute && uo.routeFrom == n_op.routeFrom && uo.routeTo == n_op.routeTo)
579           {
580             // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command.
581             erase(iuo);
582             return;
583           }
584         break;
585 
586         case UndoOp::DeleteRoute:
587           if(uo.type == UndoOp::DeleteRoute && uo.routeFrom == n_op.routeFrom && uo.routeTo == n_op.routeTo)
588           {
589             fprintf(stderr, "MusE error: Undo::insert(): Double DeleteRoute. Ignoring.\n");
590             return;
591           }
592           else if(uo.type == UndoOp::AddRoute && uo.routeFrom == n_op.routeFrom && uo.routeTo == n_op.routeTo)
593           {
594             // Add followed by delete is useless. Cancel out the add + delete by erasing the add command.
595             erase(iuo);
596             return;
597           }
598         break;
599 
600 
601         case UndoOp::ModifyTrackName:
602           if(uo.type == UndoOp::ModifyTrackName && uo.track == n_op.track)
603           {
604             fprintf(stderr, "MusE error: Undo::insert(): Double ModifyTrackName. Ignoring.\n");
605             return;
606           }
607         break;
608 
609         case UndoOp::MoveTrack:
610           if(uo.type == UndoOp::MoveTrack && uo.a == n_op.a)
611           {
612             // Simply replace the 'to track' value.
613             uo.b = n_op.b;
614             return;
615           }
616         break;
617 
618         case UndoOp::SetTrackRecord:
619           if(uo.type == UndoOp::SetTrackRecord && uo.track == n_op.track)
620           {
621             if(uo.a == n_op.a)
622             {
623               fprintf(stderr, "MusE error: Undo::insert(): Double SetTrackRecord. Ignoring.\n");
624               return;
625             }
626             else
627             {
628               // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command.
629               erase(iuo);
630               return;
631             }
632           }
633         break;
634 
635         case UndoOp::SetTrackMute:
636           if(uo.type == UndoOp::SetTrackMute && uo.track == n_op.track)
637           {
638             if(uo.a == n_op.a)
639             {
640               fprintf(stderr, "MusE error: Undo::insert(): Double SetTrackMute. Ignoring.\n");
641               return;
642             }
643             else
644             {
645               // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command.
646               erase(iuo);
647               return;
648             }
649           }
650         break;
651 
652         case UndoOp::SetTrackSolo:
653           if(uo.type == UndoOp::SetTrackSolo && uo.track == n_op.track)
654           {
655             if(uo.a == n_op.a)
656             {
657               fprintf(stderr, "MusE error: Undo::insert(): Double SetTrackSolo. Ignoring.\n");
658               return;
659             }
660             else
661             {
662               // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command.
663               erase(iuo);
664               return;
665             }
666           }
667         break;
668 
669         case UndoOp::SetTrackRecMonitor:
670           if(uo.type == UndoOp::SetTrackRecMonitor && uo.track == n_op.track)
671           {
672             if(uo.a == n_op.a)
673             {
674               fprintf(stderr, "MusE error: Undo::insert(): Double SetTrackRecMonitor. Ignoring.\n");
675               return;
676             }
677             else
678             {
679               // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command.
680               erase(iuo);
681               return;
682             }
683           }
684         break;
685 
686         case UndoOp::SetTrackOff:
687           if(uo.type == UndoOp::SetTrackOff && uo.track == n_op.track)
688           {
689             if(uo.a == n_op.a)
690             {
691               fprintf(stderr, "MusE error: Undo::insert(): Double SetTrackOff. Ignoring.\n");
692               return;
693             }
694             else
695             {
696               // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command.
697               erase(iuo);
698               return;
699             }
700           }
701         break;
702 
703         case UndoOp::ModifyPartName:
704           if(uo.type == UndoOp::ModifyPartName && uo.part == n_op.part)
705           {
706             fprintf(stderr, "MusE error: Undo::insert(): Double ModifyPartName. Ignoring.\n");
707             return;
708           }
709         break;
710 
711       case UndoOp::ModifyPartStart:
712           // TODO: events_offset is a difference requiring accumulation not simple replacement,
713           //        and events_offset_time_type might be different requiring conversion.
714 //           if(uo.type == UndoOp::ModifyPartStart)
715 //           {
716 //             if(uo.part == n_op.part)
717 //             {
718 //               // Simply replace the new values.
719 //               uo.new_partlen_or_pos = n_op.new_partlen_or_pos;
720 //               uo.new_partlen = n_op.new_partlen;
721 //               uo.events_offset = n_op.events_offset;
722 //               uo.events_offset_time_type = n_op.events_offset_time_type;
723 //               return;
724 //             }
725 //           }
726           break;
727 
728       case UndoOp::ModifyPartLength:
729           // TODO: events_offset is a difference requiring accumulation not simple replacement,
730           //        and events_offset_time_type might be different requiring conversion.
731 //           if(uo.type == UndoOp::ModifyPartLength)
732 //           {
733 //             if(uo.part == n_op.part)
734 //             {
735 //               // Simply replace the new values.
736 //               uo.new_partlen_or_pos = n_op.new_partlen_or_pos;
737 //               uo.events_offset = n_op.events_offset;
738 //               uo.events_offset_time_type = n_op.events_offset_time_type;
739 //               return;
740 //             }
741 //           }
742         break;
743 
744         case UndoOp::MovePart:
745           if(uo.type == UndoOp::MovePart && uo.part == n_op.part)
746           {
747             // Simply replace the new value and new track.
748             uo.new_partlen_or_pos = n_op.new_partlen_or_pos;
749             uo.track = n_op.track;
750             return;
751           }
752         break;
753 
754         case UndoOp::AddPart:
755           if(uo.type == UndoOp::AddPart && uo.part == n_op.part)
756           {
757             fprintf(stderr, "MusE error: Undo::insert(): Double AddPart. Ignoring.\n");
758             return;
759           }
760           else if(uo.type == UndoOp::DeletePart && uo.part == n_op.part)
761           {
762             // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command.
763             erase(iuo);
764             return;
765           }
766         break;
767 
768         case UndoOp::DeletePart:
769           if(uo.type == UndoOp::DeletePart && uo.part == n_op.part)
770           {
771             fprintf(stderr, "MusE error: Undo::insert(): Double DeletePart. Ignoring.\n");
772             return;
773           }
774           else if(uo.type == UndoOp::AddPart && uo.part == n_op.part)
775           {
776             // Add followed by delete is useless. Cancel out the add + delete by erasing the add command.
777             erase(iuo);
778             return;
779           }
780         break;
781 
782 
783         case UndoOp::AddEvent:
784           if(uo.type == UndoOp::AddEvent && uo.nEvent == n_op.nEvent && uo.part == n_op.part)
785           {
786             fprintf(stderr, "MusE error: Undo::insert(): Double AddEvent. Ignoring.\n");
787             return;
788           }
789           else if(uo.type == UndoOp::DeleteEvent && uo.part == n_op.part)
790           {
791             if(uo.nEvent == n_op.nEvent)
792             {
793               // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command.
794               erase(iuo);
795               return;
796             }
797             else
798 
799             // To allow for easy DeleteEvent + AddEvent of a given controller number at a given time,
800             //  instead of demanding ModifyEvent. Automatically transform the operations.
801             if(uo.nEvent.type() == Controller && n_op.nEvent.type() == Controller &&
802                uo.nEvent.dataA() == n_op.nEvent.dataA() &&
803                uo.nEvent.posValue() == n_op.nEvent.posValue())
804             {
805               // Transform the DeleteEvent operation into a ModifyEvent operation.
806               uo.type = UndoOp::ModifyEvent;
807               uo.oEvent = uo.nEvent;
808               uo.nEvent = n_op.nEvent;
809               return;
810             }
811           }
812           else if(uo.type == UndoOp::ModifyEvent && uo.part == n_op.part)
813           {
814             if(uo.nEvent == n_op.nEvent)
815             {
816               // Modify followed by adding of the modify's new event, is equivalent to just modifying with the added event.
817               fprintf(stderr, "MusE error: Undo::insert(): ModifyEvent, then AddEvent same new event (double AddEvent). Ignoring.\n");
818               return;
819             }
820             else if(uo.oEvent == n_op.nEvent)
821             {
822               // Modify followed by adding of the modify's old event, is equivalent to just adding the event.
823               // Transform the ModifyEvent operation into an AddEvent.
824               uo.type = UndoOp::AddEvent;
825               uo.nEvent = uo.oEvent;
826               return;
827             }
828           }
829         break;
830 
831         case UndoOp::DeleteEvent:
832           if(uo.type == UndoOp::DeleteEvent && uo.nEvent == n_op.nEvent && uo.part == n_op.part)
833           {
834             fprintf(stderr, "MusE error: Undo::insert(): Double DeleteEvent. Ignoring.\n");
835             return;
836           }
837           else if(uo.type == UndoOp::AddEvent && uo.nEvent == n_op.nEvent && uo.part == n_op.part)
838           {
839             // Add followed by delete is useless. Cancel out the add + delete by erasing the add command.
840             erase(iuo);
841             return;
842           }
843           else if(uo.type == UndoOp::ModifyEvent && uo.part == n_op.part)
844           {
845             if(uo.oEvent == n_op.nEvent)
846             {
847               // Modify followed by delete of the modify's old event, is an error - two deletes of the same event.
848               fprintf(stderr, "MusE error: Undo::insert(): ModifyEvent, then DeleteEvent same old event (double DeleteEvent). Ignoring.\n");
849               return;
850             }
851             else if(uo.nEvent == n_op.nEvent)
852             {
853               // Modify followed by delete of the modify's new event, is equivalent to just deleting the old event.
854               // Transform the operation into a DeleteEvent.
855               uo.type = UndoOp::DeleteEvent;
856               uo.nEvent = uo.oEvent;
857               return;
858             }
859           }
860         break;
861 
862         case UndoOp::ModifyEvent:
863           if(n_op.oEvent == n_op.nEvent)
864           {
865             // Equivalent to deleting then adding the same event - useless, cancels out.
866             return;
867           }
868           else if(uo.type == UndoOp::ModifyEvent && uo.part == n_op.part)
869           {
870             // For testing...
871             //fprintf(stderr, "MusE: DIAGNOSTIC: Undo::insert(): Double ModifyEvent... checking for errors...\n");
872 
873             if(uo.oEvent == n_op.oEvent)
874             {
875               if(uo.nEvent == n_op.nEvent)
876               {
877                 fprintf(stderr, "MusE error: Undo::insert(): Double ModifyEvent. Ignoring.\n");
878                 return;
879               }
880               else
881               {
882                 // For testing...
883                 //fprintf(stderr, "MusE: Undo::insert(): Double ModifyEvent. Same old events. Merging.\n");
884 
885                 // Two modify commands with old events the same is equivalent to just one modify command.
886                 // Replace the existing ModifyEvent command's new event with the requested ModifyEvent command's new event.
887                 uo.nEvent = n_op.nEvent;
888                 return;
889               }
890             }
891             // REMOVE Tim. citem. Added. Remove. I think we CAN replace two different events with the same event.
892             //else if(uo.nEvent == n_op.nEvent)
893             //{
894             //  // Cannot replace two different events with the same event.
895             //  fprintf(stderr, "MusE error: Undo::insert(): Double ModifyEvent: different old events but same new event. Ignoring.\n");
896             //  return;
897             //}
898             // Are inner new/old pair the same event?
899             else if(uo.nEvent == n_op.oEvent)
900             {
901               // Are outer old/new pair the same event?
902               if(uo.oEvent == n_op.nEvent)
903               {
904                 // First ModifyEvent old event and second ModifyEvent new event are both the same, equivalent to doing nothing.
905                 // Cancel out the two ModifyEvent operations by erasing the existing ModifyEvent command.
906                 erase(iuo);
907                 return;
908               }
909               else
910               {
911                 // For testing...
912                 //fprintf(stderr, "MusE: Undo::insert(): Double ModifyEvent. Inner new/old pair same, outer old/new pair not same. Merging to one ModifyEvent.\n");
913 
914                 // Inner new/old pair are the same event and outer old/new pair are not the same event.
915                 // A modify command with new event followed by a modify command with old event the same
916                 //  is equivalent to just one modify command. Replace the existing ModifyEvent command's
917                 //  new event with the requested ModifyEvent command's new event.
918                 uo.nEvent = n_op.nEvent;
919                 return;
920               }
921             }
922             // Inner new/old pair are not the same event. Are outer old/new pair the same event?
923             else if(uo.oEvent == n_op.nEvent)
924             {
925                 // For testing...
926                 //fprintf(stderr, "MusE: Undo::insert(): Double ModifyEvent. Inner new/old pair not same,"
927                 // " outer old/new pair same. Transforming to Add and Delete.\n");
928 
929               // Transform the existing ModifyEvent operation into an AddEvent.
930               uo.type = UndoOp::AddEvent;
931               // Transform the requested ModifyEvent operation into a DeleteEvent.
932               n_op.type = UndoOp::DeleteEvent;
933               n_op.nEvent = n_op.oEvent;
934               // Allow it to add...
935             }
936           }
937           else if(uo.type == UndoOp::AddEvent && uo.part == n_op.part)
938           {
939             // For testing...
940             //fprintf(stderr, "MusE: Undo::insert(): AddEvent then ModifyEvent...\n");
941 
942             if(uo.nEvent == n_op.oEvent)
943             {
944               // For testing...
945               //fprintf(stderr, "MusE: Undo::insert(): AddEvent then ModifyEvent. Same event. Merging to AddEvent.\n");
946 
947               // Add followed by modify with old event same as added event, is equivalent to just adding modify's new event.
948               // Replace the existing AddEvent command's event with the requested ModifyEvent command's new event.
949               uo.nEvent = n_op.nEvent;
950               return;
951             }
952             if(uo.nEvent == n_op.nEvent)
953             {
954               // Add followed by modify with new event same as added event, is a caller error.
955               fprintf(stderr, "MusE error: Undo::insert(): AddEvent, then ModifyEvent same new event (double AddEvent). Ignoring.\n");
956               return;
957             }
958           }
959           if(uo.type == UndoOp::DeleteEvent && uo.part == n_op.part)
960           {
961             if(uo.nEvent == n_op.oEvent)
962             {
963               // Delete followed by modify with old event same as deleted event, is an error.
964               fprintf(stderr, "MusE error: Undo::insert(): DeleteEvent, then ModifyEvent same old event (double DeleteEvent). Ignoring.\n");
965               return;
966             }
967             if(uo.nEvent == n_op.nEvent)
968             {
969               // For testing...
970               //fprintf(stderr, "MusE: Undo::insert(): DeleteEvent then ModifyEvent. Same event. Merging to DeleteEvent.\n");
971 
972               // Delete followed by modify with new event same as deleted event, is equivalent to just deleting modify's old event.
973               // Replace the existing DeleteEvent command's event with the requested ModifyEvent command's old event.
974               uo.nEvent = n_op.oEvent;
975             }
976           }
977         break;
978 
979         case UndoOp::AddAudioCtrlVal:
980           if(uo.type == UndoOp::AddAudioCtrlVal && uo.track == n_op.track &&
981              uo._audioCtrlID == n_op._audioCtrlID &&
982              uo._audioCtrlFrame == n_op._audioCtrlFrame)
983           {
984             // Simply replace the original value and frame.
985             uo._audioCtrlVal = n_op._audioCtrlVal;
986             return;
987           }
988 // TODO If possible.
989 //           else if(uo.type == UndoOp::DeleteAudioCtrlVal && uo.track == n_op.track &&
990 //              uo._audioCtrlID == n_op._audioCtrlID &&
991 //              uo._audioCtrlFrame == n_op._audioCtrlFrame)
992 //           {
993 //             // Delete followed by add, at the same frame. Transform the delete into a modify.
994 //             uo.type = UndoOp::ModifyAudioCtrlVal;
995 //             uo._audioCtrlVal = n_op._audioCtrlVal;
996 //             uo._audioNewCtrlFrame =
997 //             return;
998 //           }
999         break;
1000 
1001         case UndoOp::DeleteAudioCtrlVal:
1002           if(uo.type == UndoOp::DeleteAudioCtrlVal && uo.track == n_op.track &&
1003              uo._audioCtrlID == n_op._audioCtrlID &&
1004              uo._audioCtrlFrame == n_op._audioCtrlFrame)
1005           {
1006             fprintf(stderr, "MusE error: Undo::insert(): Double DeleteAudioCtrlVal. Ignoring.\n");
1007             return;
1008           }
1009           else if(uo.type == UndoOp::AddAudioCtrlVal && uo.track == n_op.track &&
1010              uo._audioCtrlID == n_op._audioCtrlID &&
1011              uo._audioCtrlFrame == n_op._audioCtrlFrame)
1012           {
1013             // Add followed by delete, at the same frame, is useless. Cancel out the add + delete by erasing the add command.
1014             erase(iuo);
1015             return;
1016           }
1017         break;
1018 
1019         case UndoOp::ModifyAudioCtrlVal:
1020           if(uo.type == UndoOp::ModifyAudioCtrlVal && uo.track == n_op.track &&
1021              uo._audioCtrlID == n_op._audioCtrlID &&
1022              uo._audioNewCtrlFrame == n_op._audioCtrlFrame)
1023           {
1024             // Simply replace the original new value and new frame.
1025             uo._audioNewCtrlVal = n_op._audioNewCtrlVal;
1026             uo._audioNewCtrlFrame = n_op._audioNewCtrlFrame;
1027             return;
1028           }
1029         break;
1030 
1031         case UndoOp::ModifyAudioCtrlValList:
1032           // Check the sanity of the requested op.
1033           if(n_op._eraseCtrlList == n_op._addCtrlList)
1034           {
1035             fprintf(stderr, "MusE error: Undo::insert(): ModifyAudioCtrlValList: Erase and add lists are the same. Ignoring.\n");
1036             return;
1037           }
1038 
1039           if(uo.type == UndoOp::ModifyAudioCtrlValList)
1040           {
1041             if(uo._ctrlListList == n_op._ctrlListList)
1042             {
1043               if(uo._addCtrlList == n_op._addCtrlList && uo._eraseCtrlList == n_op._eraseCtrlList)
1044               {
1045                 fprintf(stderr, "MusE error: Undo::insert(): Double ModifyAudioCtrlValList. Ignoring.\n");
1046                 return;
1047               }
1048               else if(uo._addCtrlList == n_op._eraseCtrlList)
1049               {
1050                 // Delete the existing ModifyAudioCtrlValList command's _addCtrlList and replace it
1051                 //  with the requested ModifyAudioCtrlValList command's _addCtrlList.
1052                 if(uo._addCtrlList)
1053                   delete uo._addCtrlList;
1054                 uo._addCtrlList = n_op._addCtrlList;
1055                 return;
1056               }
1057             }
1058             // Seems possible... remove? But maybe dangerous to have two undo ops pointing to the same lists - they will be self-deleted.
1059             else
1060             {
1061               if(uo._addCtrlList == n_op._addCtrlList)
1062               {
1063                 fprintf(stderr, "MusE error: Undo::insert(): ModifyAudioCtrlValList: Attempting to add same list to different containers. Ignoring.\n");
1064                 return;
1065               }
1066               else if(uo._eraseCtrlList == n_op._eraseCtrlList)
1067               {
1068                 fprintf(stderr, "MusE error: Undo::insert(): ModifyAudioCtrlValList: Attempting to erase same list from different containers. Ignoring.\n");
1069                 return;
1070               }
1071             }
1072           }
1073         break;
1074 
1075 
1076         case UndoOp::SetInstrument:
1077           // Check the sanity of the requested op.
1078           if(n_op._oldMidiInstrument == n_op._newMidiInstrument)
1079           {
1080             fprintf(stderr, "MusE error: Undo::insert(): SetInstrument: Old and new instruments are the same. Ignoring.\n");
1081             return;
1082           }
1083 
1084           if(uo.type == UndoOp::SetInstrument)
1085           {
1086             if(uo._midiPort == n_op._midiPort)
1087             {
1088               if(uo._oldMidiInstrument == n_op._oldMidiInstrument && uo._newMidiInstrument == n_op._newMidiInstrument)
1089               {
1090                 fprintf(stderr, "MusE error: Undo::insert(): Double SetInstrument. Ignoring.\n");
1091                 return;
1092               }
1093               else if(uo._newMidiInstrument == n_op._oldMidiInstrument)
1094               {
1095                 // Replace the existing SetInstrument command's _newMidiInstrument
1096                 //  with the requested ModifyAudioCtrlValList command's _newMidiInstrument.
1097                 uo._newMidiInstrument = n_op._newMidiInstrument;
1098                 return;
1099               }
1100             }
1101           }
1102         break;
1103 
1104 
1105         case UndoOp::AddTempo:
1106           if(uo.type == UndoOp::AddTempo && uo.a == n_op.a)
1107           {
1108             // Simply replace the value.
1109             uo.b = n_op.b;
1110             return;
1111           }
1112           else if(uo.type == UndoOp::DeleteTempo && uo.a == n_op.a)
1113           {
1114             // Delete followed by add. Transform the existing DeleteTempo operation into a ModifyTempo.
1115             uo.type = UndoOp::ModifyTempo;
1116             // a is already the tick, b is already the existing value from DeleteTempo, c is the new value.
1117             uo.c = n_op.b;
1118             return;
1119           }
1120           else if(uo.type == UndoOp::ModifyTempo && uo.a == n_op.a)
1121           {
1122             // Modify followed by add. Simply replace the value.
1123             // a is already the tick, b is already the existing value from ModifyTempo, c is the new value.
1124             uo.c = n_op.b;
1125             return;
1126           }
1127 //           else if(uo.type == UndoOp::SetTempo && uo.a == n_op.a)
1128 //           {
1129 //             // Only if the master is on.
1130 //             if(MusEGlobal::tempomap.masterFlag())
1131 //             {
1132 //               // Simply replace the value.
1133 //               uo.b = n_op.b;
1134 //               return;
1135 //             }
1136 //           }
1137         break;
1138 
1139         case UndoOp::DeleteTempo:
1140           if(uo.type == UndoOp::DeleteTempo && uo.a == n_op.a)
1141           {
1142             fprintf(stderr, "MusE error: Undo::insert(): Double DeleteTempo. Ignoring.\n");
1143             return;
1144           }
1145           else if(uo.type == UndoOp::AddTempo && uo.a == n_op.a)
1146           {
1147             // Add followed by delete is useless. Cancel out the add + delete by erasing the add command.
1148             erase(iuo);
1149             return;
1150           }
1151           else if(uo.type == UndoOp::ModifyTempo && uo.a == n_op.a)
1152           {
1153             // Modify followed by delete. Equivalent to delete. Transform existing ModifyTempo operation into a DeleteTempo.
1154             uo.type = UndoOp::DeleteTempo;
1155             // a is already the tick, b is already the existing old value from ModifyTempo.
1156             return;
1157           }
1158 //           else if(uo.type == UndoOp::SetTempo && uo.a == n_op.a)
1159 //           {
1160 //             // Only if the master is on.
1161 //             if(MusEGlobal::tempomap.masterFlag())
1162 //             {
1163 //               // Add followed by delete is useless. Cancel out the add + delete by erasing the add command.
1164 //               erase(iuo);
1165 //               return;
1166 //             }
1167 //           }
1168         break;
1169 
1170         case UndoOp::ModifyTempo:
1171           if(uo.type == UndoOp::ModifyTempo && uo.a == n_op.a)
1172           {
1173             // Simply replace c with the new value.
1174             uo.c = n_op.c;
1175             return;
1176           }
1177           else if(uo.type == UndoOp::AddTempo && uo.a == n_op.a)
1178           {
1179             // Add followed by modify. Simply replace the add value.
1180             uo.b = n_op.c;
1181             return;
1182           }
1183           else if(uo.type == UndoOp::DeleteTempo && uo.a == n_op.a)
1184           {
1185             // Delete followed by modify. Equivalent to modify. Transform existing DeleteTempo operation into a ModifyTempo.
1186             uo.type = UndoOp::ModifyTempo;
1187             // a is already the tick, b is already the existing value from DeleteTempo. c is the new value from ModifyTempo.
1188             uo.c = n_op.c;
1189             return;
1190           }
1191 //           else if(uo.type == UndoOp::SetTempo && uo.a == n_op.a)
1192 //           {
1193 //             // Only if the master is on.
1194 //             if(MusEGlobal::tempomap.masterFlag())
1195 //             {
1196 //               // Add followed by modify. Simply replace the add value.
1197 //               uo.b = n_op.c;
1198 //               return;
1199 //             }
1200 //           }
1201         break;
1202 
1203 //         case UndoOp::SetTempo:
1204 //           if(uo.type == UndoOp::SetTempo && uo.a == n_op.a)
1205 //           {
1206 //             // Simply replace the value.
1207 //             uo.b = n_op.b;
1208 //             return;
1209 //           }
1210 //           else if(uo.type == UndoOp::AddTempo && uo.a == n_op.a)
1211 //           {
1212 //             // Simply replace the value.
1213 //             uo.b = n_op.b;
1214 //             return;
1215 //           }
1216 //           else if(uo.type == UndoOp::DeleteTempo && uo.a == n_op.a)
1217 //           {
1218 //             // Only if the master is on.
1219 //             if(MusEGlobal::tempomap.masterFlag())
1220 //             {
1221 //               // Delete followed by add. Transform the existing DeleteTempo operation into a ModifyTempo.
1222 //               uo.type = UndoOp::ModifyTempo;
1223 //               // a is already the tick, b is already the existing value from DeleteTempo, c is the new value.
1224 //               uo.c = n_op.b;
1225 //               return;
1226 //             }
1227 //           }
1228 //           else if(uo.type == UndoOp::ModifyTempo && uo.a == n_op.a)
1229 //           {
1230 //             // Only if the master is on.
1231 //             if(MusEGlobal::tempomap.masterFlag())
1232 //             {
1233 //               // Modify followed by add. Simply replace the value.
1234 //               // a is already the tick, b is already the existing value from ModifyTempo, c is the new value.
1235 //               uo.c = n_op.b;
1236 //               return;
1237 //             }
1238 //           }
1239 //           else if(uo.type == UndoOp::SetStaticTempo && uo.a == n_op.a)
1240 //           {
1241 //             // Only if the master is not on.
1242 //             if(!MusEGlobal::tempomap.masterFlag())
1243 //             {
1244 //               // Simply replace the value.
1245 //               uo.b = n_op.b;
1246 //               return;
1247 //             }
1248 //           }
1249 //         break;
1250 
1251         case UndoOp::SetStaticTempo:
1252           if(uo.type == UndoOp::SetStaticTempo)
1253           {
1254             // Simply replace a with the new value.
1255             uo.a = n_op.a;
1256             return;
1257           }
1258         break;
1259 
1260         case UndoOp::SetGlobalTempo:
1261           if(uo.type == UndoOp::SetGlobalTempo)
1262           {
1263             // Simply replace a with the new value.
1264             uo.a = n_op.a;
1265             return;
1266           }
1267         break;
1268 
1269         case UndoOp::EnableMasterTrack:
1270           if(uo.type == UndoOp::EnableMasterTrack)
1271           {
1272             if(uo.a == n_op.a)
1273             {
1274               fprintf(stderr, "MusE error: Undo::insert(): Double EnableMasterTrack. Ignoring.\n");
1275               return;
1276             }
1277             else
1278             {
1279               // Toggling is useless. Cancel out the enable or disable + disable or enable by erasing the disable or enable command.
1280               erase(iuo);
1281               return;
1282             }
1283           }
1284         break;
1285 
1286 
1287         case UndoOp::AddSig:
1288           if(uo.type == UndoOp::AddSig && uo.a == n_op.a)
1289           {
1290             // Simply replace the value.
1291             uo.b = n_op.b;
1292             uo.c = n_op.c;
1293             return;
1294           }
1295           else if(uo.type == UndoOp::DeleteSig && uo.a == n_op.a)
1296           {
1297             // Delete followed by add. Transform the existing DeleteSig operation into a ModifySig.
1298             uo.type = UndoOp::ModifySig;
1299             // a is already the tick, b + c is already the existing value from DeleteSig, d + e is the new value.
1300             uo.d = n_op.b;
1301             uo.e = n_op.c;
1302             return;
1303           }
1304           else if(uo.type == UndoOp::ModifySig && uo.a == n_op.a)
1305           {
1306             // Modify followed by add. Simply replace the value.
1307             // a is already the tick, b + c is already the existing value from ModifySig, d + e is the new value.
1308             uo.d = n_op.b;
1309             uo.e = n_op.c;
1310             return;
1311           }
1312         break;
1313 
1314         case UndoOp::DeleteSig:
1315           if(uo.type == UndoOp::DeleteSig && uo.a == n_op.a)
1316           {
1317             fprintf(stderr, "MusE error: Undo::insert(): Double DeleteSig. Ignoring.\n");
1318             return;
1319           }
1320           else if(uo.type == UndoOp::AddSig && uo.a == n_op.a)
1321           {
1322             // Add followed by delete is useless. Cancel out the add + delete by erasing the add command.
1323             erase(iuo);
1324             return;
1325           }
1326           else if(uo.type == UndoOp::ModifySig && uo.a == n_op.a)
1327           {
1328             // Modify followed by delete. Equivalent to delete. Transform existing ModifySig operation into a DeleteSig.
1329             uo.type = UndoOp::DeleteSig;
1330             // a is already the tick, b + c is already the existing old value from ModifySig.
1331             return;
1332           }
1333         break;
1334 
1335         case UndoOp::ModifySig:
1336           if(uo.type == UndoOp::ModifySig && uo.a == n_op.a)
1337           {
1338             // Simply replace d + e with the new value.
1339             uo.d = n_op.d;
1340             uo.e = n_op.e;
1341             return;
1342           }
1343           else if(uo.type == UndoOp::AddSig && uo.a == n_op.a)
1344           {
1345             // Add followed by modify. Simply replace the add value.
1346             uo.b = n_op.d;
1347             uo.c = n_op.e;
1348             return;
1349           }
1350           else if(uo.type == UndoOp::DeleteSig && uo.a == n_op.a)
1351           {
1352             // Delete followed by modify. Equivalent to modify. Transform existing DeleteSig operation into a ModifySig.
1353             uo.type = UndoOp::ModifySig;
1354             // a is already the tick, b + c is already the existing value from DeleteSig. d + e is the new value from ModifySig.
1355             uo.d = n_op.d;
1356             uo.e = n_op.e;
1357             return;
1358           }
1359         break;
1360 
1361 
1362         case UndoOp::AddKey:
1363           if(uo.type == UndoOp::AddKey && uo.a == n_op.a)
1364           {
1365             // Simply replace the value.
1366             uo.b = n_op.b;
1367             uo.c = n_op.c;
1368             return;
1369           }
1370           else if(uo.type == UndoOp::DeleteKey && uo.a == n_op.a)
1371           {
1372             // Delete followed by add. Transform the existing DeleteKey operation into a ModifyKey.
1373             uo.type = UndoOp::ModifyKey;
1374             // a is already the tick, b + c is already the existing value from DeleteKey, d + e is the new value.
1375             uo.d = n_op.b;
1376             uo.e = n_op.c;
1377             return;
1378           }
1379           else if(uo.type == UndoOp::ModifyKey && uo.a == n_op.a)
1380           {
1381             // Modify followed by add. Simply replace the value.
1382             // a is already the tick, b + c is already the existing value from ModifyKey, d + e is the new value.
1383             uo.d = n_op.b;
1384             uo.e = n_op.c;
1385             return;
1386           }
1387         break;
1388 
1389         case UndoOp::DeleteKey:
1390           if(uo.type == UndoOp::DeleteKey && uo.a == n_op.a)
1391           {
1392             fprintf(stderr, "MusE error: Undo::insert(): Double DeleteKey. Ignoring.\n");
1393             return;
1394           }
1395           else if(uo.type == UndoOp::AddKey && uo.a == n_op.a)
1396           {
1397             // Add followed by delete is useless. Cancel out the add + delete by erasing the add command.
1398             erase(iuo);
1399             return;
1400           }
1401           else if(uo.type == UndoOp::ModifyKey && uo.a == n_op.a)
1402           {
1403             // Modify followed by delete. Equivalent to delete. Transform existing ModifyKey operation into a DeleteKey.
1404             uo.type = UndoOp::DeleteKey;
1405             // a is already the tick, b + c is already the existing old value from ModifyKey.
1406             return;
1407           }
1408         break;
1409 
1410         case UndoOp::ModifyKey:
1411           if(uo.type == UndoOp::ModifyKey && uo.a == n_op.a)
1412           {
1413             // Simply replace d + e with the new value.
1414             uo.d = n_op.d;
1415             uo.e = n_op.e;
1416             return;
1417           }
1418           else if(uo.type == UndoOp::AddKey && uo.a == n_op.a)
1419           {
1420             // Add followed by modify. Simply replace the add value.
1421             uo.b = n_op.d;
1422             uo.c = n_op.e;
1423             return;
1424           }
1425           else if(uo.type == UndoOp::DeleteKey && uo.a == n_op.a)
1426           {
1427             // Delete followed by modify. Equivalent to modify. Transform existing DeleteSig operation into a ModifySig.
1428             uo.type = UndoOp::ModifyKey;
1429             // a is already the tick, b + c is already the existing value from DeleteKey. d + e is the new value from ModifyKey.
1430             uo.d = n_op.d;
1431             uo.e = n_op.e;
1432             return;
1433           }
1434         break;
1435 
1436 
1437         case UndoOp::ModifySongLen:
1438           if(uo.type == UndoOp::ModifySongLen)
1439           {
1440             // Simply replace a with the new value.
1441             uo.a = n_op.a;
1442             return;
1443           }
1444         break;
1445 
1446         case UndoOp::ModifyMidiDivision:
1447           if(uo.type == UndoOp::ModifyMidiDivision)
1448           {
1449             // Simply replace a with the new value.
1450             uo.a = n_op.a;
1451             return;
1452           }
1453         break;
1454 
1455         case UndoOp::EnableAllAudioControllers:
1456           if(uo.type == UndoOp::EnableAllAudioControllers)
1457           {
1458             fprintf(stderr, "MusE error: Undo::insert(): Double EnableAllAudioControllers. Ignoring.\n");
1459             return;
1460           }
1461         break;
1462 
1463         case UndoOp::NormalizeMidiDivision:
1464           if(uo.type == UndoOp::NormalizeMidiDivision)
1465           {
1466             fprintf(stderr, "MusE error: Undo::insert(): Double NormalizeMidiDivision. Ignoring.\n");
1467             return;
1468           }
1469         break;
1470 
1471         case UndoOp::GlobalSelectAllEvents:
1472           if(uo.type == UndoOp::GlobalSelectAllEvents)
1473           {
1474             if(uo.a == n_op.a)
1475             {
1476               fprintf(stderr, "MusE error: Undo::insert(): Double GlobalSelectAllEvents. Ignoring.\n");
1477               return;
1478             }
1479             else
1480             {
1481               // Special: Do not 'cancel' out this one. The selecions may need to affect all events.
1482               // Simply replace a with the new value.
1483               uo.a = n_op.a;
1484               return;
1485             }
1486           }
1487         break;
1488 
1489 
1490         case UndoOp::AddMarker:
1491           if(uo.type == UndoOp::AddMarker && uo.newMarker->id() == n_op.newMarker->id())
1492           {
1493             // Done with older operation marker. Be sure to delete it.
1494             delete uo.newMarker;
1495             // Simply replace the existing new marker with the newer marker.
1496             uo.newMarker = n_op.newMarker;
1497             return;
1498           }
1499           else if(uo.type == UndoOp::DeleteMarker && uo.oldMarker->id() == n_op.newMarker->id())
1500           {
1501             // Delete followed by add. Transform the existing DeleteMarker operation into a ModifyMarker.
1502             uo.type = UndoOp::ModifyMarker;
1503             // Move the new marker into the ModifyMarker command's new marker.
1504             // Keep the existing DeleteMarker command's oldMarker.
1505             uo.newMarker = n_op.newMarker;
1506             return;
1507           }
1508         break;
1509 
1510         case UndoOp::DeleteMarker:
1511           if(uo.type == UndoOp::DeleteMarker && uo.oldMarker->id() == n_op.oldMarker->id())
1512           {
1513             // Done with older operation marker. Be sure to delete it.
1514             delete uo.oldMarker;
1515             // Simply replace the existing new marker with the newer marker.
1516             uo.oldMarker = n_op.oldMarker;
1517             return;
1518           }
1519           else if(uo.type == UndoOp::AddMarker && uo.newMarker->id() == n_op.oldMarker->id())
1520           {
1521             // Done with operation markers. Be sure to delete them.
1522             delete uo.oldMarker;
1523             delete n_op.newMarker;
1524             // Add followed by delete is useless. Cancel out the add + delete by erasing the add command.
1525             erase(iuo);
1526             return;
1527           }
1528         break;
1529 
1530         case UndoOp::ModifyMarker:
1531           if(uo.type == UndoOp::ModifyMarker && uo.oldMarker->id() == n_op.oldMarker->id())
1532           {
1533             // Done with these operation markers. Be sure to delete them.
1534             delete uo.newMarker;
1535             delete n_op.oldMarker;
1536             // Simply replace the older operation marker with the newer one.
1537             uo.newMarker = n_op.newMarker;
1538             return;
1539           }
1540         break;
1541 
1542         case UndoOp::SetMarkerPos:
1543           if(uo.type == UndoOp::SetMarkerPos && uo.oldMarker->id() == n_op.oldMarker->id())
1544           {
1545             // Done with these operation markers. Be sure to delete them.
1546             delete uo.newMarker;
1547             delete n_op.oldMarker;
1548             // Simply replace the older operation marker with the newer one.
1549             uo.newMarker = n_op.newMarker;
1550             return;
1551           }
1552         break;
1553 
1554         // NOTE Some other undo op types may need treatment as well !
1555 
1556         default:
1557         break;
1558       }
1559     }
1560   }
1561 
1562   std::list<UndoOp>::insert(position, n_op);
1563 }
1564 
merge_combo(const Undo & other)1565 bool Undo::merge_combo(const Undo& other)
1566 {
1567   if (other.combobreaker)
1568           return false;
1569 
1570   int has_other=0x01;
1571   int has_select_event=0x02;
1572   int has_select_part=0x04;
1573   int has_modify_aud_ctrl_val=0x08;
1574   int has_set_marker_pos=0x10;
1575 
1576   int has = 0;
1577   for (ciUndoOp op=this->begin(); op!=this->end(); op++)
1578           switch(op->type)
1579           {
1580                   case UndoOp::DoNothing: break;
1581                   case UndoOp::SelectEvent: has |= has_select_event; break;
1582                   case UndoOp::SelectPart: has |= has_select_part; break;
1583                   case UndoOp::ModifyAudioCtrlVal: has |= has_modify_aud_ctrl_val; break;
1584                   case UndoOp::SetMarkerPos: has |= has_set_marker_pos; break;
1585                   default: has |= has_other; break;
1586           }
1587 
1588   for (ciUndoOp op=other.begin(); op!=other.end(); op++)
1589           switch(op->type)
1590           {
1591                   case UndoOp::DoNothing: break;
1592                   case UndoOp::SelectEvent: has |= has_select_event; break;
1593                   case UndoOp::SelectPart: has |= has_select_part; break;
1594                   case UndoOp::ModifyAudioCtrlVal: has |= has_modify_aud_ctrl_val; break;
1595                   case UndoOp::SetMarkerPos: has |= has_set_marker_pos; break;
1596                   default: has |= has_other; break;
1597           }
1598 
1599   bool mergeable =
1600     (has == has_select_event || has == has_select_part ||
1601      has == has_modify_aud_ctrl_val || has == has_set_marker_pos);
1602 
1603   if (mergeable)
1604           this->insert(this->end(), other.begin(), other.end());
1605 
1606   return mergeable;
1607 }
1608 
applyOperation(const UndoOp & op,OperationType type,void * sender)1609 bool Song::applyOperation(const UndoOp& op, OperationType type, void* sender)
1610 {
1611 	Undo operations;
1612 	operations.push_back(op);
1613 	return applyOperationGroup(operations, type, sender);
1614 }
1615 
applyOperationGroup(Undo & group,OperationType type,void * sender)1616 bool Song::applyOperationGroup(Undo& group, OperationType type, void* sender)
1617 {
1618   bool ret = false;
1619   if (!group.empty())
1620   {
1621     // We don't use this here in applyOperationGroup or its call sequence.
1622     undoMode = false;
1623 
1624     switch(type)
1625     {
1626       case OperationExecute:
1627       case OperationUndoable:
1628       break;
1629 
1630       case OperationExecuteUpdate:
1631       case OperationUndoableUpdate:
1632       case OperationUndoMode:
1633           // Clear the updateFlags and set sender.
1634           updateFlags = SongChangedStruct_t(0, 0, sender);
1635       break;
1636     }
1637 
1638     // Execute the given operations. This can add or remove operations in the group.
1639     MusEGlobal::audio->msgExecuteOperationGroup(group);
1640 
1641     // Check whether there are actually any undoable operations in the group.
1642     // There shouldn't be any non-undoables left in the list, they are removed at execution,
1643     //  but we'll double check here which also checks list emptiness.
1644     bool has_undoables = false;
1645     for(ciUndoOp iu = group.cbegin(); iu != group.cend(); ++iu) {
1646       if(!iu->_noUndo) {
1647         has_undoables = true;
1648         break;
1649       }
1650     }
1651 
1652     switch(type)
1653     {
1654       case OperationExecute:
1655       case OperationExecuteUpdate:
1656       break;
1657 
1658       case OperationUndoMode:
1659         // NOTE: If there are only non-undoables, there is NOTHING to redo (or undo).
1660         //       Prevent one-time non-undoable operations from wiping out the redo list!
1661         if(has_undoables) {
1662           // The following does the same as startUndo but without clearing the updateFlags:
1663           // redo must be invalidated when a new undo is started
1664           redoList->clearDelete();
1665           MusEGlobal::redoAction->setEnabled(false);
1666           setUndoRedoText();
1667           undoList->push_back(Undo());
1668         }
1669       // FALLTHROUGH
1670       case OperationUndoable:
1671       case OperationUndoableUpdate:
1672         // append all elements from "group" to the end of undoList->back().
1673         // Only if there are undoable items.
1674         if(has_undoables && !undoList->empty())
1675         {
1676           Undo& curUndo = undoList->back();
1677           curUndo.insert(curUndo.end(), group.begin(), group.end());
1678           if (group.combobreaker)
1679             curUndo.combobreaker=true;
1680         }
1681       break;
1682     }
1683 
1684     switch(type)
1685     {
1686       case OperationExecute:
1687       case OperationUndoable:
1688       break;
1689 
1690       case OperationExecuteUpdate:
1691       case OperationUndoableUpdate:
1692         emit songChanged(updateFlags);
1693       break;
1694 
1695       case OperationUndoMode:
1696         if(has_undoables) {
1697           // Also emits songChanged and resets undoMode.
1698           endUndo(0);
1699           ret = true;
1700         }
1701         else {
1702           emit songChanged(updateFlags);
1703         }
1704       break;
1705     }
1706   }
1707 
1708   return ret;
1709 }
1710 
1711 //---------------------------------------------------------
1712 //   revertOperationGroup2
1713 //    real time part
1714 //---------------------------------------------------------
1715 
revertOperationGroup2(Undo &)1716 void Song::revertOperationGroup2(Undo& /*operations*/)
1717       {
1718         pendingOperations.executeRTStage();
1719 
1720         // Special for tempo: Need to normalize the tempo list, and resync audio.
1721         // To save time this is done here, not item by item.
1722         // Normalize is not needed for SC_MASTER.
1723         if(updateFlags & (SC_TEMPO | SC_DIVISION_CHANGED))
1724           MusEGlobal::tempomap.normalize();
1725         if(updateFlags & (SC_TEMPO | SC_MASTER | SC_DIVISION_CHANGED))
1726         {
1727           MusEGlobal::audio->reSyncAudio();
1728           // Must rebuild the marker list in case any markers are 'locked'.
1729           if(marker()->rebuild())
1730             updateFlags |= SC_MARKERS_REBUILT;
1731         }
1732 
1733         // Special for sig: Need to normalize the signature list.
1734         // To save time this is done here, not item by item.
1735         if(updateFlags & (SC_SIG | SC_DIVISION_CHANGED))
1736           MusEGlobal::sigmap.normalize();
1737 
1738         // Special for track inserted: If it's an aux track, need to add missing aux sends to all tracks,
1739         //  else if it's another audio track need to add aux sends to it.
1740         // To save from complexity this is done here, after all the operations.
1741         if(updateFlags & SC_TRACK_INSERTED)
1742         {
1743           int n = _auxs.size();
1744           for(iTrack i = _tracks.begin(); i != _tracks.end(); ++i)
1745           {
1746             if((*i)->isMidiTrack())
1747               continue;
1748             MusECore::AudioTrack* at = static_cast<AudioTrack*>(*i);
1749             if(at->hasAuxSend())
1750               at->addAuxSend(n);
1751           }
1752         }
1753       }
1754 
1755 //---------------------------------------------------------
1756 //   Song::executeOperationGroup2
1757 //---------------------------------------------------------
1758 
executeOperationGroup2(Undo &)1759 void Song::executeOperationGroup2(Undo& /*operations*/)
1760       {
1761         pendingOperations.executeRTStage();
1762 
1763         // Special for tempo if altered: Need to normalize the tempo list, and resync audio.
1764         // To save time this is done here, not item by item.
1765         // Normalize is not needed for SC_MASTER.
1766         if(updateFlags & (SC_TEMPO | SC_DIVISION_CHANGED))
1767           MusEGlobal::tempomap.normalize();
1768         if(updateFlags & (SC_TEMPO | SC_MASTER | SC_DIVISION_CHANGED))
1769         {
1770           MusEGlobal::audio->reSyncAudio();
1771           // Must rebuild the marker list in case any markers are 'locked'.
1772           if(marker()->rebuild())
1773             updateFlags |= SC_MARKERS_REBUILT;
1774         }
1775 
1776         // Special for sig: Need to normalize the signature list.
1777         // To save time this is done here, not item by item.
1778         if(updateFlags & (SC_SIG | SC_DIVISION_CHANGED))
1779           MusEGlobal::sigmap.normalize();
1780 
1781         // Special for track inserted: If it's an aux track, need to add missing aux sends to all tracks,
1782         //  else if it's another audio track need to add aux sends to it.
1783         // To save from complexity this is done here, after all the operations.
1784         if(updateFlags & SC_TRACK_INSERTED)
1785         {
1786           int n = _auxs.size();
1787           for(iTrack i = _tracks.begin(); i != _tracks.end(); ++i)
1788           {
1789             if((*i)->isMidiTrack())
1790               continue;
1791             MusECore::AudioTrack* at = static_cast<AudioTrack*>(*i);
1792             if(at->hasAuxSend())
1793               at->addAuxSend(n);
1794           }
1795         }
1796       }
1797 
UndoOp()1798 UndoOp::UndoOp()
1799 {
1800   type=UndoOp::DoNothing;
1801   _noUndo = true;
1802 }
1803 
UndoOp(UndoType type_,int a_,int b_,int c_,bool noUndo)1804 UndoOp::UndoOp(UndoType type_, int a_, int b_, int c_, bool noUndo)
1805       {
1806       assert(type_==AddKey || type_==DeleteKey || type_== ModifyKey ||
1807              type_==AddTempo || type_==DeleteTempo || type_==ModifyTempo ||
1808              type_==SetTempo || type_==SetStaticTempo || type_==SetGlobalTempo || type_==EnableMasterTrack ||
1809              type_==AddSig || type_==DeleteSig ||
1810              type_==ModifySongLen || type_==MoveTrack ||
1811              type_==GlobalSelectAllEvents || type_==ModifyMidiDivision);
1812 
1813       type = type_;
1814       a  = a_;
1815       b  = b_;
1816       c  = c_;
1817       _noUndo = noUndo;
1818 
1819       switch(type)
1820       {
1821         case UndoOp::SetGlobalTempo:
1822           // a is already the new tempo, b is the existing tempo.
1823           b = MusEGlobal::tempomap.globalTempo();
1824         break;
1825 
1826         case UndoOp::EnableMasterTrack:
1827           // a is already the new master flag, b is the existing master flag.
1828           b = MusEGlobal::tempomap.masterFlag();
1829         break;
1830 
1831         case UndoOp::ModifyMidiDivision:
1832           // a is already the new division, b is the existing division.
1833           b = MusEGlobal::config.division;
1834         break;
1835 
1836         // For these operations, we must check if a value already exists and transform them into modify operations...
1837         case UndoOp::AddTempo:
1838         {
1839           int t = a;
1840           if(t > MAX_TICK)
1841             t = MAX_TICK;
1842           iTEvent ite = MusEGlobal::tempomap.upper_bound(t);
1843           if((int)ite->second->tick == t)
1844           {
1845             // Transform the AddTempo operation into a ModifyTempo.
1846             // a is already the tick, b is the existing value, c is the new value.
1847             type = UndoOp::ModifyTempo;
1848             c = b;
1849             b = ite->second->tempo;
1850           }
1851         }
1852         break;
1853 
1854         case UndoOp::SetTempo:
1855         {
1856           // Only if the master is on.
1857           if(MusEGlobal::tempomap.masterFlag())
1858           {
1859             int t = a;
1860             if(t > MAX_TICK)
1861               t = MAX_TICK;
1862             iTEvent ite = MusEGlobal::tempomap.upper_bound(t);
1863             if((int)ite->second->tick == t)
1864             {
1865               // Transform the SetTempo operation into a ModifyTempo.
1866               // a is already the tick, b is the existing value, c is the new value.
1867               type = UndoOp::ModifyTempo;
1868               c = b;
1869               b = ite->second->tempo;
1870             }
1871             else
1872             {
1873               // Transform the SetTempo operation into an AddTempo.
1874               type = UndoOp::AddTempo;
1875             }
1876           }
1877           else
1878           {
1879             // a is the new tempo, b is the existing tempo.
1880             a = b;
1881             b = MusEGlobal::tempomap.staticTempo();
1882             // Transform the SetTempo operation into a SetStaticTempo.
1883             type = UndoOp::SetStaticTempo;
1884           }
1885         }
1886         break;
1887 
1888         case UndoOp::SetStaticTempo:
1889           // a is already the new tempo, b is the existing tempo.
1890           b = MusEGlobal::tempomap.staticTempo();
1891         break;
1892 
1893         case UndoOp::AddSig:
1894         {
1895           //if(t > MAX_TICK)
1896           //  t = MAX_TICK;
1897 
1898           // Must rasterize the tick value HERE instead of in SigMap::addOperation(),
1899           //  so that the rasterized value is recorded in the undo item.
1900           a = MusEGlobal::sigmap.raster1(a, 0);
1901 
1902           MusECore::iSigEvent ise = MusEGlobal::sigmap.upper_bound(a);
1903           if((int)ise->second->tick == a)
1904           {
1905             // Transform the AddSig operation into a ModifySig.
1906             // a is already the tick, b + c is the existing value, d + e is the new value.
1907             type = UndoOp::ModifySig;
1908             d = b;
1909             e = c;
1910             b = ise->second->sig.z;
1911             c = ise->second->sig.n;
1912           }
1913         }
1914         break;
1915 
1916         case UndoOp::AddKey:
1917         {
1918           int t = a;
1919           if(t > MAX_TICK)
1920             t = MAX_TICK;
1921           iKeyEvent ike = MusEGlobal::keymap.upper_bound(t);
1922           if((int)ike->second.tick == t)
1923           {
1924             // Transform the AddKey operation into a ModifyKey.
1925             // a is already the tick, b + c is the existing value, d + e is the new value.
1926             type = UndoOp::ModifyKey;
1927             d = b;
1928             e = c;
1929             b = ike->second.key;
1930             c = ike->second.minor;
1931           }
1932         }
1933         break;
1934 
1935         default:
1936         break;
1937       }
1938 
1939       }
1940 
UndoOp(UndoType type_,int tick,const MusECore::TimeSignature old_sig,const MusECore::TimeSignature new_sig,bool noUndo)1941 UndoOp::UndoOp(UndoType type_, int tick, const MusECore::TimeSignature old_sig, const MusECore::TimeSignature new_sig, bool noUndo)
1942 {
1943       assert(type_==ModifySig);
1944       type    = type_;
1945       a  = tick;
1946       b  = old_sig.z;
1947       c  = old_sig.n;
1948       d  = new_sig.z;
1949       e  = new_sig.n;
1950       _noUndo = noUndo;
1951 }
1952 
UndoOp(UndoType type_,int n,const Track * track_,bool noUndo)1953 UndoOp::UndoOp(UndoType type_, int n, const Track* track_, bool noUndo)
1954       {
1955       assert(type_==AddTrack || type_==DeleteTrack);
1956       assert(track_);
1957 
1958       type    = type_;
1959       trackno = n;
1960       track  = track_;
1961       _noUndo = noUndo;
1962       }
1963 
UndoOp(UndoType type_,const Track * track_,bool value,bool noUndo)1964 UndoOp::UndoOp(UndoType type_, const Track* track_, bool value, bool noUndo)
1965       {
1966       assert(type_ == SetTrackRecord || type_ == SetTrackMute || type_ == SetTrackSolo ||
1967              type_ == SetTrackRecMonitor || type_ == SetTrackOff);
1968       assert(track_);
1969 
1970       type    = type_;
1971       track  = track_;
1972       a = value;
1973       _noUndo = noUndo;
1974       }
1975 
UndoOp(UndoType type_,const Part * part_,bool noUndo)1976 UndoOp::UndoOp(UndoType type_, const Part* part_, bool noUndo)
1977       {
1978       assert(type_==AddPart || type_==DeletePart);
1979       assert(part_);
1980 
1981       type  = type_;
1982       part = part_;
1983       _noUndo = noUndo;
1984       }
1985 
UndoOp(UndoType type_,const Part * part_,bool selected_,bool sel_old_,bool noUndo)1986 UndoOp::UndoOp(UndoType type_, const Part* part_, bool selected_, bool sel_old_, bool noUndo)
1987 {
1988     assert(type_==SelectPart);
1989     assert(part_);
1990 
1991     type=type_;
1992     part = part_;
1993     selected=selected_;
1994     selected_old=sel_old_;
1995     _noUndo = noUndo;
1996 }
1997 
UndoOp(UndoType type_,const Part * part_,unsigned int old_len_or_pos,unsigned int new_len_or_pos,Pos::TType new_time_type_,const Track * oTrack,const Track * nTrack,bool noUndo)1998 UndoOp::UndoOp(UndoType type_, const Part* part_, unsigned int old_len_or_pos, unsigned int new_len_or_pos,
1999                Pos::TType new_time_type_, const Track* oTrack, const Track* nTrack, bool noUndo)
2000 {
2001     assert(type_== MovePart);
2002     assert(part_);
2003 
2004     type = type_;
2005     part = part_;
2006     _noUndo = noUndo;
2007     track = nTrack;
2008     oldTrack = oTrack;
2009     // Make sure both tracks exist.
2010     if(!track && !oldTrack)
2011       track = oldTrack = part->track();
2012     else if(!oldTrack)
2013       oldTrack = track;
2014     else if(!track)
2015       track = oldTrack;
2016     assert(oldTrack);
2017     assert(track);
2018     old_partlen_or_pos = old_len_or_pos;
2019     new_partlen_or_pos = new_len_or_pos;
2020     switch(part->type())
2021     {
2022       case Pos::FRAMES:
2023         switch(new_time_type_)
2024         {
2025           case Pos::FRAMES:
2026           break;
2027 
2028           case Pos::TICKS:
2029             new_partlen_or_pos = MusEGlobal::tempomap.tick2frame(new_partlen_or_pos);
2030           break;
2031         }
2032       break;
2033 
2034       case Pos::TICKS:
2035         switch(new_time_type_)
2036         {
2037           case Pos::FRAMES:
2038             new_partlen_or_pos = MusEGlobal::tempomap.frame2tick(new_partlen_or_pos);
2039           break;
2040 
2041           case Pos::TICKS:
2042           break;
2043         }
2044       break;
2045     }
2046 }
2047 
2048 
UndoOp(UndoType type_,const Part * part_,unsigned int old_pos,unsigned int new_pos,unsigned int old_len,unsigned int new_len,int64_t events_offset_,Pos::TType new_time_type_,bool noUndo)2049 UndoOp::UndoOp(UndoType type_, const Part* part_, unsigned int old_pos, unsigned int new_pos, unsigned int old_len, unsigned int new_len,
2050                int64_t events_offset_, Pos::TType new_time_type_, bool noUndo)
2051 {
2052     assert(type_ == ModifyPartStart);
2053     assert(part_);
2054 
2055     type = type_;
2056     part = part_;
2057     _noUndo = noUndo;
2058     events_offset = events_offset_;
2059     events_offset_time_type = new_time_type_;
2060     old_partlen_or_pos = old_pos;
2061     new_partlen_or_pos = new_pos;
2062     old_partlen = old_len;
2063     new_partlen = new_len;
2064 }
2065 
UndoOp(UndoType type_,const Part * part_,unsigned int old_len,unsigned int new_len,int64_t events_offset_,Pos::TType new_time_type_,bool noUndo)2066 UndoOp::UndoOp(UndoType type_, const Part* part_, unsigned int old_len, unsigned int new_len,
2067                int64_t events_offset_, Pos::TType new_time_type_, bool noUndo)
2068 {
2069     assert(type_== ModifyPartLength);
2070     assert(part_);
2071 
2072     type = type_;
2073     part = part_;
2074     _noUndo = noUndo;
2075     events_offset = events_offset_;
2076     events_offset_time_type = new_time_type_;
2077     old_partlen_or_pos = old_len;
2078     new_partlen_or_pos = new_len;
2079 }
2080 
UndoOp(UndoType type_,const Event & nev,const Event & oev,const Part * part_,bool doCtrls_,bool doClones_,bool noUndo)2081 UndoOp::UndoOp(UndoType type_, const Event& nev, const Event& oev, const Part* part_, bool doCtrls_, bool doClones_, bool noUndo)
2082       {
2083       assert(type_==ModifyEvent);
2084       assert(part_);
2085 
2086       type   = type_;
2087       nEvent = nev;
2088       oEvent = oev;
2089       part   = part_;
2090       doCtrls = doCtrls_;
2091       doClones = doClones_;
2092       _noUndo = noUndo;
2093       }
2094 
UndoOp(UndoType type_,const Event & nev,const Part * part_,bool a_,bool b_,bool noUndo)2095 UndoOp::UndoOp(UndoType type_, const Event& nev, const Part* part_, bool a_, bool b_, bool noUndo)
2096       {
2097       assert(type_==DeleteEvent || type_==AddEvent || type_==SelectEvent);
2098       assert(part_);
2099 
2100       type   = type_;
2101       nEvent = nev;
2102       part   = part_;
2103       _noUndo = noUndo;
2104       if(type_==SelectEvent)
2105       {
2106         selected = a_;
2107         selected_old = b_;
2108       }
2109       else
2110       {
2111         doCtrls = a_;
2112         doClones = b_;
2113       }
2114       }
2115 
UndoOp(UndoType type_,const Marker & oldMarker_,const Marker & newMarker_,bool noUndo)2116 UndoOp::UndoOp(UndoType type_, const Marker& oldMarker_, const Marker& newMarker_, bool noUndo)
2117       {
2118       assert(type_==ModifyMarker);
2119       type    = type_;
2120       oldMarker  = new Marker(oldMarker_);
2121       newMarker = new Marker(newMarker_);
2122       _noUndo = noUndo;
2123       }
2124 
UndoOp(UndoType type_,const Marker & marker_,bool noUndo)2125 UndoOp::UndoOp(UndoType type_, const Marker& marker_, bool noUndo)
2126       {
2127       assert(type_==AddMarker || type_==DeleteMarker);
2128       type    = type_;
2129       oldMarker = newMarker = nullptr;
2130       Marker** mp = nullptr;
2131       if(type_== AddMarker)
2132         mp = &newMarker;
2133       else
2134         mp = &oldMarker;
2135       *mp = new Marker(marker_);
2136       _noUndo = noUndo;
2137       }
2138 
UndoOp(UndoType type_,const Marker & marker_,unsigned int new_pos,Pos::TType new_time_type,bool noUndo)2139 UndoOp::UndoOp(UndoType type_, const Marker& marker_, unsigned int new_pos, Pos::TType new_time_type, bool noUndo)
2140       {
2141       assert(type_==SetMarkerPos);
2142       type    = type_;
2143       oldMarker = new Marker(marker_);
2144       newMarker = new Marker(marker_);
2145       newMarker->setPosValue(new_pos, new_time_type);
2146       _noUndo = noUndo;
2147       }
2148 
2149 // UndoOp::UndoOp(UndoType type_, MarkerList** oldMarkerList_, MarkerList* newMarkerList_, bool noUndo)
2150 //       {
2151 //       assert(type_==ModifyMarkerList);
2152 //       assert(oldMarkerList);
2153 //       assert(newMarkerList);
2154 //       type    = type_;
2155 //       oldMarkerList = oldMarkerList_;
2156 //       newMarkerList = newMarkerList_;
2157 //       _noUndo = noUndo;
2158 //       }
2159 
UndoOp(UndoType type_,const Event & changedEvent,const QString & changeData,int startframe_,int endframe_,bool noUndo)2160 UndoOp::UndoOp(UndoType type_, const Event& changedEvent, const QString& changeData, int startframe_, int endframe_, bool noUndo)
2161       {
2162       assert(type_==ModifyClip);
2163 
2164       type = type_;
2165       _noUndo = noUndo;
2166       //filename   = new QString(changedFile);
2167       nEvent = changedEvent;
2168       tmpwavfile = new QString(changeData);
2169       startframe = startframe_;
2170       endframe   = endframe_;
2171       }
2172 
UndoOp(UndoOp::UndoType type_,const Part * part_,const QString & old_name,const QString & new_name,bool noUndo)2173 UndoOp::UndoOp(UndoOp::UndoType type_, const Part* part_, const QString& old_name, const QString& new_name, bool noUndo)
2174 {
2175     assert(type_==ModifyPartName);
2176     assert(part_);
2177 
2178     type=type_;
2179     part=part_;
2180     _noUndo = noUndo;
2181     _oldName = new QString(old_name);
2182     _newName = new QString(new_name);
2183 }
2184 
UndoOp(UndoOp::UndoType type_,const Track * track_,const QString & old_name,const QString & new_name,bool noUndo)2185 UndoOp::UndoOp(UndoOp::UndoType type_, const Track* track_, const QString& old_name, const QString& new_name, bool noUndo)
2186 {
2187   assert(type_==ModifyTrackName);
2188   assert(track_);
2189 
2190   type = type_;
2191   track = track_;
2192   _noUndo = noUndo;
2193   _oldName = new QString(old_name);
2194   _newName = new QString(new_name);
2195 }
2196 
UndoOp(UndoOp::UndoType type_,const Track * track_,int oldChanOrCtrlID,int newChanOrCtrlFrame,bool noUndo)2197 UndoOp::UndoOp(UndoOp::UndoType type_, const Track* track_, int oldChanOrCtrlID, int newChanOrCtrlFrame, bool noUndo)
2198 {
2199   assert(type_ == ModifyTrackChannel || type_ == DeleteAudioCtrlVal);
2200   assert(track_);
2201 
2202   type = type_;
2203   track = track_;
2204 
2205   if(type_ == ModifyTrackChannel)
2206   {
2207     _propertyTrack = track_;
2208     _oldPropValue = oldChanOrCtrlID;
2209     _newPropValue = newChanOrCtrlFrame;
2210   }
2211   else
2212   {
2213     _audioCtrlID = oldChanOrCtrlID;
2214     _audioCtrlFrame = newChanOrCtrlFrame;
2215   }
2216   _noUndo = noUndo;
2217 }
2218 
UndoOp(UndoType type_,const Track * track_,int ctrlID,int frame,double value,bool noUndo)2219 UndoOp::UndoOp(UndoType type_, const Track* track_, int ctrlID, int frame, double value, bool noUndo)
2220 {
2221   assert(type_== AddAudioCtrlVal);
2222   assert(track_);
2223 
2224   type = type_;
2225   track = track_;
2226   _audioCtrlID = ctrlID;
2227   _audioCtrlFrame = frame;
2228   _audioCtrlVal = value;
2229   _noUndo = noUndo;
2230 }
2231 
UndoOp(UndoType type_,const Track * track_,int ctrlID,int oldFrame,int newFrame,double oldValue,double newValue,bool noUndo)2232 UndoOp::UndoOp(UndoType type_, const Track* track_, int ctrlID, int oldFrame, int newFrame, double oldValue, double newValue, bool noUndo)
2233 {
2234   assert(type_== ModifyAudioCtrlVal);
2235   assert(track_);
2236 
2237   type = type_;
2238   track = track_;
2239   _audioCtrlID = ctrlID;
2240   _audioCtrlFrame = oldFrame;
2241   _audioNewCtrlFrame = newFrame;
2242   _audioCtrlVal = oldValue;
2243   _audioNewCtrlVal = newValue;
2244   _noUndo = noUndo;
2245 }
2246 
UndoOp(UndoOp::UndoType type_,CtrlListList * ctrl_ll,CtrlList * eraseCtrlList,CtrlList * addCtrlList,bool noUndo)2247 UndoOp::UndoOp(UndoOp::UndoType type_, CtrlListList* ctrl_ll, CtrlList* eraseCtrlList, CtrlList* addCtrlList, bool noUndo)
2248 {
2249   assert(type_== ModifyAudioCtrlValList);
2250   assert(ctrl_ll);
2251   //assert(eraseCtrlList);
2252   //assert(addCtrlList);
2253   assert(eraseCtrlList || addCtrlList);
2254 
2255   type = type_;
2256   _ctrlListList = ctrl_ll;
2257   _eraseCtrlList = eraseCtrlList;
2258   _addCtrlList = addCtrlList;
2259   _noUndo = noUndo;
2260 }
2261 
UndoOp(UndoType type_,MidiPort * mp,MidiInstrument * instr,bool noUndo)2262 UndoOp::UndoOp(UndoType type_, MidiPort* mp, MidiInstrument* instr, bool noUndo)
2263 {
2264   assert(type_== SetInstrument);
2265   assert(mp);
2266   assert(instr);
2267   type = type_;
2268   _midiPort = mp;
2269   _oldMidiInstrument = _midiPort->instrument();
2270   _newMidiInstrument = instr;
2271   _noUndo = noUndo;
2272 }
2273 
UndoOp(UndoOp::UndoType type_)2274 UndoOp::UndoOp(UndoOp::UndoType type_)
2275 {
2276   assert(type_== EnableAllAudioControllers || type_ == NormalizeMidiDivision);
2277 
2278   type = type_;
2279   // Cannot be undone. 'One-time' operation only, removed after execution.
2280   _noUndo = true;
2281 }
2282 
2283 #pragma GCC diagnostic push
2284 #pragma GCC diagnostic ignored "-Wunused-parameter"
UndoOp(UndoOp::UndoType type_,const Route & route_from_,const Route & route_to_,bool noUndo)2285 UndoOp::UndoOp(UndoOp::UndoType type_, const Route& route_from_, const Route& route_to_, bool noUndo)
2286       {
2287       assert(type_ == AddRoute || type_ == DeleteRoute);
2288       _noUndo = noUndo;
2289       routeFrom = route_from_;
2290       routeTo = route_to_;
2291       }
2292 #pragma GCC diagnostic pop
2293 
2294 //---------------------------------------------------------
2295 //   addUndo
2296 //---------------------------------------------------------
2297 
addUndo(UndoOp i)2298 void Song::addUndo(UndoOp i)
2299       {
2300       if (!undoMode) {
2301             printf("internal error: undoOp without startUndo()\n");
2302             return;
2303             }
2304       undoList->back().push_back(i);
2305       emit sigDirty();
2306       }
2307 
2308 //---------------------------------------------------------
2309 //   revertOperationGroup1
2310 //    non realtime context
2311 //    return true if nothing to do
2312 //---------------------------------------------------------
2313 
revertOperationGroup1(Undo & operations)2314 void Song::revertOperationGroup1(Undo& operations)
2315       {
2316       MarkerList* new_marker_list = nullptr;
2317       TempoList* new_tempo_list = nullptr;
2318       SigList* new_sig_list = nullptr;
2319       KeyList* new_key_list = nullptr;
2320 
2321       for (riUndoOp i = operations.rbegin(); i != operations.rend(); ++i) {
2322             Track* editable_track = const_cast<Track*>(i->track);
2323             Track* editable_property_track = const_cast<Track*>(i->_propertyTrack);
2324             Part* editable_part = const_cast<Part*>(i->part);
2325             switch(i->type) {
2326                   case UndoOp::SelectPart:
2327                         pendingOperations.add(PendingOperationItem(editable_part, i->selected_old, PendingOperationItem::SelectPart));
2328                         updateFlags |= SC_PART_SELECTION;
2329                         break;
2330                   case UndoOp::SelectEvent:
2331                         pendingOperations.add(PendingOperationItem(editable_part, i->nEvent, i->selected_old, PendingOperationItem::SelectEvent));
2332                         updateFlags |= SC_SELECTION;
2333                         break;
2334 
2335                   case UndoOp::AddTrack:
2336                         switch(editable_track->type())
2337                         {
2338                           case Track::AUDIO_SOFTSYNTH:
2339                           {
2340                             SynthI* si = (SynthI*)editable_track;
2341                             if(si->hasGui())
2342                               si->showGui(false);
2343                             if(si->hasNativeGui())
2344                               si->showNativeGui(false);
2345                           }// Fall through.
2346                           case Track::WAVE:
2347                           case Track::AUDIO_OUTPUT:
2348                           case Track::AUDIO_INPUT:
2349                           case Track::AUDIO_GROUP:
2350                           case Track::AUDIO_AUX:
2351                             ((AudioTrack*)editable_track)->deleteAllEfxGuis();
2352                             updateFlags |= SC_RACK;
2353                           break;
2354 
2355                           default:
2356                           break;
2357                         }
2358 
2359                         switch(editable_track->type())
2360                         {
2361                           case Track::AUDIO_OUTPUT:
2362                           {
2363                             AudioOutput* ao = (AudioOutput*)editable_track;
2364                             for(int ch = 0; ch < ao->channels(); ++ch)
2365                             {
2366                               MusEGlobal::audioDevice->unregisterPort(ao->jackPort(ch));
2367                               //ao->setJackPort(ch, 0);  // Done in RT stage.
2368                               updateFlags |= SC_ROUTE;
2369                             }
2370                           }
2371                           break;
2372 
2373                           case Track::AUDIO_INPUT:
2374                           {
2375                             AudioOutput* ai = (AudioOutput*)editable_track;
2376                             for(int ch = 0; ch < ai->channels(); ++ch)
2377                             {
2378                               MusEGlobal::audioDevice->unregisterPort(ai->jackPort(ch));
2379                               //ai->setJackPort(ch, 0); // Done in RT stage.
2380                               updateFlags |= SC_ROUTE;
2381                             }
2382                           }
2383                           break;
2384 
2385                           case Track::AUDIO_AUX:
2386                             updateFlags |= SC_AUX;
2387                           break;
2388 
2389                           default:
2390                           break;
2391                         }
2392                         removeTrackOperation(editable_track, pendingOperations);
2393                         updateFlags |= SC_TRACK_REMOVED;
2394                         break;
2395 
2396                   case UndoOp::DeleteTrack:
2397                         switch(editable_track->type())
2398                         {
2399                           case Track::AUDIO_SOFTSYNTH:
2400                           {
2401                             SynthI* s = (SynthI*)editable_track;
2402                             Synth* sy = s->synth();
2403                             if(!s->isActivated())
2404                               s->initInstance(sy, s->name());
2405                             // FIXME TODO: We want to restore any ports using this instrument via the undo
2406                             //  system but ATM a few other things can set the instrument without an undo
2407                             //  operation so the undo sequence would not be correct. So we don't have much
2408                             //  choice but to just reset inside the PendingOperation::DeleteTrack operation
2409                             //  for now when deleting a synth track.
2410                             // Still, everything else is in place for undoable setting of instrument...
2411                           }
2412                           break;
2413 
2414                           case Track::AUDIO_OUTPUT:
2415                           {
2416                             AudioOutput* ao = (AudioOutput*)editable_track;
2417                             if(MusEGlobal::checkAudioDevice())
2418                             {
2419                               for(int ch = 0; ch < ao->channels(); ++ch)
2420                               {
2421                                 // This should be OK since the track has not yet been added in the realtime stage.
2422                                 if(ao->registerPorts(ch))
2423                                   updateFlags |= SC_ROUTE;
2424 
2425                                 // Set the route Jack ports now to relieve our graph callback handler from having to do it.
2426                                 RouteList* rl = ao->outRoutes();
2427                                 for(iRoute ir = rl->begin(); ir != rl->end(); ++ir)
2428                                   if(ir->type == Route::JACK_ROUTE && ir->channel == ch)
2429                                   {
2430                                     ir->jackPort = MusEGlobal::audioDevice->findPort(ir->persistentJackPortName);
2431                                     updateFlags |= SC_ROUTE;
2432                                   }
2433                               }
2434                             }
2435                           }
2436                           break;
2437 
2438                           case Track::AUDIO_INPUT:
2439                           {
2440                             AudioInput* ai = (AudioInput*)editable_track;
2441                             if(MusEGlobal::checkAudioDevice())
2442                             {
2443                               for(int ch = 0; ch < ai->channels(); ++ch)
2444                               {
2445                                 // This should be OK since the track has not yet been added in the realtime stage.
2446                                 if(ai->registerPorts(ch))
2447                                   updateFlags |= SC_ROUTE;
2448 
2449                                 // Set the route Jack ports now to relieve our graph callback handler from having to do it.
2450                                 RouteList* rl = ai->inRoutes();
2451                                 for(iRoute ir = rl->begin(); ir != rl->end(); ++ir)
2452                                   if(ir->type == Route::JACK_ROUTE && ir->channel == ch)
2453                                   {
2454                                     ir->jackPort = MusEGlobal::audioDevice->findPort(ir->persistentJackPortName);
2455                                     updateFlags |= SC_ROUTE;
2456                                   }
2457                               }
2458                             }
2459                           }
2460                           break;
2461 
2462                           case Track::AUDIO_AUX:
2463                             updateFlags |= SC_AUX;
2464                           break;
2465 
2466                           default:
2467                           break;
2468                         }
2469 
2470                         // Ensure that wave event sndfile file handles are opened.
2471                         // It should not be the job of the pending operations list to do this.
2472                         // TODO Coordinate close/open with part mute and/or track off.
2473                         editable_track->openAllParts();
2474 
2475                         insertTrackOperation(editable_track, i->trackno, pendingOperations);
2476                         updateFlags |= SC_TRACK_INSERTED;
2477                         break;
2478 
2479                   case UndoOp::ModifyClip:
2480                         sndFileApplyUndoFile(i->nEvent, i->tmpwavfile, i->startframe, i->endframe);
2481                         updateFlags |= SC_CLIP_MODIFIED;
2482                         break;
2483                   case UndoOp::ModifyTrackChannel:
2484                         if (editable_property_track->isMidiTrack())
2485                         {
2486                           MusECore::MidiTrack* mt = static_cast<MusECore::MidiTrack*>(editable_property_track);
2487                           if (i->_oldPropValue != mt->outChannel())
2488                           {
2489                                 MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged;
2490                                 MusEGlobal::audio->msgIdle(true);
2491                                 changed |= mt->setOutChanAndUpdate(i->_oldPropValue, false);
2492                                 MusEGlobal::audio->msgIdle(false);
2493                                 MusEGlobal::audio->msgUpdateSoloStates();
2494                                 updateFlags |= (SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0));
2495                           }
2496                         }
2497                         else
2498                         {
2499                             if(editable_property_track->type() != MusECore::Track::AUDIO_SOFTSYNTH)
2500                             {
2501                               MusECore::AudioTrack* at = static_cast<MusECore::AudioTrack*>(editable_property_track);
2502                               if (i->_oldPropValue != at->channels()) {
2503                                     MusEGlobal::audio->msgSetChannels(at, i->_oldPropValue);
2504                                     updateFlags |= SC_CHANNELS;
2505                                     }
2506                             }
2507                         }
2508                         break;
2509 
2510                   case UndoOp::SetTrackRecord:
2511                         if(!editable_track->setRecordFlag1(!i->a))
2512                           break;
2513                         pendingOperations.add(PendingOperationItem(editable_track, !i->a, PendingOperationItem::SetTrackRecord));
2514                         // FIXME: No choice but to include monitor flag. Really should try to merge pending ops flags
2515                         //  with undo flags after executing the pending ops in revertOperationGroup3...
2516                         updateFlags |= (SC_RECFLAG | SC_TRACK_REC_MONITOR);
2517                         break;
2518 
2519                   case UndoOp::SetTrackMute:
2520                         pendingOperations.add(PendingOperationItem(editable_track, !i->a, PendingOperationItem::SetTrackMute));
2521                         updateFlags |= SC_MUTE;
2522                         break;
2523 
2524                   case UndoOp::SetTrackSolo:
2525                         pendingOperations.add(PendingOperationItem(editable_track, !i->a, PendingOperationItem::SetTrackSolo));
2526                         updateFlags |= SC_SOLO;
2527                         break;
2528 
2529                   case UndoOp::SetTrackRecMonitor:
2530                         pendingOperations.add(PendingOperationItem(editable_track, !i->a, PendingOperationItem::SetTrackRecMonitor));
2531                         updateFlags |= SC_TRACK_REC_MONITOR;
2532                         break;
2533 
2534                   case UndoOp::SetTrackOff:
2535                         pendingOperations.add(PendingOperationItem(editable_track, !i->a, PendingOperationItem::SetTrackOff));
2536                         updateFlags |= SC_MUTE;
2537                         break;
2538 
2539 
2540                   case UndoOp::AddRoute:
2541 #ifdef _UNDO_DEBUG_
2542                         fprintf(stderr, "Song::revertOperationGroup1:AddRoute\n");
2543 #endif
2544                         pendingOperations.add(PendingOperationItem(i->routeFrom, i->routeTo, PendingOperationItem::DeleteRoute));
2545                         updateFlags |= SC_ROUTE;
2546                         break;
2547 
2548                   case UndoOp::DeleteRoute:
2549 #ifdef _UNDO_DEBUG_
2550                         fprintf(stderr, "Song::executeOperationGroup1:DeleteRoute\n");
2551 #endif
2552                         pendingOperations.add(PendingOperationItem(i->routeFrom, i->routeTo, PendingOperationItem::AddRoute));
2553                         updateFlags |= SC_ROUTE;
2554                         break;
2555 
2556                   case UndoOp::ModifyTrackName:
2557                         pendingOperations.add(PendingOperationItem(editable_track, i->_oldName, PendingOperationItem::ModifyTrackName));
2558                         updateFlags |= (SC_TRACK_MODIFIED | SC_MIDI_TRACK_PROP);
2559                         // If it's an aux track, notify aux UI controls to reload, or change their names etc.
2560                         if(editable_track->type() == Track::AUDIO_AUX)
2561                           updateFlags |= SC_AUX;
2562                         break;
2563 
2564                   case UndoOp::MoveTrack:
2565                         pendingOperations.add(PendingOperationItem(&_tracks, i->b, i->a, PendingOperationItem::MoveTrack));
2566                         updateFlags |= SC_TRACK_MOVED;
2567                         break;
2568 
2569                   case UndoOp::ModifyPartName:
2570                         pendingOperations.add(PendingOperationItem(editable_part, i->_oldName, PendingOperationItem::ModifyPartName));
2571                         updateFlags |= SC_PART_MODIFIED;
2572                         break;
2573 
2574                   case UndoOp::ModifyPartLength:
2575                         {
2576                         pendingOperations.modifyPartLengthOperation(
2577                           editable_part, i->old_partlen_or_pos, -i->events_offset, i->events_offset_time_type);
2578                         updateFlags |= SC_PART_MODIFIED;
2579                         // If the part had events, then treat it as if they were added/removed with separate Add/DeleteEvent operations.
2580                         // Even if they will be added/deleted later in this operations group with actual separate Add/DeleteEvent operations,
2581                         //  that's an SC_EVENT_ADDED/REMOVED anyway, so hopefully no harm.
2582                         if(i->events_offset != 0 && !editable_part->events().empty())
2583                           updateFlags |= (SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED);
2584                         }
2585                         break;
2586                   case UndoOp::ModifyPartStart:
2587                         {
2588                         pendingOperations.modifyPartStartOperation(
2589                           editable_part, i->old_partlen_or_pos, i->old_partlen, -i->events_offset, i->events_offset_time_type);
2590                         updateFlags |= SC_PART_MODIFIED;
2591                         // If the part had events, then treat it as if they were added/removed with separate Add/DeleteEvent operations.
2592                         // Even if they will be added/deleted later in this operations group with actual separate Add/DeleteEvent operations,
2593                         //  that's an SC_EVENT_ADDED/REMOVED anyway, so hopefully no harm.
2594                         if(i->events_offset != 0 && !editable_part->events().empty())
2595                           updateFlags |= (SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED);
2596                         }
2597                         break;
2598 
2599                   case UndoOp::MovePart:
2600 #ifdef _UNDO_DEBUG_
2601                         fprintf(stderr, "Song::revertOperationGroup1:MovePart ** calling parts->movePartOperation\n");
2602 #endif
2603                         pendingOperations.movePartOperation(editable_part->track()->parts(),
2604                           editable_part, i->old_partlen_or_pos, const_cast<Track*>(i->oldTrack));
2605                         if(const_cast<Track*>(i->oldTrack))
2606                           updateFlags |= SC_PART_INSERTED | SC_PART_REMOVED;
2607                         updateFlags |= SC_PART_MODIFIED;
2608                         break;
2609 
2610                   case UndoOp::AddPart:
2611 #ifdef _UNDO_DEBUG_
2612                         fprintf(stderr, "Song::revertOperationGroup1:AddPart ** calling parts->delOperation\n");
2613 #endif
2614                         pendingOperations.delPartOperation(editable_part->track()->parts(), editable_part);
2615                         updateFlags |= SC_PART_REMOVED;
2616                         // If the part had events, then treat it as if they were removed with separate DeleteEvent operations.
2617                         // Even if they will be deleted later in this operations group with actual separate DeleteEvent operations,
2618                         //  that's an SC_EVENT_REMOVED anyway, so hopefully no harm. This fixes a problem with midi controller canvas
2619                         //  not updating after such a 'delete part with events, no separate AddEvents were used when creating the part'.
2620                         if(!editable_part->events().empty())
2621                           updateFlags |= SC_EVENT_REMOVED;
2622                         break;
2623 
2624                   case UndoOp::DeletePart:
2625 #ifdef _UNDO_DEBUG_
2626                         fprintf(stderr, "Song::revertOperationGroup1:DeletePart ** calling parts->addOperation\n");
2627 #endif
2628                         // Ensure that wave event sndfile file handles are opened.
2629                         // It should not be the job of the pending operations list to do this.
2630                         // TODO Coordinate close/open with part mute and/or track off.
2631                         editable_part->openAllEvents();
2632 
2633                         pendingOperations.addPartOperation(editable_part->track()->parts(), editable_part);
2634                         updateFlags |= SC_PART_INSERTED;
2635                         // If the part has events, then treat it as if they were inserted with separate AddEvent operations.
2636                         // Even if some will be inserted later in this operations group with actual separate AddEvent operations,
2637                         //  that's an SC_EVENT_INSERTED anyway, so should be no harm.
2638                         if(!editable_part->events().empty())
2639                           updateFlags |= SC_EVENT_INSERTED;
2640                         break;
2641 
2642 
2643                   case UndoOp::AddEvent:
2644 #ifdef _UNDO_DEBUG_
2645                         fprintf(stderr, "Song::revertOperationGroup1:AddEvent ** calling deleteEvent\n");
2646 #endif
2647                         deleteEventOperation(i->nEvent, editable_part, i->doCtrls, i->doClones);
2648                         updateFlags |= SC_EVENT_REMOVED;
2649                         break;
2650 
2651                   case UndoOp::DeleteEvent:
2652                         {
2653 #ifdef _UNDO_DEBUG_
2654                           fprintf(stderr, "Song::revertOperationGroup1:DeleteEvent ** calling addEvent\n");
2655 #endif
2656                           if(!i->nEvent.empty())
2657                           {
2658                             SndFileR f = i->nEvent.sndFile();
2659                             // Ensure that wave event sndfile file handle is opened.
2660                             // It should not be the job of the pending operations list to do this.
2661                             // TODO Coordinate close/open with part mute and/or track off.
2662                             if(!f.isNull() && !f.isOpen())
2663                               f->openRead();
2664                           }
2665 
2666                           addEventOperation(i->nEvent, editable_part, i->doCtrls, i->doClones);
2667                           updateFlags |= SC_EVENT_INSERTED;
2668                         }
2669                         break;
2670 
2671                   case UndoOp::ModifyEvent:
2672 #ifdef _UNDO_DEBUG_
2673                         fprintf(stderr, "Song::revertOperationGroup1:ModifyEvent ** calling changeEvent\n");
2674 #endif
2675                         changeEventOperation(i->nEvent, i->oEvent, editable_part, i->doCtrls, i->doClones);
2676                         updateFlags |= SC_EVENT_MODIFIED;
2677                         break;
2678 
2679 
2680                   case UndoOp::AddAudioCtrlVal:
2681                   {
2682 #ifdef _UNDO_DEBUG_
2683                         fprintf(stderr, "Song::revertOperationGroup1:AddAudioCtrlVal\n");
2684 #endif
2685                         CtrlListList* cll = static_cast<AudioTrack*>(editable_track)->controller();
2686                         iCtrlList icl = cll->find(i->_audioCtrlID);
2687                         if(icl != cll->end())
2688                         {
2689                           CtrlList* cl = icl->second;
2690                           iCtrl ic = cl->find(i->_audioCtrlFrame);
2691                           if(ic != cl->end())
2692                           {
2693                             pendingOperations.add(PendingOperationItem(cl, ic, PendingOperationItem::DeleteAudioCtrlVal));
2694                             updateFlags |= SC_AUDIO_CONTROLLER;
2695                           }
2696                         }
2697                   }
2698                   break;
2699 
2700                   case UndoOp::DeleteAudioCtrlVal:
2701                   {
2702 #ifdef _UNDO_DEBUG_
2703                         fprintf(stderr, "Song::revertOperationGroup1:DeleteAudioCtrlVal\n");
2704 #endif
2705                         CtrlListList* cll = static_cast<AudioTrack*>(editable_track)->controller();
2706                         iCtrlList icl = cll->find(i->_audioCtrlID);
2707                         if(icl != cll->end())
2708                         {
2709                           //CtrlList* cl = icl->second;
2710                           //iCtrl ic = cl->find(i->_audioCtrlFrame);
2711                           //if(ic != cl->end())
2712                           //  // An existing value was found (really shouldn't happen!). Replace it with the old value.
2713                           //  pendingOperations.add(PendingOperationItem(icl->second, ic, i->_audioCtrlVal, PendingOperationItem::ModifyAudioCtrlVal));
2714                           //else
2715                             // Restore the old value.
2716                             pendingOperations.add(PendingOperationItem(icl->second, i->_audioCtrlFrame, i->_audioCtrlVal, PendingOperationItem::AddAudioCtrlVal));
2717                           updateFlags |= SC_AUDIO_CONTROLLER;
2718                         }
2719                   }
2720                   break;
2721 
2722                   case UndoOp::ModifyAudioCtrlVal:
2723                   {
2724 #ifdef _UNDO_DEBUG_
2725                         fprintf(stderr, "Song::revertOperationGroup1:ModifyAudioCtrlVal\n");
2726 #endif
2727                         CtrlListList* cll = static_cast<AudioTrack*>(editable_track)->controller();
2728                         iCtrlList icl = cll->find(i->_audioCtrlID);
2729                         if(icl != cll->end())
2730                         {
2731                           CtrlList* cl = icl->second;
2732                           iCtrl ic = cl->find(i->_audioNewCtrlFrame);
2733                           if(ic != cl->end())
2734                           {
2735                             // Restore the old value.
2736                             pendingOperations.add(PendingOperationItem(icl->second, ic, i->_audioCtrlFrame, i->_audioCtrlVal, PendingOperationItem::ModifyAudioCtrlVal));
2737                             updateFlags |= SC_AUDIO_CONTROLLER;
2738                           }
2739                         }
2740                   }
2741                   break;
2742 
2743                   case UndoOp::ModifyAudioCtrlValList:
2744                   {
2745 #ifdef _UNDO_DEBUG_
2746                         fprintf(stderr, "Song::revertOperationGroup1:ModifyAudioCtrlValList\n");
2747 #endif
2748                         // Take either id. At least one list must be valid.
2749                         const int id = i->_eraseCtrlList ? i->_eraseCtrlList->id() : i->_addCtrlList->id();
2750                         iCtrlList icl = i->_ctrlListList->find(id);
2751                         if(icl != i->_ctrlListList->end())
2752                         {
2753                           // Make a complete copy of the controller list. The list will be quickly switched in the realtime stage.
2754                           // The Pending Operations system will take 'ownership' of this and delete it at the appropriate time.
2755                           CtrlList* new_list = new CtrlList(*icl->second, CtrlList::ASSIGN_PROPERTIES | CtrlList::ASSIGN_VALUES);
2756 
2757                           // Erase any items in the add list that were added...
2758                           //if(i->_addCtrlList)
2759                           if(i->_addCtrlList && !i->_addCtrlList->empty())
2760                           {
2761                             //const std::size_t sz = i->_addCtrlList->size();
2762                             //if(sz != 0)
2763                             //{
2764                               //const CtrlList& cl_r = *i->_addCtrlList;
2765                               // Both of these should be valid.
2766                               //ciCtrl n_s = new_list->find(cl_r[0].frame);      // The first item to be erased.
2767                               //ciCtrl n_e = new_list->find(cl_r[sz - 1].frame); // The last item to be erased.
2768                               iCtrl n_s = new_list->find(i->_addCtrlList->begin()->second.frame); // The first item to be erased.
2769                               ciCtrl e_e = i->_addCtrlList->end();
2770                               --e_e;
2771                               //ciCtrl n_e = new_list->find((--i->_eraseCtrlList->end())->second.frame); // The last item to be erased.
2772                               iCtrl n_e = new_list->find(e_e->second.frame); // The last item to be erased.
2773                               if(n_s != new_list->end() && n_e != new_list->end())
2774                               {
2775                                 // Since std range does NOT include the last iterator, increment n_e so erase will get all items.
2776                                 ++n_e;
2777                                 new_list->erase(n_s, n_e);
2778                               }
2779                             //}
2780                           }
2781 
2782                           // Re-add any items in the erase list that were erased...
2783                           if(i->_eraseCtrlList && !i->_eraseCtrlList->empty())
2784                             new_list->insert(i->_eraseCtrlList->begin(), i->_eraseCtrlList->end());
2785 
2786                           // The operation will quickly switch the list in the RT stage then the delete the old list in the non-RT stage.
2787                           pendingOperations.add(PendingOperationItem(icl, new_list, PendingOperationItem::ModifyAudioCtrlValList));
2788                           updateFlags |= SC_AUDIO_CONTROLLER_LIST;
2789                         }
2790                   }
2791                   break;
2792 
2793 
2794                   case UndoOp::SetInstrument:
2795                   {
2796 #ifdef _UNDO_DEBUG_
2797                         fprintf(stderr, "Song::revertOperationGroup1:SetInstrument\n");
2798 #endif
2799                         // Restore the old value.
2800                         pendingOperations.add(PendingOperationItem(
2801                           i->_midiPort, i->_oldMidiInstrument,
2802                           PendingOperationItem::SetInstrument));
2803                         updateFlags |= SC_MIDI_INSTRUMENT;
2804                   }
2805                   break;
2806 
2807 
2808                   case UndoOp::DeleteTempo:
2809 #ifdef _UNDO_DEBUG_
2810                         fprintf(stderr, "Song::revertOperationGroup1:DeleteTempo ** calling tempomap.addOperation tick:%d tempo:%d\n", i->a, i->b);
2811 #endif
2812                         // Create the new list if it doesn't already exist.
2813                         // Make a copy of the original list.
2814                         if(!new_tempo_list)
2815                         {
2816                           new_tempo_list = new TempoList();
2817                           new_tempo_list->copy(MusEGlobal::tempomap);
2818                         }
2819                         // Defer normalize until end of stage 2.
2820                         new_tempo_list->addTempo(i->a, i->b, false);
2821 
2822                         updateFlags |= SC_TEMPO;
2823                         break;
2824 
2825                   case UndoOp::AddTempo:
2826 #ifdef _UNDO_DEBUG_
2827                         fprintf(stderr, "Song::revertOperationGroup1:AddTempo ** calling tempomap.delOperation tick:%d\n", i->a);
2828 #endif
2829                         // Create the new list if it doesn't already exist.
2830                         // Make a copy of the original list.
2831                         if(!new_tempo_list)
2832                         {
2833                           new_tempo_list = new TempoList();
2834                           new_tempo_list->copy(MusEGlobal::tempomap);
2835                         }
2836                         // Defer normalize until end of stage 2.
2837                         new_tempo_list->delTempo(i->a, false);
2838 
2839                         updateFlags |= SC_TEMPO;
2840                         break;
2841 
2842                   case UndoOp::ModifyTempo:
2843 #ifdef _UNDO_DEBUG_
2844                         fprintf(stderr, "Song::revertOperationGroup1:ModifyTempo ** calling tempomap.addOperation tick:%d tempo:%d\n", i->a, i->b);
2845 #endif
2846                         // Create the new list if it doesn't already exist.
2847                         // Make a copy of the original list.
2848                         if(!new_tempo_list)
2849                         {
2850                           new_tempo_list = new TempoList();
2851                           new_tempo_list->copy(MusEGlobal::tempomap);
2852                         }
2853                         // Defer normalize until end of stage 2.
2854                         new_tempo_list->addTempo(i->a, i->b, false);
2855 
2856                         updateFlags |= SC_TEMPO;
2857                         break;
2858 
2859 //                   case UndoOp::SetTempo:
2860 //                         // Only if the master is on.
2861 //                         if(MusEGlobal::tempomap.masterFlag())
2862 //                         {
2863 // #ifdef _UNDO_DEBUG_
2864 //                           fprintf(stderr, "Song::revertOperationGroup1:SetTempo ** calling tempomap.delOperation tick:%d\n", i->a);
2865 // #endif
2866 //                           MusEGlobal::tempomap.delOperation(i->a, pendingOperations);
2867 //                         }
2868 //                         else
2869 //                           pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, i->b, PendingOperationItem::SetStaticTempo));
2870 //                         updateFlags |= SC_TEMPO;
2871 //                         break;
2872 
2873                   case UndoOp::SetStaticTempo:
2874 #ifdef _UNDO_DEBUG_
2875                         fprintf(stderr, "Song::revertOperationGroup1:SetStaticTempo ** adding SetStaticTempo operation\n");
2876 #endif
2877                         pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, i->b, PendingOperationItem::SetStaticTempo));
2878                         updateFlags |= SC_TEMPO;
2879                         break;
2880 
2881                   case UndoOp::SetGlobalTempo:
2882 #ifdef _UNDO_DEBUG_
2883                         fprintf(stderr, "Song::revertOperationGroup1:SetGlobalTempo ** adding SetGlobalTempo operation\n");
2884 #endif
2885                         pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, i->b, PendingOperationItem::SetGlobalTempo));
2886                         updateFlags |= SC_TEMPO;
2887                         break;
2888 
2889                   case UndoOp::EnableMasterTrack:
2890 #ifdef _UNDO_DEBUG_
2891                         fprintf(stderr, "Song::revertOperationGroup1:EnableMasterTrack ** adding EnableMasterTrack operation\n");
2892 #endif
2893                         pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, (bool)i->b, PendingOperationItem::SetUseMasterTrack));
2894                         updateFlags |= SC_MASTER;
2895                         break;
2896 
2897                   case UndoOp::DeleteSig:
2898 #ifdef _UNDO_DEBUG_
2899                         fprintf(stderr, "Song::revertOperationGroup1:DeleteSig ** calling MusEGlobal::sigmap.addOperation\n");
2900 #endif
2901                         // Create the new list if it doesn't already exist.
2902                         // Make a copy of the original list.
2903                         if(!new_sig_list)
2904                         {
2905                           new_sig_list = new SigList();
2906                           new_sig_list->copy(MusEGlobal::sigmap);
2907                         }
2908                         // Defer normalize until end of stage 2.
2909                         new_sig_list->add(i->a, MusECore::TimeSignature(i->b, i->c), false);
2910 
2911                         updateFlags |= SC_SIG;
2912                         break;
2913 
2914                   case UndoOp::AddSig:
2915 #ifdef _UNDO_DEBUG_
2916                         fprintf(stderr, "Song::revertOperationGroup1:AddSig ** calling MusEGlobal::sigmap.delOperation\n");
2917 #endif
2918                         // Create the new list if it doesn't already exist.
2919                         // Make a copy of the original list.
2920                         if(!new_sig_list)
2921                         {
2922                           new_sig_list = new SigList();
2923                           new_sig_list->copy(MusEGlobal::sigmap);
2924                         }
2925                         // Defer normalize until end of stage 2.
2926                         new_sig_list->del(i->a, false);
2927 
2928                         updateFlags |= SC_SIG;
2929                         break;
2930 
2931                   case UndoOp::ModifySig:
2932 #ifdef _UNDO_DEBUG_
2933                         fprintf(stderr, "Song::revertOperationGroup1:ModifySig ** calling MusEGlobal::sigmap.addOperation\n");
2934 #endif
2935                         // Create the new list if it doesn't already exist.
2936                         // Make a copy of the original list.
2937                         if(!new_sig_list)
2938                         {
2939                           new_sig_list = new SigList();
2940                           new_sig_list->copy(MusEGlobal::sigmap);
2941                         }
2942                         // Defer normalize until end of stage 2.
2943                         // TODO: Hm should that be ->d and ->e like in executeOperationGroup1?
2944                         new_sig_list->add(i->a, MusECore::TimeSignature(i->b, i->c), false);
2945 
2946                         updateFlags |= SC_SIG;
2947                         break;
2948 
2949 
2950                   case UndoOp::DeleteKey:
2951 #ifdef _UNDO_DEBUG_
2952                         fprintf(stderr, "Song::revertOperationGroup1:DeleteKey ** calling keymap.addOperation\n");
2953 #endif
2954                         // Create the new list if it doesn't already exist.
2955                         // Make a copy of the original list.
2956                         if(!new_key_list)
2957                         {
2958                           new_key_list = new KeyList();
2959                           new_key_list->copy(MusEGlobal::keymap);
2960                         }
2961                         new_key_list->addKey(i->a, key_enum(i->b), i->c);
2962 
2963                         updateFlags |= SC_KEY;
2964                         break;
2965 
2966                   case UndoOp::AddKey:
2967 #ifdef _UNDO_DEBUG_
2968                         fprintf(stderr, "Song::revertOperationGroup1:AddKey ** calling keymap.delOperation\n");
2969 #endif
2970                         // Create the new list if it doesn't already exist.
2971                         // Make a copy of the original list.
2972                         if(!new_key_list)
2973                         {
2974                           new_key_list = new KeyList();
2975                           new_key_list->copy(MusEGlobal::keymap);
2976                         }
2977                         new_key_list->delKey(i->a);
2978 
2979                         updateFlags |= SC_KEY;
2980                         break;
2981 
2982                   case UndoOp::ModifyKey:
2983 #ifdef _UNDO_DEBUG_
2984                         fprintf(stderr, "Song::revertOperationGroup1:ModifyKey ** calling keymap.addOperation\n");
2985 #endif
2986                         // Create the new list if it doesn't already exist.
2987                         // Make a copy of the original list.
2988                         if(!new_key_list)
2989                         {
2990                           new_key_list = new KeyList();
2991                           new_key_list->copy(MusEGlobal::keymap);
2992                         }
2993                         // TODO: Hm should that be ->d and ->e like in executeOperationGroup1?
2994                         new_key_list->addKey(i->a, key_enum(i->b), i->c);
2995 
2996                         updateFlags |= SC_KEY;
2997                         break;
2998 
2999                   case UndoOp::ModifySongLen:
3000 #ifdef _UNDO_DEBUG_
3001                         fprintf(stderr, "Song::revertOperationGroup1:ModifySongLen ** adding ModifySongLen operation\n");
3002 #endif
3003                         pendingOperations.add(PendingOperationItem(i->b, PendingOperationItem::ModifySongLength));
3004                         updateFlags |= SC_EVERYTHING;  // set all flags   // TODO Refine this! Too many flags.  // REMOVE Tim.
3005                         //updateFlags |= SC_SONG_LEN;
3006                         break;
3007 
3008                   case UndoOp::ModifyMidiDivision:
3009 #ifdef _UNDO_DEBUG_
3010                         fprintf(stderr, "Song::revertOperationGroup1:ModifyMidiDivision\n");
3011 #endif
3012                         MusEGlobal::config.division = i->b;
3013                         // Make sure the AL namespace variable mirrors our variable.
3014                         AL::division = MusEGlobal::config.division;
3015                         // Defer normalize until end of stage 2.
3016                         updateFlags |= SC_DIVISION_CHANGED;
3017                         break;
3018 
3019                   case UndoOp::AddMarker:
3020                           // Create the new list if it doesn't already exist.
3021                           // Make a copy of the original list.
3022                           if(!new_marker_list)
3023                             new_marker_list = new MarkerList(*marker());
3024                           if(i->newMarker)
3025                             new_marker_list->remove(*i->newMarker);
3026                           updateFlags |= SC_MARKER_REMOVED;
3027                         break;
3028 
3029                   case UndoOp::DeleteMarker:
3030                           // Create the new list if it doesn't already exist.
3031                           // Make a copy of the original list.
3032                           if(!new_marker_list)
3033                             new_marker_list = new MarkerList(*marker());
3034                           if(i->oldMarker)
3035                             new_marker_list->add(*i->oldMarker);
3036                           updateFlags |= SC_MARKER_INSERTED;
3037                         break;
3038 
3039                   case UndoOp::ModifyMarker:
3040                   case UndoOp::SetMarkerPos:
3041                           // Create the new list if it doesn't already exist.
3042                           // Make a copy of the original list.
3043                           if(!new_marker_list)
3044                             new_marker_list = new MarkerList(*marker());
3045                           if(i->newMarker)
3046                             new_marker_list->remove(*i->newMarker);
3047                           if(i->oldMarker)
3048                             new_marker_list->add(*i->oldMarker);
3049                           updateFlags |= SC_MARKER_MODIFIED;
3050                         break;
3051 
3052                   default:
3053                         break;
3054                   }
3055             }
3056 
3057       if(new_tempo_list)
3058         pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, new_tempo_list, PendingOperationItem::ModifyTempoList));
3059 
3060       if(new_sig_list)
3061         pendingOperations.add(PendingOperationItem(&MusEGlobal::sigmap, new_sig_list, PendingOperationItem::ModifySigList));
3062 
3063       if(new_key_list)
3064         pendingOperations.add(PendingOperationItem(&MusEGlobal::keymap, new_key_list, PendingOperationItem::ModifyKeyList));
3065 
3066       if(new_marker_list)
3067         pendingOperations.add(PendingOperationItem(&_markerList, new_marker_list, PendingOperationItem::ModifyMarkerList));
3068       }
3069 
3070 //---------------------------------------------------------
3071 //   revertOperationGroup3
3072 //    non realtime context
3073 //---------------------------------------------------------
3074 
revertOperationGroup3(Undo & operations)3075 void Song::revertOperationGroup3(Undo& operations)
3076       {
3077       pendingOperations.executeNonRTStage();
3078 #ifdef _UNDO_DEBUG_
3079       fprintf(stderr, "Song::revertOperationGroup3 *** Calling pendingOperations.clear()\n");
3080 #endif
3081       pendingOperations.clear();
3082       for (riUndoOp i = operations.rbegin(); i != operations.rend(); ++i) {
3083             Track* editable_track = const_cast<Track*>(i->track);
3084 // uncomment if needed            Track* editable_property_track = const_cast<Track*>(i->_propertyTrack);
3085             Part* editable_part = const_cast<Part*>(i->part); // uncomment if needed
3086             switch(i->type) {
3087                   case UndoOp::AddTrack:
3088                         // Ensure that wave event sndfile file handles are closed.
3089                         // It should not be the job of the pending operations list to do this.
3090                         // TODO Coordinate close/open with part mute and/or track off.
3091                         editable_track->closeAllParts();
3092                         break;
3093                   case UndoOp::DeleteTrack:
3094                         switch(editable_track->type())
3095                         {
3096                           case Track::AUDIO_OUTPUT:
3097                             // Connect audio output ports to Jack ports...
3098                             if(MusEGlobal::checkAudioDevice() && MusEGlobal::audio->isRunning())
3099                             {
3100                               AudioOutput* ao = (AudioOutput*)editable_track;
3101                               for(int ch = 0; ch < ao->channels(); ++ch)
3102                               {
3103                                 void* our_port = ao->jackPort(ch);
3104                                 if(!our_port)
3105                                   continue;
3106                                 const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port);
3107                                 if(!our_port_name)
3108                                   continue;
3109                                 RouteList* rl = ao->outRoutes();
3110                                 for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir)
3111                                 {
3112                                   if(ir->type != Route::JACK_ROUTE || ir->channel != ch)
3113                                     continue;
3114                                   const char* route_name = ir->persistentJackPortName;
3115                                   //if(ir->jackPort)
3116                                   if(!MusEGlobal::audioDevice->findPort(route_name))
3117                                     continue;
3118                                   //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name))
3119                                   MusEGlobal::audioDevice->connect(our_port_name, route_name);
3120                                   updateFlags |= SC_ROUTE;
3121                                 }
3122                               }
3123                             }
3124                           break;
3125 
3126                           case Track::AUDIO_INPUT:
3127                             // Connect Jack ports to audio input ports...
3128                             if(MusEGlobal::checkAudioDevice() && MusEGlobal::audio->isRunning())
3129                             {
3130                               AudioInput* ai = (AudioInput*)editable_track;
3131                               for(int ch = 0; ch < ai->channels(); ++ch)
3132                               {
3133                                 void* our_port = ai->jackPort(ch);
3134                                 if(!our_port)
3135                                   continue;
3136                                 const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port);
3137                                 if(!our_port_name)
3138                                   continue;
3139                                 RouteList* rl = ai->inRoutes();
3140                                 for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir)
3141                                 {
3142                                   if(ir->type != Route::JACK_ROUTE || ir->channel != ch)
3143                                     continue;
3144                                   const char* route_name = ir->persistentJackPortName;
3145                                   //if(ir->jackPort)
3146                                   if(!MusEGlobal::audioDevice->findPort(route_name))
3147                                     continue;
3148                                   //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name))
3149                                   MusEGlobal::audioDevice->connect(route_name, our_port_name);
3150                                   updateFlags |= SC_ROUTE;
3151                                 }
3152                               }
3153                             }
3154                           break;
3155 
3156                           default:
3157                             break;
3158                         }
3159 
3160                         break;
3161                   case UndoOp::AddPart:
3162                         // Ensure that wave event sndfile file handles are closed.
3163                         // It should not be the job of the pending operations list to do this.
3164                         // TODO Coordinate close/open with part mute and/or track off.
3165                         editable_part->closeAllEvents();
3166                         break;
3167                   case UndoOp::AddEvent: {
3168                         if(!i->nEvent.empty())
3169                         {
3170                           SndFileR f = i->nEvent.sndFile();
3171                           // Ensure that wave event sndfile file handle is closed.
3172                           // It should not be the job of the pending operations list to do this.
3173                           // TODO Coordinate close/open with part mute and/or track off.
3174                           if(!f.isNull() && f.isOpen())
3175                             f->close();
3176                         }
3177                         }
3178                         break;
3179                   case UndoOp::ModifyMidiDivision:
3180                         // This also tells all connected models to begin/end reset.
3181                         MusEGlobal::globalRasterizer->setDivision(i->b);
3182                         break;
3183                   default:
3184                         break;
3185                   }
3186             }
3187 
3188             if(!operations.empty())
3189               emit sigDirty();
3190       }
3191 
3192 //---------------------------------------------------------
3193 //   executeOperationGroup1
3194 //    non realtime context
3195 //    return true if nothing to do
3196 //---------------------------------------------------------
3197 
executeOperationGroup1(Undo & operations)3198 void Song::executeOperationGroup1(Undo& operations)
3199       {
3200       unsigned song_len = len();
3201       MarkerList* new_marker_list = nullptr;
3202       TempoList* new_tempo_list = nullptr;
3203       SigList* new_sig_list = nullptr;
3204       KeyList* new_key_list = nullptr;
3205 
3206       for (iUndoOp i = operations.begin(); i != operations.end(); ++i) {
3207             Track* editable_track = const_cast<Track*>(i->track);
3208             Track* editable_property_track = const_cast<Track*>(i->_propertyTrack);
3209             Part* editable_part = const_cast<Part*>(i->part);
3210             switch(i->type) {
3211                   case UndoOp::SelectPart:
3212                         pendingOperations.add(PendingOperationItem(editable_part, i->selected, PendingOperationItem::SelectPart));
3213                         updateFlags |= SC_PART_SELECTION;
3214                         break;
3215                   case UndoOp::SelectEvent:
3216                         pendingOperations.add(PendingOperationItem(editable_part, i->nEvent, i->selected, PendingOperationItem::SelectEvent));
3217                         updateFlags |= SC_SELECTION;
3218                         break;
3219 
3220                   case UndoOp::AddTrack:
3221                         switch(editable_track->type())
3222                         {
3223                           case Track::AUDIO_SOFTSYNTH:
3224                           {
3225                             SynthI* s = (SynthI*)editable_track;
3226                             Synth* sy = s->synth();
3227                             if(!s->isActivated())
3228                               s->initInstance(sy, s->name());
3229                           }
3230                           break;
3231 
3232                           case Track::AUDIO_OUTPUT:
3233                           {
3234                             AudioOutput* ao = (AudioOutput*)editable_track;
3235                             if(MusEGlobal::checkAudioDevice())
3236                             {
3237                               for(int ch = 0; ch < ao->channels(); ++ch)
3238                               {
3239                                 // This should be OK since the track has not yet been added in the realtime stage.
3240                                 if(ao->registerPorts(ch))
3241                                   updateFlags |= SC_ROUTE;
3242 
3243                                 // Set the route Jack ports now to relieve our graph callback handler from having to do it.
3244                                 RouteList* rl = ao->outRoutes();
3245                                 for(iRoute ir = rl->begin(); ir != rl->end(); ++ir)
3246                                   if(ir->type == Route::JACK_ROUTE && ir->channel == ch)
3247                                   {
3248                                     ir->jackPort = MusEGlobal::audioDevice->findPort(ir->persistentJackPortName);
3249                                     updateFlags |= SC_ROUTE;
3250                                   }
3251                               }
3252                             }
3253 
3254 
3255                           }
3256                           break;
3257 
3258                           case Track::AUDIO_INPUT:
3259                           {
3260                             AudioInput* ai = (AudioInput*)editable_track;
3261                             if(MusEGlobal::checkAudioDevice())
3262                             {
3263                               for(int ch = 0; ch < ai->channels(); ++ch)
3264                               {
3265                                 // This should be OK since the track has not yet been added in the realtime stage.
3266                                 if(ai->registerPorts(ch))
3267                                   updateFlags |= SC_ROUTE;
3268 
3269                                 // Set the route Jack ports now to relieve our graph callback handler from having to do it.
3270                                 RouteList* rl = ai->inRoutes();
3271                                 for(iRoute ir = rl->begin(); ir != rl->end(); ++ir)
3272                                   if(ir->type == Route::JACK_ROUTE && ir->channel == ch)
3273                                   {
3274                                     ir->jackPort = MusEGlobal::audioDevice->findPort(ir->persistentJackPortName);
3275                                     updateFlags |= SC_ROUTE;
3276                                   }
3277                               }
3278                             }
3279                           }
3280                           break;
3281 
3282                           case Track::AUDIO_AUX:
3283                             updateFlags |= SC_AUX;
3284                           break;
3285 
3286                           default:
3287                           break;
3288                         }
3289 
3290                         // Ensure that wave event sndfile file handles are opened.
3291                         // It should not be the job of the pending operations list to do this.
3292                         // TODO Coordinate close/open with part mute and/or track off.
3293                         editable_track->openAllParts();
3294 
3295                         insertTrackOperation(editable_track, i->trackno, pendingOperations);
3296                         updateFlags |= SC_TRACK_INSERTED;
3297                         break;
3298 
3299                   case UndoOp::DeleteTrack:
3300                         switch(editable_track->type())
3301                         {
3302                           case Track::AUDIO_SOFTSYNTH:
3303                           {
3304                             SynthI* si = (SynthI*)editable_track;
3305                             if(si->hasGui())
3306                               si->showGui(false);
3307                             if(si->hasNativeGui())
3308                               si->showNativeGui(false);
3309                             // FIXME TODO: We want to clear any ports using this instrument AND make it
3310                             //  undoable but ATM a few other things can set the instrument without an undo
3311                             //  operation so the undo sequence would not be correct. So we don't have much
3312                             //  choice but to just reset inside the PendingOperation::DeleteTrack operation for now.
3313                             // Still, everything else is in place for undoable setting of instrument...
3314                           }// Fall through.
3315                           case Track::WAVE:
3316                           case Track::AUDIO_OUTPUT:
3317                           case Track::AUDIO_INPUT:
3318                           case Track::AUDIO_GROUP:
3319                           case Track::AUDIO_AUX:
3320                             ((AudioTrack*)editable_track)->deleteAllEfxGuis();
3321                             updateFlags |= SC_RACK;
3322                           break;
3323 
3324                           default:
3325                           break;
3326                         }
3327 
3328                         switch(editable_track->type())
3329                         {
3330                           case Track::AUDIO_OUTPUT:
3331                           {
3332                             AudioOutput* ao = (AudioOutput*)editable_track;
3333                             for(int ch = 0; ch < ao->channels(); ++ch)
3334                             {
3335                               MusEGlobal::audioDevice->unregisterPort(ao->jackPort(ch));
3336                               //ao->setJackPort(ch, 0);  // Done in RT stage.
3337                               updateFlags |= SC_ROUTE;
3338                             }
3339                           }
3340                           break;
3341 
3342                           case Track::AUDIO_INPUT:
3343                           {
3344                             AudioOutput* ai = (AudioOutput*)editable_track;
3345                             for(int ch = 0; ch < ai->channels(); ++ch)
3346                             {
3347                               MusEGlobal::audioDevice->unregisterPort(ai->jackPort(ch));
3348                               //ai->setJackPort(ch, 0); // Done in RT stage.
3349                               updateFlags |= SC_ROUTE;
3350                             }
3351                           }
3352                           break;
3353 
3354                           case Track::AUDIO_AUX:
3355                             updateFlags |= SC_AUX;
3356                           break;
3357 
3358                           default:
3359                           break;
3360                         }
3361                         removeTrackOperation(editable_track, pendingOperations);
3362                         updateFlags |= SC_TRACK_REMOVED;
3363                         break;
3364 
3365                   case UndoOp::ModifyClip:
3366                         sndFileApplyUndoFile(i->nEvent, i->tmpwavfile, i->startframe, i->endframe);
3367                         updateFlags |= SC_CLIP_MODIFIED;
3368                         break;
3369                   case UndoOp::ModifyTrackChannel:
3370                         if (editable_property_track->isMidiTrack())
3371                         {
3372                           MusECore::MidiTrack* mt = static_cast<MusECore::MidiTrack*>(editable_property_track);
3373                           if (i->_newPropValue != mt->outChannel())
3374                           {
3375                                 MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged;
3376                                 MusEGlobal::audio->msgIdle(true);
3377                                 changed |= mt->setOutChanAndUpdate(i->_newPropValue, false);
3378                                 MusEGlobal::audio->msgIdle(false);
3379                                 MusEGlobal::audio->msgUpdateSoloStates();
3380                                 updateFlags |= (SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0));
3381                           }
3382                         }
3383                         else
3384                         {
3385                             if(editable_property_track->type() != MusECore::Track::AUDIO_SOFTSYNTH)
3386                             {
3387                               MusECore::AudioTrack* at = static_cast<MusECore::AudioTrack*>(editable_property_track);
3388                               if (i->_newPropValue != at->channels()) {
3389                                     MusEGlobal::audio->msgSetChannels(at, i->_newPropValue);
3390                                     updateFlags |= SC_CHANNELS;
3391                                     }
3392                             }
3393                         }
3394                         break;
3395 
3396                   case UndoOp::SetTrackRecord:
3397                         if(!editable_track->setRecordFlag1(i->a))
3398                           break;
3399                         pendingOperations.add(PendingOperationItem(editable_track, i->a, PendingOperationItem::SetTrackRecord));
3400                         // FIXME: No choice but to include monitor flag. Really should try to merge pending ops flags
3401                         //  with undo flags after executing the pending ops in executeOperationGroup3...
3402                         updateFlags |= (SC_RECFLAG | SC_TRACK_REC_MONITOR);
3403                         break;
3404 
3405                   case UndoOp::SetTrackMute:
3406                         pendingOperations.add(PendingOperationItem(editable_track, i->a, PendingOperationItem::SetTrackMute));
3407                         updateFlags |= SC_MUTE;
3408                         break;
3409 
3410                   case UndoOp::SetTrackSolo:
3411                         pendingOperations.add(PendingOperationItem(editable_track, i->a, PendingOperationItem::SetTrackSolo));
3412                         updateFlags |= SC_SOLO;
3413                         break;
3414 
3415                   case UndoOp::SetTrackRecMonitor:
3416                         pendingOperations.add(PendingOperationItem(editable_track, i->a, PendingOperationItem::SetTrackRecMonitor));
3417                         updateFlags |= SC_TRACK_REC_MONITOR;
3418                         break;
3419 
3420                   case UndoOp::SetTrackOff:
3421                         pendingOperations.add(PendingOperationItem(editable_track, i->a, PendingOperationItem::SetTrackOff));
3422                         updateFlags |= SC_MUTE;
3423                         break;
3424 
3425 
3426                   case UndoOp::AddRoute:
3427 #ifdef _UNDO_DEBUG_
3428                         fprintf(stderr, "Song::executeOperationGroup1:AddRoute\n");
3429 #endif
3430                         pendingOperations.add(PendingOperationItem(i->routeFrom, i->routeTo, PendingOperationItem::AddRoute));
3431                         updateFlags |= SC_ROUTE;
3432                         break;
3433 
3434                   case UndoOp::DeleteRoute:
3435 #ifdef _UNDO_DEBUG_
3436                         fprintf(stderr, "Song::executeOperationGroup1:DeleteRoute\n");
3437 #endif
3438                         pendingOperations.add(PendingOperationItem(i->routeFrom, i->routeTo, PendingOperationItem::DeleteRoute));
3439                         updateFlags |= SC_ROUTE;
3440                         break;
3441 
3442                   case UndoOp::ModifyTrackName:
3443                         pendingOperations.add(PendingOperationItem(editable_track, i->_newName, PendingOperationItem::ModifyTrackName));
3444                         updateFlags |= (SC_TRACK_MODIFIED | SC_MIDI_TRACK_PROP);
3445                         // If it's an aux track, notify aux UI controls to reload, or change their names etc.
3446                         if(editable_track->type() == Track::AUDIO_AUX)
3447                           updateFlags |= SC_AUX;
3448                         break;
3449 
3450                   case UndoOp::MoveTrack:
3451                         pendingOperations.add(PendingOperationItem(&_tracks, i->a, i->b, PendingOperationItem::MoveTrack));
3452                         updateFlags |= SC_TRACK_MOVED;
3453                         break;
3454 
3455                   case UndoOp::ModifyPartName:
3456                         pendingOperations.add(PendingOperationItem(editable_part, i->_newName, PendingOperationItem::ModifyPartName));
3457                         updateFlags |= SC_PART_MODIFIED;
3458                         break;
3459 
3460                   case UndoOp::ModifyPartStart:
3461                       {
3462                         pendingOperations.modifyPartStartOperation(
3463                           editable_part, i->new_partlen_or_pos, i->new_partlen, i->events_offset, i->events_offset_time_type);
3464                         updateFlags |= SC_PART_MODIFIED;
3465                         // If the part had events, then treat it as if they were added/removed with separate Add/DeleteEvent operations.
3466                         // Even if they will be added/deleted later in this operations group with actual separate Add/DeleteEvent operations,
3467                         //  that's an SC_EVENT_ADDED/REMOVED anyway, so hopefully no harm.
3468                         if(i->events_offset != 0 && !editable_part->events().empty())
3469                           updateFlags |= (SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED);
3470                       }
3471                       break;
3472                   case UndoOp::ModifyPartLength:
3473                         {
3474                           unsigned p = Pos::convert(editable_part->posValue() + i->new_partlen_or_pos, editable_part->type(), Pos::TICKS);
3475                           // >= for good luck, since this (simpler) comparison is in the TICKS domain.
3476                           if(p >= song_len)
3477                           {
3478                             song_len = p + 1;
3479                             // Insert a ModifySongLen operation BEFORE this one. If insert finds an existing ModifySongLen,
3480                             //  possibly long before this one, it REPLACES that one's values.
3481                             operations.insert(i, UndoOp(UndoOp::ModifySongLen, song_len, len()));
3482                             // Since the ModifySongLen above will not be iterated now, act like the operation had just been iterated.
3483                             // The same REPLACEMENT rules apply here.
3484                             pendingOperations.add(PendingOperationItem(song_len, PendingOperationItem::ModifySongLength));
3485                             updateFlags |= SC_EVERYTHING;  // set all flags   // TODO Refine this! Too many flags.  // REMOVE Tim.
3486                             //updateFlags |= SC_SONG_LEN;
3487                           }
3488                           pendingOperations.modifyPartLengthOperation(
3489                             editable_part, i->new_partlen_or_pos, i->events_offset, i->events_offset_time_type);
3490                           updateFlags |= SC_PART_MODIFIED;
3491                           // If the part had events, then treat it as if they were added/removed with separate Add/DeleteEvent operations.
3492                           // Even if they will be added/deleted later in this operations group with actual separate Add/DeleteEvent operations,
3493                           //  that's an SC_EVENT_ADDED/REMOVED anyway, so hopefully no harm.
3494                           if(i->events_offset != 0 && !editable_part->events().empty())
3495                             updateFlags |= (SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED);
3496                         }
3497                         break;
3498 
3499                   case UndoOp::MovePart:
3500                         {
3501                           unsigned p = Pos::convert(editable_part->lenValue() + i->new_partlen_or_pos, editable_part->type(), Pos::TICKS);
3502                           // >= for good luck, since this (simpler) comparison is in the TICKS domain.
3503                           if(p >= song_len)
3504                           {
3505                             song_len = p + 1;
3506                             // Insert a ModifySongLen operation BEFORE this one. If insert finds an existing ModifySongLen,
3507                             //  possibly long before this one, it REPLACES that one's values.
3508                             operations.insert(i, UndoOp(UndoOp::ModifySongLen, song_len, len()));
3509                             // Since the ModifySongLen above will not be iterated now, act like the operation had just been iterated.
3510                             // The same REPLACEMENT rules apply here.
3511                             pendingOperations.add(PendingOperationItem(song_len, PendingOperationItem::ModifySongLength));
3512                             updateFlags |= SC_EVERYTHING;  // set all flags   // TODO Refine this! Too many flags.  // REMOVE Tim.
3513                             //updateFlags |= SC_SONG_LEN;
3514                           }
3515 #ifdef _UNDO_DEBUG_
3516                           fprintf(stderr, "Song::executeOperationGroup1:MovePart ** calling parts->movePartOperation\n");
3517 #endif
3518                           pendingOperations.movePartOperation(editable_part->track()->parts(),
3519                             editable_part, i->new_partlen_or_pos, editable_track);
3520                           if(editable_track)
3521                             updateFlags |= SC_PART_INSERTED | SC_PART_REMOVED;
3522                           updateFlags |= SC_PART_MODIFIED;
3523                         }
3524                         break;
3525 
3526                   case UndoOp::AddPart:
3527                         {
3528                           unsigned p = Pos::convert(editable_part->lenValue() + editable_part->posValue(), editable_part->type(), Pos::TICKS);
3529                           // >= for good luck, since this (simpler) comparison is in the TICKS domain.
3530                           if(p >= song_len)
3531                           {
3532                             song_len = p + 1;
3533                             // Insert a ModifySongLen operation BEFORE this one. If insert finds an existing ModifySongLen,
3534                             //  possibly long before this one, it REPLACES that one's values.
3535                             operations.insert(i, UndoOp(UndoOp::ModifySongLen, song_len, len()));
3536                             // Since the ModifySongLen above will not be iterated now, act like the operation had just been iterated.
3537                             // The same REPLACEMENT rules apply here.
3538                             pendingOperations.add(PendingOperationItem(song_len, PendingOperationItem::ModifySongLength));
3539                             updateFlags |= SC_EVERYTHING;  // set all flags   // TODO Refine this! Too many flags.  // REMOVE Tim.
3540                             //updateFlags |= SC_SONG_LEN;
3541                           }
3542 #ifdef _UNDO_DEBUG_
3543                           fprintf(stderr, "Song::executeOperationGroup1:addPart ** calling parts->addOperation\n");
3544 #endif
3545                           // Ensure that wave event sndfile file handles are opened.
3546                           // It should not be the job of the pending operations list to do this.
3547                           // TODO Coordinate close/open with part mute and/or track off.
3548                           editable_part->openAllEvents();
3549 
3550                           pendingOperations.addPartOperation(editable_part->track()->parts(), editable_part);
3551                           updateFlags |= SC_PART_INSERTED;
3552                           // If the part has events, then treat it as if they were inserted with separate AddEvent operations.
3553                           // Even if some will be inserted later in this operations group with actual separate AddEvent operations,
3554                           //  that's an SC_EVENT_INSERTED anyway, so should be no harm.
3555                           if(!editable_part->events().empty())
3556                             updateFlags |= SC_EVENT_INSERTED;
3557                         }
3558                         break;
3559 
3560                   case UndoOp::DeletePart:
3561 #ifdef _UNDO_DEBUG_
3562                         fprintf(stderr, "Song::executeOperationGroup1:deletePart ** calling parts->delOperation\n");
3563 #endif
3564                         pendingOperations.delPartOperation(editable_part->track()->parts(), editable_part);
3565                         updateFlags |= SC_PART_REMOVED;
3566                         // If the part had events, then treat it as if they were removed with separate DeleteEvent operations.
3567                         // Even if they will be deleted later in this operations group with actual separate DeleteEvent operations,
3568                         //  that's an SC_EVENT_REMOVED anyway, so hopefully no harm. This fixes a problem with midi controller canvas
3569                         //  not updating after such a 'delete part with events, no separate AddEvents were used when creating the part'.
3570                         if(!editable_part->events().empty())
3571                           updateFlags |= SC_EVENT_REMOVED;
3572                         break;
3573 
3574                   case UndoOp::AddEvent: {
3575 #ifdef _UNDO_DEBUG_
3576                         fprintf(stderr, "Song::executeOperationGroup1:AddEvent ** calling addEvent\n");
3577 #endif
3578                         if(!i->nEvent.empty())
3579                         {
3580                           SndFileR f = i->nEvent.sndFile();
3581                           // Ensure that wave event sndfile file handle is opened.
3582                           // It should not be the job of the pending operations list to do this.
3583                           // TODO Coordinate close/open with part mute and/or track off.
3584                           if(!f.isNull() && !f.isOpen())
3585                             f.openRead();
3586                         }
3587 
3588                         addEventOperation(i->nEvent, editable_part, i->doCtrls, i->doClones);
3589                         updateFlags |= SC_EVENT_INSERTED;
3590                         }
3591                         break;
3592 
3593                   case UndoOp::DeleteEvent: {
3594 #ifdef _UNDO_DEBUG_
3595                         fprintf(stderr, "Song::executeOperationGroup1:DeleteEvent ** calling deleteEvent\n");
3596 #endif
3597                         // Special: Replace the undo item's event with the real actual event found in the event lists.
3598                         // This way even a modified event can be passed in to the DeleteEvent operation constructor,
3599                         //  and as long as the ID AND position values match it will find and use the ORIGINAL event.
3600                         // (It's safe, the = operator quickly returns if the two events have the same base pointer.)
3601                         i->nEvent = deleteEventOperation(i->nEvent, editable_part, i->doCtrls, i->doClones);
3602                         updateFlags |= SC_EVENT_REMOVED;
3603                         }
3604                         break;
3605 
3606                   case UndoOp::ModifyEvent:
3607 #ifdef _UNDO_DEBUG_
3608                         fprintf(stderr, "Song::executeOperationGroup1:ModifyEvent ** calling changeEvent\n");
3609 #endif
3610                         // Special: Replace the undo item's old event with the real actual event found in the event lists.
3611                         // This way even a modified old event can be passed in to the ModifyEvent operation constructor,
3612                         //  and as long as the ID AND position values match it will find and use the ORIGINAL event.
3613                         // (It's safe, the = operator quickly returns if the two events have the same base pointer.)
3614                         i->oEvent = changeEventOperation(i->oEvent, i->nEvent, editable_part, i->doCtrls, i->doClones);
3615                         updateFlags |= SC_EVENT_MODIFIED;
3616                         break;
3617 
3618 
3619                   case UndoOp::AddAudioCtrlVal:
3620                   {
3621 #ifdef _UNDO_DEBUG_
3622                         fprintf(stderr, "Song::executeOperationGroup1:AddAudioCtrlVal\n");
3623 #endif
3624                         CtrlListList* cll = static_cast<AudioTrack*>(editable_track)->controller();
3625                         iCtrlList icl = cll->find(i->_audioCtrlID);
3626                         if(icl != cll->end())
3627                         {
3628                           //CtrlList* cl = icl->second;
3629                           //iCtrl ic = cl->find(i->_audioCtrlFrame);
3630                           //if(ic != cl->end())
3631                           //  // An existing value was found. Replace it with the new value.
3632                           //  pendingOperations.add(PendingOperationItem(icl->second, ic, i->_audioCtrlVal, PendingOperationItem::ModifyAudioCtrlVal));
3633                           //else
3634                             // Add the new value.
3635                             pendingOperations.add(PendingOperationItem(icl->second, i->_audioCtrlFrame, i->_audioCtrlVal, PendingOperationItem::AddAudioCtrlVal));
3636                           updateFlags |= SC_AUDIO_CONTROLLER;
3637                         }
3638                   }
3639                   break;
3640 
3641                   case UndoOp::DeleteAudioCtrlVal:
3642                   {
3643 #ifdef _UNDO_DEBUG_
3644                         fprintf(stderr, "Song::executeOperationGroup1:DeleteAudioCtrlVal\n");
3645 #endif
3646                         CtrlListList* cll = static_cast<AudioTrack*>(editable_track)->controller();
3647                         iCtrlList icl = cll->find(i->_audioCtrlID);
3648                         if(icl != cll->end())
3649                         {
3650                           CtrlList* cl = icl->second;
3651                           iCtrl ic = cl->find(i->_audioCtrlFrame);
3652                           if(ic != cl->end())
3653                           {
3654                             i->_audioCtrlVal = ic->second.val; // Store the existing value so it can be restored.
3655                             pendingOperations.add(PendingOperationItem(cl, ic, PendingOperationItem::DeleteAudioCtrlVal));
3656                             updateFlags |= SC_AUDIO_CONTROLLER;
3657                           }
3658                         }
3659                   }
3660                   break;
3661 
3662                   case UndoOp::ModifyAudioCtrlVal:
3663                   {
3664 #ifdef _UNDO_DEBUG_
3665                         fprintf(stderr, "Song::executeOperationGroup1:ModifyAudioCtrlVal\n");
3666 #endif
3667                         CtrlListList* cll = static_cast<AudioTrack*>(editable_track)->controller();
3668                         iCtrlList icl = cll->find(i->_audioCtrlID);
3669                         if(icl != cll->end())
3670                         {
3671                           CtrlList* cl = icl->second;
3672                           iCtrl ic = cl->find(i->_audioCtrlFrame);
3673                           if(ic != cl->end())
3674                           {
3675                             i->_audioCtrlVal = ic->second.val; // Store the existing value so it can be restored.
3676                             pendingOperations.add(PendingOperationItem(icl->second, ic, i->_audioNewCtrlFrame, i->_audioNewCtrlVal, PendingOperationItem::ModifyAudioCtrlVal));
3677                             updateFlags |= SC_AUDIO_CONTROLLER;
3678                           }
3679                         }
3680                   }
3681                   break;
3682 
3683                   case UndoOp::ModifyAudioCtrlValList:
3684                   {
3685 #ifdef _UNDO_DEBUG_
3686                         fprintf(stderr, "Song::executeOperationGroup1:ModifyAudioCtrlValList\n");
3687 #endif
3688                         // Take either id. At least one list must be valid.
3689                         const int id = i->_eraseCtrlList ? i->_eraseCtrlList->id() : i->_addCtrlList->id();
3690                         iCtrlList icl = i->_ctrlListList->find(id);
3691                         if(icl != i->_ctrlListList->end())
3692                         {
3693                           // Make a complete copy of the controller list. The list will be quickly switched in the realtime stage.
3694                           // The Pending Operations system will take 'ownership' of this and delete it at the appropriate time.
3695                           CtrlList* new_list = new CtrlList(*icl->second, CtrlList::ASSIGN_PROPERTIES | CtrlList::ASSIGN_VALUES);
3696 
3697                           if(i->_eraseCtrlList && !i->_eraseCtrlList->empty())
3698                           {
3699                               iCtrl n_s = new_list->find(i->_eraseCtrlList->begin()->second.frame); // The first item to be erased.
3700                               ciCtrl e_e = i->_eraseCtrlList->end();
3701                               --e_e;
3702                               iCtrl n_e = new_list->find(e_e->second.frame); // The last item to be erased.
3703                               if(n_s != new_list->end() && n_e != new_list->end())
3704                               {
3705                                 // Since std range does NOT include the last iterator, increment n_e so erase will get all items.
3706                                 ++n_e;
3707                                 new_list->erase(n_s, n_e);
3708                               }
3709                           }
3710 
3711                           // Add any items in the add list...
3712                           if(i->_addCtrlList && !i->_addCtrlList->empty())
3713                             new_list->insert(i->_addCtrlList->begin(), i->_addCtrlList->end());
3714 
3715                           // The operation will quickly switch the list in the RT stage then the delete the old list in the non-RT stage.
3716                           pendingOperations.add(PendingOperationItem(icl, new_list, PendingOperationItem::ModifyAudioCtrlValList));
3717                           updateFlags |= SC_AUDIO_CONTROLLER_LIST;
3718                         }
3719                   }
3720                   break;
3721 
3722 
3723                   case UndoOp::SetInstrument:
3724                   {
3725 #ifdef _UNDO_DEBUG_
3726                         fprintf(stderr, "Song::executeOperationGroup1:SetInstrument\n");
3727 #endif
3728                         // Set the new value.
3729                         pendingOperations.add(PendingOperationItem(
3730                           i->_midiPort, i->_newMidiInstrument,
3731                           PendingOperationItem::SetInstrument));
3732                         updateFlags |= SC_MIDI_INSTRUMENT;
3733                   }
3734                   break;
3735 
3736 
3737                   case UndoOp::AddTempo:
3738 #ifdef _UNDO_DEBUG_
3739                         fprintf(stderr, "Song::executeOperationGroup1:AddTempo ** calling tempomap.addOperation tick:%d tempo:%d\n", i->a, i->b);
3740 #endif
3741                         // Create the new list if it doesn't already exist.
3742                         // Make a copy of the original list.
3743                         if(!new_tempo_list)
3744                         {
3745                           new_tempo_list = new TempoList();
3746                           new_tempo_list->copy(MusEGlobal::tempomap);
3747                         }
3748                         // Defer normalize until end of stage 2.
3749                         new_tempo_list->addTempo(i->a, i->b, false);
3750 
3751                         updateFlags |= SC_TEMPO;
3752                         break;
3753 
3754                   case UndoOp::DeleteTempo:
3755 #ifdef _UNDO_DEBUG_
3756                         fprintf(stderr, "Song::executeOperationGroup1:DeleteTempo ** calling tempomap.delOperation tick:%d\n", i->a);
3757 #endif
3758                         // Create the new list if it doesn't already exist.
3759                         // Make a copy of the original list.
3760                         if(!new_tempo_list)
3761                         {
3762                           new_tempo_list = new TempoList();
3763                           new_tempo_list->copy(MusEGlobal::tempomap);
3764                         }
3765                         // Defer normalize until end of stage 2.
3766                         new_tempo_list->delTempo(i->a, false);
3767 
3768                         updateFlags |= SC_TEMPO;
3769                         break;
3770 
3771                   case UndoOp::ModifyTempo:
3772 #ifdef _UNDO_DEBUG_
3773                         fprintf(stderr, "Song::executeOperationGroup1:ModifyTempo ** calling tempomap.addOperation tick:%d tempo:%d\n", i->a, i->c);
3774 #endif
3775                         // Create the new list if it doesn't already exist.
3776                         // Make a copy of the original list.
3777                         if(!new_tempo_list)
3778                         {
3779                           new_tempo_list = new TempoList();
3780                           new_tempo_list->copy(MusEGlobal::tempomap);
3781                         }
3782                         // Defer normalize until end of stage 2.
3783                         new_tempo_list->addTempo(i->a, i->c, false);
3784 
3785                         updateFlags |= SC_TEMPO;
3786                         break;
3787 
3788 //                   case UndoOp::SetTempo:
3789 //                         // Only if the master is on.
3790 //                         if(MusEGlobal::tempomap.masterFlag())
3791 //                         {
3792 // #ifdef _UNDO_DEBUG_
3793 //                           fprintf(stderr, "Song::executeOperationGroup1:SetTempo ** calling tempomap.addOperation tick:%d tempo:%d\n", i->a, i->b);
3794 // #endif
3795 //                           MusEGlobal::tempomap.addOperation(i->a, i->b, pendingOperations);
3796 //                         }
3797 //                         else
3798 //                           pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, i->a, PendingOperationItem::SetStaticTempo));
3799 //                         updateFlags |= SC_TEMPO;
3800 //                         break;
3801 
3802                   case UndoOp::SetStaticTempo:
3803 #ifdef _UNDO_DEBUG_
3804                         fprintf(stderr, "Song::executeOperationGroup1:SetStaticTempo ** adding SetStaticTempo operation\n");
3805 #endif
3806                         pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, i->a, PendingOperationItem::SetStaticTempo));
3807                         updateFlags |= SC_TEMPO;
3808                         break;
3809 
3810                   case UndoOp::SetGlobalTempo:
3811 #ifdef _UNDO_DEBUG_
3812                         fprintf(stderr, "Song::executeOperationGroup1:SetGlobalTempo ** adding SetGlobalTempo operation\n");
3813 #endif
3814                         pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, i->a, PendingOperationItem::SetGlobalTempo));
3815                         updateFlags |= SC_TEMPO;
3816                         break;
3817 
3818                   case UndoOp::EnableMasterTrack:
3819 #ifdef _UNDO_DEBUG_
3820                         fprintf(stderr, "Song::executeOperationGroup1:EnableMasterTrack ** adding EnableMasterTrack operation\n");
3821 #endif
3822                         pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, (bool)i->a, PendingOperationItem::SetUseMasterTrack));
3823                         updateFlags |= SC_MASTER;
3824                         break;
3825 
3826                   case UndoOp::AddSig:
3827 #ifdef _UNDO_DEBUG_
3828                         fprintf(stderr, "Song::executeOperationGroup1:AddSig ** calling MusEGlobal::sigmap.addOperation\n");
3829 #endif
3830                         // Create the new list if it doesn't already exist.
3831                         // Make a copy of the original list.
3832                         if(!new_sig_list)
3833                         {
3834                           new_sig_list = new SigList();
3835                           new_sig_list->copy(MusEGlobal::sigmap);
3836                         }
3837                         // Defer normalize until end of stage 2.
3838                         new_sig_list->add(i->a, MusECore::TimeSignature(i->b, i->c), false);
3839 
3840                         updateFlags |= SC_SIG;
3841                         break;
3842 
3843                   case UndoOp::DeleteSig:
3844 #ifdef _UNDO_DEBUG_
3845                         fprintf(stderr, "Song::executeOperationGroup1:DeleteSig ** calling MusEGlobal::sigmap.delOperation\n");
3846 #endif
3847                         // Create the new list if it doesn't already exist.
3848                         // Make a copy of the original list.
3849                         if(!new_sig_list)
3850                         {
3851                           new_sig_list = new SigList();
3852                           new_sig_list->copy(MusEGlobal::sigmap);
3853                         }
3854                         // Defer normalize until end of stage 2.
3855                         new_sig_list->del(i->a, false);
3856 
3857                         updateFlags |= SC_SIG;
3858                         break;
3859 
3860                   case UndoOp::ModifySig:
3861 #ifdef _UNDO_DEBUG_
3862                         fprintf(stderr, "Song::executeOperationGroup1:ModifySig ** calling MusEGlobal::sigmap.addOperation\n");
3863 #endif
3864                         // Create the new list if it doesn't already exist.
3865                         // Make a copy of the original list.
3866                         if(!new_sig_list)
3867                         {
3868                           new_sig_list = new SigList();
3869                           new_sig_list->copy(MusEGlobal::sigmap);
3870                         }
3871                         // Defer normalize until end of stage 2.
3872                         new_sig_list->add(i->a, MusECore::TimeSignature(i->d, i->e), false);
3873 
3874                         updateFlags |= SC_SIG;
3875                         break;
3876 
3877 
3878                   case UndoOp::AddKey:
3879 #ifdef _UNDO_DEBUG_
3880                         fprintf(stderr, "Song::executeOperationGroup1:AddKey ** calling keymap.addOperation\n");
3881 #endif
3882                         // Create the new list if it doesn't already exist.
3883                         // Make a copy of the original list.
3884                         if(!new_key_list)
3885                         {
3886                           new_key_list = new KeyList();
3887                           new_key_list->copy(MusEGlobal::keymap);
3888                         }
3889                         new_key_list->addKey(i->a, key_enum(i->b), i->c);
3890 
3891                         updateFlags |= SC_KEY;
3892                         break;
3893 
3894                   case UndoOp::DeleteKey:
3895 #ifdef _UNDO_DEBUG_
3896                         fprintf(stderr, "Song::executeOperationGroup1:DeleteKey ** calling keymap.delOperation\n");
3897 #endif
3898                         // Create the new list if it doesn't already exist.
3899                         // Make a copy of the original list.
3900                         if(!new_key_list)
3901                         {
3902                           new_key_list = new KeyList();
3903                           new_key_list->copy(MusEGlobal::keymap);
3904                         }
3905                         new_key_list->delKey(i->a);
3906 
3907                         updateFlags |= SC_KEY;
3908                         break;
3909 
3910                   case UndoOp::ModifyKey:
3911 #ifdef _UNDO_DEBUG_
3912                         fprintf(stderr, "Song::executeOperationGroup1:ModifyKey ** calling keymap.addOperation\n");
3913 #endif
3914                         // Create the new list if it doesn't already exist.
3915                         // Make a copy of the original list.
3916                         if(!new_key_list)
3917                         {
3918                           new_key_list = new KeyList();
3919                           new_key_list->copy(MusEGlobal::keymap);
3920                         }
3921                         new_key_list->addKey(i->a, key_enum(i->d), i->e);
3922 
3923                         updateFlags |= SC_KEY;
3924                         break;
3925 
3926                   case UndoOp::ModifySongLen:
3927 #ifdef _UNDO_DEBUG_
3928                         fprintf(stderr, "Song::executeOperationGroup1:ModifySongLen ** adding ModifySongLen operation\n");
3929 #endif
3930                         pendingOperations.add(PendingOperationItem(i->a, PendingOperationItem::ModifySongLength));
3931                         updateFlags |= SC_EVERYTHING;  // set all flags   // TODO Refine this! Too many flags.  // REMOVE Tim.
3932                         //updateFlags |= SC_SONG_LEN;
3933                         break;
3934 
3935                   case UndoOp::ModifyMidiDivision:
3936 #ifdef _UNDO_DEBUG_
3937                         fprintf(stderr, "Song::executeOperationGroup1:ModifyMidiDivision\n");
3938 #endif
3939                         MusEGlobal::config.division = i->a;
3940                         // Make sure the AL namespace variable mirrors our variable.
3941                         AL::division = MusEGlobal::config.division;
3942                         // Defer normalize until end of stage 2.
3943                         updateFlags |= SC_DIVISION_CHANGED;
3944                         break;
3945 
3946                   case UndoOp::EnableAllAudioControllers:
3947 #ifdef _UNDO_DEBUG_
3948                         fprintf(stderr, "Song::executeOperationGroup1:EnableAllAudioControllers\n");
3949 #endif
3950                         pendingOperations.add(PendingOperationItem(PendingOperationItem::EnableAllAudioControllers));
3951                         updateFlags |= SC_AUDIO_CONTROLLER;
3952                         break;
3953 
3954                   case UndoOp::NormalizeMidiDivision:
3955 #ifdef _UNDO_DEBUG_
3956                         fprintf(stderr, "Song::executeOperationGroup1:NormalizeMidiDivision\n");
3957 #endif
3958                         // Nothing to do here.
3959                         // Defer normalize until end of stage 2.
3960                         updateFlags |= SC_DIVISION_CHANGED;
3961                         break;
3962 
3963                   case UndoOp::GlobalSelectAllEvents:
3964 #ifdef _UNDO_DEBUG_
3965                         fprintf(stderr, "Song::executeOperationGroup1:GlobalSelectAllEvents\n");
3966 #endif
3967                         pendingOperations.add(PendingOperationItem(tracks(), i->a, 0, 0, PendingOperationItem::GlobalSelectAllEvents));
3968                         updateFlags |= SC_SELECTION;
3969                         break;
3970 
3971                   case UndoOp::AddMarker:
3972                           // Create the new list if it doesn't already exist.
3973                           // Make a copy of the original list.
3974                           if(!new_marker_list)
3975                             new_marker_list = new MarkerList(*marker());
3976                           if(i->newMarker)
3977                             new_marker_list->add(*i->newMarker);
3978                           updateFlags |= SC_MARKER_INSERTED;
3979                         break;
3980 
3981                   case UndoOp::DeleteMarker:
3982                           // Create the new list if it doesn't already exist.
3983                           // Make a copy of the original list.
3984                           if(!new_marker_list)
3985                             new_marker_list = new MarkerList(*marker());
3986                           if(i->oldMarker)
3987                             new_marker_list->remove(*i->oldMarker);
3988                           updateFlags |= SC_MARKER_REMOVED;
3989                         break;
3990 
3991                   case UndoOp::ModifyMarker:
3992                   case UndoOp::SetMarkerPos:
3993                           // Create the new list if it doesn't already exist.
3994                           // Make a copy of the original list.
3995                           if(!new_marker_list)
3996                             new_marker_list = new MarkerList(*marker());
3997                           if(i->oldMarker)
3998                             new_marker_list->remove(*i->oldMarker);
3999                           if(i->newMarker)
4000                             new_marker_list->add(*i->newMarker);
4001                           updateFlags |= SC_MARKER_MODIFIED;
4002                         break;
4003 
4004                   default:
4005                         break;
4006                   }
4007             }
4008 
4009       if(new_tempo_list)
4010         pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, new_tempo_list, PendingOperationItem::ModifyTempoList));
4011 
4012       if(new_sig_list)
4013         pendingOperations.add(PendingOperationItem(&MusEGlobal::sigmap, new_sig_list, PendingOperationItem::ModifySigList));
4014 
4015       if(new_key_list)
4016         pendingOperations.add(PendingOperationItem(&MusEGlobal::keymap, new_key_list, PendingOperationItem::ModifyKeyList));
4017 
4018       if(new_marker_list)
4019         pendingOperations.add(PendingOperationItem(&_markerList, new_marker_list, PendingOperationItem::ModifyMarkerList));
4020       }
4021 
4022 //---------------------------------------------------------
4023 //   executeOperationGroup3
4024 //    non realtime context
4025 //---------------------------------------------------------
4026 
executeOperationGroup3(Undo & operations)4027 void Song::executeOperationGroup3(Undo& operations)
4028       {
4029       pendingOperations.executeNonRTStage();
4030 #ifdef _UNDO_DEBUG_
4031       fprintf(stderr, "Song::executeOperationGroup3 *** Calling pendingOperations.clear()\n");
4032 #endif
4033       pendingOperations.clear();
4034       //bool song_has_changed = !operations.empty();
4035       for (iUndoOp i = operations.begin(); i != operations.end(); ) {
4036             Track* editable_track = const_cast<Track*>(i->track);
4037 // uncomment if needed            Track* editable_property_track = const_cast<Track*>(i->_propertyTrack);
4038             Part* editable_part = const_cast<Part*>(i->part); // uncomment if needed
4039             switch(i->type) {
4040                   case UndoOp::AddTrack:
4041                         switch(editable_track->type())
4042                         {
4043                           case Track::AUDIO_OUTPUT:
4044                             // Connect audio output ports to Jack ports...
4045                             if(MusEGlobal::checkAudioDevice() && MusEGlobal::audio->isRunning())
4046                             {
4047                               AudioOutput* ao = (AudioOutput*)editable_track;
4048                               for(int ch = 0; ch < ao->channels(); ++ch)
4049                               {
4050                                 void* our_port = ao->jackPort(ch);
4051                                 if(!our_port)
4052                                   continue;
4053                                 const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port);
4054                                 if(!our_port_name)
4055                                   continue;
4056                                 RouteList* rl = ao->outRoutes();
4057                                 for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir)
4058                                 {
4059                                   if(ir->type != Route::JACK_ROUTE || ir->channel != ch)
4060                                     continue;
4061                                   const char* route_name = ir->persistentJackPortName;
4062                                   //if(ir->jackPort)
4063                                   if(!MusEGlobal::audioDevice->findPort(route_name))
4064                                     continue;
4065                                   //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name))
4066                                   MusEGlobal::audioDevice->connect(our_port_name, route_name);
4067                                   updateFlags |= SC_ROUTE;
4068                                 }
4069                               }
4070                             }
4071                           break;
4072 
4073                           case Track::AUDIO_INPUT:
4074                             // Connect Jack ports to audio input ports...
4075                             if(MusEGlobal::checkAudioDevice() && MusEGlobal::audio->isRunning())
4076                             {
4077                               AudioInput* ai = (AudioInput*)editable_track;
4078                               for(int ch = 0; ch < ai->channels(); ++ch)
4079                               {
4080                                 void* our_port = ai->jackPort(ch);
4081                                 if(!our_port)
4082                                   continue;
4083                                 const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port);
4084                                 if(!our_port_name)
4085                                   continue;
4086                                 RouteList* rl = ai->inRoutes();
4087                                 for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir)
4088                                 {
4089                                   if(ir->type != Route::JACK_ROUTE || ir->channel != ch)
4090                                     continue;
4091                                   const char* route_name = ir->persistentJackPortName;
4092                                   //if(ir->jackPort)
4093                                   if(!MusEGlobal::audioDevice->findPort(route_name))
4094                                     continue;
4095                                   //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name))
4096                                   MusEGlobal::audioDevice->connect(route_name, our_port_name);
4097                                   updateFlags |= SC_ROUTE;
4098                                 }
4099                               }
4100                             }
4101                           break;
4102 
4103                           default:
4104                             break;
4105                         }
4106 
4107                         break;
4108                   case UndoOp::DeleteTrack:
4109                         // Ensure that wave event sndfile file handles are closed.
4110                         // It should not be the job of the pending operations list to do this.
4111                         // TODO Coordinate close/open with part mute and/or track off.
4112                         editable_track->closeAllParts();
4113                         break;
4114                   case UndoOp::DeletePart:
4115                         // Ensure that wave event sndfile file handles are closed.
4116                         // It should not be the job of the pending operations list to do this.
4117                         // TODO Coordinate close/open with part mute and/or track off.
4118                         editable_part->closeAllEvents();
4119                         break;
4120                   case UndoOp::DeleteEvent: {
4121                           if(!i->nEvent.empty())
4122                           {
4123                             SndFileR f = i->nEvent.sndFile();
4124                             // Ensure that wave event sndfile file handle is closed.
4125                             // It should not be the job of the pending operations list to do this.
4126                             // TODO Coordinate close/open with part mute and/or track off.
4127                             if(!f.isNull() && f.isOpen())
4128                               f.close();
4129                           }
4130                         }
4131                         break;
4132                   case UndoOp::ModifyMidiDivision:
4133                         // This also tells all connected models to begin/end reset.
4134                         MusEGlobal::globalRasterizer->setDivision(i->a);
4135                         break;
4136                    default:
4137                         break;
4138                   }
4139 
4140             // Is the operation marked as non-undoable? Remove it from the list.
4141             if(i->_noUndo)
4142               i = operations.erase(i);
4143             else
4144               ++i;
4145             }
4146 
4147       // If some operations marked as non-undoable were removed, it is OK,
4148       //  because we only want dirty if an undoable operation was executed, right?
4149       if(!operations.empty())
4150       // Hm, no. ANY operation actually changes things, so yes, the song is dirty.
4151       //if(song_has_changed)
4152         emit sigDirty();
4153       }
4154 
4155 
empty() const4156 bool Undo::empty() const
4157 {
4158   if (std::list<UndoOp>::empty()) return true;
4159 
4160   for (const_iterator it=begin(); it!=end(); it++)
4161     if (it->type!=UndoOp::DoNothing)
4162       return false;
4163 
4164   return true;
4165 }
4166 
4167 } // namespace MusECore
4168