1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /* CajaUndoStackManager - Manages undo of file operations (implementation)
4 *
5 * Copyright (C) 2007-2010 Amos Brocco
6 * Copyright (C) 2011 Stefano Karapetsas
7 * Copyright (C) 2012-2021 The MATE developers
8 *
9 * Authors: Amos Brocco <amos.brocco@unifr.ch>,
10 * Stefano Karapetsas <stefano@karapetsas.com>
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public
23 * License along with this library; if not, write to the
24 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 * Boston, MA 02110-1301, USA.
26 */
27
28 #include "caja-undostack-manager.h"
29 #include "caja-file-operations.h"
30 #include "caja-file.h"
31 #include <gio/gio.h>
32 #include <glib/gprintf.h>
33 #include <glib-object.h>
34 #include <glib/gi18n.h>
35 #include <locale.h>
36 #include <gdk/gdk.h>
37
38 /* *****************************************************************
39 Private fields
40 ***************************************************************** */
41
42 struct _CajaUndoStackActionData
43 {
44 /* Common stuff */
45 CajaUndoStackActionType type;
46 gboolean isValid;
47 gboolean locked; /* True if the action is being undone/redone */
48 gboolean freed; /* True if the action must be freed after undo/redo */
49 guint count; /* Size of affected uris (count of items) */
50 CajaUndoStackManager *manager; /* Pointer to the manager */
51
52 /* Copy / Move stuff */
53 GFile *src_dir;
54 GFile *dest_dir;
55 GList *sources; /* Relative to src_dir */
56 GList *destinations; /* Relative to dest_dir */
57
58 /* Cached labels/descriptions */
59 char *undo_label;
60 char *undo_description;
61 char *redo_label;
62 char *redo_description;
63
64 /* Create new file/folder stuff/set permissions */
65 char *template;
66 char *target_uri;
67
68 /* Rename stuff */
69 char *old_uri;
70 char *new_uri;
71
72 /* Trash stuff */
73 GHashTable *trashed;
74
75 /* Recursive change permissions stuff */
76 GHashTable *original_permissions;
77 guint32 dir_mask;
78 guint32 dir_permissions;
79 guint32 file_mask;
80 guint32 file_permissions;
81
82 /* Single file change permissions stuff */
83 guint32 current_permissions;
84 guint32 new_permissions;
85
86 /* Group */
87 char *original_group_name_or_id;
88 char *new_group_name_or_id;
89
90 /* Owner */
91 char *original_user_name_or_id;
92 char *new_user_name_or_id;
93
94 };
95
96 struct _CajaUndoStackManagerPrivate
97 {
98 /* Private fields */
99 GQueue *stack;
100 guint undo_levels;
101 guint index;
102 GMutex mutex; /* Used to protect access to stack (because of async file ops) */
103 gboolean dispose_has_run;
104 gboolean undo_redo_flag;
105 gboolean confirm_delete;
106 };
107
108 /* *****************************************************************
109 Properties management prototypes
110 ***************************************************************** */
111 enum
112 {
113 PROP_UNDOSTACK_MANAGER_0, PROP_UNDO_LEVELS
114 };
115
116 static void caja_undostack_manager_set_property (GObject * object,
117 guint prop_id, const GValue * value, GParamSpec * pspec);
118
119 static void caja_undostack_manager_get_property (GObject * object,
120 guint prop_id, GValue * value, GParamSpec * pspec);
121
122 /* *****************************************************************
123 Destructors prototypes
124 ***************************************************************** */
125 static void caja_undostack_manager_finalize (GObject * object);
126
127 static void caja_undostack_manager_dispose (GObject * object);
128
129 /* *****************************************************************
130 Type definition
131 ***************************************************************** */
132 G_DEFINE_TYPE_WITH_PRIVATE (CajaUndoStackManager, caja_undostack_manager,
133 G_TYPE_OBJECT);
134
135 /* *****************************************************************
136 Private methods prototypes
137 ***************************************************************** */
138
139 static void stack_clear_n_oldest (GQueue * stack, guint n);
140
141 static void stack_fix_size (CajaUndoStackManagerPrivate * priv);
142
143 static gboolean can_undo (CajaUndoStackManagerPrivate * priv);
144
145 static gboolean can_redo (CajaUndoStackManagerPrivate * priv);
146
147 static void stack_push_action (CajaUndoStackManagerPrivate * priv,
148 CajaUndoStackActionData * action);
149
150 static CajaUndoStackActionData
151 * stack_scroll_left (CajaUndoStackManagerPrivate * priv);
152
153 static CajaUndoStackActionData
154 * stack_scroll_right (CajaUndoStackManagerPrivate * priv);
155
156 static CajaUndoStackActionData
157 * get_next_redo_action (CajaUndoStackManagerPrivate * priv);
158
159 static CajaUndoStackActionData
160 * get_next_undo_action (CajaUndoStackManagerPrivate * priv);
161
162 static gchar *get_undo_label (CajaUndoStackActionData * action);
163
164 static gchar *get_undo_description (CajaUndoStackActionData * action);
165
166 static gchar *get_redo_label (CajaUndoStackActionData * action);
167
168 static gchar *get_redo_description (CajaUndoStackActionData * action);
169
170 static void do_menu_update (CajaUndoStackManager * manager);
171
172 static void free_undostack_action (gpointer data, gpointer user_data);
173
174 static void undostack_dispose_all (GQueue * queue);
175
176 static void undo_redo_done_transfer_callback (GHashTable * debuting_uris,
177 gpointer data);
178
179 static void undo_redo_op_callback (gpointer callback_data);
180
181 static void undo_redo_done_rename_callback (CajaFile * file,
182 GFile * result_location, GError * error, gpointer callback_data);
183
184 static void undo_redo_done_delete_callback (GHashTable * debuting_uris,
185 gboolean user_cancel, gpointer callback_data);
186
187 static void undo_redo_done_create_callback (GFile * new_file,
188 gpointer callback_data);
189
190 static void clear_redo_actions (CajaUndoStackManagerPrivate * priv);
191
192 static gchar *get_first_target_short_name (CajaUndoStackActionData *
193 action);
194
195 static GList *construct_gfile_list (const GList * urilist, GFile * parent);
196
197 static GList *construct_gfile_list_from_uri (char *uri);
198
199 static GList *uri_list_to_gfile_list (GList * urilist);
200
201 static char *get_uri_basename (char *uri);
202
203 static char *get_uri_parent (char *uri);
204
205 static char *get_uri_parent_path (char *uri);
206
207 static GHashTable *retrieve_files_to_restore (GHashTable * trashed);
208
209 /* *****************************************************************
210 Base functions
211 ***************************************************************** */
212 static void
caja_undostack_manager_class_init(CajaUndoStackManagerClass * klass)213 caja_undostack_manager_class_init (CajaUndoStackManagerClass * klass)
214 {
215 GParamSpec *undo_levels;
216 GObjectClass *g_object_class;
217
218 /* Create properties */
219 undo_levels = g_param_spec_uint ("undo-levels", "undo levels",
220 "Number of undo levels to be stored",
221 1, UINT_MAX, 30, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
222
223 /* Set properties get/set methods */
224 g_object_class = G_OBJECT_CLASS (klass);
225
226 g_object_class->set_property = caja_undostack_manager_set_property;
227 g_object_class->get_property = caja_undostack_manager_get_property;
228
229 /* Install properties */
230 g_object_class_install_property (g_object_class, PROP_UNDO_LEVELS,
231 undo_levels);
232
233 /* The UI menu needs to update its status */
234 g_signal_new ("request-menu-update",
235 G_TYPE_FROM_CLASS (klass),
236 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE |
237 G_SIGNAL_NO_HOOKS, 0, NULL, NULL,
238 g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
239
240 /* Hook deconstructors */
241 g_object_class->dispose = caja_undostack_manager_dispose;
242 g_object_class->finalize = caja_undostack_manager_finalize;
243 }
244
245 static void
caja_undostack_manager_init(CajaUndoStackManager * self)246 caja_undostack_manager_init (CajaUndoStackManager * self)
247 {
248 CajaUndoStackManagerPrivate *priv;
249
250 priv = caja_undostack_manager_get_instance_private (self);
251
252 self->priv = priv;
253
254 /* Initialize private fields */
255 priv->stack = g_queue_new ();
256 g_mutex_init (&priv->mutex);
257 priv->index = 0;
258 priv->dispose_has_run = FALSE;
259 priv->undo_redo_flag = FALSE;
260 }
261
262 static void
caja_undostack_manager_dispose(GObject * object)263 caja_undostack_manager_dispose (GObject * object)
264 {
265 CajaUndoStackManager *self = CAJA_UNDOSTACK_MANAGER (object);
266 CajaUndoStackManagerPrivate *priv = self->priv;
267
268 if (priv->dispose_has_run)
269 return;
270
271 g_mutex_lock (&priv->mutex);
272
273 /* Free each undoable action in the stack and the stack itself */
274 undostack_dispose_all (priv->stack);
275 g_queue_free (priv->stack);
276 g_mutex_unlock (&priv->mutex);
277 g_mutex_clear (&priv->mutex);
278
279 priv->dispose_has_run = TRUE;
280
281 G_OBJECT_CLASS (caja_undostack_manager_parent_class)->dispose (object);
282 }
283
284 static void
caja_undostack_manager_finalize(GObject * object)285 caja_undostack_manager_finalize (GObject * object)
286 {
287 G_OBJECT_CLASS (caja_undostack_manager_parent_class)->finalize (object);
288 }
289
290 /* *****************************************************************
291 Property management
292 ***************************************************************** */
293 static void
caja_undostack_manager_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)294 caja_undostack_manager_set_property (GObject * object, guint prop_id,
295 const GValue * value, GParamSpec * pspec)
296 {
297 g_return_if_fail (IS_CAJA_UNDOSTACK_MANAGER (object));
298
299 CajaUndoStackManager *manager = CAJA_UNDOSTACK_MANAGER (object);
300 CajaUndoStackManagerPrivate *priv = manager->priv;
301 guint new_undo_levels;
302
303 switch (prop_id) {
304 case PROP_UNDO_LEVELS:
305 new_undo_levels = g_value_get_uint (value);
306 if (new_undo_levels > 0 && (priv->undo_levels != new_undo_levels)) {
307 priv->undo_levels = new_undo_levels;
308 g_mutex_lock (&priv->mutex);
309 stack_fix_size (priv);
310 g_mutex_unlock (&priv->mutex);
311 do_menu_update (manager);
312 }
313 break;
314 default:
315 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
316 break;
317 }
318 }
319
320 static void
caja_undostack_manager_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)321 caja_undostack_manager_get_property (GObject * object, guint prop_id,
322 GValue * value, GParamSpec * pspec)
323 {
324 g_return_if_fail (IS_CAJA_UNDOSTACK_MANAGER (object));
325
326 CajaUndoStackManager *manager = CAJA_UNDOSTACK_MANAGER (object);
327 CajaUndoStackManagerPrivate *priv = manager->priv;
328
329 switch (prop_id) {
330 case PROP_UNDO_LEVELS:
331 g_value_set_uint (value, priv->undo_levels);
332 break;
333
334 default:
335 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
336 break;
337 }
338 }
339
340 /* *****************************************************************
341 Public methods
342 ***************************************************************** */
343
344 /** ****************************************************************
345 * Returns the undo stack manager instance (singleton pattern)
346 ** ****************************************************************/
347 CajaUndoStackManager *
caja_undostack_manager_instance(void)348 caja_undostack_manager_instance (void)
349 {
350 static CajaUndoStackManager *manager = NULL;
351
352 if (manager == NULL) {
353 manager =
354 g_object_new (TYPE_CAJA_UNDOSTACK_MANAGER, "undo-levels", 32, NULL);
355 }
356
357 return manager;
358 }
359
360 /** ****************************************************************
361 * True if undoing / redoing
362 ** ****************************************************************/
363 gboolean
caja_undostack_manager_is_undo_redo(CajaUndoStackManager * manager)364 caja_undostack_manager_is_undo_redo (CajaUndoStackManager * manager)
365 {
366 CajaUndoStackManagerPrivate *priv = manager->priv;
367 if (priv->undo_redo_flag) {
368 return TRUE;
369 }
370
371 return FALSE;
372 }
373
374 void
caja_undostack_manager_request_menu_update(CajaUndoStackManager * manager)375 caja_undostack_manager_request_menu_update (CajaUndoStackManager *
376 manager)
377 {
378 do_menu_update (manager);
379 }
380
381 /** ****************************************************************
382 * Redoes the last file operation
383 ** ****************************************************************/
384 void
caja_undostack_manager_redo(CajaUndoStackManager * manager,GtkWidget * parent_view,CajaUndostackFinishCallback cb)385 caja_undostack_manager_redo (CajaUndoStackManager * manager,
386 GtkWidget * parent_view, CajaUndostackFinishCallback cb)
387 {
388 CajaUndoStackManagerPrivate *priv = manager->priv;
389
390 g_mutex_lock (&priv->mutex);
391
392 CajaUndoStackActionData *action = stack_scroll_left (priv);
393
394 /* Action will be NULL if redo is not possible */
395 if (action != NULL) {
396 action->locked = TRUE;
397 }
398
399 g_mutex_unlock (&priv->mutex);
400
401 do_menu_update (manager);
402
403 if (action != NULL) {
404 action->locked = TRUE; /* Remember to unlock when redo is finished */
405 priv->undo_redo_flag = TRUE;
406 switch (action->type) {
407 case CAJA_UNDOSTACK_COPY:
408 {
409 GList *uris;
410
411 uris = construct_gfile_list (action->sources, action->src_dir);
412 caja_file_operations_copy (uris, NULL,
413 action->dest_dir, NULL, undo_redo_done_transfer_callback, action);
414 g_list_free_full (uris, g_object_unref);
415 break;
416 }
417 case CAJA_UNDOSTACK_CREATEFILEFROMTEMPLATE:
418 {
419 char *new_name;
420 char *puri;
421
422 puri = get_uri_parent (action->target_uri);
423 new_name = get_uri_basename (action->target_uri);
424 caja_file_operations_new_file_from_template (NULL,
425 NULL,
426 puri,
427 new_name, action->template, undo_redo_done_create_callback, action);
428 g_free (puri);
429 g_free (new_name);
430 break;
431 }
432 case CAJA_UNDOSTACK_DUPLICATE:
433 {
434 GList *uris;
435
436 uris = construct_gfile_list (action->sources, action->src_dir);
437 caja_file_operations_duplicate (uris, NULL, NULL,
438 undo_redo_done_transfer_callback, action);
439 g_list_free_full (uris, g_object_unref);
440 break;
441 }
442 case CAJA_UNDOSTACK_RESTOREFROMTRASH:
443 case CAJA_UNDOSTACK_MOVE:
444 {
445 GList *uris;
446
447 uris = construct_gfile_list (action->sources, action->src_dir);
448 caja_file_operations_move (uris, NULL,
449 action->dest_dir, NULL, undo_redo_done_transfer_callback, action);
450 g_list_free_full (uris, g_object_unref);
451 break;
452 }
453 case CAJA_UNDOSTACK_RENAME:
454 {
455 CajaFile *file;
456 char *new_name;
457
458 new_name = get_uri_basename (action->new_uri);
459 file = caja_file_get_by_uri (action->old_uri);
460 caja_file_rename (file, new_name,
461 undo_redo_done_rename_callback, action);
462 g_object_unref (file);
463 g_free (new_name);
464 break;
465 }
466 case CAJA_UNDOSTACK_CREATEEMPTYFILE:
467 {
468 char *new_name;
469 char *puri;
470
471 puri = get_uri_parent (action->target_uri);
472 new_name = get_uri_basename (action->target_uri);
473 caja_file_operations_new_file (NULL, NULL, puri,
474 new_name,
475 action->template,
476 0, undo_redo_done_create_callback, action);
477 g_free (puri);
478 g_free (new_name);
479 break;
480 }
481 case CAJA_UNDOSTACK_CREATEFOLDER:
482 {
483 char *puri;
484
485 puri = get_uri_parent (action->target_uri);
486 caja_file_operations_new_folder (NULL, NULL, puri,
487 undo_redo_done_create_callback, action);
488 g_free (puri);
489 break;
490 }
491 case CAJA_UNDOSTACK_MOVETOTRASH:
492 if (g_hash_table_size (action->trashed) > 0) {
493 GList *uris;
494
495 GList *uri_to_trash = g_hash_table_get_keys (action->trashed);
496 uris = uri_list_to_gfile_list (uri_to_trash);
497 priv->undo_redo_flag = TRUE;
498 caja_file_operations_trash_or_delete
499 (uris, NULL, undo_redo_done_delete_callback, action);
500 g_list_free (uri_to_trash);
501 g_list_free_full (uris, g_object_unref);
502 }
503 break;
504 case CAJA_UNDOSTACK_CREATELINK:
505 {
506 GList *uris;
507
508 uris = construct_gfile_list (action->sources, action->src_dir);
509 caja_file_operations_link (uris, NULL,
510 action->dest_dir, NULL, undo_redo_done_transfer_callback, action);
511 g_list_free_full (uris, g_object_unref);
512 break;
513 }
514 case CAJA_UNDOSTACK_SETPERMISSIONS:
515 {
516 CajaFile *file;
517
518 file = caja_file_get_by_uri (action->target_uri);
519 caja_file_set_permissions (file,
520 action->new_permissions, undo_redo_done_rename_callback, action);
521 g_object_unref (file);
522 break;
523 }
524 case CAJA_UNDOSTACK_RECURSIVESETPERMISSIONS:
525 {
526 char *puri;
527
528 puri = g_file_get_uri (action->dest_dir);
529 caja_file_set_permissions_recursive (puri,
530 action->file_permissions,
531 action->file_mask,
532 action->dir_permissions,
533 action->dir_mask, undo_redo_op_callback, action);
534 g_free (puri);
535 break;
536 }
537 case CAJA_UNDOSTACK_CHANGEGROUP:
538 {
539 CajaFile *file;
540
541 file = caja_file_get_by_uri (action->target_uri);
542 caja_file_set_group (file,
543 action->new_group_name_or_id,
544 undo_redo_done_rename_callback, action);
545 g_object_unref (file);
546 break;
547 }
548 case CAJA_UNDOSTACK_CHANGEOWNER:
549 {
550 CajaFile *file;
551
552 file = caja_file_get_by_uri (action->target_uri);
553 caja_file_set_owner (file,
554 action->new_user_name_or_id,
555 undo_redo_done_rename_callback, action);
556 g_object_unref (file);
557 break;
558 }
559 case CAJA_UNDOSTACK_DELETE:
560 default:
561 priv->undo_redo_flag = FALSE;
562 break; /* We shouldn't be here */
563 }
564 }
565
566 (*cb) ((gpointer) parent_view);
567 }
568
569 /** ****************************************************************
570 * Undoes the last file operation
571 ** ****************************************************************/
572 void
caja_undostack_manager_undo(CajaUndoStackManager * manager,GtkWidget * parent_view,CajaUndostackFinishCallback cb)573 caja_undostack_manager_undo (CajaUndoStackManager * manager,
574 GtkWidget * parent_view, CajaUndostackFinishCallback cb)
575 {
576 CajaUndoStackManagerPrivate *priv = manager->priv;
577 GList *uris = NULL;
578
579 g_mutex_lock (&priv->mutex);
580
581 CajaUndoStackActionData *action = stack_scroll_right (priv);
582
583 if (action != NULL) {
584 action->locked = TRUE;
585 }
586
587 g_mutex_unlock (&priv->mutex);
588
589 do_menu_update (manager);
590
591 if (action != NULL) {
592 priv->undo_redo_flag = TRUE;
593 switch (action->type) {
594 case CAJA_UNDOSTACK_CREATEEMPTYFILE:
595 case CAJA_UNDOSTACK_CREATEFILEFROMTEMPLATE:
596 case CAJA_UNDOSTACK_CREATEFOLDER:
597 uris = construct_gfile_list_from_uri (action->target_uri);
598 case CAJA_UNDOSTACK_COPY:
599 case CAJA_UNDOSTACK_DUPLICATE:
600 case CAJA_UNDOSTACK_CREATELINK:
601 if (!uris) {
602 uris = construct_gfile_list (action->destinations, action->dest_dir);
603 uris = g_list_reverse (uris); // Deleting must be done in reverse
604 }
605
606 caja_file_operations_delete (uris, NULL,
607 undo_redo_done_delete_callback, action);
608 g_list_free_full (uris, g_object_unref);
609
610 break;
611 case CAJA_UNDOSTACK_RESTOREFROMTRASH:
612 uris = construct_gfile_list (action->destinations, action->dest_dir);
613 caja_file_operations_trash_or_delete (uris, NULL,
614 undo_redo_done_delete_callback, action);
615 g_list_free_full (uris, g_object_unref);
616 break;
617 case CAJA_UNDOSTACK_MOVETOTRASH:
618 {
619 GHashTable *files_to_restore;
620
621 files_to_restore = retrieve_files_to_restore (action->trashed);
622 if (g_hash_table_size (files_to_restore) > 0) {
623 GList *l;
624 GList *gfiles_in_trash = g_hash_table_get_keys (files_to_restore);
625 GFile *item = NULL;
626 GFile *dest = NULL;
627
628 for (l = gfiles_in_trash; l != NULL; l = l->next) {
629 char *value;
630
631 item = l->data;
632 value = g_hash_table_lookup (files_to_restore, item);
633 dest = g_file_new_for_uri (value);
634 g_file_move (item, dest,
635 G_FILE_COPY_NOFOLLOW_SYMLINKS, NULL, NULL, NULL, NULL);
636 g_object_unref (dest);
637 }
638
639 g_list_free (gfiles_in_trash);
640 }
641 g_hash_table_destroy (files_to_restore);
642 /* Here we must do what's necessary for the callback */
643 undo_redo_done_transfer_callback (NULL, action);
644 break;
645 }
646 case CAJA_UNDOSTACK_MOVE:
647 uris = construct_gfile_list (action->destinations, action->dest_dir);
648 caja_file_operations_move (uris, NULL,
649 action->src_dir, NULL, undo_redo_done_transfer_callback, action);
650 g_list_free_full (uris, g_object_unref);
651 break;
652 case CAJA_UNDOSTACK_RENAME:
653 {
654 CajaFile *file;
655 char *new_name;
656
657 new_name = get_uri_basename (action->old_uri);
658 file = caja_file_get_by_uri (action->new_uri);
659 caja_file_rename (file, new_name,
660 undo_redo_done_rename_callback, action);
661 g_object_unref (file);
662 g_free (new_name);
663 break;
664 }
665 case CAJA_UNDOSTACK_SETPERMISSIONS:
666 {
667 CajaFile *file;
668
669 file = caja_file_get_by_uri (action->target_uri);
670 caja_file_set_permissions (file,
671 action->current_permissions,
672 undo_redo_done_rename_callback, action);
673 g_object_unref (file);
674 break;
675 }
676 case CAJA_UNDOSTACK_RECURSIVESETPERMISSIONS:
677 if (g_hash_table_size (action->original_permissions) > 0) {
678 GList *gfiles_list =
679 g_hash_table_get_keys (action->original_permissions);
680
681 GList *l;
682 GFile *dest = NULL;
683
684 for (l = gfiles_list; l != NULL; l = l->next) {
685 guint32 *perm;
686 char *item;
687
688 item = l->data;
689 perm = g_hash_table_lookup (action->original_permissions, item);
690 dest = g_file_new_for_uri (item);
691 g_file_set_attribute_uint32 (dest,
692 G_FILE_ATTRIBUTE_UNIX_MODE,
693 *perm, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL);
694 g_object_unref (dest);
695 }
696
697 g_list_free (gfiles_list);
698 /* Here we must do what's necessary for the callback */
699 undo_redo_done_transfer_callback (NULL, action);
700 }
701 break;
702 case CAJA_UNDOSTACK_CHANGEGROUP:
703 {
704 CajaFile *file;
705
706 file = caja_file_get_by_uri (action->target_uri);
707 caja_file_set_group (file,
708 action->original_group_name_or_id,
709 undo_redo_done_rename_callback, action);
710 g_object_unref (file);
711 break;
712 }
713 case CAJA_UNDOSTACK_CHANGEOWNER:
714 {
715 CajaFile *file;
716
717 file = caja_file_get_by_uri (action->target_uri);
718 caja_file_set_owner (file,
719 action->original_user_name_or_id,
720 undo_redo_done_rename_callback, action);
721 g_object_unref (file);
722 break;
723 }
724 case CAJA_UNDOSTACK_DELETE:
725 default:
726 priv->undo_redo_flag = FALSE;
727 break; /* We shouldn't be here */
728 }
729 }
730
731 (*cb) ((gpointer) parent_view);
732 }
733
734 /** ****************************************************************
735 * Adds an operation to the stack
736 ** ****************************************************************/
737 void
caja_undostack_manager_add_action(CajaUndoStackManager * manager,CajaUndoStackActionData * action)738 caja_undostack_manager_add_action (CajaUndoStackManager * manager,
739 CajaUndoStackActionData * action)
740 {
741 CajaUndoStackManagerPrivate *priv = manager->priv;
742
743 if (!action)
744 return;
745
746 if (!(action && action->isValid)) {
747 free_undostack_action ((gpointer) action, NULL);
748 return;
749 }
750
751 action->manager = manager;
752
753 g_mutex_lock (&priv->mutex);
754
755 stack_push_action (priv, action);
756
757 g_mutex_unlock (&priv->mutex);
758
759 do_menu_update (manager);
760 }
761
762 static GList *
get_all_trashed_items(GQueue * stack)763 get_all_trashed_items (GQueue *stack)
764 {
765 CajaUndoStackActionData *action = NULL;
766 GList *trash = NULL;
767 GList *l;
768 GQueue *tmp_stack = g_queue_copy(stack);
769
770 while ((action = (CajaUndoStackActionData *) g_queue_pop_tail (tmp_stack)) != NULL)
771 if (action->trashed)
772 for (l = g_hash_table_get_keys (action->trashed); l != NULL; l=l->next) {
773 trash = g_list_append(trash, l->data);
774 }
775
776 g_queue_free (tmp_stack);
777 return (trash);
778 }
779
780 static gboolean
is_destination_uri_action_partof_trashed(GList * trash,GList * g)781 is_destination_uri_action_partof_trashed(GList *trash, GList *g)
782 {
783 GList *l;
784 char *uri;
785
786 for (l = trash; l != NULL; l=l->next) {
787 for (; g != NULL; g=g->next) {
788 //printf ("destinations: %s\n", g_file_get_uri(l->data));
789 uri = g_file_get_uri(g->data);
790 if (!strcmp (uri, l->data)) {
791 //printf ("GG %s\nZZ %s\n", uri, l->data);
792 g_free (uri);
793 return TRUE;
794 }
795 g_free (uri);
796 }
797 }
798
799 return FALSE;
800 }
801 /** ****************************************************************
802 * Callback after emptying the trash
803 ** ****************************************************************/
804 void
caja_undostack_manager_trash_has_emptied(CajaUndoStackManager * manager)805 caja_undostack_manager_trash_has_emptied (CajaUndoStackManager *
806 manager)
807 {
808 CajaUndoStackManagerPrivate *priv = manager->priv;
809
810 /* Clear actions from the oldest to the newest move to trash */
811
812 g_mutex_lock (&priv->mutex);
813
814 clear_redo_actions (priv);
815 CajaUndoStackActionData *action = NULL;
816
817 GList *g;
818 GQueue *tmp_stack = g_queue_copy(priv->stack);
819 GList *trash = get_all_trashed_items (tmp_stack);
820 while ((action = (CajaUndoStackActionData *) g_queue_pop_tail (tmp_stack)) != NULL)
821 {
822 if (action->destinations && action->dest_dir) {
823 /* what a pain rebuild again and again an uri
824 ** TODO change the struct add uri elements */
825 g = construct_gfile_list (action->destinations, action->dest_dir);
826 /* remove action for trashed item uris == destination action */
827 if (is_destination_uri_action_partof_trashed(trash, g)) {
828 g_queue_remove (priv->stack, action);
829 continue;
830 }
831 }
832 if (action->type == CAJA_UNDOSTACK_MOVETOTRASH) {
833 g_queue_remove (priv->stack, action);
834 }
835 }
836
837 g_queue_free (tmp_stack);
838 g_mutex_unlock (&priv->mutex);
839 do_menu_update (manager);
840 }
841
842 /** ****************************************************************
843 * Returns the modification time for the given file (used for undo trash)
844 ** ****************************************************************/
845 guint64
caja_undostack_manager_get_file_modification_time(GFile * file)846 caja_undostack_manager_get_file_modification_time (GFile * file)
847 {
848 GFileInfo *info;
849 guint64 mtime;
850
851 info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED,
852 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, FALSE, NULL);
853 if (info == NULL) {
854 return -1;
855 }
856
857 mtime = g_file_info_get_attribute_uint64 (info,
858 G_FILE_ATTRIBUTE_TIME_MODIFIED);
859
860 g_object_unref (info);
861
862 return mtime;
863 }
864
865 /** ****************************************************************
866 * Returns a new undo data container
867 ** ****************************************************************/
868 CajaUndoStackActionData *
caja_undostack_manager_data_new(CajaUndoStackActionType type,gint items_count)869 caja_undostack_manager_data_new (CajaUndoStackActionType type,
870 gint items_count)
871 {
872 CajaUndoStackActionData *data =
873 g_slice_new0 (CajaUndoStackActionData);
874 data->type = type;
875 data->count = items_count;
876
877 if (type == CAJA_UNDOSTACK_MOVETOTRASH) {
878 data->trashed =
879 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
880 } else if (type == CAJA_UNDOSTACK_RECURSIVESETPERMISSIONS) {
881 data->original_permissions =
882 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
883 }
884
885 return data;
886 }
887
888 /** ****************************************************************
889 * Sets the source directory
890 ** ****************************************************************/
891 void
caja_undostack_manager_data_set_src_dir(CajaUndoStackActionData * data,GFile * src)892 caja_undostack_manager_data_set_src_dir (CajaUndoStackActionData *
893 data, GFile * src)
894 {
895 if (!data)
896 return;
897
898 data->src_dir = src;
899 }
900
901 /** ****************************************************************
902 * Sets the destination directory
903 ** ****************************************************************/
904 void
caja_undostack_manager_data_set_dest_dir(CajaUndoStackActionData * data,GFile * dest)905 caja_undostack_manager_data_set_dest_dir (CajaUndoStackActionData *
906 data, GFile * dest)
907 {
908 if (!data)
909 return;
910
911 data->dest_dir = dest;
912 }
913
914 /** ****************************************************************
915 * Pushes an origin, target pair in an existing undo data container
916 ** ****************************************************************/
caja_undostack_manager_data_add_origin_target_pair(CajaUndoStackActionData * data,GFile * origin,GFile * target)917 void caja_undostack_manager_data_add_origin_target_pair
918 (CajaUndoStackActionData * data, GFile * origin, GFile * target)
919 {
920
921 if (!data)
922 return;
923
924 char *src_relative = g_file_get_relative_path (data->src_dir, origin);
925 data->sources = g_list_append (data->sources, src_relative);
926 char *dest_relative = g_file_get_relative_path (data->dest_dir, target);
927 data->destinations = g_list_append (data->destinations, dest_relative);
928
929 data->isValid = TRUE;
930 }
931
932 /** ****************************************************************
933 * Pushes an trashed file with modification time in an existing undo data container
934 ** ****************************************************************/
935 void
caja_undostack_manager_data_add_trashed_file(CajaUndoStackActionData * data,GFile * file,guint64 mtime)936 caja_undostack_manager_data_add_trashed_file (CajaUndoStackActionData
937 * data, GFile * file, guint64 mtime)
938 {
939
940 if (!data)
941 return;
942
943 guint64 *modificationTime;
944 modificationTime = (guint64 *) g_malloc (sizeof (guint64));
945 *modificationTime = mtime;
946
947 char *originalURI = g_file_get_uri (file);
948
949 g_hash_table_insert (data->trashed, originalURI, modificationTime);
950
951 data->isValid = TRUE;
952 }
953
954 /** ****************************************************************
955 * Pushes a recursive permission change data in an existing undo data container
956 ** ****************************************************************/
caja_undostack_manager_data_add_file_permissions(CajaUndoStackActionData * data,GFile * file,guint32 permission)957 void caja_undostack_manager_data_add_file_permissions
958 (CajaUndoStackActionData * data, GFile * file, guint32 permission)
959 {
960
961 if (!data)
962 return;
963
964 guint32 *currentPermission;
965 currentPermission = (guint32 *) g_malloc (sizeof (guint32));
966 *currentPermission = permission;
967
968 char *originalURI = g_file_get_uri (file);
969
970 g_hash_table_insert (data->original_permissions, originalURI,
971 currentPermission);
972
973 data->isValid = TRUE;
974 }
975
976 /** ****************************************************************
977 * Sets the original file permission in an existing undo data container
978 ** ****************************************************************/
caja_undostack_manager_data_set_file_permissions(CajaUndoStackActionData * data,char * uri,guint32 current_permissions,guint32 new_permissions)979 void caja_undostack_manager_data_set_file_permissions
980 (CajaUndoStackActionData * data, char *uri,
981 guint32 current_permissions, guint32 new_permissions)
982 {
983
984 if (!data)
985 return;
986
987 data->target_uri = uri;
988
989 data->current_permissions = current_permissions;
990 data->new_permissions = new_permissions;
991
992 data->isValid = TRUE;
993 }
994
995 /** ****************************************************************
996 * Sets the change owner information in an existing undo data container
997 ** ****************************************************************/
caja_undostack_manager_data_set_owner_change_information(CajaUndoStackActionData * data,char * uri,const char * current_user,const char * new_user)998 void caja_undostack_manager_data_set_owner_change_information
999 (CajaUndoStackActionData * data, char *uri,
1000 const char *current_user, const char *new_user)
1001 {
1002
1003 if (!data)
1004 return;
1005
1006 data->target_uri = uri;
1007
1008 data->original_user_name_or_id = g_strdup (current_user);
1009 data->new_user_name_or_id = g_strdup (new_user);
1010
1011 data->isValid = TRUE;
1012 }
1013
1014 /** ****************************************************************
1015 * Sets the change group information in an existing undo data container
1016 ** ****************************************************************/
caja_undostack_manager_data_set_group_change_information(CajaUndoStackActionData * data,char * uri,const char * current_group,const char * new_group)1017 void caja_undostack_manager_data_set_group_change_information
1018 (CajaUndoStackActionData * data, char *uri,
1019 const char *current_group, const char *new_group)
1020 {
1021
1022 if (!data)
1023 return;
1024
1025 data->target_uri = uri;
1026
1027 data->original_group_name_or_id = g_strdup (current_group);
1028 data->new_group_name_or_id = g_strdup (new_group);
1029
1030 data->isValid = TRUE;
1031 }
1032
1033 /** ****************************************************************
1034 * Sets the permission change mask
1035 ** ****************************************************************/
caja_undostack_manager_data_set_recursive_permissions(CajaUndoStackActionData * data,guint32 file_permissions,guint32 file_mask,guint32 dir_permissions,guint32 dir_mask)1036 void caja_undostack_manager_data_set_recursive_permissions
1037 (CajaUndoStackActionData * data, guint32 file_permissions,
1038 guint32 file_mask, guint32 dir_permissions, guint32 dir_mask)
1039 {
1040
1041 if (!data)
1042 return;
1043
1044 data->file_permissions = file_permissions;
1045 data->file_mask = file_mask;
1046 data->dir_permissions = dir_permissions;
1047 data->dir_mask = dir_mask;
1048
1049 data->isValid = TRUE;
1050 }
1051
1052 /** ****************************************************************
1053 * Sets create file information
1054 ** ****************************************************************/
1055 void
caja_undostack_manager_data_set_create_data(CajaUndoStackActionData * data,char * target_uri,char * template)1056 caja_undostack_manager_data_set_create_data (CajaUndoStackActionData *
1057 data, char *target_uri, char *template)
1058 {
1059
1060 if (!data)
1061 return;
1062
1063 data->template = g_strdup (template);
1064 data->target_uri = g_strdup (target_uri);
1065
1066 data->isValid = TRUE;
1067 }
1068
1069 /** ****************************************************************
1070 * Sets rename information
1071 ** ****************************************************************/
caja_undostack_manager_data_set_rename_information(CajaUndoStackActionData * data,GFile * old_file,GFile * new_file)1072 void caja_undostack_manager_data_set_rename_information
1073 (CajaUndoStackActionData * data, GFile * old_file, GFile * new_file)
1074 {
1075
1076 if (!data)
1077 return;
1078
1079 data->old_uri = g_file_get_uri (old_file);
1080 data->new_uri = g_file_get_uri (new_file);
1081
1082 data->isValid = TRUE;
1083 }
1084
1085 /* *****************************************************************
1086 Private methods (nothing to see here, move along)
1087 ***************************************************************** */
1088
1089 static CajaUndoStackActionData *
stack_scroll_right(CajaUndoStackManagerPrivate * priv)1090 stack_scroll_right (CajaUndoStackManagerPrivate * priv)
1091 {
1092 gpointer data = NULL;
1093
1094 if (!can_undo (priv))
1095 return NULL;
1096
1097 data = g_queue_peek_nth (priv->stack, priv->index);
1098 if (priv->index < g_queue_get_length (priv->stack)) {
1099 priv->index++;
1100 }
1101
1102 return data;
1103 }
1104
1105 /** ---------------------------------------------------------------- */
1106 static CajaUndoStackActionData *
stack_scroll_left(CajaUndoStackManagerPrivate * priv)1107 stack_scroll_left (CajaUndoStackManagerPrivate * priv)
1108 {
1109 gpointer data = NULL;
1110
1111 if (!can_redo (priv))
1112 return NULL;
1113
1114 priv->index--;
1115 data = g_queue_peek_nth (priv->stack, priv->index);
1116
1117 return data;
1118 }
1119
1120 /** ---------------------------------------------------------------- */
1121 static void
stack_clear_n_oldest(GQueue * stack,guint n)1122 stack_clear_n_oldest (GQueue * stack, guint n)
1123 {
1124 guint i;
1125 CajaUndoStackActionData *action = NULL;
1126
1127 for (i = 0; i < n; i++) {
1128 if ((action = (CajaUndoStackActionData *) g_queue_pop_tail (stack)) == NULL)
1129 break;
1130 if (action->locked) {
1131 action->freed = TRUE;
1132 } else {
1133 free_undostack_action (action, NULL);
1134 }
1135 }
1136 }
1137
1138 /** ---------------------------------------------------------------- */
1139 static void
stack_fix_size(CajaUndoStackManagerPrivate * priv)1140 stack_fix_size (CajaUndoStackManagerPrivate * priv)
1141 {
1142 guint length = g_queue_get_length (priv->stack);
1143
1144 if (length > priv->undo_levels) {
1145 if (priv->index > (priv->undo_levels + 1)) {
1146 /* If the index will fall off the stack
1147 * move it back to the maximum position */
1148 priv->index = priv->undo_levels + 1;
1149 }
1150 stack_clear_n_oldest (priv->stack, length - (priv->undo_levels));
1151 }
1152 }
1153
1154 /** ---------------------------------------------------------------- */
1155 static void
clear_redo_actions(CajaUndoStackManagerPrivate * priv)1156 clear_redo_actions (CajaUndoStackManagerPrivate * priv)
1157 {
1158 while (priv->index > 0) {
1159 CajaUndoStackActionData *head = (CajaUndoStackActionData *)
1160 g_queue_pop_head (priv->stack);
1161 free_undostack_action (head, NULL);
1162 priv->index--;
1163 }
1164 }
1165
1166 /** ---------------------------------------------------------------- */
1167 static void
stack_push_action(CajaUndoStackManagerPrivate * priv,CajaUndoStackActionData * action)1168 stack_push_action (CajaUndoStackManagerPrivate * priv,
1169 CajaUndoStackActionData * action)
1170 {
1171 guint length;
1172
1173 clear_redo_actions (priv);
1174
1175 g_queue_push_head (priv->stack, (gpointer) action);
1176 length = g_queue_get_length (priv->stack);
1177
1178 if (length > priv->undo_levels) {
1179 stack_fix_size (priv);
1180 }
1181 }
1182
1183 /** ---------------------------------------------------------------- */
1184 static gchar *
get_first_target_short_name(CajaUndoStackActionData * action)1185 get_first_target_short_name (CajaUndoStackActionData * action)
1186 {
1187 GList *targets_first;
1188 gchar *file_name;
1189
1190 targets_first = g_list_first (action->destinations);
1191 file_name = (gchar *) g_strdup (targets_first->data);
1192
1193 return file_name;
1194 }
1195
1196 /** ---------------------------------------------------------------- */
1197 static gchar *
get_undo_description(CajaUndoStackActionData * action)1198 get_undo_description (CajaUndoStackActionData * action)
1199 {
1200 gchar *description = NULL;
1201 gchar *source = NULL;
1202 guint count;
1203
1204 if (action != NULL) {
1205 if (action->undo_description == NULL) {
1206 if (action->src_dir) {
1207 source = g_file_get_path (action->src_dir);
1208 }
1209 count = action->count;
1210 switch (action->type) {
1211 case CAJA_UNDOSTACK_COPY:
1212 if (count != 1) {
1213 description = g_strdup_printf (_("Delete %d copied items"), count);
1214 } else {
1215 gchar *name = get_first_target_short_name (action);
1216 description = g_strdup_printf (_("Delete '%s'"), name);
1217 g_free (name);
1218 }
1219 break;
1220 case CAJA_UNDOSTACK_DUPLICATE:
1221 if (count != 1) {
1222 description =
1223 g_strdup_printf (_("Delete %d duplicated items"), count);
1224 } else {
1225 gchar *name = get_first_target_short_name (action);
1226 description = g_strdup_printf (_("Delete '%s'"), name);
1227 g_free (name);
1228 }
1229 break;
1230 case CAJA_UNDOSTACK_MOVE:
1231 if (count != 1) {
1232 description =
1233 g_strdup_printf (_
1234 ("Move %d items back to '%s'"), count, source);
1235 } else {
1236 gchar *name = get_first_target_short_name (action);
1237 description =
1238 g_strdup_printf (_("Move '%s' back to '%s'"), name, source);
1239 g_free (name);
1240 }
1241 break;
1242 case CAJA_UNDOSTACK_RENAME:
1243 {
1244 char *from_name = get_uri_basename (action->new_uri);
1245 char *to_name = get_uri_basename (action->old_uri);
1246 description =
1247 g_strdup_printf (_("Rename '%s' as '%s'"), from_name, to_name);
1248 g_free (from_name);
1249 g_free (to_name);
1250 }
1251 break;
1252 case CAJA_UNDOSTACK_CREATEFILEFROMTEMPLATE:
1253 case CAJA_UNDOSTACK_CREATEEMPTYFILE:
1254 case CAJA_UNDOSTACK_CREATEFOLDER:
1255 {
1256 char *name = get_uri_basename (action->target_uri);
1257 description = g_strdup_printf (_("Delete '%s'"), name);
1258 g_free (name);
1259 }
1260 break;
1261 case CAJA_UNDOSTACK_MOVETOTRASH:
1262 {
1263 count = g_hash_table_size (action->trashed);
1264 if (count != 1) {
1265 description =
1266 g_strdup_printf (_("Restore %d items from trash"), count);
1267 } else {
1268 GList *keys = g_hash_table_get_keys (action->trashed);
1269 GList *first = g_list_first (keys);
1270 char *item = (char *) first->data;
1271 char *name = get_uri_basename (item);
1272 char *orig_path = get_uri_parent_path (item);
1273 description =
1274 g_strdup_printf (_("Restore '%s' to '%s'"), name, orig_path);
1275 g_free (name);
1276 g_free (orig_path);
1277 g_list_free (keys);
1278 }
1279 }
1280 break;
1281 case CAJA_UNDOSTACK_RESTOREFROMTRASH:
1282 {
1283 if (count != 1) {
1284 description =
1285 g_strdup_printf (_("Move %d items back to trash"), count);
1286 } else {
1287 gchar *name = get_first_target_short_name (action);
1288 description = g_strdup_printf (_("Move '%s' back to trash"), name);
1289 g_free (name);
1290 }
1291 }
1292 break;
1293 case CAJA_UNDOSTACK_CREATELINK:
1294 {
1295 if (count != 1) {
1296 description =
1297 g_strdup_printf (_("Delete links to %d items"), count);
1298 } else {
1299 gchar *name = get_first_target_short_name (action);
1300 description = g_strdup_printf (_("Delete link to '%s'"), name);
1301 g_free (name);
1302 }
1303 }
1304 break;
1305 case CAJA_UNDOSTACK_RECURSIVESETPERMISSIONS:
1306 {
1307 char *name = g_file_get_path (action->dest_dir);
1308 description =
1309 g_strdup_printf (_
1310 ("Restore original permissions of items enclosed in '%s'"), name);
1311 g_free (name);
1312 }
1313 break;
1314 case CAJA_UNDOSTACK_SETPERMISSIONS:
1315 {
1316 char *name = get_uri_basename (action->target_uri);
1317 description =
1318 g_strdup_printf (_("Restore original permissions of '%s'"), name);
1319 g_free (name);
1320 }
1321 break;
1322 case CAJA_UNDOSTACK_CHANGEGROUP:
1323 {
1324 char *name = get_uri_basename (action->target_uri);
1325 description =
1326 g_strdup_printf (_
1327 ("Restore group of '%s' to '%s'"),
1328 name, action->original_group_name_or_id);
1329 g_free (name);
1330 }
1331 break;
1332 case CAJA_UNDOSTACK_CHANGEOWNER:
1333 {
1334 char *name = get_uri_basename (action->target_uri);
1335 description =
1336 g_strdup_printf (_
1337 ("Restore owner of '%s' to '%s'"),
1338 name, action->original_user_name_or_id);
1339 g_free (name);
1340 }
1341 break;
1342 default:
1343 break;
1344 }
1345 if (source) {
1346 g_free (source);
1347 }
1348 action->undo_description = description;
1349 } else {
1350 return action->undo_description;
1351 }
1352 }
1353
1354 return description;
1355 }
1356
1357 /** ---------------------------------------------------------------- */
1358 static gchar *
get_redo_description(CajaUndoStackActionData * action)1359 get_redo_description (CajaUndoStackActionData * action)
1360 {
1361 gchar *description = NULL;
1362 gchar *destination = NULL;
1363 guint count;
1364
1365 if (action != NULL) {
1366 if (action->redo_description == NULL) {
1367 if (action->dest_dir) {
1368 destination = g_file_get_path (action->dest_dir);
1369 }
1370 count = action->count;
1371 switch (action->type) {
1372 case CAJA_UNDOSTACK_COPY:
1373 if (count != 1) {
1374 description =
1375 g_strdup_printf (_
1376 ("Copy %d items to '%s'"), count, destination);
1377 } else {
1378 gchar *name = get_first_target_short_name (action);
1379 description =
1380 g_strdup_printf (_("Copy '%s' to '%s'"), name, destination);
1381 g_free (name);
1382 }
1383 break;
1384 case CAJA_UNDOSTACK_DUPLICATE:
1385 if (count != 1) {
1386 description =
1387 g_strdup_printf (_
1388 ("Duplicate of %d items in '%s'"), count, destination);
1389 } else {
1390 gchar *name = get_first_target_short_name (action);
1391 description =
1392 g_strdup_printf (_
1393 ("Duplicate '%s' in '%s'"), name, destination);
1394 g_free (name);
1395 }
1396 break;
1397 case CAJA_UNDOSTACK_MOVE:
1398 if (count != 1) {
1399 description =
1400 g_strdup_printf (_
1401 ("Move %d items to '%s'"), count, destination);
1402 } else {
1403 gchar *name = get_first_target_short_name (action);
1404 description =
1405 g_strdup_printf (_("Move '%s' to '%s'"), name, destination);
1406 g_free (name);
1407 }
1408 break;
1409 case CAJA_UNDOSTACK_RENAME:
1410 {
1411 char *from_name = get_uri_basename (action->old_uri);
1412 char *to_name = get_uri_basename (action->new_uri);
1413 description =
1414 g_strdup_printf (_("Rename '%s' as '%s'"), from_name, to_name);
1415 g_free (from_name);
1416 g_free (to_name);
1417 }
1418 break;
1419 case CAJA_UNDOSTACK_CREATEFILEFROMTEMPLATE:
1420 {
1421 char *name = get_uri_basename (action->target_uri);
1422 description =
1423 g_strdup_printf (_("Create new file '%s' from template "), name);
1424 g_free (name);
1425 }
1426 break;
1427 case CAJA_UNDOSTACK_CREATEEMPTYFILE:
1428 {
1429 char *name = get_uri_basename (action->target_uri);
1430 description = g_strdup_printf (_("Create an empty file '%s'"), name);
1431 g_free (name);
1432 }
1433 break;
1434 case CAJA_UNDOSTACK_CREATEFOLDER:
1435 {
1436 char *name = get_uri_basename (action->target_uri);
1437 description = g_strdup_printf (_("Create a new folder '%s'"), name);
1438 g_free (name);
1439 }
1440 break;
1441 case CAJA_UNDOSTACK_MOVETOTRASH:
1442 {
1443 count = g_hash_table_size (action->trashed);
1444 if (count != 1) {
1445 description = g_strdup_printf (_("Move %d items to trash"), count);
1446 } else {
1447 GList *keys = g_hash_table_get_keys (action->trashed);
1448 GList *first = g_list_first (keys);
1449 char *item = (char *) first->data;
1450 char *name = get_uri_basename (item);
1451 description = g_strdup_printf (_("Move '%s' to trash"), name);
1452 g_free (name);
1453 g_list_free (keys);
1454 }
1455 }
1456 break;
1457 case CAJA_UNDOSTACK_RESTOREFROMTRASH:
1458 {
1459 if (count != 1) {
1460 description =
1461 g_strdup_printf (_("Restore %d items from trash"), count);
1462 } else {
1463 gchar *name = get_first_target_short_name (action);
1464 description = g_strdup_printf (_("Restore '%s' from trash"), name);
1465 g_free (name);
1466 }
1467 }
1468 break;
1469 case CAJA_UNDOSTACK_CREATELINK:
1470 {
1471 if (count != 1) {
1472 description =
1473 g_strdup_printf (_("Create links to %d items"), count);
1474 } else {
1475 gchar *name = get_first_target_short_name (action);
1476 description = g_strdup_printf (_("Create link to '%s'"), name);
1477 g_free (name);
1478 }
1479 }
1480 break;
1481 case CAJA_UNDOSTACK_RECURSIVESETPERMISSIONS:
1482 {
1483 char *name = g_file_get_path (action->dest_dir);
1484 description =
1485 g_strdup_printf (_("Set permissions of items enclosed in '%s'"),
1486 name);
1487 g_free (name);
1488 }
1489 break;
1490 case CAJA_UNDOSTACK_SETPERMISSIONS:
1491 {
1492 char *name = get_uri_basename (action->target_uri);
1493 description = g_strdup_printf (_("Set permissions of '%s'"), name);
1494 g_free (name);
1495 }
1496 break;
1497 case CAJA_UNDOSTACK_CHANGEGROUP:
1498 {
1499 char *name = get_uri_basename (action->target_uri);
1500 description =
1501 g_strdup_printf (_
1502 ("Set group of '%s' to '%s'"),
1503 name, action->new_group_name_or_id);
1504 g_free (name);
1505 }
1506 break;
1507 case CAJA_UNDOSTACK_CHANGEOWNER:
1508 {
1509 char *name = get_uri_basename (action->target_uri);
1510 description =
1511 g_strdup_printf (_
1512 ("Set owner of '%s' to '%s'"), name, action->new_user_name_or_id);
1513 g_free (name);
1514 }
1515 break;
1516 default:
1517 break;
1518 }
1519 if (destination) {
1520 g_free (destination);
1521 }
1522 action->redo_description = description;
1523 } else {
1524 return action->redo_description;
1525 }
1526 }
1527
1528 return description;
1529 }
1530
1531 /** ---------------------------------------------------------------- */
1532 static gchar *
get_undo_label(CajaUndoStackActionData * action)1533 get_undo_label (CajaUndoStackActionData * action)
1534 {
1535 gchar *label = NULL;
1536 guint count;
1537
1538 if (action != NULL) {
1539 if (action->undo_label == NULL) {
1540 count = action->count;
1541 switch (action->type) {
1542 case CAJA_UNDOSTACK_COPY:
1543 label = g_strdup_printf (ngettext
1544 ("_Undo copy of %d item",
1545 "_Undo copy of %d items", count), count);
1546 break;
1547 case CAJA_UNDOSTACK_DUPLICATE:
1548 label = g_strdup_printf (ngettext
1549 ("_Undo duplicate of %d item",
1550 "_Undo duplicate of %d items", count), count);
1551 break;
1552 case CAJA_UNDOSTACK_MOVE:
1553 label = g_strdup_printf (ngettext
1554 ("_Undo move of %d item",
1555 "_Undo move of %d items", count), count);
1556 break;
1557 case CAJA_UNDOSTACK_RENAME:
1558 label = g_strdup_printf (ngettext
1559 ("_Undo rename of %d item",
1560 "_Undo rename of %d items", count), count);
1561 break;
1562 case CAJA_UNDOSTACK_CREATEEMPTYFILE:
1563 label = g_strdup_printf (_("_Undo creation of an empty file"));
1564 break;
1565 case CAJA_UNDOSTACK_CREATEFILEFROMTEMPLATE:
1566 label = g_strdup_printf (_("_Undo creation of a file from template"));
1567 break;
1568 case CAJA_UNDOSTACK_CREATEFOLDER:
1569 label = g_strdup_printf (ngettext
1570 ("_Undo creation of %d folder",
1571 "_Undo creation of %d folders", count), count);
1572 break;
1573 case CAJA_UNDOSTACK_MOVETOTRASH:
1574 label = g_strdup_printf (ngettext
1575 ("_Undo move to trash of %d item",
1576 "_Undo move to trash of %d items", count), count);
1577 break;
1578 case CAJA_UNDOSTACK_RESTOREFROMTRASH:
1579 label = g_strdup_printf (ngettext
1580 ("_Undo restore from trash of %d item",
1581 "_Undo restore from trash of %d items", count), count);
1582 break;
1583 case CAJA_UNDOSTACK_CREATELINK:
1584 label = g_strdup_printf (ngettext
1585 ("_Undo create link to %d item",
1586 "_Undo create link to %d items", count), count);
1587 break;
1588 case CAJA_UNDOSTACK_DELETE:
1589 label = g_strdup_printf (ngettext
1590 ("_Undo delete of %d item",
1591 "_Undo delete of %d items", count), count);
1592 break;
1593 case CAJA_UNDOSTACK_RECURSIVESETPERMISSIONS:
1594 label = g_strdup_printf (ngettext
1595 ("Undo recursive change permissions of %d item",
1596 "Undo recursive change permissions of %d items",
1597 count), count);
1598 break;
1599 case CAJA_UNDOSTACK_SETPERMISSIONS:
1600 label = g_strdup_printf (ngettext
1601 ("Undo change permissions of %d item",
1602 "Undo change permissions of %d items", count), count);
1603 break;
1604 case CAJA_UNDOSTACK_CHANGEGROUP:
1605 label = g_strdup_printf (ngettext
1606 ("Undo change group of %d item",
1607 "Undo change group of %d items", count), count);
1608 break;
1609 case CAJA_UNDOSTACK_CHANGEOWNER:
1610 label = g_strdup_printf (ngettext
1611 ("Undo change owner of %d item",
1612 "Undo change owner of %d items", count), count);
1613 break;
1614 default:
1615 break;
1616 }
1617 action->undo_label = label;
1618 } else {
1619 return action->undo_label;
1620 }
1621 }
1622
1623 return label;
1624 }
1625
1626 /** ---------------------------------------------------------------- */
1627 static gchar *
get_redo_label(CajaUndoStackActionData * action)1628 get_redo_label (CajaUndoStackActionData * action)
1629 {
1630 gchar *label = NULL;
1631 guint count;
1632
1633 if (action != NULL) {
1634 if (action->redo_label == NULL) {
1635 count = action->count;
1636 switch (action->type) {
1637 case CAJA_UNDOSTACK_COPY:
1638 label = g_strdup_printf (ngettext
1639 ("_Redo copy of %d item",
1640 "_Redo copy of %d items", count), count);
1641 break;
1642 case CAJA_UNDOSTACK_DUPLICATE:
1643 label = g_strdup_printf (ngettext
1644 ("_Redo duplicate of %d item",
1645 "_Redo duplicate of %d items", count), count);
1646 break;
1647 case CAJA_UNDOSTACK_MOVE:
1648 label = g_strdup_printf (ngettext
1649 ("_Redo move of %d item",
1650 "_Redo move of %d items", count), count);
1651 break;
1652 case CAJA_UNDOSTACK_RENAME:
1653 label = g_strdup_printf (ngettext
1654 ("_Redo rename of %d item",
1655 "_Redo rename of %d items", count), count);
1656 break;
1657 case CAJA_UNDOSTACK_CREATEEMPTYFILE:
1658 label = g_strdup_printf (_("_Redo creation of an empty file"));
1659 break;
1660 case CAJA_UNDOSTACK_CREATEFILEFROMTEMPLATE:
1661 label = g_strdup_printf (_("_Redo creation of a file from template"));
1662 break;
1663 case CAJA_UNDOSTACK_CREATEFOLDER:
1664 label = g_strdup_printf (ngettext
1665 ("_Redo creation of %d folder",
1666 "_Redo creation of %d folders", count), count);
1667 break;
1668 case CAJA_UNDOSTACK_MOVETOTRASH:
1669 label = g_strdup_printf (ngettext
1670 ("_Redo move to trash of %d item",
1671 "_Redo move to trash of %d items", count), count);
1672 break;
1673 case CAJA_UNDOSTACK_RESTOREFROMTRASH:
1674 label = g_strdup_printf (ngettext
1675 ("_Redo restore from trash of %d item",
1676 "_Redo restore from trash of %d items", count), count);
1677 break;
1678 case CAJA_UNDOSTACK_CREATELINK:
1679 label = g_strdup_printf (ngettext
1680 ("_Redo create link to %d item",
1681 "_Redo create link to %d items", count), count);
1682 break;
1683 case CAJA_UNDOSTACK_DELETE:
1684 label = g_strdup_printf (ngettext
1685 ("_Redo delete of %d item",
1686 "_Redo delete of %d items", count), count);
1687 break;
1688 case CAJA_UNDOSTACK_RECURSIVESETPERMISSIONS:
1689 label = g_strdup_printf (ngettext
1690 ("Redo recursive change permissions of %d item",
1691 "Redo recursive change permissions of %d items",
1692 count), count);
1693 break;
1694 case CAJA_UNDOSTACK_SETPERMISSIONS:
1695 label = g_strdup_printf (ngettext
1696 ("Redo change permissions of %d item",
1697 "Redo change permissions of %d items", count), count);
1698 break;
1699 case CAJA_UNDOSTACK_CHANGEGROUP:
1700 label = g_strdup_printf (ngettext
1701 ("Redo change group of %d item",
1702 "Redo change group of %d items", count), count);
1703 break;
1704 case CAJA_UNDOSTACK_CHANGEOWNER:
1705 label = g_strdup_printf (ngettext
1706 ("Redo change owner of %d item",
1707 "Redo change owner of %d items", count), count);
1708 break;
1709 default:
1710 break;
1711 }
1712 action->redo_label = label;
1713 } else {
1714 return action->redo_label;
1715 }
1716 }
1717
1718 return label;
1719 }
1720
1721 /** ---------------------------------------------------------------- */
1722 static void
undo_redo_done_transfer_callback(GHashTable * debuting_uris,gpointer data)1723 undo_redo_done_transfer_callback (GHashTable * debuting_uris, gpointer data)
1724 {
1725 CajaUndoStackActionData *action;
1726
1727 action = (CajaUndoStackActionData *) data;
1728
1729 /* If the action needed to be freed but was locked, free now */
1730 if (action->freed) {
1731 free_undostack_action (action, NULL);
1732 } else {
1733 action->locked = FALSE;
1734 }
1735
1736 CajaUndoStackManager *manager = action->manager;
1737 manager->priv->undo_redo_flag = FALSE;
1738
1739 /* Update menus */
1740 do_menu_update (action->manager);
1741 }
1742
1743 /** ---------------------------------------------------------------- */
1744 static void
undo_redo_done_delete_callback(GHashTable * debuting_uris,gboolean user_cancel,gpointer callback_data)1745 undo_redo_done_delete_callback (GHashTable *
1746 debuting_uris, gboolean user_cancel, gpointer callback_data)
1747 {
1748 undo_redo_done_transfer_callback (debuting_uris, callback_data);
1749 }
1750
1751 /** ---------------------------------------------------------------- */
1752 static void
undo_redo_done_create_callback(GFile * new_file,gpointer callback_data)1753 undo_redo_done_create_callback (GFile * new_file, gpointer callback_data)
1754 {
1755 undo_redo_done_transfer_callback (NULL, callback_data);
1756 }
1757
1758 /** ---------------------------------------------------------------- */
1759 static void
undo_redo_op_callback(gpointer callback_data)1760 undo_redo_op_callback (gpointer callback_data)
1761 {
1762 undo_redo_done_transfer_callback (NULL, callback_data);
1763 }
1764
1765 /** ---------------------------------------------------------------- */
1766 static void
undo_redo_done_rename_callback(CajaFile * file,GFile * result_location,GError * error,gpointer callback_data)1767 undo_redo_done_rename_callback (CajaFile * file,
1768 GFile * result_location, GError * error, gpointer callback_data)
1769 {
1770 undo_redo_done_transfer_callback (NULL, callback_data);
1771 }
1772
1773 /** ---------------------------------------------------------------- */
1774 static void
free_undostack_action(gpointer data,gpointer user_data)1775 free_undostack_action (gpointer data, gpointer user_data)
1776 {
1777 CajaUndoStackActionData *action = (CajaUndoStackActionData *) data;
1778
1779 if (!action)
1780 return;
1781
1782 g_free (action->template);
1783 g_free (action->target_uri);
1784 g_free (action->old_uri);
1785 g_free (action->new_uri);
1786
1787 g_free (action->undo_label);
1788 g_free (action->undo_description);
1789 g_free (action->redo_label);
1790 g_free (action->redo_description);
1791
1792 g_free (action->original_group_name_or_id);
1793 g_free (action->original_user_name_or_id);
1794 g_free (action->new_group_name_or_id);
1795 g_free (action->new_user_name_or_id);
1796
1797 if (action->sources) {
1798 g_list_free_full (action->sources, g_free);
1799 }
1800 if (action->destinations) {
1801 g_list_free_full (action->destinations, g_free);
1802 }
1803
1804 if (action->trashed) {
1805 g_hash_table_destroy (action->trashed);
1806 }
1807
1808 if (action->original_permissions) {
1809 g_hash_table_destroy (action->original_permissions);
1810 }
1811
1812 if (action->src_dir)
1813 g_object_unref (action->src_dir);
1814 if (action->dest_dir)
1815 g_object_unref (action->dest_dir);
1816
1817 if (action)
1818 g_slice_free (CajaUndoStackActionData, action);
1819 }
1820
1821 /** ---------------------------------------------------------------- */
1822 static void
undostack_dispose_all(GQueue * queue)1823 undostack_dispose_all (GQueue * queue)
1824 {
1825 g_queue_foreach (queue, free_undostack_action, NULL);
1826 }
1827
1828 /** ---------------------------------------------------------------- */
1829 static gboolean
can_undo(CajaUndoStackManagerPrivate * priv)1830 can_undo (CajaUndoStackManagerPrivate * priv)
1831 {
1832 return (get_next_undo_action (priv) != NULL);
1833 }
1834
1835 /** ---------------------------------------------------------------- */
1836 static gboolean
can_redo(CajaUndoStackManagerPrivate * priv)1837 can_redo (CajaUndoStackManagerPrivate * priv)
1838 {
1839 return (get_next_redo_action (priv) != NULL);
1840 }
1841
1842 /** ---------------------------------------------------------------- */
1843 static CajaUndoStackActionData *
get_next_redo_action(CajaUndoStackManagerPrivate * priv)1844 get_next_redo_action (CajaUndoStackManagerPrivate * priv)
1845 {
1846 if (g_queue_is_empty (priv->stack)) {
1847 return NULL;
1848 }
1849
1850 if (priv->index == 0) {
1851 /* ... no redo actions */
1852 return NULL;
1853 }
1854
1855 CajaUndoStackActionData *action = g_queue_peek_nth (priv->stack,
1856 priv->index - 1);
1857
1858 if (action->locked) {
1859 return NULL;
1860 } else {
1861 return action;
1862 }
1863 }
1864
1865 /** ---------------------------------------------------------------- */
1866 static CajaUndoStackActionData *
get_next_undo_action(CajaUndoStackManagerPrivate * priv)1867 get_next_undo_action (CajaUndoStackManagerPrivate * priv)
1868 {
1869 if (g_queue_is_empty (priv->stack)) {
1870 return NULL;
1871 }
1872
1873 guint stack_size = g_queue_get_length (priv->stack);
1874
1875 if (priv->index == stack_size) {
1876 return NULL;
1877 }
1878
1879 CajaUndoStackActionData *action = g_queue_peek_nth (priv->stack,
1880 priv->index);
1881
1882 if (action->locked) {
1883 return NULL;
1884 } else {
1885 return action;
1886 }
1887 }
1888
1889 /** ---------------------------------------------------------------- */
1890 static void
do_menu_update(CajaUndoStackManager * manager)1891 do_menu_update (CajaUndoStackManager * manager)
1892 {
1893
1894 if (!manager)
1895 return;
1896
1897 CajaUndoStackActionData *action;
1898 CajaUndoStackManagerPrivate *priv = manager->priv;
1899 CajaUndoStackMenuData *data = g_slice_new0 (CajaUndoStackMenuData);
1900
1901 g_mutex_lock (&priv->mutex);
1902
1903 action = get_next_undo_action (priv);
1904 data->undo_label = get_undo_label (action);
1905 data->undo_description = get_undo_description (action);
1906
1907 action = get_next_redo_action (priv);
1908
1909 data->redo_label = get_redo_label (action);
1910 data->redo_description = get_redo_description (action);
1911
1912 g_mutex_unlock (&priv->mutex);
1913
1914 /* Update menus */
1915 g_signal_emit_by_name (manager, "request-menu-update", data);
1916
1917 /* Free the signal data */
1918 // Note: we do not own labels and descriptions, they are part of the action.
1919 g_slice_free (CajaUndoStackMenuData, data);
1920 }
1921
1922 /** ---------------------------------------------------------------- */
1923 static GList *
construct_gfile_list(const GList * urilist,GFile * parent)1924 construct_gfile_list (const GList * urilist, GFile * parent)
1925 {
1926 const GList *l;
1927 GList *file_list = NULL;
1928 GFile *file = NULL;
1929
1930 for (l = urilist; l != NULL; l = l->next) {
1931 file = g_file_get_child (parent, l->data);
1932 file_list = g_list_append (file_list, file);
1933 }
1934
1935 return file_list;
1936 }
1937
1938 /** ---------------------------------------------------------------- */
1939 static GList *
construct_gfile_list_from_uri(char * uri)1940 construct_gfile_list_from_uri (char *uri)
1941 {
1942 GList *file_list = NULL;
1943 GFile *file;
1944
1945 file = g_file_new_for_uri (uri);
1946 file_list = g_list_append (file_list, file);
1947
1948 return file_list;
1949 }
1950
1951 /** ---------------------------------------------------------------- */
1952 static GList *
uri_list_to_gfile_list(GList * urilist)1953 uri_list_to_gfile_list (GList * urilist)
1954 {
1955 const GList *l;
1956 GList *file_list = NULL;
1957 GFile *file = NULL;
1958
1959 for (l = urilist; l != NULL; l = l->next) {
1960 file = g_file_new_for_uri (l->data);
1961 file_list = g_list_append (file_list, file);
1962 }
1963
1964 return file_list;
1965 }
1966
1967 /** ---------------------------------------------------------------- */
1968 static char *
get_uri_basename(char * uri)1969 get_uri_basename (char *uri)
1970 {
1971 GFile *f = g_file_new_for_uri (uri);
1972 char *basename = g_file_get_basename (f);
1973 g_object_unref (f);
1974 return basename;
1975 }
1976
1977 /** ---------------------------------------------------------------- */
1978 static char *
get_uri_parent(char * uri)1979 get_uri_parent (char *uri)
1980 {
1981 GFile *f = g_file_new_for_uri (uri);
1982 GFile *p = g_file_get_parent (f);
1983 char *parent = g_file_get_uri (p);
1984 g_object_unref (f);
1985 g_object_unref (p);
1986 return parent;
1987 }
1988
1989 /** ---------------------------------------------------------------- */
1990 static char *
get_uri_parent_path(char * uri)1991 get_uri_parent_path (char *uri)
1992 {
1993 GFile *f = g_file_new_for_uri (uri);
1994 GFile *p = g_file_get_parent (f);
1995 char *parent = g_file_get_path (p);
1996 g_object_unref (f);
1997 g_object_unref (p);
1998 return parent;
1999 }
2000
2001 /** ---------------------------------------------------------------- */
2002 static GHashTable *
retrieve_files_to_restore(GHashTable * trashed)2003 retrieve_files_to_restore (GHashTable * trashed)
2004 {
2005 if ((!(g_hash_table_size (trashed))) > 0) {
2006 return NULL;
2007 }
2008
2009 GFile *trash = g_file_new_for_uri ("trash:");
2010
2011 GFileEnumerator *enumerator = g_file_enumerate_children (trash,
2012 G_FILE_ATTRIBUTE_STANDARD_NAME
2013 ","
2014 G_FILE_ATTRIBUTE_TIME_MODIFIED
2015 ","
2016 G_FILE_ATTRIBUTE_TRASH_ORIG_PATH,
2017 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, FALSE, NULL);
2018
2019 GHashTable *to_restore = g_hash_table_new_full (g_direct_hash,
2020 g_direct_equal, g_object_unref, g_free);
2021
2022 if (enumerator) {
2023 GFileInfo *info;
2024 while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)) != NULL) {
2025 /* Retrieve the original file uri */
2026 const char *origpath = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH);
2027 if (origpath == NULL) {
2028 g_warning ("The item cannot be restored from trash: could not determine original location");
2029 continue;
2030 }
2031
2032 GFile *origfile = g_file_new_for_path (origpath);
2033 char *origuri = g_file_get_uri (origfile);
2034 g_object_unref (origfile);
2035
2036 gboolean origuri_inserted = FALSE;
2037 gpointer lookupvalue = g_hash_table_lookup (trashed, origuri);
2038
2039 if (lookupvalue) {
2040 guint64 *mtime = (guint64 *) lookupvalue;
2041 guint64 mtime_item = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
2042 if (*mtime == mtime_item) {
2043 GFile *item = g_file_get_child (trash, g_file_info_get_name (info)); /* File in the trash */
2044 g_hash_table_insert (to_restore, item, origuri);
2045 origuri_inserted = TRUE;
2046 }
2047 }
2048
2049 if (!origuri_inserted) {
2050 g_free (origuri);
2051 }
2052 }
2053
2054 g_file_enumerator_close (enumerator, FALSE, NULL);
2055 g_object_unref (enumerator);
2056 }
2057
2058 g_object_unref (trash);
2059
2060 return to_restore;
2061 }
2062
2063 /** ---------------------------------------------------------------- */
2064