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 (&current_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 (&current_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