1 /*
2  * Copyright (C) 2020-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/undo_stack.h"
21 #include "settings/settings.h"
22 #include "utils/arrays.h"
23 #include "utils/mem.h"
24 #include "utils/objects.h"
25 #include "utils/stack.h"
26 #include "zrythm.h"
27 #include "zrythm_app.h"
28 
29 NONNULL
30 size_t
undo_stack_get_total_cached_actions(UndoStack * self)31 undo_stack_get_total_cached_actions (
32   UndoStack * self)
33 {
34   size_t total =
35     self->num_as_actions +
36     self->num_mixer_selections_actions +
37     self->num_tracklist_selections_actions +
38     self->num_channel_send_actions +
39     self->num_midi_mapping_actions +
40     self->num_port_actions +
41     self->num_port_connection_actions +
42     self->num_range_actions +
43     self->num_transport_actions;
44 
45   if ((int) total > self->stack->max_length)
46     {
47       g_critical (
48         "total stack members (%zu) higher than "
49         "max length (%d)",
50         total, self->stack->max_length);
51     }
52 
53   return total;
54 }
55 
56 void
undo_stack_init_loaded(UndoStack * self)57 undo_stack_init_loaded (
58   UndoStack * self)
59 {
60   /* use the remembered max length instead of the
61    * one in settings so they match */
62   int undo_stack_length = self->stack->max_length;
63   stack_free (self->stack);
64   self->stack =
65     stack_new (undo_stack_length);
66   self->stack->top = -1;
67 
68   size_t as_actions_idx = 0;
69   size_t mixer_selections_actions_idx = 0;
70   size_t tracklist_selections_actions_idx = 0;
71   size_t channel_send_actions_idx = 0;
72   size_t port_actions_idx = 0;
73   size_t port_connection_actions_idx = 0;
74   size_t midi_mapping_actions_idx = 0;
75   size_t range_actions_idx = 0;
76   size_t transport_actions_idx = 0;
77 
78   size_t total_actions =
79     undo_stack_get_total_cached_actions (self);
80 
81 #define DO_SIMPLE(cc,sc) \
82   /* if there are still actions of this type */ \
83   if (sc##_actions_idx < self->num_##sc##_actions) \
84     { \
85       cc##Action * a = \
86         self->sc##_actions[sc##_actions_idx]; \
87       UndoableAction * ua = \
88         (UndoableAction *) a; \
89       undoable_action_init_loaded (ua); \
90       if (self->stack->top + 1 == ua->stack_idx) \
91         { \
92           STACK_PUSH (self->stack, ua); \
93           sc##_actions_idx++; \
94         } \
95     }
96 
97   for (size_t i = 0; i < total_actions; i++)
98     {
99       DO_SIMPLE (ArrangerSelections, as)
100       DO_SIMPLE (TracklistSelections, tracklist_selections)
101       DO_SIMPLE (ChannelSend, channel_send)
102       DO_SIMPLE (MixerSelections, mixer_selections)
103       DO_SIMPLE (PortConnection, port_connection)
104       DO_SIMPLE (Port, port)
105       DO_SIMPLE (MidiMapping, midi_mapping)
106       DO_SIMPLE (Range, range)
107       DO_SIMPLE (Transport, transport)
108     }
109 
110   g_return_if_fail (
111     self->stack->top + 1 ==
112       (int)
113       undo_stack_get_total_cached_actions (self));
114 }
115 
116 UndoStack *
undo_stack_new(void)117 undo_stack_new (void)
118 {
119   g_return_val_if_fail (
120     (ZRYTHM && ZRYTHM->testing) ||
121       G_IS_SETTINGS (S_P_EDITING_UNDO), NULL);
122 
123   UndoStack * self = object_new (UndoStack);
124   self->schema_version = UNDO_STACK_SCHEMA_VERSION;
125 
126   int undo_stack_length =
127     ZRYTHM_TESTING ?
128       ZRYTHM->undo_stack_len :
129       g_settings_get_int (
130         S_P_EDITING_UNDO, "undo-stack-length");
131   self->stack =
132     stack_new (undo_stack_length);
133   self->stack->top = -1;
134 
135   return self;
136 }
137 
138 UndoStack *
undo_stack_clone(const UndoStack * src)139 undo_stack_clone (
140   const UndoStack * src)
141 {
142   UndoStack * self = object_new (UndoStack);
143   self->schema_version = UNDO_STACK_SCHEMA_VERSION;
144 
145   int top_before = src->stack->top;
146 
147   self->stack =
148     stack_new (src->stack->max_length);
149   self->stack->top = -1;
150 
151   /* clone all actions */
152 #define CLONE_ACTIONS(arr,fn,type) \
153   self->arr = \
154     object_new_n (src->num_##arr, type *); \
155   for (size_t i = 0; i < src->num_##arr; i++) \
156     { \
157       self->arr[i] = fn##_clone (src->arr[i]); \
158       g_return_val_if_fail (self->arr[i], NULL); \
159     } \
160   self->num_##arr = src->num_##arr
161 
162   CLONE_ACTIONS (
163     as_actions, arranger_selections_action,
164     ArrangerSelectionsAction);
165   CLONE_ACTIONS (
166     mixer_selections_actions,
167     mixer_selections_action,
168     MixerSelectionsAction);
169   CLONE_ACTIONS (
170     tracklist_selections_actions,
171     tracklist_selections_action,
172     TracklistSelectionsAction);
173   CLONE_ACTIONS (
174     channel_send_actions, channel_send_action,
175     ChannelSendAction);
176   CLONE_ACTIONS (
177     port_connection_actions, port_connection_action,
178     PortConnectionAction);
179   CLONE_ACTIONS (
180     port_actions, port_action,
181     PortAction);
182   CLONE_ACTIONS (
183     midi_mapping_actions, midi_mapping_action,
184     MidiMappingAction);
185   CLONE_ACTIONS (
186     range_actions, range_action, RangeAction);
187   CLONE_ACTIONS (
188     transport_actions, transport_action,
189     TransportAction);
190 
191 #undef CLONE_ACTIONS
192 
193   /* init loaded to load the actions on the stack */
194   undo_stack_init_loaded (self);
195 
196   g_return_val_if_fail (
197     top_before == self->stack->top, NULL);
198 
199   return self;
200 }
201 
202 /**
203  * Gets the list of actions as a string.
204  */
205 char *
undo_stack_get_as_string(UndoStack * self,int limit)206 undo_stack_get_as_string (
207   UndoStack * self,
208   int         limit)
209 {
210   GString * g_str = g_string_new (NULL);
211 
212   Stack * stack = self->stack;
213   for (int i = stack->top; i >= 0; i--)
214     {
215       UndoableAction * action =
216         (UndoableAction *) stack->elements[i];
217 
218       char * action_str =
219         undoable_action_to_string (action);
220       g_string_append_printf (
221         g_str, "[%d] %s\n",
222         stack->top - i, action_str);
223       g_free (action_str);
224 
225       if (stack->top - i == limit)
226         break;
227     }
228 
229   return g_string_free (g_str, false);
230 }
231 
232 void
undo_stack_push(UndoStack * self,UndoableAction * action)233 undo_stack_push (
234   UndoStack *      self,
235   UndoableAction * action)
236 {
237   g_message ("pushed to undo/redo stack");
238 
239   /* push to stack */
240   STACK_PUSH (self->stack, action);
241 
242   action->stack_idx = self->stack->top;
243 
244   /* CAPS, CamelCase, snake_case */
245 #define APPEND_ELEMENT(caps,cc,sc) \
246   case UA_##caps: \
247     array_double_size_if_full ( \
248       self->sc##_actions, \
249       self->num_##sc##_actions, \
250       self->sc##_actions_size, \
251       cc##Action *); \
252     array_append ( \
253       self->sc##_actions, \
254       self->num_##sc##_actions, \
255       (cc##Action *) action); \
256     break
257 
258   switch (action->type)
259     {
260     APPEND_ELEMENT (
261       TRACKLIST_SELECTIONS, TracklistSelections,
262       tracklist_selections);
263     APPEND_ELEMENT (
264       CHANNEL_SEND, ChannelSend, channel_send);
265     APPEND_ELEMENT (
266       MIXER_SELECTIONS, MixerSelections,
267       mixer_selections);
268     APPEND_ELEMENT (
269       PORT_CONNECTION, PortConnection,
270       port_connection);
271     APPEND_ELEMENT (PORT, Port, port);
272     APPEND_ELEMENT (
273       MIDI_MAPPING, MidiMapping,
274       midi_mapping);
275     APPEND_ELEMENT (
276       RANGE, Range, range);
277     APPEND_ELEMENT (
278       TRANSPORT, Transport, transport);
279     APPEND_ELEMENT (
280       ARRANGER_SELECTIONS, ArrangerSelections, as);
281     }
282 }
283 
284 static bool
remove_action(UndoStack * self,UndoableAction * action)285 remove_action (
286   UndoStack *      self,
287   UndoableAction * action)
288 {
289   /* CAPS, CamelCase, snake_case */
290 #define REMOVE_ELEMENT(caps,cc,sc) \
291   case UA_##caps: \
292     array_delete_confirm ( \
293       self->sc##_actions, \
294       self->num_##sc##_actions, \
295       (cc##Action *) action, \
296       removed); \
297     g_return_val_if_fail (removed, removed); \
298     if ((int) self->num_##sc##_actions > \
299           g_atomic_int_get ( \
300             &self->stack->top) + 1) \
301       { \
302         g_critical ( \
303           "num " #sc " actions (%zu) is greater " \
304           "than current stack top (%d)", \
305           self->num_##sc##_actions, \
306           g_atomic_int_get ( \
307             &self->stack->top) + 1); \
308         return removed; \
309       } \
310     break
311 
312   bool removed = false;
313   switch (action->type)
314     {
315     REMOVE_ELEMENT (
316       TRACKLIST_SELECTIONS, TracklistSelections,
317       tracklist_selections);
318     REMOVE_ELEMENT (
319       CHANNEL_SEND, ChannelSend, channel_send);
320     REMOVE_ELEMENT (
321       MIXER_SELECTIONS, MixerSelections,
322       mixer_selections);
323     REMOVE_ELEMENT (
324       PORT_CONNECTION, PortConnection,
325       port_connection);
326     REMOVE_ELEMENT (PORT, Port, port);
327     REMOVE_ELEMENT (
328       MIDI_MAPPING, MidiMapping,
329       midi_mapping);
330     REMOVE_ELEMENT (
331       RANGE, Range, range);
332     REMOVE_ELEMENT (
333       TRANSPORT, Transport, transport);
334     case UA_ARRANGER_SELECTIONS:
335       array_delete_confirm (
336         self->as_actions,
337         self->num_as_actions,
338         (ArrangerSelectionsAction *) action,
339         removed);
340       g_return_val_if_fail (
341         (int) self->num_as_actions <=
342           g_atomic_int_get (&self->stack->top) + 1,
343         removed);
344       break;
345     }
346 
347   /* re-set the indices */
348   for (int i = 0;
349        i <= g_atomic_int_get (&self->stack->top);
350        i++)
351     {
352       UndoableAction * ua =
353         (UndoableAction *)
354         self->stack->elements[i];
355       ua->stack_idx = i;
356     }
357 
358   return removed;
359 }
360 
361 UndoableAction *
undo_stack_pop(UndoStack * self)362 undo_stack_pop (
363   UndoStack * self)
364 {
365   /* pop from stack */
366   UndoableAction * action =
367     (UndoableAction *) stack_pop (self->stack);
368 
369   /* remove the action */
370   int removed = remove_action (self, action);
371   g_return_val_if_fail (removed, action);
372 
373   /* return it */
374   return action;
375 }
376 
377 /**
378  * Pops the last element and moves everything back.
379  */
380 UndoableAction *
undo_stack_pop_last(UndoStack * self)381 undo_stack_pop_last (
382   UndoStack * self)
383 {
384   g_message (
385     "<undo stack> popping last (top = %d)",
386     g_atomic_int_get (&self->stack->top));
387 
388   /* pop from stack */
389   UndoableAction * action =
390     (UndoableAction *) stack_pop_last (self->stack);
391 
392   /* remove the action */
393   bool removed = remove_action (self, action);
394   g_return_val_if_fail (removed, action);
395 
396   /* return it */
397   return action;
398 }
399 
400 bool
undo_stack_contains_clip(UndoStack * self,AudioClip * clip)401 undo_stack_contains_clip (
402   UndoStack * self,
403   AudioClip * clip)
404 {
405   for (int i = 0; i <= self->stack->top; i++)
406     {
407       UndoableAction * ua =
408         (UndoableAction *)
409         self->stack->elements[i];
410 
411       if (undoable_action_contains_clip (ua, clip))
412         return true;
413     }
414 
415   return false;
416 }
417 
418 /**
419  * Checks if the undo stack contains the given
420  * action pointer.
421  */
422 bool
undo_stack_contains_action(UndoStack * self,UndoableAction * ua)423 undo_stack_contains_action (
424   UndoStack *      self,
425   UndoableAction * ua)
426 {
427   for (int i = 0; i <= self->stack->top; i++)
428     {
429       UndoableAction * action =
430         (UndoableAction *)
431         self->stack->elements[i];
432       if (ua == action)
433         return true;
434     }
435 
436   return false;
437 }
438 
439 /**
440  * Clears the stack, optionally freeing all the
441  * elements.
442  */
443 void
undo_stack_clear(UndoStack * self,bool free)444 undo_stack_clear (
445   UndoStack * self,
446   bool        free)
447 {
448   while (!undo_stack_is_empty (self))
449     {
450       UndoableAction * ua = undo_stack_pop (self);
451       if (free)
452         {
453           undoable_action_free (ua);
454         }
455     }
456 }
457 
458 void
undo_stack_free(UndoStack * self)459 undo_stack_free (
460   UndoStack * self)
461 {
462   g_message ("%s: freeing...", __func__);
463 
464   while (!undo_stack_is_empty (self))
465     {
466       UndoableAction * ua = undo_stack_pop (self);
467       char * type_str =
468         undoable_action_to_string (ua);
469       g_debug (
470         "%s: freeing %s", __func__, type_str);
471       g_free (type_str);
472       undoable_action_free (ua);
473     }
474 
475   object_zero_and_free (self);
476 
477   g_message ("%s: done", __func__);
478 }
479