1 /*
2  * Copyright (C) 2018-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/undoable_action.h"
21 #include "actions/undo_stack.h"
22 #include "actions/undo_manager.h"
23 #include "gui/backend/event.h"
24 #include "gui/backend/event_manager.h"
25 #include "gui/widgets/header.h"
26 #include "gui/widgets/home_toolbar.h"
27 #include "gui/widgets/main_window.h"
28 #include "project.h"
29 #include "utils/error.h"
30 #include "utils/objects.h"
31 #include "utils/stack.h"
32 #include "utils/ui.h"
33 #include "zrythm_app.h"
34 
35 #include <glib/gi18n.h>
36 
37 typedef enum
38 {
39   Z_ACTIONS_UNDO_MANAGER_ERROR_FAILED,
40   Z_ACTIONS_UNDO_MANAGER_ERROR_ACTION_FAILED,
41 } ZActionsUndoManagerError;
42 
43 #define Z_ACTIONS_UNDO_MANAGER_ERROR \
44   z_actions_undo_manager_error_quark ()
45 GQuark z_actions_undo_manager_error_quark (void);
46 G_DEFINE_QUARK (
47   z-actions-undo-manager-error-quark, z_actions_undo_manager_error)
48 
49 /**
50  * Inits the undo manager by populating the
51  * undo/redo stacks.
52  */
53 void
undo_manager_init_loaded(UndoManager * self)54 undo_manager_init_loaded (
55   UndoManager * self)
56 {
57   g_message ("%s: loading...", __func__);
58   undo_stack_init_loaded (self->undo_stack);
59   undo_stack_init_loaded (self->redo_stack);
60   zix_sem_init (&self->action_sem, 1);
61   g_message ("%s: done", __func__);
62 }
63 
64 /**
65  * Inits the undo manager by creating the undo/redo
66  * stacks.
67  */
68 UndoManager *
undo_manager_new(void)69 undo_manager_new (void)
70 {
71   g_message ("%s: creating...", __func__);
72   UndoManager * self = object_new (UndoManager);
73   self->schema_version = UNDO_MANAGER_SCHEMA_VERSION;
74 
75   self->undo_stack = undo_stack_new ();
76   self->redo_stack = undo_stack_new ();
77 
78   zix_sem_init (&self->action_sem, 1);
79 
80   g_message ("%s: done", __func__);
81 
82   return self;
83 }
84 
85 /**
86  * Does or undoes the given action.
87  *
88  * @param action An action, when performing,
89  *   otherwise NULL if undoing/redoing.
90  * @param main_stack Undo stack if undoing, redo
91  *   stack if doing.
92  */
93 static int
do_or_undo_action(UndoManager * self,UndoableAction * action,UndoStack * main_stack,UndoStack * opposite_stack,GError ** error)94 do_or_undo_action (
95   UndoManager *    self,
96   UndoableAction * action,
97   UndoStack *      main_stack,
98   UndoStack *      opposite_stack,
99   GError **        error)
100 {
101   bool need_pop = false;
102   if (!action)
103     {
104       /* pop the action from the undo stack and
105        * undo it */
106       action =
107         (UndoableAction *)
108         undo_stack_peek (main_stack);
109       need_pop = true;
110     }
111 
112   if (ZRYTHM_HAVE_UI)
113     {
114       /* process UI events now */
115       event_manager_process_now (EVENT_MANAGER);
116     }
117 
118   int ret = 0;
119   if (main_stack == self->undo_stack)
120     {
121       ret = undoable_action_undo (action, error);
122     }
123   else if (main_stack == self->redo_stack)
124     {
125       ret = undoable_action_do (action, error);
126     }
127   else
128     {
129       /* invalid stack */
130       g_critical (
131         "%s: invalid stack (err %d)",
132         __func__, ret);
133       return -1;
134     }
135 
136   /* if error return */
137   if (ret != 0)
138     {
139       zix_sem_post (&self->action_sem);
140       g_warning (
141         "%s: action not performed (err %d)",
142         __func__, ret);
143       return -1;
144     }
145   /* else if no error pop from the stack */
146   else if (need_pop)
147     {
148       undo_stack_pop (main_stack);
149     }
150 
151   /* if the redo stack is full, delete the last
152    * element */
153   if (undo_stack_is_full (opposite_stack))
154     {
155       UndoableAction * action_to_delete =
156         (UndoableAction *)
157         undo_stack_pop_last (opposite_stack);
158 
159       /* TODO create functions to delete
160        * unnecessary files held by the action
161        * (eg, something that calls
162        * plugin_delete_state_files()) */
163       undoable_action_free (action_to_delete);
164     }
165 
166   /* push action to the redo stack */
167   undo_stack_push (opposite_stack, action);
168   undo_stack_get_total_cached_actions (
169     opposite_stack);
170 
171   return 0;
172 }
173 
174 /**
175  * Undo last action.
176  */
177 int
undo_manager_undo(UndoManager * self,GError ** error)178 undo_manager_undo (
179   UndoManager * self,
180   GError **     error)
181 {
182   g_warn_if_fail (
183     !undo_stack_is_empty (self->undo_stack));
184 
185   zix_sem_wait (&self->action_sem);
186 
187   UndoableAction * action =
188     (UndoableAction *)
189     undo_stack_peek (self->undo_stack);
190   g_return_val_if_fail (action, -1);
191   const int num_actions = action->num_actions;
192   g_return_val_if_fail (num_actions > 0, -1);
193 
194   int ret = 0;
195   for (int i = 0; i < num_actions; i++)
196     {
197       g_message (
198         "[ACTION %d/%d]", i + 1, num_actions);
199       action =
200         (UndoableAction *)
201         undo_stack_peek (self->undo_stack);
202       if (i == 0)
203         action->num_actions = 1;
204       else if (i == num_actions - 1)
205         action->num_actions = num_actions;
206 
207       GError * err = NULL;
208       ret =
209         do_or_undo_action (
210           self, NULL, self->undo_stack,
211           self->redo_stack, &err);
212       if (ret != 0)
213         {
214           PROPAGATE_PREFIXED_ERROR (
215             error, err, "%s",
216             _("Failed to undo action"));
217           break;
218         }
219     }
220 
221   if (ZRYTHM_HAVE_UI)
222     {
223       EVENTS_PUSH (ET_UNDO_REDO_ACTION_DONE, NULL);
224 
225       /* process UI events now */
226       event_manager_process_now (EVENT_MANAGER);
227     }
228 
229   zix_sem_post (&self->action_sem);
230 
231   return ret;
232 }
233 
234 /**
235  * Redo last undone action.
236  */
237 int
undo_manager_redo(UndoManager * self,GError ** error)238 undo_manager_redo (
239   UndoManager * self,
240   GError **     error)
241 {
242   g_warn_if_fail (
243     !undo_stack_is_empty (self->redo_stack));
244 
245   zix_sem_wait (&self->action_sem);
246 
247   UndoableAction * action =
248     (UndoableAction *)
249     undo_stack_peek (self->redo_stack);
250   g_return_val_if_fail (action, -1);
251   const int num_actions = action->num_actions;
252   g_return_val_if_fail (num_actions > 0, -1);
253 
254   int ret = 0;
255   for (int i = 0; i < num_actions; i++)
256     {
257       g_message (
258         "[ACTION %d/%d]", i + 1, num_actions);
259       action =
260         (UndoableAction *)
261         undo_stack_peek (self->redo_stack);
262       if (i == 0)
263         action->num_actions = 1;
264       else if (i == num_actions - 1)
265         action->num_actions = num_actions;
266 
267       GError * err = NULL;
268       ret =
269         do_or_undo_action (
270           self, NULL, self->redo_stack,
271           self->undo_stack, &err);
272       if (ret != 0)
273         {
274           PROPAGATE_PREFIXED_ERROR (
275             error, err, "%s",
276             _("Failed to redo action"));
277           break;
278         }
279     }
280 
281   if (ZRYTHM_HAVE_UI)
282     {
283       EVENTS_PUSH (ET_UNDO_REDO_ACTION_DONE, NULL);
284 
285       /* process UI events now */
286       event_manager_process_now (EVENT_MANAGER);
287     }
288 
289   zix_sem_post (&self->action_sem);
290 
291   return ret;
292 }
293 
294 /**
295  * Does performs the action and pushes it to the
296  * undo stack, clearing the redo stack.
297  *
298  * @return Non-zero if error.
299  */
300 int
undo_manager_perform(UndoManager * self,UndoableAction * action,GError ** error)301 undo_manager_perform (
302   UndoManager *    self,
303   UndoableAction * action,
304   GError **        error)
305 {
306   /* check that action is not already in the
307    * stacks */
308   g_return_val_if_fail (
309     !undo_stack_contains_action (
310        self->undo_stack, action)
311     &&
312     !undo_stack_contains_action (
313        self->redo_stack, action),
314     -1);
315 
316   zix_sem_wait (&self->action_sem);
317 
318   /* if error return */
319   GError * err = NULL;
320   int ret =
321     do_or_undo_action (
322       self, action, self->redo_stack,
323       self->undo_stack, &err);
324   if (ret != 0)
325     {
326       PROPAGATE_PREFIXED_ERROR (
327         error, err, "%s",
328         _("Failed to perform action"));
329       zix_sem_post (&self->action_sem);
330       return ret;
331     }
332 
333   undo_stack_clear (self->redo_stack, true);
334 
335   if (ZRYTHM_HAVE_UI)
336     {
337       EVENTS_PUSH (ET_UNDO_REDO_ACTION_DONE, NULL);
338 
339       /* process UI events now */
340       event_manager_process_now (EVENT_MANAGER);
341     }
342 
343   zix_sem_post (&self->action_sem);
344 
345   return 0;
346 }
347 
348 /**
349  * Returns whether the given clip is used by any
350  * stack.
351  */
352 bool
undo_manager_contains_clip(UndoManager * self,AudioClip * clip)353 undo_manager_contains_clip (
354   UndoManager * self,
355   AudioClip *   clip)
356 {
357   bool ret =
358     undo_stack_contains_clip (
359       self->undo_stack, clip) ||
360     undo_stack_contains_clip (
361       self->redo_stack, clip);
362 
363   g_debug ("%s: %d", __func__, ret);
364 
365   return ret;
366 }
367 
368 /**
369  * Returns the last performed action, or NULL if
370  * the stack is empty.
371  */
372 UndoableAction *
undo_manager_get_last_action(UndoManager * self)373 undo_manager_get_last_action (
374   UndoManager * self)
375 {
376   UndoableAction * action =
377     (UndoableAction *)
378     undo_stack_peek (self->undo_stack);
379   return action;
380 }
381 
382 /**
383  * Clears the undo and redo stacks.
384  */
385 void
undo_manager_clear_stacks(UndoManager * self,bool free)386 undo_manager_clear_stacks (
387   UndoManager * self,
388   bool          free)
389 {
390   g_return_if_fail (
391     self->undo_stack && self->redo_stack);
392   undo_stack_clear (self->undo_stack, free);
393   undo_stack_clear (self->redo_stack, free);
394 }
395 
396 UndoManager *
undo_manager_clone(const UndoManager * src)397 undo_manager_clone (
398   const UndoManager * src)
399 {
400   UndoManager * self = object_new (UndoManager);
401   self->schema_version = UNDO_MANAGER_SCHEMA_VERSION;
402 
403   self->undo_stack =
404     undo_stack_clone (src->undo_stack);
405   self->redo_stack =
406     undo_stack_clone (src->redo_stack);
407 
408   zix_sem_init (&self->action_sem, 1);
409 
410   return self;
411 }
412 
413 void
undo_manager_free(UndoManager * self)414 undo_manager_free (
415   UndoManager * self)
416 {
417   g_message ("%s: freeing...", __func__);
418 
419   object_free_w_func_and_null (
420     undo_stack_free, self->undo_stack);
421   object_free_w_func_and_null (
422     undo_stack_free, self->redo_stack);
423 
424   zix_sem_destroy (&self->action_sem);
425 
426   object_zero_and_free (self);
427 
428   g_message ("%s: done", __func__);
429 }
430