1 /*
2 * Copyright (C) 2019-2021 Alexandros Theodotou <alex at zrythm dot org>
3 *
4 * This file is part of Zrythm
5 *
6 * Zrythm is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Zrythm is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with Zrythm. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include "actions/arranger_selections.h"
21 #include "audio/audio_region.h"
22 #include "audio/automation_region.h"
23 #include "audio/automation_track.h"
24 #include "audio/chord_region.h"
25 #include "audio/chord_track.h"
26 #include "audio/marker_track.h"
27 #include "audio/router.h"
28 #include "audio/track.h"
29 #include "gui/backend/event.h"
30 #include "gui/backend/event_manager.h"
31 #include "gui/backend/arranger_selections.h"
32 #include "gui/widgets/arranger.h"
33 #include "gui/widgets/center_dock.h"
34 #include "project.h"
35 #include "settings/settings.h"
36 #include "utils/debug.h"
37 #include "utils/error.h"
38 #include "utils/flags.h"
39 #include "utils/math.h"
40 #include "utils/object_utils.h"
41 #include "utils/objects.h"
42 #include "utils/string.h"
43 #include "zrythm_app.h"
44
45 #include <glib/gi18n.h>
46
47 typedef enum
48 {
49 Z_ACTIONS_ARRANGER_SELECTIONS_ERROR_FAILED,
50 } ZActionsArrangerSelectionsError;
51
52 #define Z_ACTIONS_ARRANGER_SELECTIONS_ERROR \
53 z_actions_arranger_selections_error_quark ()
54 GQuark z_actions_arranger_selections_error_quark (void);
55 G_DEFINE_QUARK (
56 z-actions-arranger-selections-error-quark, z_actions_arranger_selections_error)
57
58 static void
59 move_obj_by_tracks_and_lanes (
60 ArrangerObject * obj,
61 const int tracks_diff,
62 const int lanes_diff,
63 bool use_index_in_prev_lane,
64 int index_in_prev_lane);
65
66 void
arranger_selections_action_init_loaded(ArrangerSelectionsAction * self)67 arranger_selections_action_init_loaded (
68 ArrangerSelectionsAction * self)
69 {
70 #define DO_SELECTIONS(sc) \
71 if (self->sc##_sel) \
72 { \
73 self->sel = \
74 (ArrangerSelections *) \
75 self->sc##_sel; \
76 self->sel_after = \
77 (ArrangerSelections *) \
78 self->sc##_sel_after; \
79 arranger_selections_init_loaded ( \
80 self->sel, false); \
81 if (self->sel_after) \
82 { \
83 arranger_selections_init_loaded ( \
84 self->sel_after, false); \
85 } \
86 }
87 DO_SELECTIONS (chord);
88 DO_SELECTIONS (tl);
89 DO_SELECTIONS (ma);
90 DO_SELECTIONS (automation);
91 DO_SELECTIONS (audio);
92
93 for (int j = 0; j < self->num_split_objs; j++)
94 {
95 if (self->region_r1[j])
96 {
97 arranger_object_init_loaded (
98 (ArrangerObject *) self->region_r1[j]);
99 arranger_object_init_loaded (
100 (ArrangerObject *) self->region_r2[j]);
101 self->r1[j] =
102 (ArrangerObject *)
103 self->region_r1[j];
104 self->r2[j] =
105 (ArrangerObject *)
106 self->region_r2[j];
107 }
108 else if (self->mn_r1[j])
109 {
110 arranger_object_init_loaded (
111 (ArrangerObject *) self->mn_r1[j]);
112 arranger_object_init_loaded (
113 (ArrangerObject *) self->mn_r2[j]);
114 self->r1[j] =
115 (ArrangerObject *) self->mn_r1[j];
116 self->r2[j] =
117 (ArrangerObject *) self->mn_r2[j];
118 }
119 }
120
121 if (self->region_before)
122 {
123 arranger_object_init_loaded (
124 (ArrangerObject *) self->region_before);
125 }
126 if (self->region_after)
127 {
128 arranger_object_init_loaded (
129 (ArrangerObject *) self->region_after);
130 }
131 }
132
133 /**
134 * Sets the selections used when serializing.
135 *
136 * @param clone Clone the given selections first.
137 * @param is_after If the selections should be set
138 * to \ref ArrangerSelectionsAction.sel_after.
139 */
140 static void
set_selections(ArrangerSelectionsAction * self,ArrangerSelections * _sel,int clone,int is_after)141 set_selections (
142 ArrangerSelectionsAction * self,
143 ArrangerSelections * _sel,
144 int clone,
145 int is_after)
146 {
147 ArrangerSelections * sel = _sel;
148 if (clone)
149 {
150 sel = arranger_selections_clone (_sel);
151 }
152
153 if (ZRYTHM_TESTING)
154 {
155 arranger_selections_verify (_sel);
156 if (_sel != sel)
157 arranger_selections_verify (sel);
158 }
159
160 if (is_after)
161 {
162 self->sel_after = sel;
163 }
164 else
165 {
166 self->sel = sel;
167 }
168
169 #define SET_SEL(cc,sc) \
170 if (is_after) \
171 self->sc##_sel_after = \
172 (cc##Selections *) sel; \
173 else \
174 self->sc##_sel = \
175 (cc##Selections *) sel; \
176 break
177
178 switch (sel->type)
179 {
180 case ARRANGER_SELECTIONS_TYPE_CHORD:
181 SET_SEL (Chord, chord);
182 case ARRANGER_SELECTIONS_TYPE_TIMELINE:
183 SET_SEL (Timeline, tl);
184 case ARRANGER_SELECTIONS_TYPE_MIDI:
185 SET_SEL (MidiArranger, ma);
186 case ARRANGER_SELECTIONS_TYPE_AUTOMATION:
187 SET_SEL (Automation, automation);
188 case ARRANGER_SELECTIONS_TYPE_AUDIO:
189 SET_SEL (Audio, audio);
190 default:
191 g_return_if_reached ();
192 }
193
194 #undef SET_SEL
195 }
196
197 /**
198 * Optionally clones the given objects and saves
199 * them to self->r1 and self->r2.
200 */
201 NONNULL
202 static void
set_split_objects(ArrangerSelectionsAction * self,int i,ArrangerObject * r1,ArrangerObject * r2,bool clone)203 set_split_objects (
204 ArrangerSelectionsAction * self,
205 int i,
206 ArrangerObject * r1,
207 ArrangerObject * r2,
208 bool clone)
209 {
210 if (clone)
211 {
212 r1 = arranger_object_clone (r1);
213 r2 = arranger_object_clone (r2);
214 }
215 self->r1[i] = r1;
216 self->r2[i] = r2;
217
218 switch (r1->type)
219 {
220 case ARRANGER_OBJECT_TYPE_REGION:
221 self->region_r1[i] = (ZRegion *) r1;
222 self->region_r2[i] = (ZRegion *) r2;
223 break;
224 case ARRANGER_OBJECT_TYPE_MIDI_NOTE:
225 self->mn_r1[i] = (MidiNote *) r1;
226 self->mn_r2[i] = (MidiNote *) r2;
227 break;
228 default:
229 g_return_if_reached ();
230 }
231 }
232
233 static void
free_split_objects(ArrangerSelectionsAction * self,int i)234 free_split_objects (
235 ArrangerSelectionsAction * self,
236 int i)
237 {
238 arranger_object_free (
239 self->r1[i]);
240 arranger_object_free (
241 self->r2[i]);
242
243 self->r1[i] = NULL;
244 self->r2[i] = NULL;
245 self->mn_r1[i] = NULL;
246 self->mn_r2[i] = NULL;
247 self->region_r1[i] = NULL;
248 self->region_r2[i] = NULL;
249 }
250
251 static ArrangerSelectionsAction *
_create_action(ArrangerSelections * sel)252 _create_action (
253 ArrangerSelections * sel)
254 {
255 ArrangerSelectionsAction * self =
256 object_new (ArrangerSelectionsAction);
257
258 set_selections (self, sel, true, false);
259 self->first_run = true;
260
261 undoable_action_init (
262 (UndoableAction *) self,
263 UA_ARRANGER_SELECTIONS);
264
265 return self;
266 }
267
268 static ArrangerSelections *
get_actual_arranger_selections(ArrangerSelectionsAction * self)269 get_actual_arranger_selections (
270 ArrangerSelectionsAction * self)
271 {
272 switch (self->sel->type)
273 {
274 case ARRANGER_SELECTIONS_TYPE_TIMELINE:
275 return
276 (ArrangerSelections *) TL_SELECTIONS;
277 case ARRANGER_SELECTIONS_TYPE_MIDI:
278 return
279 (ArrangerSelections *) MA_SELECTIONS;
280 case ARRANGER_SELECTIONS_TYPE_AUTOMATION:
281 return
282 (ArrangerSelections *)
283 AUTOMATION_SELECTIONS;
284 case ARRANGER_SELECTIONS_TYPE_CHORD:
285 return
286 (ArrangerSelections *) CHORD_SELECTIONS;
287 case ARRANGER_SELECTIONS_TYPE_AUDIO:
288 return
289 (ArrangerSelections *) AUDIO_SELECTIONS;
290 default:
291 g_return_val_if_reached (NULL);
292 break;
293 }
294
295 g_return_val_if_reached (NULL);
296 }
297
298 /**
299 * Creates a new action for moving or duplicating
300 * objects.
301 *
302 * @param move True to move, false to duplicate.
303 * @param already_moved If this is true, the first
304 * DO will do nothing.
305 * @param delta_normalized_amount Difference in a
306 * normalized amount, such as automation point
307 * normalized value.
308 */
309 UndoableAction *
arranger_selections_action_new_move_or_duplicate(ArrangerSelections * sel,const bool move,const double ticks,const int delta_chords,const int delta_pitch,const int delta_tracks,const int delta_lanes,const double delta_normalized_amount,const bool already_moved,GError ** error)310 arranger_selections_action_new_move_or_duplicate (
311 ArrangerSelections * sel,
312 const bool move,
313 const double ticks,
314 const int delta_chords,
315 const int delta_pitch,
316 const int delta_tracks,
317 const int delta_lanes,
318 const double delta_normalized_amount,
319 const bool already_moved,
320 GError ** error)
321 {
322 g_return_val_if_fail (
323 IS_ARRANGER_SELECTIONS (sel) &&
324 arranger_selections_has_any (sel), NULL);
325
326 ArrangerSelectionsAction * self =
327 _create_action (sel);
328 UndoableAction * ua = (UndoableAction *) self;
329 if (move)
330 {
331 self->type = AS_ACTION_MOVE;
332 }
333 else
334 {
335 self->type = AS_ACTION_DUPLICATE;
336 set_selections (self, sel, true, true);
337 }
338
339 if (!already_moved)
340 {
341 self->first_run = 0;
342 }
343
344 self->ticks = ticks;
345 self->delta_chords = delta_chords;
346 self->delta_lanes = delta_lanes;
347 self->delta_tracks = delta_tracks;
348 self->delta_pitch = delta_pitch;
349 self->delta_normalized_amount =
350 delta_normalized_amount;
351
352 return ua;
353 }
354
355 /**
356 * Creates a new action for linking regions.
357 *
358 * @param already_moved If this is true, the first
359 * DO will do nothing.
360 * @param sel_before Original selections.
361 * @param sel_after Selections after duplication.
362 */
363 UndoableAction *
arranger_selections_action_new_link(ArrangerSelections * sel_before,ArrangerSelections * sel_after,const double ticks,const int delta_tracks,const int delta_lanes,const bool already_moved,GError ** error)364 arranger_selections_action_new_link (
365 ArrangerSelections * sel_before,
366 ArrangerSelections * sel_after,
367 const double ticks,
368 const int delta_tracks,
369 const int delta_lanes,
370 const bool already_moved,
371 GError ** error)
372 {
373 g_return_val_if_fail (
374 sel_before && sel_after, NULL);
375
376 ArrangerSelectionsAction * self =
377 _create_action (sel_before);
378 self->type = AS_ACTION_LINK;
379
380 set_selections (
381 self, sel_after, F_CLONE, F_IS_AFTER);
382
383 if (!already_moved)
384 self->first_run = false;
385
386 self->ticks = ticks;
387 self->delta_tracks = delta_tracks;
388 self->delta_lanes = delta_lanes;
389
390 UndoableAction * ua = (UndoableAction *) self;
391 return ua;
392 }
393
394 /**
395 * Creates a new action for creating/deleting objects.
396 *
397 * @param create 1 to create 0 to delte.
398 */
399 UndoableAction *
arranger_selections_action_new_create_or_delete(ArrangerSelections * sel,const bool create,GError ** error)400 arranger_selections_action_new_create_or_delete (
401 ArrangerSelections * sel,
402 const bool create,
403 GError ** error)
404 {
405 g_return_val_if_fail (
406 IS_ARRANGER_SELECTIONS (sel) &&
407 arranger_selections_has_any (sel), NULL);
408
409 if (arranger_selections_contains_undeletable_object (
410 sel))
411 {
412 g_set_error (
413 error, Z_ACTIONS_ARRANGER_SELECTIONS_ERROR,
414 Z_ACTIONS_ARRANGER_SELECTIONS_ERROR_FAILED,
415 "%s",
416 _("Arranger selections contain an "
417 "undeletable object"));
418 return NULL;
419 }
420
421 ArrangerSelectionsAction * self =
422 _create_action (sel);
423 if (create)
424 {
425 self->type = AS_ACTION_CREATE;
426 }
427 else
428 {
429 self->type = AS_ACTION_DELETE;
430 }
431
432 UndoableAction * ua = (UndoableAction *) self;
433 return ua;
434 }
435
436 UndoableAction *
arranger_selections_action_new_record(ArrangerSelections * sel_before,ArrangerSelections * sel_after,const bool already_recorded,GError ** error)437 arranger_selections_action_new_record (
438 ArrangerSelections * sel_before,
439 ArrangerSelections * sel_after,
440 const bool already_recorded,
441 GError ** error)
442 {
443 ArrangerSelectionsAction * self =
444 _create_action (sel_before);
445 self->type = AS_ACTION_RECORD;
446
447 set_selections (self, sel_after, 1, 1);
448
449 if (!already_recorded)
450 self->first_run = 0;
451
452 UndoableAction * ua = (UndoableAction *) self;
453 return ua;
454 }
455
456 /**
457 * Creates a new action for editing properties
458 * of an object.
459 *
460 * @param sel_before The selections before the
461 * change.
462 * @param sel_after The selections after the
463 * change, or NULL if not already edited.
464 * @param type Indication of which field has changed.
465 */
466 UndoableAction *
arranger_selections_action_new_edit(ArrangerSelections * sel_before,ArrangerSelections * sel_after,ArrangerSelectionsActionEditType type,bool already_edited,GError ** error)467 arranger_selections_action_new_edit (
468 ArrangerSelections * sel_before,
469 ArrangerSelections * sel_after,
470 ArrangerSelectionsActionEditType type,
471 bool already_edited,
472 GError ** error)
473 {
474 ArrangerSelectionsAction * self =
475 _create_action (sel_before);
476 self->type = AS_ACTION_EDIT;
477
478 self->edit_type = type;
479
480 set_selections (
481 self, sel_before, F_CLONE, F_NOT_IS_AFTER);
482
483 if (sel_after)
484 {
485 set_selections (
486 self, sel_after, F_CLONE, F_IS_AFTER);
487 }
488 else
489 {
490 if (already_edited)
491 {
492 g_critical (
493 "sel_after must be passed or already "
494 "edited must be false");
495 return NULL;
496 }
497
498 set_selections (
499 self, sel_before, F_CLONE, F_IS_AFTER);
500 }
501
502 if (!already_edited)
503 {
504 self->first_run = 0;
505 }
506
507 UndoableAction * ua = (UndoableAction *) self;
508 return ua;
509 }
510
511 /**
512 * Wrapper over
513 * arranger_selections_action_new_edit() for MIDI
514 * functions.
515 */
516 UndoableAction *
arranger_selections_action_new_edit_midi_function(ArrangerSelections * sel_before,MidiFunctionType midi_func_type,GError ** error)517 arranger_selections_action_new_edit_midi_function (
518 ArrangerSelections * sel_before,
519 MidiFunctionType midi_func_type,
520 GError ** error)
521 {
522 ArrangerSelections * sel_after =
523 arranger_selections_clone (sel_before);
524
525 GError * err = NULL;
526 int ret =
527 midi_function_apply (
528 sel_after, midi_func_type, &err);
529 if (ret != 0)
530 {
531 PROPAGATE_PREFIXED_ERROR (
532 error, err, "%s",
533 _("Failed to apply MIDI function"));
534 arranger_selections_free_full (sel_after);
535 return NULL;
536 }
537
538 UndoableAction * ua =
539 arranger_selections_action_new_edit (
540 sel_before, sel_after,
541 ARRANGER_SELECTIONS_ACTION_EDIT_EDITOR_FUNCTION,
542 F_NOT_ALREADY_EDITED, error);
543
544 arranger_selections_free_full (sel_after);
545
546 return ua;
547 }
548
549 /**
550 * Wrapper over
551 * arranger_selections_action_new_edit() for
552 * automation functions.
553 */
554 UndoableAction *
arranger_selections_action_new_edit_automation_function(ArrangerSelections * sel_before,AutomationFunctionType automation_func_type,GError ** error)555 arranger_selections_action_new_edit_automation_function (
556 ArrangerSelections * sel_before,
557 AutomationFunctionType automation_func_type,
558 GError ** error)
559 {
560 ArrangerSelections * sel_after =
561 arranger_selections_clone (sel_before);
562
563 GError * err = NULL;
564 int ret =
565 automation_function_apply (
566 sel_after, automation_func_type, &err);
567 if (ret != 0)
568 {
569 PROPAGATE_PREFIXED_ERROR (
570 error, err, "%s",
571 _("Failed to apply automation function"));
572 arranger_selections_free_full (sel_after);
573 return NULL;
574 }
575
576 UndoableAction * ua =
577 arranger_selections_action_new_edit (
578 sel_before, sel_after,
579 ARRANGER_SELECTIONS_ACTION_EDIT_EDITOR_FUNCTION,
580 F_NOT_ALREADY_EDITED, error);
581
582 arranger_selections_free_full (sel_after);
583
584 return ua;
585 }
586
587 /**
588 * Wrapper over
589 * arranger_selections_action_new_edit() for
590 * automation functions.
591 */
592 UndoableAction *
arranger_selections_action_new_edit_audio_function(ArrangerSelections * sel_before,AudioFunctionType audio_func_type,const char * uri,GError ** error)593 arranger_selections_action_new_edit_audio_function (
594 ArrangerSelections * sel_before,
595 AudioFunctionType audio_func_type,
596 const char * uri,
597 GError ** error)
598 {
599 /* prepare selections before */
600 ArrangerSelections * sel_before_clone =
601 arranger_selections_clone (sel_before);
602
603 g_debug (
604 "saving file before applying audio func...");
605 GError * err = NULL;
606 int ret =
607 audio_function_apply (
608 sel_before_clone, AUDIO_FUNCTION_INVALID,
609 NULL, &err);
610 if (ret != 0)
611 {
612 PROPAGATE_PREFIXED_ERROR (
613 error, err, "%s",
614 _("Failed to apply audio function"));
615 arranger_selections_free_full (
616 sel_before_clone);
617 return NULL;
618 }
619
620 ArrangerSelections * sel_after =
621 arranger_selections_clone (sel_before);
622 g_debug ("applying actual audio func...");
623 ret =
624 audio_function_apply (
625 sel_after, audio_func_type, uri, &err);
626 if (ret != 0)
627 {
628 PROPAGATE_PREFIXED_ERROR (
629 error, err, "%s",
630 _("Failed to apply audio function"));
631 arranger_selections_free_full (sel_after);
632 return NULL;
633 }
634
635 UndoableAction * ua =
636 arranger_selections_action_new_edit (
637 sel_before_clone, sel_after,
638 ARRANGER_SELECTIONS_ACTION_EDIT_EDITOR_FUNCTION,
639 F_NOT_ALREADY_EDITED, error);
640
641 arranger_selections_free_full (sel_after);
642 arranger_selections_free_full (sel_before_clone);
643
644 return ua;
645 }
646
647 /**
648 * Creates a new action for automation autofill.
649 *
650 * @param region_before The region before the
651 * change.
652 * @param region_after The region after the
653 * change.
654 * @param already_changed Whether the change was
655 * already made.
656 */
657 UndoableAction *
arranger_selections_action_new_automation_fill(ZRegion * region_before,ZRegion * region_after,bool already_changed,GError ** error)658 arranger_selections_action_new_automation_fill (
659 ZRegion * region_before,
660 ZRegion * region_after,
661 bool already_changed,
662 GError ** error)
663 {
664 ArrangerSelectionsAction * self =
665 object_new (ArrangerSelectionsAction);
666
667 UndoableAction * ua = (UndoableAction *) self;
668 undoable_action_init (
669 ua, UA_ARRANGER_SELECTIONS);
670
671 self->first_run = true;
672
673 self->type = AS_ACTION_AUTOMATION_FILL;
674
675 self->region_before =
676 (ZRegion *)
677 arranger_object_clone (
678 (ArrangerObject *) region_before);
679 self->region_after =
680 (ZRegion *)
681 arranger_object_clone (
682 (ArrangerObject *) region_after);
683
684 if (!already_changed)
685 {
686 self->first_run = 0;
687 }
688
689 return ua;
690 }
691
692 /**
693 * Creates a new action for splitting
694 * ArrangerObject's.
695 *
696 * @param pos Global position to split at.
697 */
698 UndoableAction *
arranger_selections_action_new_split(ArrangerSelections * sel,const Position * pos,GError ** error)699 arranger_selections_action_new_split (
700 ArrangerSelections * sel,
701 const Position * pos,
702 GError ** error)
703 {
704 ArrangerSelectionsAction * self =
705 _create_action (sel);
706 self->type = AS_ACTION_SPLIT;
707
708 ArrangerObject ** objs =
709 arranger_selections_get_all_objects (
710 self->sel, &self->num_split_objs);
711 free (objs);
712 self->pos = *pos;
713 position_update_frames_from_ticks (&self->pos);
714
715 UndoableAction * ua = (UndoableAction *) self;
716 return ua;
717 }
718
719 /**
720 * Creates a new action for merging
721 * ArrangerObject's.
722 */
723 UndoableAction *
arranger_selections_action_new_merge(ArrangerSelections * sel,GError ** error)724 arranger_selections_action_new_merge (
725 ArrangerSelections * sel,
726 GError ** error)
727 {
728 ArrangerSelectionsAction * self =
729 _create_action (sel);
730 self->type = AS_ACTION_MERGE;
731
732 UndoableAction * ua = (UndoableAction *) self;
733 return ua;
734 }
735
736 /**
737 * Creates a new action for resizing
738 * ArrangerObject's.
739 *
740 * @param ticks How many ticks to add to the resizing
741 * edge.
742 */
743 UndoableAction *
arranger_selections_action_new_resize(ArrangerSelections * sel,ArrangerSelectionsActionResizeType type,const double ticks,const bool already_resized,GError ** error)744 arranger_selections_action_new_resize (
745 ArrangerSelections * sel,
746 ArrangerSelectionsActionResizeType type,
747 const double ticks,
748 const bool already_resized,
749 GError ** error)
750 {
751 /* validate */
752 bool have_unresizable =
753 arranger_selections_contains_object_with_property (
754 sel,
755 ARRANGER_SELECTIONS_PROPERTY_HAS_LENGTH,
756 false);
757 if (have_unresizable)
758 {
759 g_set_error (
760 error, Z_ACTIONS_ARRANGER_SELECTIONS_ERROR,
761 Z_ACTIONS_ARRANGER_SELECTIONS_ERROR_FAILED,
762 "%s",
763 _("Attempted to resize unresizable "
764 "objects"));
765 return NULL;
766 }
767
768 bool have_looped =
769 arranger_selections_contains_object_with_property (
770 sel,
771 ARRANGER_SELECTIONS_PROPERTY_HAS_LOOPED,
772 true);
773 if (have_looped
774 &&
775 (type
776 == ARRANGER_SELECTIONS_ACTION_RESIZE_L
777 ||
778 type
779 == ARRANGER_SELECTIONS_ACTION_RESIZE_R
780 ||
781 type
782 == ARRANGER_SELECTIONS_ACTION_STRETCH_L
783 ||
784 type
785 == ARRANGER_SELECTIONS_ACTION_STRETCH_R))
786 {
787 g_set_error (
788 error, Z_ACTIONS_ARRANGER_SELECTIONS_ERROR,
789 Z_ACTIONS_ARRANGER_SELECTIONS_ERROR_FAILED,
790 _("Cannot perform %s resize - selections "
791 "contain looped objects"),
792 arranger_selections_action_resize_type_strings[type].str);
793 return NULL;
794 }
795
796 bool have_unloopable =
797 arranger_selections_contains_object_with_property (
798 sel,
799 ARRANGER_SELECTIONS_PROPERTY_CAN_LOOP,
800 false);
801 if (have_unloopable
802 &&
803 (type
804 == ARRANGER_SELECTIONS_ACTION_RESIZE_L_LOOP
805 ||
806 type
807 == ARRANGER_SELECTIONS_ACTION_RESIZE_R_LOOP))
808 {
809 g_set_error (
810 error, Z_ACTIONS_ARRANGER_SELECTIONS_ERROR,
811 Z_ACTIONS_ARRANGER_SELECTIONS_ERROR_FAILED,
812 _("Cannot perform %s resize - selections "
813 "contain unloopable objects"),
814 arranger_selections_action_resize_type_strings[type].str);
815 return NULL;
816 }
817
818 ArrangerSelectionsAction * self =
819 _create_action (sel);
820 self->type = AS_ACTION_RESIZE;
821
822 self->resize_type = type;
823 self->ticks = ticks;
824
825 if (!already_resized)
826 {
827 self->first_run = 0;
828 }
829
830 UndoableAction * ua = (UndoableAction *) self;
831 return ua;
832 }
833
834 /**
835 * Creates a new action fro quantizing
836 * ArrangerObject's.
837 *
838 * @param opts Quantize options.
839 */
840 UndoableAction *
arranger_selections_action_new_quantize(ArrangerSelections * sel,QuantizeOptions * opts,GError ** error)841 arranger_selections_action_new_quantize (
842 ArrangerSelections * sel,
843 QuantizeOptions * opts,
844 GError ** error)
845 {
846 ArrangerSelectionsAction * self =
847 _create_action (sel);
848 self->type = AS_ACTION_QUANTIZE;
849
850 set_selections (self, sel, 1, 1);
851 self->opts = quantize_options_clone (opts);
852
853 UndoableAction * ua = (UndoableAction *) self;
854 return ua;
855 }
856
857 ArrangerSelectionsAction *
arranger_selections_action_clone(const ArrangerSelectionsAction * src)858 arranger_selections_action_clone (
859 const ArrangerSelectionsAction * src)
860 {
861 ArrangerSelectionsAction * self =
862 object_new (ArrangerSelectionsAction);
863
864 self->parent_instance = src->parent_instance;
865 self->type = src->type;
866
867 if (src->sel)
868 self->sel =
869 arranger_selections_clone (src->sel);
870 if (src->sel_after)
871 self->sel_after =
872 arranger_selections_clone (src->sel_after);
873
874 #define SET_SEL(cc,sc,after) \
875 if (after) \
876 self->sc##_sel_after = \
877 (cc##Selections *) self->sel_after; \
878 else \
879 self->sc##_sel = \
880 (cc##Selections *) self->sel; \
881 break
882
883 if (self->sel)
884 {
885 switch (self->sel->type)
886 {
887 case ARRANGER_SELECTIONS_TYPE_CHORD:
888 SET_SEL (Chord, chord, false);
889 case ARRANGER_SELECTIONS_TYPE_TIMELINE:
890 SET_SEL (Timeline, tl, false);
891 case ARRANGER_SELECTIONS_TYPE_MIDI:
892 SET_SEL (MidiArranger, ma, false);
893 case ARRANGER_SELECTIONS_TYPE_AUTOMATION:
894 SET_SEL (Automation, automation, false);
895 case ARRANGER_SELECTIONS_TYPE_AUDIO:
896 SET_SEL (Audio, audio, false);
897 default:
898 g_return_val_if_reached (NULL);
899 }
900 }
901 if (self->sel_after)
902 {
903 switch (self->sel_after->type)
904 {
905 case ARRANGER_SELECTIONS_TYPE_CHORD:
906 SET_SEL (Chord, chord, true);
907 case ARRANGER_SELECTIONS_TYPE_TIMELINE:
908 SET_SEL (Timeline, tl, true);
909 case ARRANGER_SELECTIONS_TYPE_MIDI:
910 SET_SEL (MidiArranger, ma, true);
911 case ARRANGER_SELECTIONS_TYPE_AUTOMATION:
912 SET_SEL (Automation, automation, true);
913 case ARRANGER_SELECTIONS_TYPE_AUDIO:
914 SET_SEL (Audio, audio, true);
915 default:
916 g_return_val_if_reached (NULL);
917 }
918 }
919
920 #undef SET_SEL
921
922 self->edit_type = src->edit_type;
923 self->resize_type = src->resize_type;
924 self->ticks = src->ticks;
925 self->delta_tracks = src->delta_tracks;
926 self->delta_lanes = src->delta_lanes;
927 self->delta_chords = src->delta_chords;
928 self->delta_pitch = src->delta_pitch;
929 self->delta_vel = src->delta_vel;
930 self->delta_normalized_amount =
931 src->delta_normalized_amount;
932 self->str = g_strdup (src->str);
933 self->pos = src->pos;
934
935 for (int i = 0; i < src->num_split_objs; i++)
936 {
937 g_return_val_if_fail (src->r1[i], NULL);
938 g_return_val_if_fail (src->r2[i], NULL);
939
940 ArrangerObject * r1 =
941 arranger_object_clone (src->r1[i]);
942 ArrangerObject * r2 =
943 arranger_object_clone (src->r2[i]);
944 self->r1[i] = r1;
945 self->r2[i] = r2;
946 switch (r1->type)
947 {
948 case ARRANGER_OBJECT_TYPE_REGION:
949 self->region_r1[i] = (ZRegion *) r1;
950 self->region_r2[i] = (ZRegion *) r2;
951 break;
952 case ARRANGER_OBJECT_TYPE_MIDI_NOTE:
953 self->mn_r1[i] = (MidiNote *) r1;
954 self->mn_r2[i] = (MidiNote *) r2;
955 break;
956 default:
957 g_return_val_if_reached (NULL);
958 }
959 }
960 self->num_split_objs = src->num_split_objs;
961
962 self->first_run = src->first_run;
963 if (src->opts)
964 self->opts =
965 quantize_options_clone (src->opts);
966
967 return self;
968 }
969
970 bool
arranger_selections_action_perform_create_or_delete(ArrangerSelections * sel,const bool create,GError ** error)971 arranger_selections_action_perform_create_or_delete (
972 ArrangerSelections * sel,
973 const bool create,
974 GError ** error)
975 {
976 UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
977 arranger_selections_action_new_create_or_delete,
978 error, sel, create, error);
979 }
980
981 bool
arranger_selections_action_perform_record(ArrangerSelections * sel_before,ArrangerSelections * sel_after,const bool already_recorded,GError ** error)982 arranger_selections_action_perform_record (
983 ArrangerSelections * sel_before,
984 ArrangerSelections * sel_after,
985 const bool already_recorded,
986 GError ** error)
987 {
988 UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
989 arranger_selections_action_new_record,
990 error, sel_before, sel_after, already_recorded,
991 error);
992 }
993
994 bool
arranger_selections_action_perform_move_or_duplicate(ArrangerSelections * sel,const bool move,const double ticks,const int delta_chords,const int delta_pitch,const int delta_tracks,const int delta_lanes,const double delta_normalized_amount,const bool already_moved,GError ** error)995 arranger_selections_action_perform_move_or_duplicate (
996 ArrangerSelections * sel,
997 const bool move,
998 const double ticks,
999 const int delta_chords,
1000 const int delta_pitch,
1001 const int delta_tracks,
1002 const int delta_lanes,
1003 const double delta_normalized_amount,
1004 const bool already_moved,
1005 GError ** error)
1006 {
1007 UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
1008 arranger_selections_action_new_move_or_duplicate,
1009 error, sel, move, ticks, delta_chords,
1010 delta_pitch,
1011 delta_tracks, delta_lanes,
1012 delta_normalized_amount, already_moved, error);
1013 }
1014
1015 bool
arranger_selections_action_perform_link(ArrangerSelections * sel_before,ArrangerSelections * sel_after,const double ticks,const int delta_tracks,const int delta_lanes,const bool already_moved,GError ** error)1016 arranger_selections_action_perform_link (
1017 ArrangerSelections * sel_before,
1018 ArrangerSelections * sel_after,
1019 const double ticks,
1020 const int delta_tracks,
1021 const int delta_lanes,
1022 const bool already_moved,
1023 GError ** error)
1024 {
1025 UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
1026 arranger_selections_action_new_link,
1027 error, sel_before, sel_after, ticks,
1028 delta_tracks, delta_lanes, already_moved,
1029 error);
1030 }
1031
1032 bool
arranger_selections_action_perform_edit(ArrangerSelections * sel_before,ArrangerSelections * sel_after,ArrangerSelectionsActionEditType type,bool already_edited,GError ** error)1033 arranger_selections_action_perform_edit (
1034 ArrangerSelections * sel_before,
1035 ArrangerSelections * sel_after,
1036 ArrangerSelectionsActionEditType type,
1037 bool already_edited,
1038 GError ** error)
1039 {
1040 UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
1041 arranger_selections_action_new_edit, error,
1042 sel_before, sel_after, type, already_edited,
1043 error);
1044 }
1045
1046 bool
arranger_selections_action_perform_edit_midi_function(ArrangerSelections * sel_before,MidiFunctionType midi_func_type,GError ** error)1047 arranger_selections_action_perform_edit_midi_function (
1048 ArrangerSelections * sel_before,
1049 MidiFunctionType midi_func_type,
1050 GError ** error)
1051 {
1052 UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
1053 arranger_selections_action_new_edit_midi_function,
1054 error, sel_before, midi_func_type, error);
1055 }
1056
1057 bool
arranger_selections_action_perform_edit_automation_function(ArrangerSelections * sel_before,AutomationFunctionType automation_func_type,GError ** error)1058 arranger_selections_action_perform_edit_automation_function (
1059 ArrangerSelections * sel_before,
1060 AutomationFunctionType automation_func_type,
1061 GError ** error)
1062 {
1063 UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
1064 arranger_selections_action_new_edit_automation_function,
1065 error, sel_before, automation_func_type, error);
1066 }
1067
1068 bool
arranger_selections_action_perform_edit_audio_function(ArrangerSelections * sel_before,AudioFunctionType audio_func_type,const char * uri,GError ** error)1069 arranger_selections_action_perform_edit_audio_function (
1070 ArrangerSelections * sel_before,
1071 AudioFunctionType audio_func_type,
1072 const char * uri,
1073 GError ** error)
1074 {
1075 UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
1076 arranger_selections_action_new_edit_audio_function,
1077 error, sel_before, audio_func_type, uri,
1078 error);
1079 }
1080
1081 bool
arranger_selections_action_perform_automation_fill(ZRegion * region_before,ZRegion * region_after,bool already_changed,GError ** error)1082 arranger_selections_action_perform_automation_fill (
1083 ZRegion * region_before,
1084 ZRegion * region_after,
1085 bool already_changed,
1086 GError ** error)
1087 {
1088 UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
1089 arranger_selections_action_new_automation_fill,
1090 error, region_before, region_after,
1091 already_changed, error);
1092 }
1093
1094 bool
arranger_selections_action_perform_split(ArrangerSelections * sel,const Position * pos,GError ** error)1095 arranger_selections_action_perform_split (
1096 ArrangerSelections * sel,
1097 const Position * pos,
1098 GError ** error)
1099 {
1100 UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
1101 arranger_selections_action_new_split,
1102 error, sel, pos, error);
1103 }
1104
1105 bool
arranger_selections_action_perform_merge(ArrangerSelections * sel,GError ** error)1106 arranger_selections_action_perform_merge (
1107 ArrangerSelections * sel,
1108 GError ** error)
1109 {
1110 UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
1111 arranger_selections_action_new_merge,
1112 error, sel, error);
1113 }
1114
1115 bool
arranger_selections_action_perform_resize(ArrangerSelections * sel,ArrangerSelectionsActionResizeType type,const double ticks,const bool already_resized,GError ** error)1116 arranger_selections_action_perform_resize (
1117 ArrangerSelections * sel,
1118 ArrangerSelectionsActionResizeType type,
1119 const double ticks,
1120 const bool already_resized,
1121 GError ** error)
1122 {
1123 UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
1124 arranger_selections_action_new_resize,
1125 error, sel, type, ticks, already_resized,
1126 error);
1127 }
1128
1129 bool
arranger_selections_action_perform_quantize(ArrangerSelections * sel,QuantizeOptions * opts,GError ** error)1130 arranger_selections_action_perform_quantize (
1131 ArrangerSelections * sel,
1132 QuantizeOptions * opts,
1133 GError ** error)
1134 {
1135 UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
1136 arranger_selections_action_new_quantize,
1137 error, sel, opts, error);
1138 }
1139
1140 static void
update_region_link_groups(ArrangerObject ** objs,int size)1141 update_region_link_groups (
1142 ArrangerObject ** objs,
1143 int size)
1144 {
1145 /* handle children of linked regions */
1146 for (int i = 0; i < size; i++)
1147 {
1148 /* get the actual object from the
1149 * project */
1150 ArrangerObject * obj =
1151 arranger_object_find (objs[i]);
1152 g_return_if_fail (obj);
1153
1154 if (arranger_object_owned_by_region (obj))
1155 {
1156 ZRegion * region =
1157 arranger_object_get_region (obj);
1158 g_return_if_fail (region);
1159
1160 /* shift all linked objects */
1161 region_update_link_group (region);
1162 }
1163 }
1164 }
1165
1166 /**
1167 * Does or undoes the action.
1168 *
1169 * @param _do 1 to do, 0 to undo.
1170 */
1171 static int
do_or_undo_move(ArrangerSelectionsAction * self,const bool _do,GError ** error)1172 do_or_undo_move (
1173 ArrangerSelectionsAction * self,
1174 const bool _do,
1175 GError ** error)
1176 {
1177 int size = 0;
1178 arranger_selections_sort_by_indices (
1179 self->sel, !_do);
1180 ArrangerObject ** objs =
1181 arranger_selections_get_all_objects (
1182 self->sel, &size);
1183
1184 double ticks =
1185 _do ? self->ticks : - self->ticks;
1186 int delta_tracks =
1187 _do ? self->delta_tracks :
1188 - self->delta_tracks;
1189 int delta_lanes =
1190 _do ? self->delta_lanes : - self->delta_lanes;
1191 int delta_chords =
1192 _do ? self->delta_chords :
1193 - self->delta_chords;
1194 int delta_pitch =
1195 _do ? self->delta_pitch : - self->delta_pitch;
1196 double delta_normalized_amt =
1197 _do ? self->delta_normalized_amount :
1198 - self->delta_normalized_amount;
1199
1200 /* this is used for automation points to
1201 * keep track of which automation point in the
1202 * project matches which automation point in
1203 * the cached selections */
1204 GHashTable * ht =
1205 g_hash_table_new (NULL, NULL);
1206
1207 if (!self->first_run)
1208 {
1209 for (int i = 0; i < size; i++)
1210 {
1211 objs[i]->flags |=
1212 ARRANGER_OBJECT_FLAG_NON_PROJECT;
1213
1214 /* get the actual object from the
1215 * project */
1216 ArrangerObject * obj =
1217 arranger_object_find (objs[i]);
1218 g_return_val_if_fail (obj, -1);
1219
1220 g_hash_table_insert (
1221 ht, obj, objs[i]);
1222
1223 if (!math_doubles_equal (ticks, 0.0))
1224 {
1225 /* shift the actual object */
1226 arranger_object_move (
1227 obj, ticks);
1228
1229 /* also shift the copy */
1230 arranger_object_move (
1231 objs[i], ticks);
1232 }
1233
1234 if (delta_tracks != 0)
1235 {
1236 g_return_val_if_fail (
1237 obj->type ==
1238 ARRANGER_OBJECT_TYPE_REGION, -1);
1239
1240 ZRegion * r = (ZRegion *) obj;
1241
1242 Track * track_to_move_to =
1243 tracklist_get_visible_track_after_delta (
1244 TRACKLIST,
1245 arranger_object_get_track (obj),
1246 delta_tracks);
1247 g_warn_if_fail (track_to_move_to);
1248
1249 /* shift the actual object by
1250 * tracks */
1251 region_move_to_track (
1252 r, track_to_move_to, -1);
1253
1254 /* remember info in identifier */
1255 ZRegion * r_clone =
1256 (ZRegion *) objs[i];
1257 region_identifier_copy (
1258 &r_clone->id, &r->id);
1259 }
1260
1261 if (delta_pitch != 0)
1262 {
1263 g_return_val_if_fail (
1264 obj->type ==
1265 ARRANGER_OBJECT_TYPE_MIDI_NOTE, -1);
1266
1267 MidiNote * mn =
1268 (MidiNote *) obj;
1269
1270 /* shift the actual object */
1271 midi_note_shift_pitch (
1272 mn, delta_pitch);
1273
1274 /* also shift the copy so they can
1275 * match */
1276 midi_note_shift_pitch (
1277 (MidiNote *) objs[i],
1278 delta_pitch);
1279 }
1280
1281 if (delta_lanes != 0)
1282 {
1283 g_return_val_if_fail (
1284 obj->type ==
1285 ARRANGER_OBJECT_TYPE_REGION, -1);
1286
1287 ZRegion * r = (ZRegion *) obj;
1288
1289 Track * region_track =
1290 arranger_object_get_track (obj);
1291 int new_lane_pos =
1292 r->id.lane_pos + delta_lanes;
1293 g_return_val_if_fail (
1294 new_lane_pos >= 0, -1);
1295 track_create_missing_lanes (
1296 region_track, new_lane_pos);
1297 TrackLane * lane_to_move_to =
1298 region_track->lanes[new_lane_pos];
1299
1300 /* shift the actual object by
1301 * lanes */
1302 region_move_to_lane (
1303 r, lane_to_move_to, -1);
1304
1305 /* remember info in identifier */
1306 ZRegion * r_own =
1307 (ZRegion *) objs[i];
1308 region_identifier_copy (
1309 &r_own->id, &r->id);
1310 }
1311
1312 if (delta_chords != 0)
1313 {
1314 /* TODO */
1315 }
1316
1317 if (!math_doubles_equal (
1318 delta_normalized_amt, 0.0))
1319 {
1320 if (obj->type ==
1321 ARRANGER_OBJECT_TYPE_AUTOMATION_POINT)
1322 {
1323 AutomationPoint * ap =
1324 (AutomationPoint *) obj;
1325
1326 /* shift the actual object */
1327 automation_point_set_fvalue (
1328 ap,
1329 ap->normalized_val +
1330 (float)
1331 delta_normalized_amt,
1332 F_NORMALIZED,
1333 F_NO_PUBLISH_EVENTS);
1334
1335 /* also shift the copy so they
1336 * can match */
1337 AutomationPoint * copy_ap =
1338 (AutomationPoint *) objs[i];
1339 automation_point_set_fvalue (
1340 copy_ap,
1341 copy_ap->normalized_val +
1342 (float)
1343 delta_normalized_amt,
1344 F_NORMALIZED,
1345 F_NO_PUBLISH_EVENTS);
1346 }
1347 }
1348 }
1349
1350 /* if moving automation points, re-sort
1351 * the region and remember the new
1352 * indices */
1353 if (objs[0]->type ==
1354 ARRANGER_OBJECT_TYPE_AUTOMATION_POINT)
1355 {
1356 ArrangerObject * obj =
1357 /* get the actual object from the
1358 * project */
1359 arranger_object_find (objs[0]);
1360 g_return_val_if_fail (obj, -1);
1361
1362 ZRegion * region =
1363 arranger_object_get_region (obj);
1364 g_return_val_if_fail (region, -1);
1365 automation_region_force_sort (region);
1366
1367 GHashTableIter iter;
1368 gpointer key, value;
1369
1370 g_hash_table_iter_init (&iter, ht);
1371 while (g_hash_table_iter_next (
1372 &iter, &key, &value))
1373 {
1374 AutomationPoint * prj_ap =
1375 (AutomationPoint *) key;
1376 AutomationPoint * cached_ap =
1377 (AutomationPoint *) value;
1378 automation_point_set_region_and_index (
1379 cached_ap, region, prj_ap->index);
1380 }
1381 }
1382 }
1383
1384 update_region_link_groups (objs, size);
1385
1386 free (objs);
1387 g_hash_table_destroy (ht);
1388
1389 ArrangerSelections * sel =
1390 get_actual_arranger_selections (self);
1391 EVENTS_PUSH (
1392 ET_ARRANGER_SELECTIONS_CHANGED, sel);
1393 EVENTS_PUSH (
1394 ET_ARRANGER_SELECTIONS_MOVED, sel);
1395
1396 self->first_run = 0;
1397
1398 return 0;
1399 }
1400
1401 static void
move_obj_by_tracks_and_lanes(ArrangerObject * obj,const int tracks_diff,const int lanes_diff,bool use_index_in_prev_lane,int index_in_prev_lane)1402 move_obj_by_tracks_and_lanes (
1403 ArrangerObject * obj,
1404 const int tracks_diff,
1405 const int lanes_diff,
1406 bool use_index_in_prev_lane,
1407 int index_in_prev_lane)
1408 {
1409 g_return_if_fail (IS_ARRANGER_OBJECT (obj));
1410 if (tracks_diff)
1411 {
1412 g_return_if_fail (
1413 obj->type ==
1414 ARRANGER_OBJECT_TYPE_REGION);
1415
1416 ZRegion * r = (ZRegion *) obj;
1417
1418 Track * track_to_move_to =
1419 tracklist_get_visible_track_after_delta (
1420 TRACKLIST,
1421 arranger_object_get_track (obj),
1422 tracks_diff);
1423
1424 /* shift the actual object by tracks */
1425 if (obj->flags &
1426 ARRANGER_OBJECT_FLAG_NON_PROJECT)
1427 {
1428 r->id.track_name_hash =
1429 track_get_name_hash (
1430 track_to_move_to);
1431 }
1432 else
1433 {
1434 region_move_to_track (
1435 r, track_to_move_to,
1436 use_index_in_prev_lane ?
1437 index_in_prev_lane : -1);
1438 }
1439 }
1440 if (lanes_diff)
1441 {
1442 ZRegion * r = (ZRegion *) obj;
1443
1444 Track * region_track =
1445 arranger_object_get_track (obj);
1446 int new_lane_pos =
1447 r->id.lane_pos + lanes_diff;
1448 g_return_if_fail (
1449 new_lane_pos >= 0);
1450
1451 /* shift the actual object by lanes */
1452 if (obj->flags &
1453 ARRANGER_OBJECT_FLAG_NON_PROJECT)
1454 {
1455 r->id.lane_pos = new_lane_pos;
1456 }
1457 else
1458 {
1459 track_create_missing_lanes (
1460 region_track, new_lane_pos);
1461 TrackLane * lane_to_move_to =
1462 region_track->lanes[new_lane_pos];
1463 region_move_to_lane (
1464 r, lane_to_move_to,
1465 use_index_in_prev_lane ?
1466 index_in_prev_lane : -1);
1467 }
1468 }
1469 }
1470
1471 /**
1472 * Does or undoes the action.
1473 *
1474 * @param _do 1 to do, 0 to undo.
1475 */
1476 static int
do_or_undo_duplicate_or_link(ArrangerSelectionsAction * self,const bool link,const bool _do,GError ** error)1477 do_or_undo_duplicate_or_link (
1478 ArrangerSelectionsAction * self,
1479 const bool link,
1480 const bool _do,
1481 GError ** error)
1482 {
1483 arranger_selections_sort_by_indices (
1484 self->sel, !_do);
1485 arranger_selections_sort_by_indices (
1486 self->sel_after, !_do);
1487 int size = 0;
1488 ArrangerObject ** objs =
1489 arranger_selections_get_all_objects (
1490 self->sel_after, &size);
1491 if (ZRYTHM_TESTING)
1492 {
1493 arranger_selections_verify (self->sel_after);
1494 }
1495 /* objects the duplication/link was based from */
1496 ArrangerObject ** orig_objs =
1497 arranger_selections_get_all_objects (
1498 self->sel, &size);
1499 ArrangerSelections * sel =
1500 get_actual_arranger_selections (self);
1501 g_return_val_if_fail (sel, -1);
1502
1503 double ticks =
1504 _do ? self->ticks : - self->ticks;
1505 int delta_tracks =
1506 _do ? self->delta_tracks : - self->delta_tracks;
1507 int delta_lanes =
1508 _do ? self->delta_lanes : - self->delta_lanes;
1509 int delta_chords =
1510 _do ? self->delta_chords : - self->delta_chords;
1511 int delta_pitch =
1512 _do ? self->delta_pitch : - self->delta_pitch;
1513 float delta_normalized_amount =
1514 _do ? (float) self->delta_normalized_amount :
1515 (float) - self->delta_normalized_amount;
1516
1517 /* clear current selections in the project */
1518 arranger_selections_clear (
1519 sel, F_NO_FREE, F_NO_PUBLISH_EVENTS);
1520
1521 /* this is used for automation points to
1522 * keep track of which automation point in the
1523 * project matches which automation point in
1524 * the cached selections */
1525 GHashTable * ht = g_hash_table_new (NULL, NULL);
1526
1527 for (int i = size - 1; i >= 0; i--)
1528 {
1529 objs[i]->flags |=
1530 ARRANGER_OBJECT_FLAG_NON_PROJECT;
1531 orig_objs[i]->flags |=
1532 ARRANGER_OBJECT_FLAG_NON_PROJECT;
1533 g_warn_if_fail (
1534 IS_ARRANGER_OBJECT (objs[i]));
1535
1536 /* on first run, we need to first move
1537 * the original object backwards (the
1538 * project object too) */
1539 if (_do && self->first_run)
1540 {
1541 if (objs[i]->type ==
1542 ARRANGER_OBJECT_TYPE_REGION)
1543 {
1544 g_debug ("our:");
1545 region_print (
1546 (ZRegion *) objs[i]);
1547 }
1548 ZRegion * r_orig = NULL;
1549 ZRegion * r_our = NULL;
1550 if (objs[i]->type ==
1551 ARRANGER_OBJECT_TYPE_REGION)
1552 {
1553 r_orig = (ZRegion *) orig_objs[i];
1554 r_our = (ZRegion *) objs[i];
1555 g_return_val_if_fail (
1556 IS_REGION_AND_NONNULL (r_orig) &&
1557 IS_REGION_AND_NONNULL (r_our), -1);
1558 }
1559 ArrangerObject * obj =
1560 arranger_object_find (objs[i]);
1561 g_return_val_if_fail (
1562 IS_ARRANGER_OBJECT (obj), -1);
1563
1564 g_debug (
1565 "[%d] moving original object "
1566 "backwards", i);
1567
1568 /* ticks */
1569 if (!math_doubles_equal (
1570 self->ticks, 0.0))
1571 {
1572 arranger_object_move (
1573 obj, - self->ticks);
1574 arranger_object_move (
1575 objs[i], - self->ticks);
1576 }
1577
1578 /* tracks & lanes */
1579 if (delta_tracks || delta_lanes)
1580 {
1581 g_return_val_if_fail (r_our, -1);
1582
1583 g_message ("moving prj obj");
1584 move_obj_by_tracks_and_lanes (
1585 obj, - delta_tracks,
1586 - delta_lanes, true,
1587 objs[i]->index_in_prev_lane);
1588
1589 g_message ("moving own obj");
1590 RegionIdentifier own_id_before_move =
1591 r_our->id;
1592 move_obj_by_tracks_and_lanes (
1593 objs[i], - delta_tracks,
1594 - delta_lanes, true,
1595 objs[i]->index_in_prev_lane);
1596
1597 /* since the object moved outside
1598 * of its lane, decrement the index
1599 * inside the lane for all of our
1600 * cached objects in the same lane */
1601 for (int j = i + 1; j < size; j++)
1602 {
1603 ZRegion * own_r =
1604 (ZRegion *) objs[j];
1605 if (own_id_before_move.
1606 track_name_hash ==
1607 own_r->id.track_name_hash &&
1608 own_id_before_move.
1609 lane_pos ==
1610 own_r->id.lane_pos &&
1611 own_id_before_move.
1612 at_idx ==
1613 own_r->id.at_idx)
1614 {
1615 own_r->id.idx--;
1616 }
1617 }
1618 }
1619
1620 /* chords */
1621 /* TODO */
1622
1623 /* pitch */
1624 if (delta_pitch)
1625 {
1626 midi_note_shift_pitch (
1627 (MidiNote *) obj,
1628 - delta_pitch);
1629 midi_note_shift_pitch (
1630 (MidiNote *) objs[i],
1631 - delta_pitch);
1632 }
1633
1634 /* automation value */
1635 if (!math_floats_equal (
1636 delta_normalized_amount, 0.f))
1637 {
1638 AutomationPoint * ap =
1639 (AutomationPoint *) obj;
1640 AutomationPoint * cached_ap =
1641 (AutomationPoint *) objs[i];
1642 automation_point_set_fvalue (
1643 ap,
1644 ap->normalized_val -
1645 delta_normalized_amount,
1646 F_NORMALIZED,
1647 F_NO_PUBLISH_EVENTS);
1648 automation_point_set_fvalue (
1649 cached_ap,
1650 cached_ap->normalized_val -
1651 delta_normalized_amount,
1652 F_NORMALIZED,
1653 F_NO_PUBLISH_EVENTS);
1654 }
1655 } /* if do and first run */
1656 }
1657
1658 g_debug ("moved original objects back");
1659
1660 for (int i = 0; i < size; i++)
1661 {
1662 ArrangerObject * obj;
1663 if (_do)
1664 {
1665 /* clone the clone */
1666 obj = arranger_object_clone (objs[i]);
1667
1668 /* if region, clear the remembered index
1669 * so that the region gets appended
1670 * instead of inserted */
1671 if (obj->type ==
1672 ARRANGER_OBJECT_TYPE_REGION)
1673 {
1674 ZRegion * r =
1675 (ZRegion *) obj;
1676 r->id.idx = -1;
1677 }
1678
1679 /* add to track. */
1680 arranger_object_add_to_project (
1681 obj, F_NO_PUBLISH_EVENTS);
1682 } /* endif do */
1683 else /* if undo */
1684 {
1685 /* find the actual object */
1686 obj = arranger_object_find (objs[i]);
1687 g_return_val_if_fail (
1688 IS_ARRANGER_OBJECT_AND_NONNULL (obj),
1689 -1);
1690
1691 /* if the object was created with linking,
1692 * delete the links */
1693 if (link &&
1694 obj->type ==
1695 ARRANGER_OBJECT_TYPE_REGION)
1696 {
1697 /* remove link from created object
1698 * (this will also automatically
1699 * remove the link from the parent
1700 * region if it is the only region in
1701 * the link group) */
1702 ZRegion * region = (ZRegion *) obj;
1703 g_warn_if_fail (
1704 IS_REGION (region) &&
1705 region_has_link_group (region));
1706 region_unlink (region);
1707 g_warn_if_fail (
1708 region->id.link_group == -1);
1709
1710 /* unlink remembered link groups */
1711 region = (ZRegion *) orig_objs[i];
1712 region_unlink (region);
1713 region = (ZRegion *) objs[i];
1714 region_unlink (region);
1715 }
1716
1717 /* remove it */
1718 arranger_object_remove_from_project (obj);
1719
1720 } /* endif undo */
1721
1722 if (!math_doubles_equal (
1723 ticks, 0.0))
1724 {
1725 if (_do)
1726 {
1727 /* shift it */
1728 arranger_object_move (
1729 obj, ticks);
1730 }
1731
1732 /* also shift the copy */
1733 arranger_object_move (
1734 objs[i], ticks);
1735 }
1736
1737 if (delta_tracks != 0)
1738 {
1739 if (_do)
1740 {
1741 move_obj_by_tracks_and_lanes (
1742 obj, delta_tracks, 0, false, -1);
1743 }
1744
1745 /* also shift the copy */
1746 move_obj_by_tracks_and_lanes (
1747 objs[i], delta_tracks, false, -1, 0);
1748 }
1749
1750 if (delta_pitch != 0)
1751 {
1752 if (_do)
1753 {
1754 MidiNote * mn = (MidiNote *) obj;
1755
1756 /* shift the actual object */
1757 midi_note_shift_pitch (
1758 mn, delta_pitch);
1759 }
1760
1761 /* also shift the copy so they can
1762 * match */
1763 midi_note_shift_pitch (
1764 (MidiNote *) objs[i],
1765 delta_pitch);
1766 }
1767
1768 if (delta_lanes != 0)
1769 {
1770 if (_do)
1771 {
1772 move_obj_by_tracks_and_lanes (
1773 obj, 0, delta_lanes, false, -1);
1774 }
1775
1776 /* also shift the copy */
1777 move_obj_by_tracks_and_lanes (
1778 objs[i], 0, delta_lanes, false, -1);
1779 }
1780
1781 if (delta_chords != 0)
1782 {
1783 /* TODO */
1784 }
1785
1786 if (!math_floats_equal (
1787 delta_normalized_amount, 0.f))
1788 {
1789 /* shift the obj */
1790 AutomationPoint * ap =
1791 (AutomationPoint *) obj;
1792 automation_point_set_fvalue (
1793 ap,
1794 ap->normalized_val +
1795 delta_normalized_amount,
1796 F_NORMALIZED,
1797 F_NO_PUBLISH_EVENTS);
1798
1799 /* also shift the copy */
1800 AutomationPoint * cached_ap =
1801 (AutomationPoint *) objs[i];
1802 automation_point_set_fvalue (
1803 cached_ap,
1804 cached_ap->normalized_val +
1805 delta_normalized_amount,
1806 F_NORMALIZED,
1807 F_NO_PUBLISH_EVENTS);
1808 }
1809
1810 if (_do)
1811 {
1812 /* if we are linking, create the
1813 * necessary links */
1814 if (link &&
1815 orig_objs[i]->type ==
1816 ARRANGER_OBJECT_TYPE_REGION)
1817 {
1818 /* add link group to original object
1819 * if necessary */
1820 ArrangerObject * orig_obj =
1821 arranger_object_find (
1822 orig_objs[i]);
1823 ZRegion * orig_r =
1824 (ZRegion *) orig_obj;
1825 g_return_val_if_fail (
1826 orig_r->id.idx >= 0, -1);
1827 region_create_link_group_if_none (
1828 orig_r);
1829 int link_group =
1830 orig_r->id.link_group;
1831
1832 /* add link group to clone */
1833 ZRegion * r_obj = (ZRegion *) obj;
1834 g_return_val_if_fail (
1835 r_obj->id.type == orig_r->id.type,
1836 -1);
1837 g_return_val_if_fail (
1838 r_obj->id.idx >= 0, -1);
1839 region_set_link_group (
1840 r_obj, link_group, true);
1841
1842 /* remember link groups */
1843 ZRegion * r =
1844 (ZRegion *) orig_objs[i];
1845 region_set_link_group (
1846 r, link_group, true);
1847 r = (ZRegion *) objs[i];
1848 region_set_link_group (
1849 r, link_group, true);
1850
1851 region_link_group_manager_validate (
1852 REGION_LINK_GROUP_MANAGER);
1853 }
1854 /* else if we are not linking and this
1855 * is a region */
1856 else if (obj->type ==
1857 ARRANGER_OBJECT_TYPE_REGION)
1858 {
1859 ZRegion * region = (ZRegion *) obj;
1860
1861 /* remove link group if first run */
1862 if (self->first_run)
1863 {
1864 if (region_has_link_group (
1865 region))
1866 {
1867 region_unlink (region);
1868 }
1869 }
1870
1871 /* if this is an audio region,
1872 * duplicate the clip */
1873 if (region->id.type ==
1874 REGION_TYPE_AUDIO)
1875 {
1876 AudioClip * clip =
1877 audio_region_get_clip (region);
1878 int id =
1879 audio_pool_duplicate_clip (
1880 AUDIO_POOL, clip->pool_id,
1881 F_WRITE_FILE);
1882 clip =
1883 audio_pool_get_clip (
1884 AUDIO_POOL, id);
1885 g_return_val_if_fail (clip, -1);
1886 region->pool_id = clip->pool_id;
1887 }
1888 }
1889 }
1890
1891 /* add the mapping to the hashtable */
1892 g_hash_table_insert (ht, obj, objs[i]);
1893
1894 if (_do)
1895 {
1896 /* select it */
1897 arranger_object_select (
1898 obj, F_SELECT, F_APPEND,
1899 F_NO_PUBLISH_EVENTS);
1900
1901 /* remember the identifier */
1902 arranger_object_copy_identifier (
1903 objs[i], obj);
1904 }
1905
1906 region_link_group_manager_validate (
1907 REGION_LINK_GROUP_MANAGER);
1908 }
1909
1910 /* if copy-moving automation points, re-sort
1911 * the region and remember the new indices */
1912 if (objs[0]->type ==
1913 ARRANGER_OBJECT_TYPE_AUTOMATION_POINT)
1914 {
1915 /* get the actual object from the
1916 * project */
1917 ArrangerObject * obj =
1918 g_hash_table_get_keys_as_array (
1919 ht, NULL)[0];
1920 g_return_val_if_fail (
1921 IS_ARRANGER_OBJECT (obj), -1);
1922 ZRegion * region =
1923 arranger_object_get_region (obj);
1924 g_return_val_if_fail (region, -1);
1925 automation_region_force_sort (region);
1926
1927 GHashTableIter iter;
1928 gpointer key, value;
1929
1930 g_hash_table_iter_init (&iter, ht);
1931 while (g_hash_table_iter_next (
1932 &iter, &key, &value))
1933 {
1934 AutomationPoint * prj_ap =
1935 (AutomationPoint *) key;
1936 AutomationPoint * cached_ap =
1937 (AutomationPoint *) value;
1938 automation_point_set_region_and_index (
1939 cached_ap, region, prj_ap->index);
1940 }
1941 }
1942
1943 marker_track_validate (P_MARKER_TRACK);
1944 chord_track_validate (P_CHORD_TRACK);
1945 region_link_group_manager_validate (
1946 REGION_LINK_GROUP_MANAGER);
1947
1948 free (objs);
1949 free (orig_objs);
1950 g_hash_table_destroy (ht);
1951
1952 sel = get_actual_arranger_selections (self);
1953 if (_do)
1954 {
1955 transport_recalculate_total_bars (
1956 TRANSPORT, self->sel_after);
1957
1958 EVENTS_PUSH (
1959 ET_ARRANGER_SELECTIONS_CREATED, sel);
1960 }
1961 else
1962 {
1963 EVENTS_PUSH (
1964 ET_ARRANGER_SELECTIONS_REMOVED, sel);
1965 }
1966
1967 self->first_run = 0;
1968
1969 return 0;
1970 }
1971
1972 /**
1973 * Does or undoes the action.
1974 *
1975 * @param _do 1 to do, 0 to undo.
1976 * @param create 1 to create, 0 to delete. This just
1977 * reverses the actions done by undo/redo.
1978 */
1979 static int
do_or_undo_create_or_delete(ArrangerSelectionsAction * self,const int _do,const int create,GError ** error)1980 do_or_undo_create_or_delete (
1981 ArrangerSelectionsAction * self,
1982 const int _do,
1983 const int create,
1984 GError ** error)
1985 {
1986 int size = 0;
1987 if (create)
1988 {
1989 arranger_selections_sort_by_indices (
1990 self->sel,
1991 _do ? F_NOT_DESCENDING : F_DESCENDING);
1992 }
1993 else
1994 {
1995 arranger_selections_sort_by_indices (
1996 self->sel,
1997 _do ? F_DESCENDING : F_NOT_DESCENDING);
1998 }
1999 ArrangerObject ** objs =
2000 arranger_selections_get_all_objects (
2001 self->sel, &size);
2002 ArrangerSelections * sel =
2003 get_actual_arranger_selections (self);
2004 g_return_val_if_fail (sel, -1);
2005
2006 if (!self->first_run || !create)
2007 {
2008 /* clear current selections in the project */
2009 arranger_selections_clear (
2010 sel, F_NO_FREE, F_NO_PUBLISH_EVENTS);
2011
2012 for (int i = 0; i < size; i++)
2013 {
2014 objs[i]->flags |=
2015 ARRANGER_OBJECT_FLAG_NON_PROJECT;
2016
2017 /* if doing in a create action or undoing
2018 * in a delete action */
2019 if ((_do && create) ||
2020 (!_do && !create))
2021 {
2022 /* clone the clone */
2023 ArrangerObject * obj =
2024 arranger_object_clone (objs[i]);
2025
2026 /* add it to the project */
2027 if (create)
2028 {
2029 arranger_object_add_to_project (
2030 obj, F_NO_PUBLISH_EVENTS);
2031 }
2032 else
2033 {
2034 arranger_object_insert_to_project (
2035 obj);
2036 }
2037
2038 /* select it */
2039 arranger_object_select (
2040 obj, F_SELECT, F_APPEND,
2041 F_NO_PUBLISH_EVENTS);
2042
2043 /* remember new info */
2044 arranger_object_copy_identifier (
2045 objs[i], obj);
2046 }
2047
2048 /* if removing */
2049 else
2050 {
2051 /* get the actual object from the
2052 * project */
2053 ArrangerObject * obj =
2054 arranger_object_find (objs[i]);
2055 g_return_val_if_fail (obj, -1);
2056
2057 /* if region, remove link */
2058 if (obj->type ==
2059 ARRANGER_OBJECT_TYPE_REGION)
2060 {
2061 ZRegion * region =
2062 (ZRegion *) obj;
2063 if (region_has_link_group (
2064 region))
2065 {
2066 region_unlink (region);
2067
2068 /* unlink remembered link
2069 * groups */
2070 region = (ZRegion *) objs[i];
2071 region_unlink (region);
2072 }
2073 }
2074
2075 /* remove it */
2076 arranger_object_remove_from_project (
2077 obj);
2078 }
2079 }
2080 }
2081
2082 /* if first time creating the object, save the
2083 * length for use by SnapGrid */
2084 if (ZRYTHM_HAVE_UI && self->first_run &&
2085 create && _do && size == 1)
2086 {
2087 ArrangerObject * obj = objs[0];
2088 obj = arranger_object_find (obj);
2089 g_return_val_if_fail (obj, -1);
2090 if (arranger_object_type_has_length (
2091 obj->type))
2092 {
2093 double ticks =
2094 arranger_object_get_length_in_ticks (
2095 obj);
2096 ArrangerWidget * arranger =
2097 arranger_object_get_arranger (obj);
2098 if (arranger->type ==
2099 ARRANGER_WIDGET_TYPE_TIMELINE)
2100 {
2101 g_settings_set_double (
2102 S_UI, "timeline-last-object-length",
2103 ticks);
2104 }
2105 else
2106 {
2107 g_settings_set_double (
2108 S_UI, "editor-last-object-length",
2109 ticks);
2110 }
2111 }
2112 }
2113
2114 /* if creating */
2115 if ((_do && create) || (!_do && !create))
2116 {
2117 update_region_link_groups (objs, size);
2118
2119 transport_recalculate_total_bars (
2120 TRANSPORT, self->sel);
2121
2122 EVENTS_PUSH (
2123 ET_ARRANGER_SELECTIONS_CREATED, sel);
2124 }
2125 /* if deleting */
2126 else
2127 {
2128 EVENTS_PUSH (
2129 ET_ARRANGER_SELECTIONS_REMOVED, sel);
2130 }
2131
2132 marker_track_validate (P_MARKER_TRACK);
2133 chord_track_validate (P_CHORD_TRACK);
2134 region_link_group_manager_validate (
2135 REGION_LINK_GROUP_MANAGER);
2136
2137 free (objs);
2138
2139 self->first_run = 0;
2140
2141 if (self->sel->type ==
2142 ARRANGER_SELECTIONS_TYPE_TIMELINE)
2143 {
2144 TimelineSelections * ts =
2145 (TimelineSelections *) self->sel;
2146 bool have_automation_region = false;
2147 for (int i = 0; i < ts->num_regions; i++)
2148 {
2149 ZRegion * r = ts->regions[i];
2150 if (r->id.type == REGION_TYPE_AUTOMATION)
2151 {
2152 have_automation_region = true;
2153 break;
2154 }
2155 }
2156
2157 if (have_automation_region)
2158 {
2159 router_recalc_graph (ROUTER, F_NOT_SOFT);
2160 }
2161 }
2162
2163 return 0;
2164 }
2165
2166 /**
2167 * Does or undoes the action.
2168 *
2169 * @param _do 1 to do, 0 to undo.
2170 * @param create 1 to create, 0 to delete. This just
2171 * reverses the actions done by undo/redo.
2172 */
2173 static int
do_or_undo_record(ArrangerSelectionsAction * self,const bool _do,GError ** error)2174 do_or_undo_record (
2175 ArrangerSelectionsAction * self,
2176 const bool _do,
2177 GError ** error)
2178 {
2179 int size_before = 0;
2180 int size_after = 0;
2181 arranger_selections_sort_by_indices (
2182 self->sel, _do ? false : true);
2183 arranger_selections_sort_by_indices (
2184 self->sel_after, _do ? false : true);
2185 ArrangerObject ** before_objs =
2186 arranger_selections_get_all_objects (
2187 self->sel, &size_before);
2188 ArrangerObject ** after_objs =
2189 arranger_selections_get_all_objects (
2190 self->sel_after, &size_after);
2191 ArrangerSelections * sel =
2192 get_actual_arranger_selections (self);
2193 g_return_val_if_fail (sel, -1);
2194
2195 if (!self->first_run)
2196 {
2197 /* clear current selections in the project */
2198 arranger_selections_clear (
2199 sel, F_NO_FREE, F_NO_PUBLISH_EVENTS);
2200
2201 /* if do/redoing */
2202 if (_do)
2203 {
2204 /* create the newly recorded objects */
2205 for (int i = 0; i < size_after; i++)
2206 {
2207 after_objs[i]->flags |=
2208 ARRANGER_OBJECT_FLAG_NON_PROJECT;
2209
2210 /* clone the clone */
2211 ArrangerObject * obj =
2212 arranger_object_clone (
2213 after_objs[i]);
2214
2215 /* add it to the project */
2216 arranger_object_add_to_project (
2217 obj, F_NO_PUBLISH_EVENTS);
2218
2219 /* select it */
2220 arranger_object_select (
2221 obj, F_SELECT, F_APPEND,
2222 F_NO_PUBLISH_EVENTS);
2223
2224 /* remember new info */
2225 arranger_object_copy_identifier (
2226 after_objs[i], obj);
2227 }
2228
2229 /* delete the previous objects */
2230 for (int i = 0; i < size_before; i++)
2231 {
2232 before_objs[i]->flags |=
2233 ARRANGER_OBJECT_FLAG_NON_PROJECT;
2234
2235 /* get the actual object from the
2236 * project */
2237 ArrangerObject * obj =
2238 arranger_object_find (
2239 before_objs[i]);
2240 g_return_val_if_fail (obj, -1);
2241
2242 /* remove it */
2243 arranger_object_remove_from_project (obj);
2244 }
2245 }
2246
2247 /* if undoing */
2248 else
2249 {
2250 /* delete the newly recorded objects */
2251 for (int i = 0; i < size_after; i++)
2252 {
2253 after_objs[i]->flags |=
2254 ARRANGER_OBJECT_FLAG_NON_PROJECT;
2255
2256 /* get the actual object from the
2257 * project */
2258 ArrangerObject * obj =
2259 arranger_object_find (
2260 after_objs[i]);
2261 g_return_val_if_fail (obj, -1);
2262
2263 /* remove it */
2264 arranger_object_remove_from_project (obj);
2265 }
2266
2267 /* add the objects before the recording */
2268 for (int i = 0; i < size_before; i++)
2269 {
2270 before_objs[i]->flags |=
2271 ARRANGER_OBJECT_FLAG_NON_PROJECT;
2272
2273 /* clone the clone */
2274 ArrangerObject * obj =
2275 arranger_object_clone (
2276 before_objs[i]);
2277
2278 /* add it to the project */
2279 arranger_object_add_to_project (
2280 obj, F_NO_PUBLISH_EVENTS);
2281
2282 /* select it */
2283 arranger_object_select (
2284 obj, F_SELECT, F_APPEND,
2285 F_NO_PUBLISH_EVENTS);
2286
2287 /* remember new info */
2288 arranger_object_copy_identifier (
2289 before_objs[i], obj);
2290 }
2291 }
2292 }
2293
2294 free (before_objs);
2295 free (after_objs);
2296
2297 EVENTS_PUSH (
2298 ET_ARRANGER_SELECTIONS_CREATED, sel);
2299 EVENTS_PUSH (
2300 ET_ARRANGER_SELECTIONS_REMOVED, sel);
2301
2302 self->first_run = 0;
2303
2304 if (self->sel->type ==
2305 ARRANGER_SELECTIONS_TYPE_TIMELINE)
2306 {
2307 TimelineSelections * ts =
2308 (TimelineSelections *) self->sel;
2309 bool have_automation_region = false;
2310 for (int i = 0; i < ts->num_regions; i++)
2311 {
2312 ZRegion * r = ts->regions[i];
2313 if (r->id.type == REGION_TYPE_AUTOMATION)
2314 {
2315 have_automation_region = true;
2316 break;
2317 }
2318 }
2319
2320 if (have_automation_region)
2321 {
2322 router_recalc_graph (ROUTER, F_NOT_SOFT);
2323 }
2324 }
2325
2326 return 0;
2327 }
2328
2329 /**
2330 * Does or undoes the action.
2331 *
2332 * @param _do 1 to do, 0 to undo.
2333 * @param create 1 to create, 0 to delete. This just
2334 * reverses the actions done by undo/redo.
2335 */
2336 static int
do_or_undo_edit(ArrangerSelectionsAction * self,const int _do,GError ** error)2337 do_or_undo_edit (
2338 ArrangerSelectionsAction * self,
2339 const int _do,
2340 GError ** error)
2341 {
2342 int size = 0;
2343 ArrangerObject ** objs_before =
2344 arranger_selections_get_all_objects (
2345 self->sel, &size);
2346 ArrangerObject ** objs_after =
2347 arranger_selections_get_all_objects (
2348 self->sel_after, &size);
2349
2350 ArrangerObject ** src_objs =
2351 _do ? objs_before : objs_after;
2352 ArrangerObject ** dest_objs =
2353 _do ? objs_after : objs_before;
2354
2355 if (!self->first_run)
2356 {
2357 if (self->edit_type ==
2358 ARRANGER_SELECTIONS_ACTION_EDIT_EDITOR_FUNCTION
2359 &&
2360 self->sel->type ==
2361 ARRANGER_SELECTIONS_TYPE_AUDIO)
2362 {
2363 AudioSelections * src_audio_sel =
2364 (AudioSelections *)
2365 (_do ? self->sel_after : self->sel);
2366 ZRegion * r =
2367 region_find (&src_audio_sel->region_id);
2368 AudioClip * src_clip =
2369 audio_pool_get_clip (
2370 AUDIO_POOL, src_audio_sel->pool_id);
2371
2372 /* adjust the positions */
2373 Position start, end;
2374 position_set_to_pos (
2375 &start, &src_audio_sel->sel_start);
2376 position_set_to_pos (
2377 &end, &src_audio_sel->sel_end);
2378 position_add_frames (
2379 &start, - r->base.pos.frames);
2380 position_add_frames (
2381 &end, - r->base.pos.frames);
2382 size_t num_frames =
2383 (size_t) (end.frames - start.frames);
2384 g_return_val_if_fail (
2385 (long) num_frames ==
2386 src_clip->num_frames, -1);
2387
2388 char * src_clip_path =
2389 audio_clip_get_path_in_pool (
2390 src_clip, F_NOT_BACKUP);
2391 g_message (
2392 "replacing audio region %s frames with "
2393 "'%s' frames",
2394 r->name, src_clip_path);
2395 g_free (src_clip_path);
2396
2397 /* replace the frames in the region */
2398 audio_region_replace_frames (
2399 r, src_clip->frames,
2400 (size_t) start.frames,
2401 num_frames, F_NO_DUPLICATE_CLIP);
2402 }
2403 else /* not audio function */
2404 {
2405 for (int i = 0; i < size; i++)
2406 {
2407 g_return_val_if_fail (
2408 src_objs[i], -1);
2409 g_return_val_if_fail (
2410 dest_objs[i], -1);
2411 src_objs[i]->flags |=
2412 ARRANGER_OBJECT_FLAG_NON_PROJECT;
2413 dest_objs[i]->flags |=
2414 ARRANGER_OBJECT_FLAG_NON_PROJECT;
2415
2416 /* find the actual object */
2417 ArrangerObject * obj =
2418 arranger_object_find (src_objs[i]);
2419 g_return_val_if_fail (obj, -1);
2420
2421 /* change the parameter */
2422 switch (self->edit_type)
2423 {
2424 case ARRANGER_SELECTIONS_ACTION_EDIT_NAME:
2425 {
2426 g_return_val_if_fail (
2427 arranger_object_type_has_name (
2428 obj->type), -1);
2429 const char * name =
2430 arranger_object_get_name (
2431 dest_objs[i]);
2432 arranger_object_set_name (
2433 obj, name,
2434 F_NO_PUBLISH_EVENTS);
2435 }
2436 break;
2437 case ARRANGER_SELECTIONS_ACTION_EDIT_POS:
2438 obj->pos =
2439 dest_objs[i]->pos;
2440 obj->end_pos =
2441 dest_objs[i]->end_pos;
2442 obj->clip_start_pos =
2443 dest_objs[i]->clip_start_pos;
2444 obj->loop_start_pos =
2445 dest_objs[i]->loop_start_pos;
2446 obj->loop_end_pos =
2447 dest_objs[i]->loop_end_pos;
2448 break;
2449 case ARRANGER_SELECTIONS_ACTION_EDIT_FADES:
2450 obj->fade_in_pos =
2451 dest_objs[i]->fade_in_pos;
2452 obj->fade_out_pos =
2453 dest_objs[i]->fade_out_pos;
2454 obj->fade_in_opts =
2455 dest_objs[i]->fade_in_opts;
2456 obj->fade_out_opts =
2457 dest_objs[i]->fade_out_opts;
2458 break;
2459 case ARRANGER_SELECTIONS_ACTION_EDIT_PRIMITIVE:
2460 #define SET_PRIMITIVE(cc,member) \
2461 ((cc *) obj)->member = ((cc *) dest_objs[i])->member
2462
2463 switch (obj->type)
2464 {
2465 case ARRANGER_OBJECT_TYPE_REGION:
2466 {
2467 SET_PRIMITIVE (
2468 ArrangerObject, muted);
2469 SET_PRIMITIVE (ZRegion, color);
2470 SET_PRIMITIVE (
2471 ZRegion, musical_mode);
2472 SET_PRIMITIVE (
2473 ZRegion, gain);
2474 }
2475 break;
2476 case ARRANGER_OBJECT_TYPE_MIDI_NOTE:
2477 {
2478 SET_PRIMITIVE (MidiNote, muted);
2479
2480 /* set velocity and cache vel */
2481 MidiNote * mn =
2482 (MidiNote *) obj;
2483 MidiNote * dest_mn =
2484 (MidiNote *) dest_objs[i];
2485 velocity_set_val (
2486 mn->vel, dest_mn->vel->vel);
2487 }
2488 break;
2489 case ARRANGER_OBJECT_TYPE_AUTOMATION_POINT:
2490 {
2491 SET_PRIMITIVE (
2492 AutomationPoint, curve_opts);
2493 SET_PRIMITIVE (
2494 AutomationPoint, fvalue);
2495 SET_PRIMITIVE (
2496 AutomationPoint,
2497 normalized_val);
2498 }
2499 break;
2500 default:
2501 break;
2502 }
2503 break;
2504 case ARRANGER_SELECTIONS_ACTION_EDIT_EDITOR_FUNCTION:
2505 switch (obj->type)
2506 {
2507 case ARRANGER_OBJECT_TYPE_MIDI_NOTE:
2508 {
2509 SET_PRIMITIVE (MidiNote, muted);
2510
2511 /* set velocity and cache vel */
2512 MidiNote * mn =
2513 (MidiNote *) obj;
2514 MidiNote * dest_mn =
2515 (MidiNote *) dest_objs[i];
2516 velocity_set_val (
2517 mn->vel, dest_mn->vel->vel);
2518 }
2519 break;
2520 case ARRANGER_OBJECT_TYPE_AUTOMATION_POINT:
2521 {
2522 SET_PRIMITIVE (
2523 AutomationPoint, curve_opts);
2524 SET_PRIMITIVE (
2525 AutomationPoint, fvalue);
2526 SET_PRIMITIVE (
2527 AutomationPoint,
2528 normalized_val);
2529 }
2530 break;
2531 default:
2532 break;
2533 }
2534 #undef SET_PRIMITIVE
2535 break;
2536 case ARRANGER_SELECTIONS_ACTION_EDIT_SCALE:
2537 {
2538 ScaleObject * scale =
2539 (ScaleObject *) obj;
2540 ScaleObject * dest_scale =
2541 (ScaleObject *) dest_objs[i];
2542
2543 /* set the new scale */
2544 MusicalScale * old = scale->scale;
2545 scale->scale =
2546 musical_scale_clone (
2547 dest_scale->scale);
2548 free_later (old, musical_scale_free);
2549 }
2550 break;
2551 case ARRANGER_SELECTIONS_ACTION_EDIT_MUTE:
2552 {
2553 /* set the new status */
2554 arranger_object_set_muted (
2555 obj, !obj->muted, false);
2556 }
2557 break;
2558 default:
2559 break;
2560 }
2561 }
2562 } /* endif audio function */
2563 } /* endif not first run */
2564
2565 update_region_link_groups (dest_objs, size);
2566
2567 free (src_objs);
2568 free (dest_objs);
2569
2570 ArrangerSelections * sel =
2571 get_actual_arranger_selections (self);
2572 EVENTS_PUSH (
2573 ET_ARRANGER_SELECTIONS_CHANGED_REDRAW_EVERYTHING, sel);
2574
2575 self->first_run = false;
2576
2577 return 0;
2578 }
2579
2580 /**
2581 * Does or undoes the action.
2582 *
2583 * @param _do 1 to do, 0 to undo.
2584 * @param create 1 to create, 0 to delete. This just
2585 * reverses the actions done by undo/redo.
2586 */
2587 static int
do_or_undo_automation_fill(ArrangerSelectionsAction * self,const int _do,GError ** error)2588 do_or_undo_automation_fill (
2589 ArrangerSelectionsAction * self,
2590 const int _do,
2591 GError ** error)
2592 {
2593 if (!self->first_run)
2594 {
2595 /* clear current selections in the project */
2596 arranger_selections_clear (
2597 (ArrangerSelections *) TL_SELECTIONS,
2598 F_NO_FREE, F_NO_PUBLISH_EVENTS);
2599
2600 /* get the actual object from the
2601 * project */
2602 ArrangerObject * obj =
2603 arranger_object_find (
2604 _do ?
2605 (ArrangerObject *) self->region_before :
2606 (ArrangerObject *) self->region_after);
2607 g_return_val_if_fail (obj, -1);
2608
2609 /* remove link */
2610 ZRegion * region =
2611 (ZRegion *) obj;
2612 if (region_has_link_group (
2613 region))
2614 {
2615 region_unlink (region);
2616
2617 /* unlink remembered link
2618 * groups */
2619 region =
2620 _do ?
2621 self->region_before :
2622 self->region_after;
2623 region_unlink (region);
2624 }
2625
2626 /* remove it */
2627 arranger_object_remove_from_project (obj);
2628
2629 /* clone the clone */
2630 obj =
2631 arranger_object_clone (
2632 _do ?
2633 (ArrangerObject *) self->region_after :
2634 (ArrangerObject *) self->region_before);
2635
2636 /* add it to the project */
2637 arranger_object_add_to_project (
2638 obj, F_NO_PUBLISH_EVENTS);
2639
2640 /* select it */
2641 arranger_object_select (
2642 obj, F_SELECT, F_APPEND,
2643 F_NO_PUBLISH_EVENTS);
2644
2645 /* remember new info */
2646 arranger_object_copy_identifier (
2647 _do ?
2648 (ArrangerObject *) self->region_after :
2649 (ArrangerObject *) self->region_before,
2650 obj);
2651 }
2652
2653 self->first_run = false;
2654
2655 return 0;
2656 }
2657
2658 /**
2659 * Does or undoes the action.
2660 *
2661 * @param _do 1 to do, 0 to undo.
2662 */
2663 static int
do_or_undo_split(ArrangerSelectionsAction * self,const int _do,GError ** error)2664 do_or_undo_split (
2665 ArrangerSelectionsAction * self,
2666 const int _do,
2667 GError ** error)
2668 {
2669 int size = 0;
2670 ArrangerObject ** objs =
2671 arranger_selections_get_all_objects (
2672 self->sel, &size);
2673
2674 for (int i = 0; i < size; i++)
2675 {
2676 objs[i]->flags |=
2677 ARRANGER_OBJECT_FLAG_NON_PROJECT;
2678
2679 ArrangerObject * obj, * r1, * r2;
2680 if (_do)
2681 {
2682 /* find the actual object */
2683 obj =
2684 arranger_object_find (objs[i]);
2685 g_return_val_if_fail (obj, -1);
2686
2687 /* split */
2688 arranger_object_split (
2689 obj, &self->pos, 0, &self->r1[i],
2690 &self->r2[i], true);
2691
2692 /* r1 and r2 are now inside the project,
2693 * clone them to keep copies */
2694 g_return_val_if_fail (self->r1[i], -1);
2695 g_return_val_if_fail (self->r2[i], -1);
2696 set_split_objects (
2697 self, i, self->r1[i], self->r2[i],
2698 true);
2699 }
2700 /* else if undoing split */
2701 else
2702 {
2703 /* find the actual objects */
2704 r1 =
2705 arranger_object_find (self->r1[i]);
2706 r2 =
2707 arranger_object_find (self->r2[i]);
2708 g_return_val_if_fail (r1 && r2, -1);
2709
2710 /* unsplit */
2711 arranger_object_unsplit (
2712 r1, r2, &obj, F_NO_PUBLISH_EVENTS);
2713 if (obj->type ==
2714 ARRANGER_OBJECT_TYPE_REGION)
2715 {
2716 arranger_object_set_name (
2717 obj,
2718 ((ZRegion *) objs[i])->name,
2719 F_NO_PUBLISH_EVENTS);
2720 }
2721
2722 /* re-insert object at its original
2723 * position */
2724 arranger_object_remove_from_project (
2725 obj);
2726 obj =
2727 arranger_object_clone (objs[i]);
2728 arranger_object_insert_to_project (
2729 obj);
2730
2731 /* free the copies created in _do */
2732 free_split_objects (self, i);
2733 }
2734 }
2735 free (objs);
2736
2737 if (_do)
2738 self->num_split_objs = size;
2739 else
2740 self->num_split_objs = 0;
2741
2742 ArrangerSelections * sel =
2743 get_actual_arranger_selections (self);
2744 EVENTS_PUSH (
2745 ET_ARRANGER_SELECTIONS_CHANGED, sel);
2746
2747 self->first_run = 0;
2748
2749 return 0;
2750 }
2751
2752 /**
2753 * Does or undoes the action.
2754 *
2755 * @param _do 1 to do, 0 to undo.
2756 */
2757 static int
do_or_undo_merge(ArrangerSelectionsAction * self,const bool _do,GError ** error)2758 do_or_undo_merge (
2759 ArrangerSelectionsAction * self,
2760 const bool _do,
2761 GError ** error)
2762 {
2763 /* if first run, merge */
2764 if (self->first_run)
2765 {
2766 self->sel_after =
2767 arranger_selections_clone (self->sel);
2768 arranger_selections_merge (self->sel_after);
2769 }
2770
2771 arranger_selections_sort_by_indices (
2772 self->sel, !_do);
2773 arranger_selections_sort_by_indices (
2774 self->sel_after, !_do);
2775
2776 int before_size = 0;
2777 ArrangerObject ** before_objs =
2778 arranger_selections_get_all_objects (
2779 _do ? self->sel : self->sel_after,
2780 &before_size);
2781 int after_size = 0;
2782 ArrangerObject ** after_objs =
2783 arranger_selections_get_all_objects (
2784 _do ? self->sel_after : self->sel,
2785 &after_size);
2786
2787 /* remove the before objects from the project */
2788 for (int i = before_size - 1; i >= 0; i--)
2789 {
2790 before_objs[i]->flags |=
2791 ARRANGER_OBJECT_FLAG_NON_PROJECT;
2792
2793 /* find the actual object */
2794 ArrangerObject * prj_obj =
2795 arranger_object_find (before_objs[i]);
2796 g_return_val_if_fail (prj_obj, -1);
2797
2798 /* remove */
2799 arranger_object_remove_from_project (prj_obj);
2800 }
2801
2802 /* add the after objects to the project */
2803 for (int i = after_size - 1; i >= 0; i--)
2804 {
2805 after_objs[i]->flags |=
2806 ARRANGER_OBJECT_FLAG_NON_PROJECT;
2807
2808 ArrangerObject * clone_obj =
2809 arranger_object_clone (after_objs[i]);
2810
2811 arranger_object_add_to_project (
2812 clone_obj, F_NO_PUBLISH_EVENTS);
2813
2814 /* remember positions */
2815 arranger_object_copy_identifier (
2816 after_objs[i], clone_obj);
2817 }
2818
2819 free (before_objs);
2820 free (after_objs);
2821
2822 ArrangerSelections * sel =
2823 get_actual_arranger_selections (self);
2824 EVENTS_PUSH (
2825 ET_ARRANGER_SELECTIONS_CHANGED, sel);
2826
2827 self->first_run = 0;
2828
2829 return 0;
2830 }
2831
2832 /**
2833 * Does or undoes the action.
2834 *
2835 * @param _do 1 to do, 0 to undo.
2836 */
2837 static int
do_or_undo_resize(ArrangerSelectionsAction * self,const int _do,GError ** error)2838 do_or_undo_resize (
2839 ArrangerSelectionsAction * self,
2840 const int _do,
2841 GError ** error)
2842 {
2843 int size = 0;
2844 ArrangerObject ** objs =
2845 arranger_selections_get_all_objects (
2846 self->sel, &size);
2847
2848 double ticks =
2849 _do ? self->ticks : - self->ticks;
2850
2851 if (!self->first_run)
2852 {
2853 for (int i = 0; i < size; i++)
2854 {
2855 objs[i]->flags |=
2856 ARRANGER_OBJECT_FLAG_NON_PROJECT;
2857
2858 /* find the actual object */
2859 ArrangerObject * obj =
2860 arranger_object_find (objs[i]);
2861 g_return_val_if_fail (obj, -1);
2862
2863 /* resize */
2864 ArrangerObjectResizeType type = -1;
2865 int left = 0;
2866 switch (self->resize_type)
2867 {
2868 case ARRANGER_SELECTIONS_ACTION_RESIZE_L:
2869 type = ARRANGER_OBJECT_RESIZE_NORMAL;
2870 left = true;
2871 break;
2872 case ARRANGER_SELECTIONS_ACTION_STRETCH_L:
2873 type = ARRANGER_OBJECT_RESIZE_STRETCH;
2874 left = true;
2875 break;
2876 case ARRANGER_SELECTIONS_ACTION_RESIZE_L_LOOP:
2877 left = true;
2878 type = ARRANGER_OBJECT_RESIZE_LOOP;
2879 break;
2880 case ARRANGER_SELECTIONS_ACTION_RESIZE_L_FADE:
2881 left = true;
2882 type = ARRANGER_OBJECT_RESIZE_FADE;
2883 break;
2884 case ARRANGER_SELECTIONS_ACTION_RESIZE_R:
2885 type = ARRANGER_OBJECT_RESIZE_NORMAL;
2886 break;
2887 case ARRANGER_SELECTIONS_ACTION_STRETCH_R:
2888 type = ARRANGER_OBJECT_RESIZE_STRETCH;
2889 break;
2890 case ARRANGER_SELECTIONS_ACTION_RESIZE_R_LOOP:
2891 type = ARRANGER_OBJECT_RESIZE_LOOP;
2892 break;
2893 case ARRANGER_SELECTIONS_ACTION_RESIZE_R_FADE:
2894 type = ARRANGER_OBJECT_RESIZE_FADE;
2895 break;
2896 default:
2897 g_warn_if_reached ();
2898 }
2899 arranger_object_resize (
2900 obj, left, type, ticks, false);
2901
2902 /* also resize the clone so we can find
2903 * the actual object next time */
2904 arranger_object_resize (
2905 objs[i], left, type, ticks, false);
2906 }
2907 }
2908
2909 update_region_link_groups (objs, size);
2910
2911 free (objs);
2912
2913 ArrangerSelections * sel =
2914 get_actual_arranger_selections (self);
2915 EVENTS_PUSH (
2916 ET_ARRANGER_SELECTIONS_CHANGED, sel);
2917 EVENTS_PUSH (
2918 ET_ARRANGER_SELECTIONS_ACTION_FINISHED, sel);
2919
2920 self->first_run = 0;
2921
2922 return 0;
2923 }
2924
2925 /**
2926 * Does or undoes the action.
2927 *
2928 * @param _do 1 to do, 0 to undo.
2929 */
2930 static int
do_or_undo_quantize(ArrangerSelectionsAction * self,const int _do,GError ** error)2931 do_or_undo_quantize (
2932 ArrangerSelectionsAction * self,
2933 const int _do,
2934 GError ** error)
2935 {
2936 int size = 0;
2937 ArrangerObject ** objs =
2938 arranger_selections_get_all_objects (
2939 self->sel, &size);
2940 ArrangerObject ** quantized_objs =
2941 arranger_selections_get_all_objects (
2942 self->sel_after, &size);
2943
2944 for (int i = 0; i < size; i++)
2945 {
2946 objs[i]->flags |=
2947 ARRANGER_OBJECT_FLAG_NON_PROJECT;
2948 quantized_objs[i]->flags |=
2949 ARRANGER_OBJECT_FLAG_NON_PROJECT;
2950
2951 /* find the actual object */
2952 ArrangerObject * obj;
2953 if (_do)
2954 {
2955 obj =
2956 arranger_object_find (objs[i]);
2957 }
2958 else
2959 {
2960 obj =
2961 arranger_object_find (quantized_objs[i]);
2962 }
2963 g_return_val_if_fail (obj, -1);
2964
2965 if (_do)
2966 {
2967 /* quantize it */
2968 if (self->opts->adj_start)
2969 {
2970 double ticks =
2971 quantize_options_quantize_position (
2972 self->opts, &obj->pos);
2973 position_add_ticks (
2974 &obj->end_pos, ticks);
2975 }
2976 if (self->opts->adj_end)
2977 {
2978 quantize_options_quantize_position (
2979 self->opts, &obj->end_pos);
2980 }
2981 arranger_object_pos_setter (
2982 obj, &obj->pos);
2983 arranger_object_end_pos_setter (
2984 obj, &obj->end_pos);
2985
2986 /* remember the quantized position so we
2987 * can find the object when undoing */
2988 position_set_to_pos (
2989 &quantized_objs[i]->pos, &obj->pos);
2990 position_set_to_pos (
2991 &quantized_objs[i]->end_pos,
2992 &obj->end_pos);
2993 }
2994 else
2995 {
2996 /* unquantize it */
2997 arranger_object_pos_setter (
2998 obj, &objs[i]->pos);
2999 arranger_object_end_pos_setter (
3000 obj, &objs[i]->end_pos);
3001 }
3002 }
3003 free (objs);
3004
3005 ArrangerSelections * sel =
3006 get_actual_arranger_selections (self);
3007 EVENTS_PUSH (
3008 ET_ARRANGER_SELECTIONS_QUANTIZED, sel);
3009
3010 self->first_run = 0;
3011
3012 return 0;
3013 }
3014
3015 static int
do_or_undo(ArrangerSelectionsAction * self,bool _do,GError ** error)3016 do_or_undo (
3017 ArrangerSelectionsAction * self,
3018 bool _do,
3019 GError ** error)
3020 {
3021 switch (self->type)
3022 {
3023 case AS_ACTION_CREATE:
3024 return
3025 do_or_undo_create_or_delete (
3026 self, _do, true, error);
3027 break;
3028 case AS_ACTION_DELETE:
3029 return
3030 do_or_undo_create_or_delete (
3031 self, _do, false, error);
3032 break;
3033 case AS_ACTION_DUPLICATE:
3034 return
3035 do_or_undo_duplicate_or_link (
3036 self, false, _do, error);
3037 case AS_ACTION_MOVE:
3038 return do_or_undo_move (self, _do, error);
3039 case AS_ACTION_LINK:
3040 return
3041 do_or_undo_duplicate_or_link (
3042 self, true, _do, error);
3043 case AS_ACTION_RECORD:
3044 return do_or_undo_record (self, _do, error);
3045 break;
3046 case AS_ACTION_EDIT:
3047 return do_or_undo_edit (self, _do, error);
3048 break;
3049 case AS_ACTION_AUTOMATION_FILL:
3050 return do_or_undo_automation_fill (
3051 self, _do, error);
3052 case AS_ACTION_SPLIT:
3053 return do_or_undo_split (self, _do, error);
3054 break;
3055 case AS_ACTION_MERGE:
3056 return do_or_undo_merge (self, _do, error);
3057 break;
3058 case AS_ACTION_RESIZE:
3059 return do_or_undo_resize (self, _do, error);
3060 break;
3061 case AS_ACTION_QUANTIZE:
3062 return do_or_undo_quantize (self, _do, error);
3063 break;
3064 default:
3065 break;
3066 }
3067 g_return_val_if_reached (-1);
3068 }
3069
3070 int
arranger_selections_action_do(ArrangerSelectionsAction * self,GError ** error)3071 arranger_selections_action_do (
3072 ArrangerSelectionsAction * self,
3073 GError ** error)
3074 {
3075 return do_or_undo (self, true, error);
3076 }
3077
3078 int
arranger_selections_action_undo(ArrangerSelectionsAction * self,GError ** error)3079 arranger_selections_action_undo (
3080 ArrangerSelectionsAction * self,
3081 GError ** error)
3082 {
3083 return do_or_undo (self, false, error);
3084 }
3085
3086 bool
arranger_selections_action_contains_clip(ArrangerSelectionsAction * self,AudioClip * clip)3087 arranger_selections_action_contains_clip (
3088 ArrangerSelectionsAction * self,
3089 AudioClip * clip)
3090 {
3091 if (self->sel &&
3092 arranger_selections_contains_clip (
3093 self->sel, clip))
3094 {
3095 return true;
3096 }
3097 if (self->sel_after &&
3098 arranger_selections_contains_clip (
3099 self->sel_after, clip))
3100 {
3101 return true;
3102 }
3103
3104 /* check split regions (if any) */
3105 for (int i = 0; i < self->num_split_objs; i++)
3106 {
3107 ZRegion * r1 = self->region_r1[i];
3108 ZRegion * r2 = self->region_r2[i];
3109 if (r1 && r2)
3110 {
3111 if (r1->id.type != REGION_TYPE_AUDIO)
3112 break;
3113
3114 if (r1->pool_id == clip->pool_id ||
3115 r2->pool_id == clip->pool_id)
3116 {
3117 return true;
3118 }
3119 }
3120 else
3121 break;
3122 }
3123
3124 return false;
3125 }
3126
3127 char *
arranger_selections_action_stringize(ArrangerSelectionsAction * self)3128 arranger_selections_action_stringize (
3129 ArrangerSelectionsAction * self)
3130 {
3131 switch (self->type)
3132 {
3133 case AS_ACTION_CREATE:
3134 switch (self->sel->type)
3135 {
3136 case ARRANGER_SELECTIONS_TYPE_TIMELINE:
3137 return
3138 g_strdup (
3139 _("Create timeline selections"));
3140 case ARRANGER_SELECTIONS_TYPE_AUDIO:
3141 return
3142 g_strdup (
3143 _("Create audio selections"));
3144 case ARRANGER_SELECTIONS_TYPE_AUTOMATION:
3145 return
3146 g_strdup (
3147 _("Create automation selections"));
3148 case ARRANGER_SELECTIONS_TYPE_CHORD:
3149 return
3150 g_strdup (
3151 _("Create chord selections"));
3152 case ARRANGER_SELECTIONS_TYPE_MIDI:
3153 return
3154 g_strdup (
3155 _("Create MIDI selections"));
3156 default:
3157 g_return_val_if_reached (NULL);
3158 }
3159 case AS_ACTION_DELETE:
3160 return
3161 g_strdup (_("Delete arranger selections"));
3162 case AS_ACTION_DUPLICATE:
3163 return
3164 g_strdup (
3165 _("Duplicate arranger selections"));
3166 case AS_ACTION_MOVE:
3167 return
3168 g_strdup (_("Move arranger selections"));
3169 case AS_ACTION_LINK:
3170 return
3171 g_strdup (_("Link arranger selections"));
3172 case AS_ACTION_RECORD:
3173 return
3174 g_strdup (_("Record arranger selections"));
3175 case AS_ACTION_EDIT:
3176 return
3177 g_strdup (_("Edit arranger selections"));
3178 case AS_ACTION_AUTOMATION_FILL:
3179 return
3180 g_strdup (_("Automation fill"));
3181 case AS_ACTION_SPLIT:
3182 return
3183 g_strdup (_("Split arranger selections"));
3184 case AS_ACTION_MERGE:
3185 return
3186 g_strdup (_("Merge arranger selections"));
3187 case AS_ACTION_RESIZE:
3188 return
3189 g_strdup (_("Resize arranger selections"));
3190 case AS_ACTION_QUANTIZE:
3191 return
3192 g_strdup (
3193 _("Quantize arranger selections"));
3194 default:
3195 break;
3196 }
3197
3198 g_return_val_if_reached (g_strdup (""));
3199 }
3200
3201 void
arranger_selections_action_free(ArrangerSelectionsAction * self)3202 arranger_selections_action_free (
3203 ArrangerSelectionsAction * self)
3204 {
3205 object_free_w_func_and_null (
3206 arranger_selections_free_full, self->sel);
3207
3208 object_zero_and_free (self);
3209 }
3210