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