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