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