nautilus-3.6.3/libnautilus-private/nautilus-file-undo-operations.c

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