1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /* nemo-file-undo-operations.c - Manages undo/redo of file operations
4 *
5 * Copyright (C) 2007-2011 Amos Brocco
6 * Copyright (C) 2010, 2012 Red Hat, Inc.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin Street - Suite 500,
21 * Boston, MA 02110-1335, USA.
22 *
23 * Authors: Amos Brocco <amos.brocco@gmail.com>
24 * Cosimo Cecchi <cosimoc@redhat.com>
25 *
26 */
27
28 #include <config.h>
29
30 #include "nemo-file-undo-operations.h"
31
32 #include <stdlib.h>
33 #include <glib/gi18n.h>
34
35
36 #include "nemo-file-operations.h"
37 #include "nemo-file.h"
38 #include "nemo-file-undo-manager.h"
39
40 /* Since we use g_get_current_time for setting "orig_trash_time" in the undo
41 * info, there are situations where the difference between this value and the
42 * real deletion time can differ enough to make the rounding a difference of 1
43 * second, failing the equality check. To make sure we avoid this, and to be
44 * preventive, use 2 seconds epsilon.
45 */
46 #define TRASH_TIME_EPSILON 2
47
48 #define HEAD(q) (g_queue_peek_head_link(q))
49
50 G_DEFINE_TYPE (NemoFileUndoInfo, nemo_file_undo_info, G_TYPE_OBJECT)
51
52 enum {
53 PROP_OP_TYPE = 1,
54 PROP_ITEM_COUNT,
55 N_PROPERTIES
56 };
57
58 static GParamSpec *properties[N_PROPERTIES] = { NULL, };
59
60 struct _NemoFileUndoInfoDetails {
61 NemoFileUndoOp op_type;
62 guint count; /* Number of items */
63
64 GSimpleAsyncResult *apply_async_result;
65
66 gchar *undo_label;
67 gchar *redo_label;
68 gchar *undo_description;
69 gchar *redo_description;
70 };
71
72 /* description helpers */
73 static void
nemo_file_undo_info_init(NemoFileUndoInfo * self)74 nemo_file_undo_info_init (NemoFileUndoInfo *self)
75 {
76 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NEMO_TYPE_FILE_UNDO_INFO,
77 NemoFileUndoInfoDetails);
78 self->priv->apply_async_result = NULL;
79 }
80
81 static void
nemo_file_undo_info_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)82 nemo_file_undo_info_get_property (GObject *object,
83 guint property_id,
84 GValue *value,
85 GParamSpec *pspec)
86 {
87 NemoFileUndoInfo *self = NEMO_FILE_UNDO_INFO (object);
88
89 switch (property_id) {
90 case PROP_OP_TYPE:
91 g_value_set_int (value, self->priv->op_type);
92 break;
93 case PROP_ITEM_COUNT:
94 g_value_set_int (value, self->priv->count);
95 break;
96 default:
97 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
98 break;
99 }
100 }
101
102 static void
nemo_file_undo_info_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)103 nemo_file_undo_info_set_property (GObject *object,
104 guint property_id,
105 const GValue *value,
106 GParamSpec *pspec)
107 {
108 NemoFileUndoInfo *self = NEMO_FILE_UNDO_INFO (object);
109
110 switch (property_id) {
111 case PROP_OP_TYPE:
112 self->priv->op_type = g_value_get_int (value);
113 break;
114 case PROP_ITEM_COUNT:
115 self->priv->count = g_value_get_int (value);
116 break;
117 default:
118 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
119 break;
120 }
121 }
122
123 static void
nemo_file_redo_info_warn_redo(NemoFileUndoInfo * self,GtkWindow * parent_window)124 nemo_file_redo_info_warn_redo (NemoFileUndoInfo *self,
125 GtkWindow *parent_window)
126 {
127 g_critical ("Object %p of type %s does not implement redo_func!!",
128 self, G_OBJECT_TYPE_NAME (self));
129 }
130
131 static void
nemo_file_undo_info_warn_undo(NemoFileUndoInfo * self,GtkWindow * parent_window)132 nemo_file_undo_info_warn_undo (NemoFileUndoInfo *self,
133 GtkWindow *parent_window)
134 {
135 g_critical ("Object %p of type %s does not implement undo_func!!",
136 self, G_OBJECT_TYPE_NAME (self));
137 }
138
139 static void
nemo_file_undo_info_strings_func(NemoFileUndoInfo * self,gchar ** undo_label,gchar ** undo_description,gchar ** redo_label,gchar ** redo_description)140 nemo_file_undo_info_strings_func (NemoFileUndoInfo *self,
141 gchar **undo_label,
142 gchar **undo_description,
143 gchar **redo_label,
144 gchar **redo_description)
145 {
146 if (undo_label != NULL) {
147 *undo_label = g_strdup (_("Undo"));
148 }
149 if (undo_description != NULL) {
150 *undo_description = g_strdup (_("Undo last action"));
151 }
152
153 if (redo_label != NULL) {
154 *redo_label = g_strdup (_("Redo"));
155 }
156 if (redo_description != NULL) {
157 *redo_description = g_strdup (_("Redo last undone action"));
158 }
159 }
160
161 static void
nemo_file_undo_info_finalize(GObject * obj)162 nemo_file_undo_info_finalize (GObject *obj)
163 {
164 NemoFileUndoInfo *self = NEMO_FILE_UNDO_INFO (obj);
165
166 g_clear_object (&self->priv->apply_async_result);
167
168 G_OBJECT_CLASS (nemo_file_undo_info_parent_class)->finalize (obj);
169 }
170
171 static void
nemo_file_undo_info_class_init(NemoFileUndoInfoClass * klass)172 nemo_file_undo_info_class_init (NemoFileUndoInfoClass *klass)
173 {
174 GObjectClass *oclass = G_OBJECT_CLASS (klass);
175
176 oclass->finalize = nemo_file_undo_info_finalize;
177 oclass->get_property = nemo_file_undo_info_get_property;
178 oclass->set_property = nemo_file_undo_info_set_property;
179
180 klass->undo_func = nemo_file_undo_info_warn_undo;
181 klass->redo_func = nemo_file_redo_info_warn_redo;
182 klass->strings_func = nemo_file_undo_info_strings_func;
183
184 properties[PROP_OP_TYPE] =
185 g_param_spec_int ("op-type",
186 "Undo info op type",
187 "Type of undo operation",
188 0, NEMO_FILE_UNDO_OP_NUM_TYPES - 1, 0,
189 G_PARAM_READWRITE |
190 G_PARAM_CONSTRUCT_ONLY);
191 properties[PROP_ITEM_COUNT] =
192 g_param_spec_int ("item-count",
193 "Number of items",
194 "Number of items",
195 0, G_MAXINT, 0,
196 G_PARAM_READWRITE |
197 G_PARAM_CONSTRUCT_ONLY);
198
199 g_type_class_add_private (klass, sizeof (NemoFileUndoInfoDetails));
200 g_object_class_install_properties (oclass, N_PROPERTIES, properties);
201 }
202
203 static NemoFileUndoOp
nemo_file_undo_info_get_op_type(NemoFileUndoInfo * self)204 nemo_file_undo_info_get_op_type (NemoFileUndoInfo *self)
205 {
206 return self->priv->op_type;
207 }
208
209 static gint
nemo_file_undo_info_get_item_count(NemoFileUndoInfo * self)210 nemo_file_undo_info_get_item_count (NemoFileUndoInfo *self)
211 {
212 return self->priv->count;
213 }
214
215 void
nemo_file_undo_info_apply_async(NemoFileUndoInfo * self,gboolean undo,GtkWindow * parent_window,GAsyncReadyCallback callback,gpointer user_data)216 nemo_file_undo_info_apply_async (NemoFileUndoInfo *self,
217 gboolean undo,
218 GtkWindow *parent_window,
219 GAsyncReadyCallback callback,
220 gpointer user_data)
221 {
222 g_assert (self->priv->apply_async_result == NULL);
223
224 self->priv->apply_async_result =
225 g_simple_async_result_new (G_OBJECT (self),
226 callback, user_data,
227 nemo_file_undo_info_apply_async);
228
229 if (undo) {
230 NEMO_FILE_UNDO_INFO_CLASS (G_OBJECT_GET_CLASS (self))->undo_func (self, parent_window);
231 } else {
232 NEMO_FILE_UNDO_INFO_CLASS (G_OBJECT_GET_CLASS (self))->redo_func (self, parent_window);
233 }
234 }
235
236 typedef struct {
237 gboolean success;
238 gboolean user_cancel;
239 } FileUndoInfoOpRes;
240
241 static void
file_undo_info_op_res_free(gpointer data)242 file_undo_info_op_res_free (gpointer data)
243 {
244 g_slice_free (FileUndoInfoOpRes, data);
245 }
246
247 gboolean
nemo_file_undo_info_apply_finish(NemoFileUndoInfo * self,GAsyncResult * res,gboolean * user_cancel,GError ** error)248 nemo_file_undo_info_apply_finish (NemoFileUndoInfo *self,
249 GAsyncResult *res,
250 gboolean *user_cancel,
251 GError **error)
252 {
253 FileUndoInfoOpRes *op_res;
254
255 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) {
256 return FALSE;
257 }
258
259 op_res = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
260 *user_cancel = op_res->user_cancel;
261
262 return op_res->success;
263 }
264
265 void
nemo_file_undo_info_get_strings(NemoFileUndoInfo * self,gchar ** undo_label,gchar ** undo_description,gchar ** redo_label,gchar ** redo_description)266 nemo_file_undo_info_get_strings (NemoFileUndoInfo *self,
267 gchar **undo_label,
268 gchar **undo_description,
269 gchar **redo_label,
270 gchar **redo_description)
271 {
272 return NEMO_FILE_UNDO_INFO_CLASS (G_OBJECT_GET_CLASS (self))->strings_func (self,
273 undo_label, undo_description,
274 redo_label, redo_description);
275 }
276
277 static void
file_undo_info_complete_apply(NemoFileUndoInfo * self,gboolean success,gboolean user_cancel)278 file_undo_info_complete_apply (NemoFileUndoInfo *self,
279 gboolean success,
280 gboolean user_cancel)
281 {
282 FileUndoInfoOpRes *op_res = g_slice_new0 (FileUndoInfoOpRes);
283
284 op_res->user_cancel = user_cancel;
285 op_res->success = success;
286
287
288 g_simple_async_result_set_op_res_gpointer (self->priv->apply_async_result, op_res,
289 file_undo_info_op_res_free);
290 g_simple_async_result_complete_in_idle (self->priv->apply_async_result);
291
292 g_clear_object (&self->priv->apply_async_result);
293 }
294
295 static void
file_undo_info_transfer_callback(GHashTable * debuting_uris,gboolean success,gpointer user_data)296 file_undo_info_transfer_callback (GHashTable * debuting_uris,
297 gboolean success,
298 gpointer user_data)
299 {
300 NemoFileUndoInfo *self = user_data;
301
302 /* TODO: we need to forward the cancelled state from
303 * the file operation to the file undo info object.
304 */
305 file_undo_info_complete_apply (self, success, FALSE);
306 }
307
308 static void
file_undo_info_operation_callback(NemoFile * file,GFile * result_location,GError * error,gpointer user_data)309 file_undo_info_operation_callback (NemoFile * file,
310 GFile * result_location,
311 GError * error,
312 gpointer user_data)
313 {
314 NemoFileUndoInfo *self = user_data;
315
316 file_undo_info_complete_apply (self, (error == NULL),
317 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED));
318 }
319
320 static void
file_undo_info_delete_callback(GHashTable * debuting_uris,gboolean user_cancel,gpointer user_data)321 file_undo_info_delete_callback (GHashTable *debuting_uris,
322 gboolean user_cancel,
323 gpointer user_data)
324 {
325 NemoFileUndoInfo *self = user_data;
326
327 file_undo_info_complete_apply (self,
328 !user_cancel,
329 user_cancel);
330 }
331
332 /* copy/move/duplicate/link/restore from trash */
333 G_DEFINE_TYPE (NemoFileUndoInfoExt, nemo_file_undo_info_ext, NEMO_TYPE_FILE_UNDO_INFO)
334
335 struct _NemoFileUndoInfoExtDetails {
336 GFile *src_dir;
337 GFile *dest_dir;
338 GQueue *sources; /* Relative to src_dir */
339 GQueue *destinations; /* Relative to dest_dir */
340 };
341
342 static char *
ext_get_first_target_short_name(NemoFileUndoInfoExt * self)343 ext_get_first_target_short_name (NemoFileUndoInfoExt *self)
344 {
345 GFile *first_file;
346 char *file_name = NULL;
347
348 first_file = G_FILE (g_queue_peek_head (self->priv->destinations));
349
350 if (first_file != NULL) {
351 file_name = g_file_get_basename (first_file);
352 }
353
354 return file_name;
355 }
356
357 static void
ext_strings_func(NemoFileUndoInfo * info,gchar ** undo_label,gchar ** undo_description,gchar ** redo_label,gchar ** redo_description)358 ext_strings_func (NemoFileUndoInfo *info,
359 gchar **undo_label,
360 gchar **undo_description,
361 gchar **redo_label,
362 gchar **redo_description)
363 {
364 NemoFileUndoInfoExt *self = NEMO_FILE_UNDO_INFO_EXT (info);
365 NemoFileUndoOp op_type = nemo_file_undo_info_get_op_type (info);
366 gint count = nemo_file_undo_info_get_item_count (info);
367 gchar *name = NULL, *source, *destination;
368
369 source = g_file_get_path (self->priv->src_dir);
370 destination = g_file_get_path (self->priv->dest_dir);
371
372 if (count <= 1) {
373 name = ext_get_first_target_short_name (self);
374 }
375
376 if (op_type == NEMO_FILE_UNDO_OP_MOVE) {
377 if (count > 1) {
378 *undo_description = g_strdup_printf (ngettext ("Move %d item back to '%s'",
379 "Move %d items back to '%s'", count),
380 count, source);
381 *redo_description = g_strdup_printf (ngettext ("Move %d item to '%s'",
382 "Move %d items to '%s'", count),
383 count, destination);
384
385 *undo_label = g_strdup_printf (ngettext ("_Undo Move %d item",
386 "_Undo Move %d items", count),
387 count);
388 *redo_label = g_strdup_printf (ngettext ("_Redo Move %d item",
389 "_Redo Move %d items", count),
390 count);
391 } else {
392 *undo_description = g_strdup_printf (_("Move '%s' back to '%s'"), name, source);
393 *redo_description = g_strdup_printf (_("Move '%s' to '%s'"), name, destination);
394
395 *undo_label = g_strdup (_("_Undo Move"));
396 *redo_label = g_strdup (_("_Redo Move"));
397 }
398 } else if (op_type == NEMO_FILE_UNDO_OP_RESTORE_FROM_TRASH) {
399 *undo_label = g_strdup (_("_Undo Restore from Trash"));
400 *redo_label = g_strdup (_("_Redo Restore from Trash"));
401
402 if (count > 1) {
403 *undo_description = g_strdup_printf (ngettext ("Move %d item back to trash",
404 "Move %d items back to trash", count),
405 count);
406 *redo_description = g_strdup_printf (ngettext ("Restore %d item from trash",
407 "Restore %d items from trash", count),
408 count);
409 } else {
410 *undo_description = g_strdup_printf (_("Move '%s' back to trash"), name);
411 *redo_description = g_strdup_printf (_("Restore '%s' from trash"), name);
412 }
413 } else if (op_type == NEMO_FILE_UNDO_OP_COPY) {
414 if (count > 1) {
415 *undo_description = g_strdup_printf (ngettext ("Delete %d copied item",
416 "Delete %d copied items", count),
417 count);
418 *redo_description = g_strdup_printf (ngettext ("Copy %d item to '%s'",
419 "Copy %d items to '%s'", count),
420 count, destination);
421
422 *undo_label = g_strdup_printf (ngettext ("_Undo Copy %d item",
423 "_Undo Copy %d items", count),
424 count);
425 *redo_label = g_strdup_printf (ngettext ("_Redo Copy %d item",
426 "_Redo Copy %d items", count),
427 count);
428 } else {
429 *undo_description = g_strdup_printf (_("Delete '%s'"), name);
430 *redo_description = g_strdup_printf (_("Copy '%s' to '%s'"), name, destination);
431
432 *undo_label = g_strdup (_("_Undo Copy"));
433 *redo_label = g_strdup (_("_Redo Copy"));
434 }
435 } else if (op_type == NEMO_FILE_UNDO_OP_DUPLICATE) {
436 if (count > 1) {
437 *undo_description = g_strdup_printf (ngettext ("Delete %d duplicated item",
438 "Delete %d duplicated items", count),
439 count);
440 *redo_description = g_strdup_printf (ngettext ("Duplicate %d item in '%s'",
441 "Duplicate %d items in '%s'", count),
442 count, destination);
443
444 *undo_label = g_strdup_printf (ngettext ("_Undo Duplicate %d item",
445 "_Undo Duplicate %d items", count),
446 count);
447 *redo_label = g_strdup_printf (ngettext ("_Redo Duplicate %d item",
448 "_Redo Duplicate %d items", count),
449 count);
450 } else {
451 *undo_description = g_strdup_printf (_("Delete '%s'"), name);
452 *redo_description = g_strdup_printf (_("Duplicate '%s' in '%s'"),
453 name, destination);
454
455 *undo_label = g_strdup (_("_Undo Duplicate"));
456 *redo_label = g_strdup (_("_Redo Duplicate"));
457 }
458 } else if (op_type == NEMO_FILE_UNDO_OP_CREATE_LINK) {
459 if (count > 1) {
460 *undo_description = g_strdup_printf (ngettext ("Delete links to %d item",
461 "Delete links to %d items", count),
462 count);
463 *redo_description = g_strdup_printf (ngettext ("Create links to %d item",
464 "Create links to %d items", count),
465 count);
466 } else {
467 *undo_description = g_strdup_printf (_("Delete link to '%s'"), name);
468 *redo_description = g_strdup_printf (_("Create link to '%s'"), name);
469
470 *undo_label = g_strdup (_("_Undo Create Link"));
471 *redo_label = g_strdup (_("_Redo Create Link"));
472 }
473 } else {
474 g_assert_not_reached ();
475 }
476
477 g_free (name);
478 g_free (source);
479 g_free (destination);
480 }
481
482 static void
ext_create_link_redo_func(NemoFileUndoInfoExt * self,GtkWindow * parent_window)483 ext_create_link_redo_func (NemoFileUndoInfoExt *self,
484 GtkWindow *parent_window)
485 {
486 nemo_file_operations_link (HEAD (self->priv->sources), NULL,
487 self->priv->dest_dir, parent_window,
488 file_undo_info_transfer_callback, self);
489 }
490
491 static void
ext_duplicate_redo_func(NemoFileUndoInfoExt * self,GtkWindow * parent_window)492 ext_duplicate_redo_func (NemoFileUndoInfoExt *self,
493 GtkWindow *parent_window)
494 {
495 nemo_file_operations_duplicate (HEAD (self->priv->sources), NULL, parent_window,
496 file_undo_info_transfer_callback, self);
497 }
498
499 static void
ext_copy_redo_func(NemoFileUndoInfoExt * self,GtkWindow * parent_window)500 ext_copy_redo_func (NemoFileUndoInfoExt *self,
501 GtkWindow *parent_window)
502 {
503 nemo_file_operations_copy (HEAD (self->priv->sources), NULL,
504 self->priv->dest_dir, parent_window,
505 file_undo_info_transfer_callback, self);
506 }
507
508 static void
ext_move_restore_redo_func(NemoFileUndoInfoExt * self,GtkWindow * parent_window)509 ext_move_restore_redo_func (NemoFileUndoInfoExt *self,
510 GtkWindow *parent_window)
511 {
512 nemo_file_operations_move (HEAD (self->priv->sources), NULL,
513 self->priv->dest_dir, parent_window,
514 file_undo_info_transfer_callback, self);
515 }
516
517 static void
ext_redo_func(NemoFileUndoInfo * info,GtkWindow * parent_window)518 ext_redo_func (NemoFileUndoInfo *info,
519 GtkWindow *parent_window)
520 {
521 NemoFileUndoInfoExt *self = NEMO_FILE_UNDO_INFO_EXT (info);
522 NemoFileUndoOp op_type = nemo_file_undo_info_get_op_type (info);
523
524 if (op_type == NEMO_FILE_UNDO_OP_MOVE ||
525 op_type == NEMO_FILE_UNDO_OP_RESTORE_FROM_TRASH) {
526 ext_move_restore_redo_func (self, parent_window);
527 } else if (op_type == NEMO_FILE_UNDO_OP_COPY) {
528 ext_copy_redo_func (self, parent_window);
529 } else if (op_type == NEMO_FILE_UNDO_OP_DUPLICATE) {
530 ext_duplicate_redo_func (self, parent_window);
531 } else if (op_type == NEMO_FILE_UNDO_OP_CREATE_LINK) {
532 ext_create_link_redo_func (self, parent_window);
533 } else {
534 g_assert_not_reached ();
535 }
536 }
537
538 static void
ext_restore_undo_func(NemoFileUndoInfoExt * self,GtkWindow * parent_window)539 ext_restore_undo_func (NemoFileUndoInfoExt *self,
540 GtkWindow *parent_window)
541 {
542 nemo_file_operations_trash_or_delete (HEAD (self->priv->destinations), parent_window,
543 file_undo_info_delete_callback, self);
544 }
545
546
547 static void
ext_move_undo_func(NemoFileUndoInfoExt * self,GtkWindow * parent_window)548 ext_move_undo_func (NemoFileUndoInfoExt *self,
549 GtkWindow *parent_window)
550 {
551 nemo_file_operations_move (HEAD (self->priv->destinations), NULL,
552 self->priv->src_dir, parent_window,
553 file_undo_info_transfer_callback, self);
554 }
555
556 static void
ext_copy_duplicate_undo_func(NemoFileUndoInfoExt * self,GtkWindow * parent_window)557 ext_copy_duplicate_undo_func (NemoFileUndoInfoExt *self,
558 GtkWindow *parent_window)
559 {
560 GList *files;
561
562 files = g_list_copy (HEAD (self->priv->destinations));
563 files = g_list_reverse (files); /* Deleting must be done in reverse */
564
565 nemo_file_operations_delete (files, parent_window,
566 file_undo_info_delete_callback, self);
567
568 g_list_free (files);
569 }
570
571 static void
ext_undo_func(NemoFileUndoInfo * info,GtkWindow * parent_window)572 ext_undo_func (NemoFileUndoInfo *info,
573 GtkWindow *parent_window)
574 {
575 NemoFileUndoInfoExt *self = NEMO_FILE_UNDO_INFO_EXT (info);
576 NemoFileUndoOp op_type = nemo_file_undo_info_get_op_type (info);
577
578 if (op_type == NEMO_FILE_UNDO_OP_COPY ||
579 op_type == NEMO_FILE_UNDO_OP_DUPLICATE ||
580 op_type == NEMO_FILE_UNDO_OP_CREATE_LINK) {
581 ext_copy_duplicate_undo_func (self, parent_window);
582 } else if (op_type == NEMO_FILE_UNDO_OP_MOVE) {
583 ext_move_undo_func (self, parent_window);
584 } else if (op_type == NEMO_FILE_UNDO_OP_RESTORE_FROM_TRASH) {
585 ext_restore_undo_func (self, parent_window);
586 } else {
587 g_assert_not_reached ();
588 }
589 }
590
591 static void
nemo_file_undo_info_ext_init(NemoFileUndoInfoExt * self)592 nemo_file_undo_info_ext_init (NemoFileUndoInfoExt *self)
593 {
594 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, nemo_file_undo_info_ext_get_type (),
595 NemoFileUndoInfoExtDetails);
596 }
597
598 static void
nemo_file_undo_info_ext_finalize(GObject * obj)599 nemo_file_undo_info_ext_finalize (GObject *obj)
600 {
601 NemoFileUndoInfoExt *self = NEMO_FILE_UNDO_INFO_EXT (obj);
602
603 if (self->priv->sources) {
604 g_queue_free_full (self->priv->sources, g_object_unref);
605 }
606
607 if (self->priv->destinations) {
608 g_queue_free_full (self->priv->destinations, g_object_unref);
609 }
610
611 g_clear_object (&self->priv->src_dir);
612 g_clear_object (&self->priv->dest_dir);
613
614 G_OBJECT_CLASS (nemo_file_undo_info_ext_parent_class)->finalize (obj);
615 }
616
617 static void
nemo_file_undo_info_ext_class_init(NemoFileUndoInfoExtClass * klass)618 nemo_file_undo_info_ext_class_init (NemoFileUndoInfoExtClass *klass)
619 {
620 GObjectClass *oclass = G_OBJECT_CLASS (klass);
621 NemoFileUndoInfoClass *iclass = NEMO_FILE_UNDO_INFO_CLASS (klass);
622
623 oclass->finalize = nemo_file_undo_info_ext_finalize;
624
625 iclass->undo_func = ext_undo_func;
626 iclass->redo_func = ext_redo_func;
627 iclass->strings_func = ext_strings_func;
628
629 g_type_class_add_private (klass, sizeof (NemoFileUndoInfoExtDetails));
630 }
631
632 NemoFileUndoInfo *
nemo_file_undo_info_ext_new(NemoFileUndoOp op_type,gint item_count,GFile * src_dir,GFile * target_dir)633 nemo_file_undo_info_ext_new (NemoFileUndoOp op_type,
634 gint item_count,
635 GFile *src_dir,
636 GFile *target_dir)
637 {
638 NemoFileUndoInfoExt *retval;
639
640 retval = g_object_new (NEMO_TYPE_FILE_UNDO_INFO_EXT,
641 "op-type", op_type,
642 "item-count", item_count,
643 NULL);
644
645 retval->priv->src_dir = g_object_ref (src_dir);
646 retval->priv->dest_dir = g_object_ref (target_dir);
647 retval->priv->destinations = g_queue_new ();
648 retval->priv->sources = g_queue_new ();
649
650 return NEMO_FILE_UNDO_INFO (retval);
651 }
652
653 void
nemo_file_undo_info_ext_add_origin_target_pair(NemoFileUndoInfoExt * self,GFile * origin,GFile * target)654 nemo_file_undo_info_ext_add_origin_target_pair (NemoFileUndoInfoExt *self,
655 GFile *origin,
656 GFile *target)
657 {
658 g_queue_push_tail (self->priv->sources, g_object_ref (origin));
659 g_queue_push_tail (self->priv->destinations, g_object_ref (target));
660 }
661
662 /* create new file/folder */
663 G_DEFINE_TYPE (NemoFileUndoInfoCreate, nemo_file_undo_info_create, NEMO_TYPE_FILE_UNDO_INFO)
664
665 struct _NemoFileUndoInfoCreateDetails {
666 char *template;
667 GFile *target_file;
668 gint length;
669 };
670
671 static void
create_strings_func(NemoFileUndoInfo * info,gchar ** undo_label,gchar ** undo_description,gchar ** redo_label,gchar ** redo_description)672 create_strings_func (NemoFileUndoInfo *info,
673 gchar **undo_label,
674 gchar **undo_description,
675 gchar **redo_label,
676 gchar **redo_description)
677
678 {
679 NemoFileUndoInfoCreate *self = NEMO_FILE_UNDO_INFO_CREATE (info);
680 NemoFileUndoOp op_type = nemo_file_undo_info_get_op_type (info);
681 char *name;
682
683 name = g_file_get_parse_name (self->priv->target_file);
684 *undo_description = g_strdup_printf (_("Delete '%s'"), name);
685
686 if (op_type == NEMO_FILE_UNDO_OP_CREATE_EMPTY_FILE) {
687 *redo_description = g_strdup_printf (_("Create an empty file '%s'"), name);
688
689 *undo_label = g_strdup (_("_Undo Create Empty File"));
690 *redo_label = g_strdup (_("_Redo Create Empty File"));
691 } else if (op_type == NEMO_FILE_UNDO_OP_CREATE_FOLDER) {
692 *redo_description = g_strdup_printf (_("Create a new folder '%s'"), name);
693
694 *undo_label = g_strdup (_("_Undo Create Folder"));
695 *redo_label = g_strdup (_("_Redo Create Folder"));
696 } else if (op_type == NEMO_FILE_UNDO_OP_CREATE_FILE_FROM_TEMPLATE) {
697 *redo_description = g_strdup_printf (_("Create new file '%s' from template "), name);
698
699 *undo_label = g_strdup (_("_Undo Create from Template"));
700 *redo_label = g_strdup (_("_Redo Create from Template"));
701 } else {
702 g_assert_not_reached ();
703 }
704
705 g_free (name);
706 }
707
708 static void
create_callback(GFile * new_file,gboolean success,gpointer callback_data)709 create_callback (GFile * new_file,
710 gboolean success,
711 gpointer callback_data)
712 {
713 file_undo_info_transfer_callback (NULL, success, callback_data);
714 }
715
716 static void
create_from_template_redo_func(NemoFileUndoInfoCreate * self,GtkWindow * parent_window)717 create_from_template_redo_func (NemoFileUndoInfoCreate *self,
718 GtkWindow *parent_window)
719 {
720 GFile *parent;
721 gchar *parent_uri, *new_name;
722
723 parent = g_file_get_parent (self->priv->target_file);
724 parent_uri = g_file_get_uri (parent);
725 new_name = g_file_get_parse_name (self->priv->target_file);
726 nemo_file_operations_new_file_from_template (NULL, NULL,
727 parent_uri, new_name,
728 self->priv->template,
729 create_callback, self);
730
731 g_free (parent_uri);
732 g_free (new_name);
733 g_object_unref (parent);
734 }
735
736 static void
create_folder_redo_func(NemoFileUndoInfoCreate * self,GtkWindow * parent_window)737 create_folder_redo_func (NemoFileUndoInfoCreate *self,
738 GtkWindow *parent_window)
739 {
740 GFile *parent;
741 gchar *parent_uri;
742
743 parent = g_file_get_parent (self->priv->target_file);
744 parent_uri = g_file_get_uri (parent);
745 nemo_file_operations_new_folder (NULL, NULL, parent_uri,
746 create_callback, self);
747
748 g_free (parent_uri);
749 g_object_unref (parent);
750 }
751
752 static void
create_empty_redo_func(NemoFileUndoInfoCreate * self,GtkWindow * parent_window)753 create_empty_redo_func (NemoFileUndoInfoCreate *self,
754 GtkWindow *parent_window)
755
756 {
757 GFile *parent;
758 gchar *parent_uri;
759 gchar *new_name;
760
761 parent = g_file_get_parent (self->priv->target_file);
762 parent_uri = g_file_get_uri (parent);
763 new_name = g_file_get_parse_name (self->priv->target_file);
764 nemo_file_operations_new_file (NULL, NULL, parent_uri,
765 new_name,
766 self->priv->template,
767 self->priv->length,
768 create_callback, self);
769
770 g_free (parent_uri);
771 g_free (new_name);
772 g_object_unref (parent);
773 }
774
775 static void
create_redo_func(NemoFileUndoInfo * info,GtkWindow * parent_window)776 create_redo_func (NemoFileUndoInfo *info,
777 GtkWindow *parent_window)
778 {
779 NemoFileUndoInfoCreate *self = NEMO_FILE_UNDO_INFO_CREATE (info);
780 NemoFileUndoOp op_type = nemo_file_undo_info_get_op_type (info);
781
782 if (op_type == NEMO_FILE_UNDO_OP_CREATE_EMPTY_FILE) {
783 create_empty_redo_func (self, parent_window);
784 } else if (op_type == NEMO_FILE_UNDO_OP_CREATE_FOLDER) {
785 create_folder_redo_func (self, parent_window);
786 } else if (op_type == NEMO_FILE_UNDO_OP_CREATE_FILE_FROM_TEMPLATE) {
787 create_from_template_redo_func (self, parent_window);
788 } else {
789 g_assert_not_reached ();
790 }
791 }
792
793 static void
create_undo_func(NemoFileUndoInfo * info,GtkWindow * parent_window)794 create_undo_func (NemoFileUndoInfo *info,
795 GtkWindow *parent_window)
796 {
797 NemoFileUndoInfoCreate *self = NEMO_FILE_UNDO_INFO_CREATE (info);
798 GList *files = NULL;
799
800 files = g_list_append (files, g_object_ref (self->priv->target_file));
801 nemo_file_operations_delete (files, parent_window,
802 file_undo_info_delete_callback, self);
803
804 g_list_free_full (files, g_object_unref);
805 }
806
807 static void
nemo_file_undo_info_create_init(NemoFileUndoInfoCreate * self)808 nemo_file_undo_info_create_init (NemoFileUndoInfoCreate *self)
809 {
810 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, nemo_file_undo_info_create_get_type (),
811 NemoFileUndoInfoCreateDetails);
812 }
813
814 static void
nemo_file_undo_info_create_finalize(GObject * obj)815 nemo_file_undo_info_create_finalize (GObject *obj)
816 {
817 NemoFileUndoInfoCreate *self = NEMO_FILE_UNDO_INFO_CREATE (obj);
818 g_clear_object (&self->priv->target_file);
819 g_free (self->priv->template);
820
821 G_OBJECT_CLASS (nemo_file_undo_info_create_parent_class)->finalize (obj);
822 }
823
824 static void
nemo_file_undo_info_create_class_init(NemoFileUndoInfoCreateClass * klass)825 nemo_file_undo_info_create_class_init (NemoFileUndoInfoCreateClass *klass)
826 {
827 GObjectClass *oclass = G_OBJECT_CLASS (klass);
828 NemoFileUndoInfoClass *iclass = NEMO_FILE_UNDO_INFO_CLASS (klass);
829
830 oclass->finalize = nemo_file_undo_info_create_finalize;
831
832 iclass->undo_func = create_undo_func;
833 iclass->redo_func = create_redo_func;
834 iclass->strings_func = create_strings_func;
835
836 g_type_class_add_private (klass, sizeof (NemoFileUndoInfoCreateDetails));
837 }
838
839 NemoFileUndoInfo *
nemo_file_undo_info_create_new(NemoFileUndoOp op_type)840 nemo_file_undo_info_create_new (NemoFileUndoOp op_type)
841 {
842 return g_object_new (NEMO_TYPE_FILE_UNDO_INFO_CREATE,
843 "op-type", op_type,
844 "item-count", 1,
845 NULL);
846 }
847
848 void
nemo_file_undo_info_create_set_data(NemoFileUndoInfoCreate * self,GFile * file,const char * template,gint length)849 nemo_file_undo_info_create_set_data (NemoFileUndoInfoCreate *self,
850 GFile *file,
851 const char *template,
852 gint length)
853 {
854 self->priv->target_file = g_object_ref (file);
855 self->priv->template = g_strdup (template);
856 self->priv->length = length;
857 }
858
859 /* rename */
860 G_DEFINE_TYPE (NemoFileUndoInfoRename, nemo_file_undo_info_rename, NEMO_TYPE_FILE_UNDO_INFO)
861
862 struct _NemoFileUndoInfoRenameDetails {
863 GFile *old_file;
864 GFile *new_file;
865 gchar *old_display_name;
866 gchar *new_display_name;
867 };
868
869 static void
rename_strings_func(NemoFileUndoInfo * info,gchar ** undo_label,gchar ** undo_description,gchar ** redo_label,gchar ** redo_description)870 rename_strings_func (NemoFileUndoInfo *info,
871 gchar **undo_label,
872 gchar **undo_description,
873 gchar **redo_label,
874 gchar **redo_description)
875 {
876 NemoFileUndoInfoRename *self = NEMO_FILE_UNDO_INFO_RENAME (info);
877 gchar *new_name, *old_name;
878
879 new_name = g_file_get_parse_name (self->priv->new_file);
880 old_name = g_file_get_parse_name (self->priv->old_file);
881
882 *undo_description = g_strdup_printf (_("Rename '%s' as '%s'"), new_name, old_name);
883 *redo_description = g_strdup_printf (_("Rename '%s' as '%s'"), old_name, new_name);
884
885 *undo_label = g_strdup (_("_Undo Rename"));
886 *redo_label = g_strdup (_("_Redo Rename"));
887
888 g_free (old_name);
889 g_free (new_name);
890 }
891
892 static void
rename_redo_func(NemoFileUndoInfo * info,GtkWindow * parent_window)893 rename_redo_func (NemoFileUndoInfo *info,
894 GtkWindow *parent_window)
895 {
896 NemoFileUndoInfoRename *self = NEMO_FILE_UNDO_INFO_RENAME (info);
897 NemoFile *file;
898
899 file = nemo_file_get (self->priv->old_file);
900 nemo_file_rename (file, self->priv->new_display_name,
901 file_undo_info_operation_callback, self);
902
903 nemo_file_unref (file);
904 }
905
906 static void
rename_undo_func(NemoFileUndoInfo * info,GtkWindow * parent_window)907 rename_undo_func (NemoFileUndoInfo *info,
908 GtkWindow *parent_window)
909 {
910 NemoFileUndoInfoRename *self = NEMO_FILE_UNDO_INFO_RENAME (info);
911 NemoFile *file;
912
913 file = nemo_file_get (self->priv->new_file);
914 nemo_file_rename (file, self->priv->old_display_name,
915 file_undo_info_operation_callback, self);
916
917 nemo_file_unref (file);
918 }
919
920 static void
nemo_file_undo_info_rename_init(NemoFileUndoInfoRename * self)921 nemo_file_undo_info_rename_init (NemoFileUndoInfoRename *self)
922 {
923 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, nemo_file_undo_info_rename_get_type (),
924 NemoFileUndoInfoRenameDetails);
925 }
926
927 static void
nemo_file_undo_info_rename_finalize(GObject * obj)928 nemo_file_undo_info_rename_finalize (GObject *obj)
929 {
930 NemoFileUndoInfoRename *self = NEMO_FILE_UNDO_INFO_RENAME (obj);
931 g_clear_object (&self->priv->old_file);
932 g_clear_object (&self->priv->new_file);
933 g_free (self->priv->old_display_name);
934 g_free (self->priv->new_display_name);
935
936 G_OBJECT_CLASS (nemo_file_undo_info_rename_parent_class)->finalize (obj);
937 }
938
939 static void
nemo_file_undo_info_rename_class_init(NemoFileUndoInfoRenameClass * klass)940 nemo_file_undo_info_rename_class_init (NemoFileUndoInfoRenameClass *klass)
941 {
942 GObjectClass *oclass = G_OBJECT_CLASS (klass);
943 NemoFileUndoInfoClass *iclass = NEMO_FILE_UNDO_INFO_CLASS (klass);
944
945 oclass->finalize = nemo_file_undo_info_rename_finalize;
946
947 iclass->undo_func = rename_undo_func;
948 iclass->redo_func = rename_redo_func;
949 iclass->strings_func = rename_strings_func;
950
951 g_type_class_add_private (klass, sizeof (NemoFileUndoInfoRenameDetails));
952 }
953
954 NemoFileUndoInfo *
nemo_file_undo_info_rename_new(void)955 nemo_file_undo_info_rename_new (void)
956 {
957 return g_object_new (NEMO_TYPE_FILE_UNDO_INFO_RENAME,
958 "op-type", NEMO_FILE_UNDO_OP_RENAME,
959 "item-count", 1,
960 NULL);
961 }
962
963 void
nemo_file_undo_info_rename_set_data_pre(NemoFileUndoInfoRename * self,GFile * old_file,gchar * old_display_name,gchar * new_display_name)964 nemo_file_undo_info_rename_set_data_pre (NemoFileUndoInfoRename *self,
965 GFile *old_file,
966 gchar *old_display_name,
967 gchar *new_display_name)
968 {
969 self->priv->old_file = g_object_ref (old_file);
970 self->priv->old_display_name = g_strdup (old_display_name);
971 self->priv->new_display_name = g_strdup (new_display_name);
972 }
973
974 void
nemo_file_undo_info_rename_set_data_post(NemoFileUndoInfoRename * self,GFile * new_file)975 nemo_file_undo_info_rename_set_data_post (NemoFileUndoInfoRename *self,
976 GFile *new_file)
977 {
978 self->priv->new_file = g_object_ref (new_file);
979 }
980
981 /* trash */
982 G_DEFINE_TYPE (NemoFileUndoInfoTrash, nemo_file_undo_info_trash, NEMO_TYPE_FILE_UNDO_INFO)
983
984 struct _NemoFileUndoInfoTrashDetails {
985 GHashTable *trashed;
986 };
987
988 static void
trash_strings_func(NemoFileUndoInfo * info,gchar ** undo_label,gchar ** undo_description,gchar ** redo_label,gchar ** redo_description)989 trash_strings_func (NemoFileUndoInfo *info,
990 gchar **undo_label,
991 gchar **undo_description,
992 gchar **redo_label,
993 gchar **redo_description)
994 {
995 NemoFileUndoInfoTrash *self = NEMO_FILE_UNDO_INFO_TRASH (info);
996 gint count = g_hash_table_size (self->priv->trashed);
997
998 if (count != 1) {
999 *undo_description = g_strdup_printf (ngettext ("Restore %d item from trash",
1000 "Restore %d items from trash", count),
1001 count);
1002 *redo_description = g_strdup_printf (ngettext ("Move %d item to trash",
1003 "Move %d items to trash", count),
1004 count);
1005 } else {
1006 GList *keys;
1007 char *name, *orig_path;
1008 GFile *file;
1009
1010 keys = g_hash_table_get_keys (self->priv->trashed);
1011 file = keys->data;
1012 name = g_file_get_basename (file);
1013 orig_path = g_file_get_path (file);
1014 *undo_description = g_strdup_printf (_("Restore '%s' to '%s'"), name, orig_path);
1015
1016 g_free (name);
1017 g_free (orig_path);
1018 g_list_free (keys);
1019
1020 name = g_file_get_parse_name (file);
1021 *redo_description = g_strdup_printf (_("Move '%s' to trash"), name);
1022
1023 g_free (name);
1024
1025 *undo_label = g_strdup (_("_Undo Trash"));
1026 *redo_label = g_strdup (_("_Redo Trash"));
1027 }
1028 }
1029
1030 static void
trash_redo_func_callback(GHashTable * debuting_uris,gboolean user_cancel,gpointer user_data)1031 trash_redo_func_callback (GHashTable *debuting_uris,
1032 gboolean user_cancel,
1033 gpointer user_data)
1034 {
1035 NemoFileUndoInfoTrash *self = user_data;
1036 GHashTable *new_trashed_files;
1037 GTimeVal current_time;
1038 gsize updated_trash_time;
1039 GFile *file;
1040 GList *keys, *l;
1041
1042 if (!user_cancel) {
1043 new_trashed_files =
1044 g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal,
1045 g_object_unref, NULL);
1046
1047 keys = g_hash_table_get_keys (self->priv->trashed);
1048
1049 g_get_current_time (¤t_time);
1050 updated_trash_time = current_time.tv_sec;
1051
1052 for (l = keys; l != NULL; l = l->next) {
1053 file = l->data;
1054 g_hash_table_insert (new_trashed_files,
1055 g_object_ref (file), GSIZE_TO_POINTER (updated_trash_time));
1056 }
1057
1058 g_list_free (keys);
1059 g_hash_table_destroy (self->priv->trashed);
1060
1061 self->priv->trashed = new_trashed_files;
1062 }
1063
1064 file_undo_info_delete_callback (debuting_uris, user_cancel, user_data);
1065 }
1066
1067 static void
trash_redo_func(NemoFileUndoInfo * info,GtkWindow * parent_window)1068 trash_redo_func (NemoFileUndoInfo *info,
1069 GtkWindow *parent_window)
1070 {
1071 NemoFileUndoInfoTrash *self = NEMO_FILE_UNDO_INFO_TRASH (info);
1072
1073 if (g_hash_table_size (self->priv->trashed) > 0) {
1074 GList *locations;
1075
1076 locations = g_hash_table_get_keys (self->priv->trashed);
1077 nemo_file_operations_trash_or_delete (locations, parent_window,
1078 trash_redo_func_callback, self);
1079
1080 g_list_free (locations);
1081 }
1082 }
1083
1084 static GHashTable *
trash_retrieve_files_to_restore_finish(NemoFileUndoInfoTrash * self,GAsyncResult * res,GError ** error)1085 trash_retrieve_files_to_restore_finish (NemoFileUndoInfoTrash *self,
1086 GAsyncResult *res,
1087 GError **error)
1088 {
1089 GHashTable *retval = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
1090
1091 if (retval == NULL) {
1092 g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
1093 }
1094
1095 return retval;
1096 }
1097
1098 static void
trash_retrieve_files_to_restore_thread(GSimpleAsyncResult * res,GObject * object,GCancellable * cancellable)1099 trash_retrieve_files_to_restore_thread (GSimpleAsyncResult *res,
1100 GObject *object,
1101 GCancellable *cancellable)
1102 {
1103 NemoFileUndoInfoTrash *self = NEMO_FILE_UNDO_INFO_TRASH (object);
1104 GFileEnumerator *enumerator;
1105 GHashTable *to_restore;
1106 GFile *trash;
1107 GError *error = NULL;
1108
1109 to_restore = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal,
1110 g_object_unref, g_object_unref);
1111
1112 trash = g_file_new_for_uri ("trash:///");
1113
1114 enumerator = g_file_enumerate_children (trash,
1115 G_FILE_ATTRIBUTE_STANDARD_NAME","
1116 G_FILE_ATTRIBUTE_TRASH_DELETION_DATE","
1117 G_FILE_ATTRIBUTE_TRASH_ORIG_PATH,
1118 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
1119 NULL, &error);
1120
1121 if (enumerator) {
1122 GFileInfo *info;
1123 gpointer lookupvalue;
1124 GFile *item;
1125 GTimeVal timeval;
1126 glong trash_time, orig_trash_time;
1127 const char *origpath;
1128 GFile *origfile;
1129 const char *time_string;
1130
1131 while ((info = g_file_enumerator_next_file (enumerator, NULL, &error)) != NULL) {
1132 /* Retrieve the original file uri */
1133 origpath = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH);
1134 origfile = g_file_new_for_path (origpath);
1135
1136 lookupvalue = g_hash_table_lookup (self->priv->trashed, origfile);
1137
1138 if (lookupvalue) {
1139 orig_trash_time = GPOINTER_TO_SIZE (lookupvalue);
1140 time_string = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_TRASH_DELETION_DATE);
1141 if (time_string != NULL) {
1142 g_time_val_from_iso8601 (time_string, &timeval);
1143 trash_time = timeval.tv_sec;
1144 } else {
1145 trash_time = 0;
1146 }
1147
1148 if (ABS (orig_trash_time - trash_time) <= TRASH_TIME_EPSILON) {
1149 /* File in the trash */
1150 item = g_file_get_child (trash, g_file_info_get_name (info));
1151 g_hash_table_insert (to_restore, item, g_object_ref (origfile));
1152 }
1153 }
1154
1155 g_object_unref (origfile);
1156
1157 }
1158 g_file_enumerator_close (enumerator, FALSE, NULL);
1159 g_object_unref (enumerator);
1160 }
1161 g_object_unref (trash);
1162
1163 if (error != NULL) {
1164 g_simple_async_result_take_error (res, error);
1165 g_hash_table_destroy (to_restore);
1166 } else {
1167 g_simple_async_result_set_op_res_gpointer (res, to_restore, NULL);
1168 }
1169 }
1170
1171 static void
trash_retrieve_files_to_restore_async(NemoFileUndoInfoTrash * self,GAsyncReadyCallback callback,gpointer user_data)1172 trash_retrieve_files_to_restore_async (NemoFileUndoInfoTrash *self,
1173 GAsyncReadyCallback callback,
1174 gpointer user_data)
1175 {
1176 GSimpleAsyncResult *async_op;
1177
1178 async_op = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
1179 trash_retrieve_files_to_restore_async);
1180 g_simple_async_result_run_in_thread (async_op, trash_retrieve_files_to_restore_thread,
1181 G_PRIORITY_DEFAULT, NULL);
1182
1183 g_object_unref (async_op);
1184 }
1185
1186 static void
trash_retrieve_files_ready(GObject * source,GAsyncResult * res,gpointer user_data)1187 trash_retrieve_files_ready (GObject *source,
1188 GAsyncResult *res,
1189 gpointer user_data)
1190 {
1191 NemoFileUndoInfoTrash *self = NEMO_FILE_UNDO_INFO_TRASH (source);
1192 GHashTable *files_to_restore;
1193 GError *error = NULL;
1194
1195 files_to_restore = trash_retrieve_files_to_restore_finish (self, res, &error);
1196
1197 if (error == NULL && g_hash_table_size (files_to_restore) > 0) {
1198 GList *gfiles_in_trash, *l;
1199 GFile *item;
1200 GFile *dest;
1201
1202 gfiles_in_trash = g_hash_table_get_keys (files_to_restore);
1203
1204 for (l = gfiles_in_trash; l != NULL; l = l->next) {
1205 item = l->data;
1206 dest = g_hash_table_lookup (files_to_restore, item);
1207
1208 g_file_move (item, dest, G_FILE_COPY_NOFOLLOW_SYMLINKS, NULL, NULL, NULL, NULL);
1209 }
1210
1211 g_list_free (gfiles_in_trash);
1212
1213 /* Here we must do what's necessary for the callback */
1214 file_undo_info_transfer_callback (NULL, (error == NULL), self);
1215 } else {
1216 file_undo_info_transfer_callback (NULL, FALSE, self);
1217 }
1218
1219 if (files_to_restore != NULL) {
1220 g_hash_table_destroy (files_to_restore);
1221 }
1222
1223 g_clear_error (&error);
1224 }
1225
1226 static void
trash_undo_func(NemoFileUndoInfo * info,GtkWindow * parent_window)1227 trash_undo_func (NemoFileUndoInfo *info,
1228 GtkWindow *parent_window)
1229 {
1230 NemoFileUndoInfoTrash *self = NEMO_FILE_UNDO_INFO_TRASH (info);
1231
1232 /* Internally managed op, pop flag. */
1233 nemo_file_undo_manager_pop_flag ();
1234
1235 trash_retrieve_files_to_restore_async (self, trash_retrieve_files_ready, NULL);
1236 }
1237
1238 static void
nemo_file_undo_info_trash_init(NemoFileUndoInfoTrash * self)1239 nemo_file_undo_info_trash_init (NemoFileUndoInfoTrash *self)
1240 {
1241 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, nemo_file_undo_info_trash_get_type (),
1242 NemoFileUndoInfoTrashDetails);
1243 self->priv->trashed =
1244 g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal,
1245 g_object_unref, NULL);
1246 }
1247
1248 static void
nemo_file_undo_info_trash_finalize(GObject * obj)1249 nemo_file_undo_info_trash_finalize (GObject *obj)
1250 {
1251 NemoFileUndoInfoTrash *self = NEMO_FILE_UNDO_INFO_TRASH (obj);
1252 g_hash_table_destroy (self->priv->trashed);
1253
1254 G_OBJECT_CLASS (nemo_file_undo_info_trash_parent_class)->finalize (obj);
1255 }
1256
1257 static void
nemo_file_undo_info_trash_class_init(NemoFileUndoInfoTrashClass * klass)1258 nemo_file_undo_info_trash_class_init (NemoFileUndoInfoTrashClass *klass)
1259 {
1260 GObjectClass *oclass = G_OBJECT_CLASS (klass);
1261 NemoFileUndoInfoClass *iclass = NEMO_FILE_UNDO_INFO_CLASS (klass);
1262
1263 oclass->finalize = nemo_file_undo_info_trash_finalize;
1264
1265 iclass->undo_func = trash_undo_func;
1266 iclass->redo_func = trash_redo_func;
1267 iclass->strings_func = trash_strings_func;
1268
1269 g_type_class_add_private (klass, sizeof (NemoFileUndoInfoTrashDetails));
1270 }
1271
1272 NemoFileUndoInfo *
nemo_file_undo_info_trash_new(gint item_count)1273 nemo_file_undo_info_trash_new (gint item_count)
1274 {
1275 return g_object_new (NEMO_TYPE_FILE_UNDO_INFO_TRASH,
1276 "op-type", NEMO_FILE_UNDO_OP_MOVE_TO_TRASH,
1277 "item-count", item_count,
1278 NULL);
1279 }
1280
1281 void
nemo_file_undo_info_trash_add_file(NemoFileUndoInfoTrash * self,GFile * file)1282 nemo_file_undo_info_trash_add_file (NemoFileUndoInfoTrash *self,
1283 GFile *file)
1284 {
1285 GTimeVal current_time;
1286 gsize orig_trash_time;
1287
1288 g_get_current_time (¤t_time);
1289 orig_trash_time = current_time.tv_sec;
1290
1291 g_hash_table_insert (self->priv->trashed, g_object_ref (file), GSIZE_TO_POINTER (orig_trash_time));
1292 }
1293
1294 /* recursive permissions */
1295 G_DEFINE_TYPE (NemoFileUndoInfoRecPermissions, nemo_file_undo_info_rec_permissions, NEMO_TYPE_FILE_UNDO_INFO)
1296
1297 struct _NemoFileUndoInfoRecPermissionsDetails {
1298 GFile *dest_dir;
1299 GHashTable *original_permissions;
1300 guint32 dir_mask;
1301 guint32 dir_permissions;
1302 guint32 file_mask;
1303 guint32 file_permissions;
1304 };
1305
1306 static void
rec_permissions_strings_func(NemoFileUndoInfo * info,gchar ** undo_label,gchar ** undo_description,gchar ** redo_label,gchar ** redo_description)1307 rec_permissions_strings_func (NemoFileUndoInfo *info,
1308 gchar **undo_label,
1309 gchar **undo_description,
1310 gchar **redo_label,
1311 gchar **redo_description)
1312 {
1313 NemoFileUndoInfoRecPermissions *self = NEMO_FILE_UNDO_INFO_REC_PERMISSIONS (info);
1314 char *name;
1315
1316 name = g_file_get_path (self->priv->dest_dir);
1317
1318 *undo_description = g_strdup_printf (_("Restore original permissions of items enclosed in '%s'"), name);
1319 *redo_description = g_strdup_printf (_("Set permissions of items enclosed in '%s'"), name);
1320
1321 *undo_label = g_strdup (_("_Undo Change Permissions"));
1322 *redo_label = g_strdup (_("_Redo Change Permissions"));
1323
1324 g_free (name);
1325 }
1326
1327 static void
rec_permissions_callback(gboolean success,gpointer callback_data)1328 rec_permissions_callback (gboolean success,
1329 gpointer callback_data)
1330 {
1331 file_undo_info_transfer_callback (NULL, success, callback_data);
1332 }
1333
1334 static void
rec_permissions_redo_func(NemoFileUndoInfo * info,GtkWindow * parent_window)1335 rec_permissions_redo_func (NemoFileUndoInfo *info,
1336 GtkWindow *parent_window)
1337 {
1338 NemoFileUndoInfoRecPermissions *self = NEMO_FILE_UNDO_INFO_REC_PERMISSIONS (info);
1339 gchar *parent_uri;
1340
1341 parent_uri = g_file_get_uri (self->priv->dest_dir);
1342 nemo_file_set_permissions_recursive (parent_uri,
1343 self->priv->file_permissions,
1344 self->priv->file_mask,
1345 self->priv->dir_permissions,
1346 self->priv->dir_mask,
1347 rec_permissions_callback, self);
1348 g_free (parent_uri);
1349 }
1350
1351 static void
rec_permissions_undo_func(NemoFileUndoInfo * info,GtkWindow * parent_window)1352 rec_permissions_undo_func (NemoFileUndoInfo *info,
1353 GtkWindow *parent_window)
1354 {
1355 NemoFileUndoInfoRecPermissions *self = NEMO_FILE_UNDO_INFO_REC_PERMISSIONS (info);
1356
1357 /* Internally managed op, pop flag. */
1358 /* TODO: why? */
1359 nemo_file_undo_manager_pop_flag ();
1360
1361 if (g_hash_table_size (self->priv->original_permissions) > 0) {
1362 GList *gfiles_list;
1363 guint32 perm;
1364 GList *l;
1365 GFile *dest;
1366 char *item;
1367
1368 gfiles_list = g_hash_table_get_keys (self->priv->original_permissions);
1369 for (l = gfiles_list; l != NULL; l = l->next) {
1370 item = l->data;
1371 perm = GPOINTER_TO_UINT (g_hash_table_lookup (self->priv->original_permissions, item));
1372 dest = g_file_new_for_uri (item);
1373 g_file_set_attribute_uint32 (dest,
1374 G_FILE_ATTRIBUTE_UNIX_MODE,
1375 perm, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL);
1376 g_object_unref (dest);
1377 }
1378
1379 g_list_free (gfiles_list);
1380 /* Here we must do what's necessary for the callback */
1381 file_undo_info_transfer_callback (NULL, TRUE, self);
1382 }
1383 }
1384
1385 static void
nemo_file_undo_info_rec_permissions_init(NemoFileUndoInfoRecPermissions * self)1386 nemo_file_undo_info_rec_permissions_init (NemoFileUndoInfoRecPermissions *self)
1387 {
1388 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, nemo_file_undo_info_rec_permissions_get_type (),
1389 NemoFileUndoInfoRecPermissionsDetails);
1390
1391 self->priv->original_permissions =
1392 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1393 }
1394
1395 static void
nemo_file_undo_info_rec_permissions_finalize(GObject * obj)1396 nemo_file_undo_info_rec_permissions_finalize (GObject *obj)
1397 {
1398 NemoFileUndoInfoRecPermissions *self = NEMO_FILE_UNDO_INFO_REC_PERMISSIONS (obj);
1399
1400 g_hash_table_destroy (self->priv->original_permissions);
1401 g_clear_object (&self->priv->dest_dir);
1402
1403 G_OBJECT_CLASS (nemo_file_undo_info_rec_permissions_parent_class)->finalize (obj);
1404 }
1405
1406 static void
nemo_file_undo_info_rec_permissions_class_init(NemoFileUndoInfoRecPermissionsClass * klass)1407 nemo_file_undo_info_rec_permissions_class_init (NemoFileUndoInfoRecPermissionsClass *klass)
1408 {
1409 GObjectClass *oclass = G_OBJECT_CLASS (klass);
1410 NemoFileUndoInfoClass *iclass = NEMO_FILE_UNDO_INFO_CLASS (klass);
1411
1412 oclass->finalize = nemo_file_undo_info_rec_permissions_finalize;
1413
1414 iclass->undo_func = rec_permissions_undo_func;
1415 iclass->redo_func = rec_permissions_redo_func;
1416 iclass->strings_func = rec_permissions_strings_func;
1417
1418 g_type_class_add_private (klass, sizeof (NemoFileUndoInfoRecPermissionsDetails));
1419 }
1420
1421 NemoFileUndoInfo *
nemo_file_undo_info_rec_permissions_new(GFile * dest,guint32 file_permissions,guint32 file_mask,guint32 dir_permissions,guint32 dir_mask)1422 nemo_file_undo_info_rec_permissions_new (GFile *dest,
1423 guint32 file_permissions,
1424 guint32 file_mask,
1425 guint32 dir_permissions,
1426 guint32 dir_mask)
1427 {
1428 NemoFileUndoInfoRecPermissions *retval;
1429
1430 retval = g_object_new (NEMO_TYPE_FILE_UNDO_INFO_REC_PERMISSIONS,
1431 "op-type", NEMO_FILE_UNDO_OP_RECURSIVE_SET_PERMISSIONS,
1432 "item-count", 1,
1433 NULL);
1434
1435 retval->priv->dest_dir = g_object_ref (dest);
1436 retval->priv->file_permissions = file_permissions;
1437 retval->priv->file_mask = file_mask;
1438 retval->priv->dir_permissions = dir_permissions;
1439 retval->priv->dir_mask = dir_mask;
1440
1441 return NEMO_FILE_UNDO_INFO (retval);
1442 }
1443
1444 void
nemo_file_undo_info_rec_permissions_add_file(NemoFileUndoInfoRecPermissions * self,GFile * file,guint32 permission)1445 nemo_file_undo_info_rec_permissions_add_file (NemoFileUndoInfoRecPermissions *self,
1446 GFile *file,
1447 guint32 permission)
1448 {
1449 gchar *original_uri = g_file_get_uri (file);
1450 g_hash_table_insert (self->priv->original_permissions, original_uri, GUINT_TO_POINTER (permission));
1451 }
1452
1453 /* single file change permissions */
1454 G_DEFINE_TYPE (NemoFileUndoInfoPermissions, nemo_file_undo_info_permissions, NEMO_TYPE_FILE_UNDO_INFO)
1455
1456 struct _NemoFileUndoInfoPermissionsDetails {
1457 GFile *target_file;
1458 guint32 current_permissions;
1459 guint32 new_permissions;
1460 };
1461
1462 static void
permissions_strings_func(NemoFileUndoInfo * info,gchar ** undo_label,gchar ** undo_description,gchar ** redo_label,gchar ** redo_description)1463 permissions_strings_func (NemoFileUndoInfo *info,
1464 gchar **undo_label,
1465 gchar **undo_description,
1466 gchar **redo_label,
1467 gchar **redo_description)
1468 {
1469 NemoFileUndoInfoPermissions *self = NEMO_FILE_UNDO_INFO_PERMISSIONS (info);
1470 gchar *name;
1471
1472 name = g_file_get_parse_name (self->priv->target_file);
1473 *undo_description = g_strdup_printf (_("Restore original permissions of '%s'"), name);
1474 *redo_description = g_strdup_printf (_("Set permissions of '%s'"), name);
1475
1476 *undo_label = g_strdup (_("_Undo Change Permissions"));
1477 *redo_label = g_strdup (_("_Redo Change Permissions"));
1478
1479 g_free (name);
1480 }
1481
1482 static void
permissions_real_func(NemoFileUndoInfoPermissions * self,guint32 permissions)1483 permissions_real_func (NemoFileUndoInfoPermissions *self,
1484 guint32 permissions)
1485 {
1486 NemoFile *file;
1487
1488 file = nemo_file_get (self->priv->target_file);
1489 nemo_file_set_permissions (file, permissions,
1490 file_undo_info_operation_callback, self);
1491
1492 nemo_file_unref (file);
1493 }
1494
1495 static void
permissions_redo_func(NemoFileUndoInfo * info,GtkWindow * parent_window)1496 permissions_redo_func (NemoFileUndoInfo *info,
1497 GtkWindow *parent_window)
1498 {
1499 NemoFileUndoInfoPermissions *self = NEMO_FILE_UNDO_INFO_PERMISSIONS (info);
1500 permissions_real_func (self, self->priv->new_permissions);
1501 }
1502
1503 static void
permissions_undo_func(NemoFileUndoInfo * info,GtkWindow * parent_window)1504 permissions_undo_func (NemoFileUndoInfo *info,
1505 GtkWindow *parent_window)
1506 {
1507 NemoFileUndoInfoPermissions *self = NEMO_FILE_UNDO_INFO_PERMISSIONS (info);
1508 permissions_real_func (self, self->priv->current_permissions);
1509 }
1510
1511 static void
nemo_file_undo_info_permissions_init(NemoFileUndoInfoPermissions * self)1512 nemo_file_undo_info_permissions_init (NemoFileUndoInfoPermissions *self)
1513 {
1514 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, nemo_file_undo_info_permissions_get_type (),
1515 NemoFileUndoInfoPermissionsDetails);
1516 }
1517
1518 static void
nemo_file_undo_info_permissions_finalize(GObject * obj)1519 nemo_file_undo_info_permissions_finalize (GObject *obj)
1520 {
1521 NemoFileUndoInfoPermissions *self = NEMO_FILE_UNDO_INFO_PERMISSIONS (obj);
1522 g_clear_object (&self->priv->target_file);
1523
1524 G_OBJECT_CLASS (nemo_file_undo_info_permissions_parent_class)->finalize (obj);
1525 }
1526
1527 static void
nemo_file_undo_info_permissions_class_init(NemoFileUndoInfoPermissionsClass * klass)1528 nemo_file_undo_info_permissions_class_init (NemoFileUndoInfoPermissionsClass *klass)
1529 {
1530 GObjectClass *oclass = G_OBJECT_CLASS (klass);
1531 NemoFileUndoInfoClass *iclass = NEMO_FILE_UNDO_INFO_CLASS (klass);
1532
1533 oclass->finalize = nemo_file_undo_info_permissions_finalize;
1534
1535 iclass->undo_func = permissions_undo_func;
1536 iclass->redo_func = permissions_redo_func;
1537 iclass->strings_func = permissions_strings_func;
1538
1539 g_type_class_add_private (klass, sizeof (NemoFileUndoInfoPermissionsDetails));
1540 }
1541
1542 NemoFileUndoInfo *
nemo_file_undo_info_permissions_new(GFile * file,guint32 current_permissions,guint32 new_permissions)1543 nemo_file_undo_info_permissions_new (GFile *file,
1544 guint32 current_permissions,
1545 guint32 new_permissions)
1546 {
1547 NemoFileUndoInfoPermissions *retval;
1548
1549 retval = g_object_new (NEMO_TYPE_FILE_UNDO_INFO_PERMISSIONS,
1550 "op-type", NEMO_FILE_UNDO_OP_SET_PERMISSIONS,
1551 "item-count", 1,
1552 NULL);
1553
1554 retval->priv->target_file = g_object_ref (file);
1555 retval->priv->current_permissions = current_permissions;
1556 retval->priv->new_permissions = new_permissions;
1557
1558 return NEMO_FILE_UNDO_INFO (retval);
1559 }
1560
1561 /* group and owner change */
1562 G_DEFINE_TYPE (NemoFileUndoInfoOwnership, nemo_file_undo_info_ownership, NEMO_TYPE_FILE_UNDO_INFO)
1563
1564 struct _NemoFileUndoInfoOwnershipDetails {
1565 GFile *target_file;
1566 char *original_ownership;
1567 char *new_ownership;
1568 };
1569
1570 static void
ownership_strings_func(NemoFileUndoInfo * info,gchar ** undo_label,gchar ** undo_description,gchar ** redo_label,gchar ** redo_description)1571 ownership_strings_func (NemoFileUndoInfo *info,
1572 gchar **undo_label,
1573 gchar **undo_description,
1574 gchar **redo_label,
1575 gchar **redo_description)
1576 {
1577 NemoFileUndoInfoOwnership *self = NEMO_FILE_UNDO_INFO_OWNERSHIP (info);
1578 NemoFileUndoOp op_type = nemo_file_undo_info_get_op_type (info);
1579 gchar *name;
1580
1581 name = g_file_get_parse_name (self->priv->target_file);
1582
1583 if (op_type == NEMO_FILE_UNDO_OP_CHANGE_GROUP) {
1584 *undo_description = g_strdup_printf (_("Restore group of '%s' to '%s'"),
1585 name, self->priv->original_ownership);
1586 *redo_description = g_strdup_printf (_("Set group of '%s' to '%s'"),
1587 name, self->priv->new_ownership);
1588
1589 *undo_label = g_strdup (_("_Undo Change Group"));
1590 *redo_label = g_strdup (_("_Redo Change Group"));
1591 } else if (op_type == NEMO_FILE_UNDO_OP_CHANGE_OWNER) {
1592 *undo_description = g_strdup_printf (_("Restore owner of '%s' to '%s'"),
1593 name, self->priv->original_ownership);
1594 *redo_description = g_strdup_printf (_("Set owner of '%s' to '%s'"),
1595 name, self->priv->new_ownership);
1596
1597 *undo_label = g_strdup (_("_Undo Change Owner"));
1598 *redo_label = g_strdup (_("_Redo Change Owner"));
1599 }
1600
1601 g_free (name);
1602 }
1603
1604 static void
ownership_real_func(NemoFileUndoInfoOwnership * self,const gchar * ownership)1605 ownership_real_func (NemoFileUndoInfoOwnership *self,
1606 const gchar *ownership)
1607 {
1608 NemoFileUndoOp op_type = nemo_file_undo_info_get_op_type (NEMO_FILE_UNDO_INFO (self));
1609 NemoFile *file;
1610
1611 file = nemo_file_get (self->priv->target_file);
1612
1613 if (op_type == NEMO_FILE_UNDO_OP_CHANGE_OWNER) {
1614 nemo_file_set_owner (file,
1615 ownership,
1616 file_undo_info_operation_callback, self);
1617 } else if (op_type == NEMO_FILE_UNDO_OP_CHANGE_GROUP) {
1618 nemo_file_set_group (file,
1619 ownership,
1620 file_undo_info_operation_callback, self);
1621 }
1622
1623 nemo_file_unref (file);
1624 }
1625
1626 static void
ownership_redo_func(NemoFileUndoInfo * info,GtkWindow * parent_window)1627 ownership_redo_func (NemoFileUndoInfo *info,
1628 GtkWindow *parent_window)
1629 {
1630 NemoFileUndoInfoOwnership *self = NEMO_FILE_UNDO_INFO_OWNERSHIP (info);
1631 ownership_real_func (self, self->priv->new_ownership);
1632 }
1633
1634 static void
ownership_undo_func(NemoFileUndoInfo * info,GtkWindow * parent_window)1635 ownership_undo_func (NemoFileUndoInfo *info,
1636 GtkWindow *parent_window)
1637 {
1638 NemoFileUndoInfoOwnership *self = NEMO_FILE_UNDO_INFO_OWNERSHIP (info);
1639 ownership_real_func (self, self->priv->original_ownership);
1640 }
1641
1642 static void
nemo_file_undo_info_ownership_init(NemoFileUndoInfoOwnership * self)1643 nemo_file_undo_info_ownership_init (NemoFileUndoInfoOwnership *self)
1644 {
1645 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, nemo_file_undo_info_ownership_get_type (),
1646 NemoFileUndoInfoOwnershipDetails);
1647 }
1648
1649 static void
nemo_file_undo_info_ownership_finalize(GObject * obj)1650 nemo_file_undo_info_ownership_finalize (GObject *obj)
1651 {
1652 NemoFileUndoInfoOwnership *self = NEMO_FILE_UNDO_INFO_OWNERSHIP (obj);
1653
1654 g_clear_object (&self->priv->target_file);
1655 g_free (self->priv->original_ownership);
1656 g_free (self->priv->new_ownership);
1657
1658 G_OBJECT_CLASS (nemo_file_undo_info_ownership_parent_class)->finalize (obj);
1659 }
1660
1661 static void
nemo_file_undo_info_ownership_class_init(NemoFileUndoInfoOwnershipClass * klass)1662 nemo_file_undo_info_ownership_class_init (NemoFileUndoInfoOwnershipClass *klass)
1663 {
1664 GObjectClass *oclass = G_OBJECT_CLASS (klass);
1665 NemoFileUndoInfoClass *iclass = NEMO_FILE_UNDO_INFO_CLASS (klass);
1666
1667 oclass->finalize = nemo_file_undo_info_ownership_finalize;
1668
1669 iclass->undo_func = ownership_undo_func;
1670 iclass->redo_func = ownership_redo_func;
1671 iclass->strings_func = ownership_strings_func;
1672
1673 g_type_class_add_private (klass, sizeof (NemoFileUndoInfoOwnershipDetails));
1674 }
1675
1676 NemoFileUndoInfo *
nemo_file_undo_info_ownership_new(NemoFileUndoOp op_type,GFile * file,const char * current_data,const char * new_data)1677 nemo_file_undo_info_ownership_new (NemoFileUndoOp op_type,
1678 GFile *file,
1679 const char *current_data,
1680 const char *new_data)
1681 {
1682 NemoFileUndoInfoOwnership *retval;
1683
1684 retval = g_object_new (NEMO_TYPE_FILE_UNDO_INFO_OWNERSHIP,
1685 "item-count", 1,
1686 "op-type", op_type,
1687 NULL);
1688
1689 retval->priv->target_file = g_object_ref (file);
1690 retval->priv->original_ownership = g_strdup (current_data);
1691 retval->priv->new_ownership = g_strdup (new_data);
1692
1693 return NEMO_FILE_UNDO_INFO (retval);
1694 }
1695