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