[gthumb/ext: 59/79] Implemented recursive folder copy and deletion



commit 055522da46dadfb1337b818794251eee1c06139a
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Fri Jul 24 10:28:23 2009 +0200

    Implemented recursive folder copy and deletion
    
    Ask confirmation when trying to delete a non-empty folder.

 extensions/catalogs/gth-file-source-catalogs.c |   12 +-
 extensions/file_manager/Makefile.am            |    2 +
 extensions/file_manager/actions.c              |   86 +++-
 extensions/file_manager/gth-delete-task.c      |  152 ++++++
 extensions/file_manager/gth-delete-task.h      |   56 +++
 gthumb/file-cache.c                            |   24 +-
 gthumb/file-cache.h                            |    7 +-
 gthumb/gio-utils.c                             |  614 +++++++++++++++---------
 gthumb/gio-utils.h                             |   26 +-
 gthumb/glib-utils.c                            |   10 +
 gthumb/glib-utils.h                            |    4 +-
 gthumb/gth-metadata-provider.c                 |   15 +-
 12 files changed, 713 insertions(+), 295 deletions(-)
---
diff --git a/extensions/catalogs/gth-file-source-catalogs.c b/extensions/catalogs/gth-file-source-catalogs.c
index 01a216f..7e2c4a3 100644
--- a/extensions/catalogs/gth-file-source-catalogs.c
+++ b/extensions/catalogs/gth-file-source-catalogs.c
@@ -441,11 +441,13 @@ gth_file_source_catalogs_copy (GthFileSource    *file_source,
 		g_free (message);
 	}
 
-	g_query_info_async (cod->file_list,
-			    G_FILE_ATTRIBUTE_STANDARD_TYPE,
-			    gth_file_source_get_cancellable (file_source),
-			    copy__file_list_info_ready_cb,
-			    cod);
+	_g_query_info_async (cod->file_list,
+			     FALSE,
+			     TRUE,
+			     GFILE_NAME_TYPE_ATTRIBUTES,
+			     gth_file_source_get_cancellable (file_source),
+			     copy__file_list_info_ready_cb,
+			     cod);
 }
 
 
diff --git a/extensions/file_manager/Makefile.am b/extensions/file_manager/Makefile.am
index dc81cec..3d01974 100644
--- a/extensions/file_manager/Makefile.am
+++ b/extensions/file_manager/Makefile.am
@@ -8,6 +8,8 @@ libfile_manager_la_SOURCES = 		\
 	callbacks.h			\
 	gth-copy-task.c			\
 	gth-copy-task.h			\
+	gth-delete-task.c		\
+	gth-delete-task.h		\
 	gth-duplicate-task.c		\
 	gth-duplicate-task.h		\
 	main.c
diff --git a/extensions/file_manager/actions.c b/extensions/file_manager/actions.c
index 10bce2b..62a233f 100644
--- a/extensions/file_manager/actions.c
+++ b/extensions/file_manager/actions.c
@@ -25,6 +25,7 @@
 #include <glib/gi18n.h>
 #include <gthumb.h>
 #include "gth-copy-task.h"
+#include "gth-delete-task.h"
 #include "gth-duplicate-task.h"
 
 
@@ -609,16 +610,54 @@ gth_browser_activate_action_folder_paste (GtkAction  *action,
 /* -- gth_browser_activate_action_folder_trash -- */
 
 
+typedef struct {
+	GthBrowser  *browser;
+	GthFileData *file_data;
+} DeleteFolderData;
+
+
 static void
-delete_folder_permanently (GtkWindow   *window,
-			   GthFileData *file_data)
+delete_data_free (DeleteFolderData *delete_data)
 {
-	GList  *files;
-	GError *error = NULL;
+	_g_object_unref (delete_data->browser);
+	_g_object_unref (delete_data->file_data);
+	g_free (delete_data);
+}
+
+
+static void
+delete_folder_permanently (GtkWindow        *window,
+			   DeleteFolderData *delete_data)
+{
+	GthFileData *file_data = delete_data->file_data;
+	GError      *error = NULL;
+	GList       *files;
 
 	files = g_list_prepend (NULL, file_data->file);
 	if (! _g_delete_files (files, TRUE, &error)) {
-		_gtk_error_dialog_from_gerror_show (window, _("Could not delete the folder"), &error);
+		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_EMPTY)) {
+			GtkWidget *d;
+			int        response;
+
+			d = _gtk_yesno_dialog_new (GTK_WINDOW (delete_data->browser),
+					           GTK_DIALOG_MODAL,
+					           _("The folder is not empty, do you want to delete the folder and its content permanently?"),
+					           GTK_STOCK_CANCEL,
+					           GTK_STOCK_DELETE);
+			response = gtk_dialog_run (GTK_DIALOG (d));
+			if (response == GTK_RESPONSE_YES) {
+				GthTask *task;
+
+				task = gth_delete_task_new (files);
+				gth_browser_exec_task (delete_data->browser, task, FALSE);
+
+				g_object_unref (task);
+			}
+
+			gtk_widget_destroy (d);
+		}
+		else
+			_gtk_error_dialog_from_gerror_show (window, _("Could not delete the folder"), &error);
 	}
 	else {
 		GFile *parent;
@@ -633,6 +672,7 @@ delete_folder_permanently (GtkWindow   *window,
 	}
 
 	g_list_free (files);
+	delete_data_free (delete_data);
 }
 
 
@@ -641,13 +681,14 @@ delete_folder_permanently_response_cb (GtkDialog *dialog,
 				       int        response_id,
 				       gpointer   user_data)
 {
-	GthFileData *file_data = user_data;
-
-	if (response_id == GTK_RESPONSE_YES)
-		delete_folder_permanently (gtk_window_get_transient_for (GTK_WINDOW (dialog)), file_data);
+	DeleteFolderData *delete_data = user_data;
 
 	gtk_widget_destroy (GTK_WIDGET (dialog));
-	_g_object_unref (file_data);
+
+	if (response_id == GTK_RESPONSE_YES)
+		delete_folder_permanently (GTK_WINDOW (delete_data->browser), delete_data);
+	else
+		delete_data_free (delete_data);
 }
 
 
@@ -664,19 +705,22 @@ gth_browser_activate_action_folder_trash (GtkAction  *action,
 
 	if (! g_file_trash (file_data->file, NULL, &error)) {
 		if (g_error_matches (error, G_IO_ERROR,  G_IO_ERROR_NOT_SUPPORTED)) {
-			GtkWidget *d;
+			DeleteFolderData *delete_data;
+			GtkWidget       *d;
 
 			g_clear_error (&error);
 
+			delete_data = g_new0 (DeleteFolderData, 1);
+			delete_data->browser = g_object_ref (browser);
+			delete_data->file_data = g_object_ref (file_data);
+
 			d = _gtk_yesno_dialog_new (GTK_WINDOW (browser),
 						   GTK_DIALOG_MODAL,
 						   _("The folder cannot be moved to the Trash. Do you want to delete it permanently?"),
 						   GTK_STOCK_CANCEL,
 						   GTK_STOCK_DELETE);
-			g_signal_connect (d, "response", G_CALLBACK (delete_folder_permanently_response_cb), file_data);
+			g_signal_connect (d, "response", G_CALLBACK (delete_folder_permanently_response_cb), delete_data);
 			gtk_widget_show (d);
-
-			file_data = NULL;
 		}
 		else
 			_gtk_error_dialog_from_gerror_show (GTK_WINDOW (browser), _("Could not move the folder to the Trash"), &error);
@@ -704,15 +748,21 @@ void
 gth_browser_activate_action_folder_delete (GtkAction  *action,
 					   GthBrowser *browser)
 {
-	GthFileData *file_data;
-	char        *prompt;
-	GtkWidget   *d;
+	GthFileData      *file_data;
+	char             *prompt;
+	DeleteFolderData *delete_data;
+	GtkWidget        *d;
 
 	file_data = gth_folder_tree_get_selected (GTH_FOLDER_TREE (gth_browser_get_folder_tree (browser)));
 	if (file_data == NULL)
 		return;
 
 	prompt = g_strdup_printf (_("Are you sure you want to permanently delete \"%s\"?"), g_file_info_get_display_name (file_data->info));
+
+	delete_data = g_new0 (DeleteFolderData, 1);
+	delete_data->browser = g_object_ref (browser);
+	delete_data->file_data = g_object_ref (file_data);
+
 	d = _gtk_message_dialog_new (GTK_WINDOW (browser),
 				     GTK_DIALOG_MODAL,
 				     GTK_STOCK_DIALOG_QUESTION,
@@ -721,7 +771,7 @@ gth_browser_activate_action_folder_delete (GtkAction  *action,
 				     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 				     GTK_STOCK_DELETE, GTK_RESPONSE_YES,
 				     NULL);
-	g_signal_connect (d, "response", G_CALLBACK (delete_folder_permanently_response_cb), file_data);
+	g_signal_connect (d, "response", G_CALLBACK (delete_folder_permanently_response_cb), delete_data);
 	gtk_widget_show (d);
 
 	g_free (prompt);
diff --git a/extensions/file_manager/gth-delete-task.c b/extensions/file_manager/gth-delete-task.c
new file mode 100644
index 0000000..f35d856
--- /dev/null
+++ b/extensions/file_manager/gth-delete-task.c
@@ -0,0 +1,152 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include "gth-delete-task.h"
+
+
+struct _GthDeleteTaskPrivate {
+	GCancellable *cancellable;
+	GList        *file_list;
+};
+
+
+static gpointer parent_class = NULL;
+
+
+static void
+gth_delete_task_finalize (GObject *object)
+{
+	GthDeleteTask *self;
+
+	self = GTH_DELETE_TASK (object);
+
+	_g_object_list_unref (self->priv->file_list);
+	_g_object_unref (self->priv->cancellable);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+delete_ready_cb (GError   *error,
+		 gpointer  user_data)
+{
+	gth_task_completed (GTH_TASK (user_data), error);
+}
+
+
+static void
+gth_delete_task_exec (GthTask *task)
+{
+	GthDeleteTask *self;
+
+	self = GTH_DELETE_TASK (task);
+
+	gth_task_progress (task, _("Deleting files"), NULL, TRUE, 0.0);
+
+	_g_delete_files_async (self->priv->file_list,
+			       TRUE,
+			       TRUE,
+			       self->priv->cancellable,
+			       delete_ready_cb,
+			       self);
+}
+
+
+static void
+gth_delete_task_cancel (GthTask *task)
+{
+	GthDeleteTask *self;
+
+	g_return_if_fail (GTH_IS_DELETE_TASK (task));
+
+	self = GTH_DELETE_TASK (task);
+
+	g_cancellable_cancel (self->priv->cancellable);
+}
+
+
+static void
+gth_delete_task_class_init (GthDeleteTaskClass *klass)
+{
+	GObjectClass *object_class;
+	GthTaskClass *task_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (GthDeleteTaskPrivate));
+
+	object_class = G_OBJECT_CLASS (klass);
+	object_class->finalize = gth_delete_task_finalize;
+
+	task_class = GTH_TASK_CLASS (klass);
+	task_class->exec = gth_delete_task_exec;
+	task_class->cancel = gth_delete_task_cancel;
+}
+
+
+static void
+gth_delete_task_init (GthDeleteTask *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_DELETE_TASK, GthDeleteTaskPrivate);
+	self->priv->cancellable = g_cancellable_new ();
+}
+
+
+GType
+gth_delete_task_get_type (void)
+{
+	static GType type = 0;
+
+	if (! type) {
+		GTypeInfo type_info = {
+			sizeof (GthDeleteTaskClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) gth_delete_task_class_init,
+			NULL,
+			NULL,
+			sizeof (GthDeleteTask),
+			0,
+			(GInstanceInitFunc) gth_delete_task_init
+		};
+
+		type = g_type_register_static (GTH_TYPE_TASK,
+					       "GthDeleteTask",
+					       &type_info,
+					       0);
+	}
+
+	return type;
+}
+
+
+GthTask *
+gth_delete_task_new (GList *file_list)
+{
+	GthDeleteTask *self;
+
+	self = GTH_DELETE_TASK (g_object_new (GTH_TYPE_DELETE_TASK, NULL));
+	self->priv->file_list = _g_object_list_ref (file_list);
+
+	return (GthTask *) self;
+}
diff --git a/extensions/file_manager/gth-delete-task.h b/extensions/file_manager/gth-delete-task.h
new file mode 100644
index 0000000..c101c22
--- /dev/null
+++ b/extensions/file_manager/gth-delete-task.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009 The Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GTH_DELETE_TASK_H
+#define GTH_DELETE_TASK_H
+
+#include <glib.h>
+#include <gthumb.h>
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_DELETE_TASK            (gth_delete_task_get_type ())
+#define GTH_DELETE_TASK(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_DELETE_TASK, GthDeleteTask))
+#define GTH_DELETE_TASK_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_DELETE_TASK, GthDeleteTaskClass))
+#define GTH_IS_DELETE_TASK(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_DELETE_TASK))
+#define GTH_IS_DELETE_TASK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_DELETE_TASK))
+#define GTH_DELETE_TASK_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GTH_TYPE_DELETE_TASK, GthDeleteTaskClass))
+
+typedef struct _GthDeleteTask        GthDeleteTask;
+typedef struct _GthDeleteTaskClass   GthDeleteTaskClass;
+typedef struct _GthDeleteTaskPrivate GthDeleteTaskPrivate;
+
+struct _GthDeleteTask {
+	GthTask __parent;
+	GthDeleteTaskPrivate *priv;
+};
+
+struct _GthDeleteTaskClass {
+	GthTaskClass __parent;
+};
+
+GType         gth_delete_task_get_type     (void);
+GthTask *     gth_delete_task_new          (GList *file_list);
+
+G_END_DECLS
+
+#endif /* GTH_DELETE_TASK_H */
diff --git a/gthumb/file-cache.c b/gthumb/file-cache.c
index bb48cf9..afe800e 100644
--- a/gthumb/file-cache.c
+++ b/gthumb/file-cache.c
@@ -214,10 +214,10 @@ check_cache_free_space (void)
 
 
 typedef struct {
-	CopyDoneCallback  done_func;
-	gpointer          done_data;
-	GFile            *cache_file;
-	gboolean          dummy;
+	ReadyFunc  done_func;
+	gpointer   done_data;
+	GFile     *cache_file;
+	gboolean   dummy;
 } CopyToCacheData;
 
 
@@ -241,10 +241,10 @@ copy_remote_file_to_cache_ready (GError   *error,
 
 
 void
-copy_remote_file_to_cache (GthFileData      *file_data,
-			   GCancellable     *cancellable,
-			   CopyDoneCallback  done_func,
-			   gpointer          done_data)
+copy_remote_file_to_cache (GthFileData  *file_data,
+			   GCancellable *cancellable,
+			   ReadyFunc     done_func,
+			   gpointer      done_data)
 {
 	CopyToCacheData *copy_data;
 	GFile           *cache_file;
@@ -310,10 +310,10 @@ obtain_local_file (GthFileData *file_data)
 
 
 void
-update_file_from_cache (GthFileData      *file_data,
-			GCancellable     *cancellable,
-			CopyDoneCallback  done_func,
-			gpointer          done_data)
+update_file_from_cache (GthFileData  *file_data,
+			GCancellable *cancellable,
+			ReadyFunc     done_func,
+			gpointer      done_data)
 {
 	GFile    *cache_file;
 	GTimeVal  cache_mtime;
diff --git a/gthumb/file-cache.h b/gthumb/file-cache.h
index 5ac8508..9dee1fa 100644
--- a/gthumb/file-cache.h
+++ b/gthumb/file-cache.h
@@ -27,22 +27,23 @@
 #include <gio/gio.h>
 #include "gth-file-data.h"
 #include "gio-utils.h"
+#include "typedefs.h"
 
 G_BEGIN_DECLS
 
-char *   get_file_cache_full_path  (const char       *filename, 
+char *   get_file_cache_full_path  (const char       *filename,
 		                    const char       *extension);
 GFile *  _g_file_get_cache_file    (GFile            *file);
 void     free_file_cache           (void);
 void     check_cache_free_space    (void);
 void     copy_remote_file_to_cache (GthFileData      *file_data,
 				    GCancellable     *cancellable,
-				    CopyDoneCallback  done_func,
+				    ReadyFunc         done_func,
 				    gpointer          done_data);
 GFile *  obtain_local_file         (GthFileData      *file_data);
 void     update_file_from_cache    (GthFileData      *file_data,
 				    GCancellable     *cancellable,
-			  	    CopyDoneCallback  done_func,
+			  	    ReadyFunc         done_func,
 				    gpointer          done_data);
 
 G_END_DECLS
diff --git a/gthumb/gio-utils.c b/gthumb/gio-utils.c
index 42dad45..15c580f 100644
--- a/gthumb/gio-utils.c
+++ b/gthumb/gio-utils.c
@@ -153,10 +153,8 @@ child_data_free (ChildData *data)
 {
 	if (data == NULL)
 		return;
-	if (data->file != NULL)
-		g_object_unref (data->file);
-	if (data->info != NULL)
-		g_object_unref (data->info);
+	_g_object_unref (data->file);
+	_g_object_unref (data->info);
 	g_free (data);
 }
 
@@ -176,7 +174,7 @@ typedef struct {
 	gboolean              follow_links;
 	StartDirCallback      start_dir_func;
 	ForEachChildCallback  for_each_file_func;
-	ForEachDoneCallback   done_func;
+	ReadyFunc             done_func;
 	gpointer              user_data;
 
 	/* private */
@@ -206,6 +204,7 @@ for_each_child_data_free (ForEachChildData *fec)
 		g_list_foreach (fec->to_visit, (GFunc) child_data_free, NULL);
 		g_list_free (fec->to_visit);
 	}
+	_g_object_unref (fec->cancellable);
 	g_free (fec);
 }
 
@@ -466,7 +465,7 @@ g_directory_foreach_child (GFile                *directory,
 			   GCancellable         *cancellable,
 			   StartDirCallback      start_dir_func,
 			   ForEachChildCallback  for_each_file_func,
-			   ForEachDoneCallback   done_func,
+			   ReadyFunc             done_func,
 			   gpointer              user_data)
 {
 	ForEachChildData *fec;
@@ -479,7 +478,7 @@ g_directory_foreach_child (GFile                *directory,
 	fec->recursive = recursive;
 	fec->follow_links = follow_links;
 	fec->attributes = attributes;
-	fec->cancellable = cancellable;
+	fec->cancellable = _g_object_ref (cancellable);
 	fec->start_dir_func = start_dir_func;
 	fec->for_each_file_func = for_each_file_func;
 	fec->done_func = done_func;
@@ -493,7 +492,7 @@ g_directory_foreach_child (GFile                *directory,
 				 fec->attributes,
 				 G_FILE_QUERY_INFO_NONE,
 				 G_PRIORITY_DEFAULT,
-				 cancellable,
+				 fec->cancellable,
 				 directory_info_ready_cb,
 				 fec);
 }
@@ -782,92 +781,164 @@ g_directory_list_async (GFile             *directory,
 }
 
 
-/* -- g_query_info_async -- */
+/* -- _g_query_info_async -- */
 
 
 typedef struct {
-	GList             *files;
-	GList             *current;
-	const char        *attributes;
+	GList             *file_list;
+	gboolean           recursive;
+	gboolean           follow_links;
+	char              *attributes;
 	GCancellable      *cancellable;
-	InfoReadyCallback  ready_func;
+	InfoReadyCallback  callback;
 	gpointer           user_data;
-	GList             *file_data;
+	GList             *current;
+	GList             *files;
 } QueryInfoData;
 
 
 static void
 query_data_free (QueryInfoData *query_data)
 {
+	_g_object_list_unref (query_data->file_list);
 	_g_object_list_unref (query_data->files);
+	_g_object_unref (query_data->cancellable);
+	g_free (query_data->attributes);
 	g_free (query_data);
 }
 
 
+static void query_info__query_current (QueryInfoData *query_data);
+
+
 static void
-query_info_ready_cb (GObject      *source_object,
-		     GAsyncResult *result,
-		     gpointer      user_data)
+query_info__query_next (QueryInfoData *query_data)
+{
+	query_data->current = query_data->current->next;
+	query_info__query_current (query_data);
+}
+
+
+static void
+query_data__done_cb (GError   *error,
+		     gpointer  user_data)
+{
+	QueryInfoData *query_data = user_data;
+
+	if (error != NULL) {
+		query_data->callback (NULL, error, query_data->user_data);
+		query_data_free (query_data);
+		return;
+	}
+
+	query_info__query_next (query_data);
+}
+
+
+static void
+query_data__for_each_file_cb (GFile     *file,
+			      GFileInfo *info,
+			      gpointer   user_data)
+{
+	QueryInfoData *query_data = user_data;
+
+	if (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
+		query_data->files = g_list_prepend (query_data->files, gth_file_data_new (file, info));
+}
+
+
+static DirOp
+query_data__start_dir_cb (GFile       *directory,
+		          GFileInfo   *info,
+		          GError     **error,
+		          gpointer     user_data)
+{
+	QueryInfoData *query_data = user_data;
+
+	query_data->files = g_list_prepend (query_data->files, gth_file_data_new (directory, info));
+
+	return DIR_OP_CONTINUE;
+}
+
+
+static void
+query_data_info_ready_cb (GObject      *source_object,
+                          GAsyncResult *result,
+                          gpointer      user_data)
 {
 	QueryInfoData *query_data = user_data;
-	GFile         *file;
-	GFileInfo     *info;
 	GError        *error = NULL;
+	GFileInfo     *info;
 
-	file = (GFile*) source_object;
-	info = g_file_query_info_finish (file, result, &error);
+	info = g_file_query_info_finish ((GFile *) source_object, result, &error);
 	if (info == NULL) {
-		query_data->ready_func (NULL, error, query_data->user_data);
+		query_data->callback (NULL, error, query_data->user_data);
 		query_data_free (query_data);
 		return;
 	}
 
-	query_data->file_data = g_list_prepend (query_data->file_data, gth_file_data_new (file, info));
+	if (query_data->recursive && (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)) {
+		g_directory_foreach_child ((GFile *) query_data->current->data,
+					   TRUE,
+					   query_data->follow_links,
+					   query_data->attributes,
+					   query_data->cancellable,
+					   query_data__start_dir_cb,
+					   query_data__for_each_file_cb,
+					   query_data__done_cb,
+					   query_data);
+	}
+	else {
+		query_data->files = g_list_prepend (query_data->files, gth_file_data_new ((GFile *) query_data->current->data, info));
+		query_info__query_next (query_data);
+	}
+
 	g_object_unref (info);
+}
 
-	query_data->current = query_data->current->next;
+
+static void
+query_info__query_current (QueryInfoData *query_data)
+{
 	if (query_data->current == NULL) {
-		query_data->file_data = g_list_reverse (query_data->file_data);
-		query_data->ready_func (query_data->file_data, NULL, query_data->user_data);
+		query_data->files = g_list_reverse (query_data->files);
+		query_data->callback (query_data->files, NULL, query_data->user_data);
 		query_data_free (query_data);
 		return;
 	}
 
-	g_file_query_info_async ((GFile*) query_data->current->data,
+	g_file_query_info_async ((GFile *) query_data->current->data,
 				 query_data->attributes,
-				 0,
+				 (query_data->follow_links ? G_FILE_QUERY_INFO_NONE : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS),
 				 G_PRIORITY_DEFAULT,
 				 query_data->cancellable,
-				 query_info_ready_cb,
+				 query_data_info_ready_cb,
 				 query_data);
 }
 
 
 void
-g_query_info_async (GList             *files,
-		    const char        *attributes,
-		    GCancellable      *cancellable,
-		    InfoReadyCallback  ready_func,
-		    gpointer           user_data)
+_g_query_info_async (GList             *file_list,
+		     gboolean           recursive,
+		     gboolean           follow_links,
+		     const char        *attributes,
+		     GCancellable      *cancellable,
+		     InfoReadyCallback  ready_callback,
+		     gpointer           user_data)
 {
 	QueryInfoData *query_data;
 
 	query_data = g_new0 (QueryInfoData, 1);
-	query_data->files = _g_object_list_ref (files);
-	query_data->attributes = attributes;
-	query_data->cancellable = cancellable;
-	query_data->ready_func = ready_func;
+	query_data->file_list = _g_object_list_ref (file_list);
+	query_data->recursive = recursive;
+	query_data->follow_links = follow_links;
+	query_data->attributes = g_strdup (attributes);
+	query_data->cancellable = _g_object_ref (cancellable);
+	query_data->callback = ready_callback;
 	query_data->user_data = user_data;
 
-	query_data->current = query_data->files;
-
-	g_file_query_info_async ((GFile*) query_data->current->data,
-				 query_data->attributes,
-				 0,
-				 G_PRIORITY_DEFAULT,
-				 query_data->cancellable,
-				 query_info_ready_cb,
-				 query_data);
+	query_data->current = query_data->file_list;
+	query_info__query_current (query_data);
 }
 
 
@@ -875,9 +946,9 @@ g_query_info_async (GList             *files,
 
 
 typedef struct {
-	CopyDoneCallback callback;
-	gpointer         user_data;
-	gulong           dummy_event;
+	ReadyFunc callback;
+	gpointer  user_data;
+	gulong    dummy_event;
 } DummyFileCopy;
 
 
@@ -901,8 +972,8 @@ _g_dummy_file_op_completed (gpointer data)
 
 
 void
-_g_dummy_file_op_async (CopyDoneCallback callback,
-			gpointer         user_data)
+_g_dummy_file_op_async (ReadyFunc callback,
+			gpointer  user_data)
 {
 	DummyFileCopy *dfd;
 
@@ -917,25 +988,25 @@ _g_dummy_file_op_async (CopyDoneCallback callback,
 
 
 typedef struct {
+	GHashTable        *source_hash;
 	GFile             *destination;
 
-	GList             *dirs;  /* GFile list */
-	GList             *current_dir;
-
-	GList             *sources;  /* GFile list */
-	GList             *destinations;  /* GFile list */
-	GList             *current_source;
-	GList             *current_destination;
+	GList             *files;  /* GthFileData list */
+	GList             *current;
+	GFile             *source_base;
+	GFile             *current_destination;
 
 	GList             *source_sidecars;  /* GFile list */
 	GList             *destination_sidecars;  /* GFile list */
 	GList             *current_source_sidecar;
 	GList             *current_destination_sidecar;
 
-	GList             *current_directory_childs;  /* GFile list */
+	goffset            tot_size;
+	goffset            copied_size;
+	gsize              tot_files;
+	gsize              copied_files;
 
-	int                n_files;
-	int                n_current;
+	char              *message;
 
 	gboolean           move;
 	GFileCopyFlags     flags;
@@ -943,7 +1014,7 @@ typedef struct {
 	GCancellable      *cancellable;
 	ProgressCallback   progress_callback;
 	gpointer           progress_callback_data;
-	CopyDoneCallback   done_callback;
+	ReadyFunc          done_callback;
 	gpointer           user_data;
 } CopyData;
 
@@ -951,114 +1022,90 @@ typedef struct {
 static void
 copy_data_free (CopyData *copy_data)
 {
-	_g_object_list_unref (copy_data->current_directory_childs);
+	g_free (copy_data->message);
 	_g_object_list_unref (copy_data->destination_sidecars);
 	_g_object_list_unref (copy_data->source_sidecars);
-	_g_object_list_unref (copy_data->destinations);
-	_g_object_list_unref (copy_data->sources);
-	_g_object_list_unref (copy_data->dirs);
+	_g_object_unref (copy_data->current_destination);
+	_g_object_list_unref (copy_data->files);
+	_g_object_unref (copy_data->source_base);
+	g_hash_table_destroy (copy_data->source_hash);
 	g_object_unref (copy_data->destination);
 	_g_object_unref (copy_data->cancellable);
 	g_free (copy_data);
 }
 
 
-static void copy_data__copy_current_directory_to_destination (CopyData *copy_data);
+static GFile *
+get_destination_file (GFile *source,
+		      GFile *source_base,
+		      GFile *destination_folder)
+{
+	char       *source_uri;
+	const char *source_suffix;
+	char       *destination_folder_uri;
+	char       *destination_uri;
+	GFile      *destination;
 
+	source_uri = g_file_get_uri (source);
+	if (source_base != NULL) {
+		char *source_base_uri;
 
-static void
-copy_data__copy_current_directory__done_cb (GError   *error,
-				            gpointer  user_data)
-{
-	CopyData *copy_data = user_data;
+		source_base_uri = g_file_get_uri (source_base);
+		source_suffix = source_uri + strlen (source_base_uri);
 
-	if (error != NULL) {
-		copy_data->done_callback (error, copy_data->user_data);
-		copy_data_free (copy_data);
-		return;
+		g_free (source_base_uri);
 	}
+	else
+		source_suffix = _g_uri_get_basename (source_uri);
 
-	if (copy_data->move && ! g_file_delete ((GFile *) copy_data->current_dir->data, copy_data->cancellable, &error)) {
-		copy_data->done_callback (error, copy_data->user_data);
-		copy_data_free (copy_data);
-		return;
-	}
+	destination_folder_uri = g_file_get_uri (destination_folder);
+	destination_uri = g_strconcat (destination_folder_uri, "/", source_suffix, NULL);
+	destination = g_file_new_for_uri (destination_uri);
+
+	g_free (destination_uri);
+	g_free (destination_folder_uri);
+	g_free (source_uri);
 
-	copy_data->current_dir = copy_data->current_dir->next;
-	copy_data__copy_current_directory_to_destination (copy_data);
+	return destination;
 }
 
 
 static void
-copy_data__copy_current_directory__childs_done_cb (GError   *error,
-				                   gpointer  user_data)
+copy_data__delete_source (CopyData *copy_data)
 {
-	CopyData *copy_data = user_data;
-	GFile    *current_directory;
-	char     *name;
-	GFile    *destination;
+	GError *error = NULL;
+	GList  *scan;
 
-	if (error != NULL) {
-		copy_data->done_callback (error, copy_data->user_data);
+	if (! copy_data->move) {
+		copy_data->done_callback (NULL, copy_data->user_data);
 		copy_data_free (copy_data);
 		return;
 	}
 
-	current_directory = (GFile *) copy_data->current_dir->data;
-	name = g_file_get_basename (current_directory);
-	destination = g_file_get_child (copy_data->destination, name);
+	copy_data->files = g_list_reverse (copy_data->files);
+	for (scan = copy_data->files; scan; scan = scan->next) {
+		GthFileData *file_data = scan->data;
 
-	if (! g_file_make_directory (destination, copy_data->cancellable, &error)) {
-		copy_data->done_callback (error, copy_data->user_data);
-		copy_data_free (copy_data);
-		return;
+		if (! g_file_delete (file_data->file, copy_data->cancellable, &error))
+			break;
 	}
 
-	_g_copy_files_async (copy_data->current_directory_childs,
-			     destination,
-			     copy_data->move,
-			     copy_data->flags,
-			     copy_data->io_priority,
-			     copy_data->cancellable,
-			     copy_data->progress_callback,
-			     copy_data->progress_callback_data,
-			     copy_data__copy_current_directory__done_cb,
-			     copy_data);
-
-	g_object_unref (destination);
+	copy_data->done_callback (error, copy_data->user_data);
+	copy_data_free (copy_data);
 }
 
 
-static void
-copy_data__copy_current_directory__for_each_file_cb (GFile     *file,
-						     GFileInfo *info,
-						     gpointer   user_data)
-{
-	CopyData *copy_data = user_data;
-	copy_data->current_directory_childs = g_list_prepend (copy_data->current_directory_childs, g_object_ref (file));
-}
+static void copy_data__copy_current_file (CopyData *copy_data);
 
 
 static void
-copy_data__copy_current_directory_to_destination (CopyData *copy_data)
+copy_data__copy_next_file (CopyData *copy_data)
 {
-	if (copy_data->current_dir == NULL) {
-		copy_data->done_callback (NULL, copy_data->user_data);
-		copy_data_free (copy_data);
-		return;
-	}
+	GthFileData *source = (GthFileData *) copy_data->current->data;
 
-	_g_object_list_unref (copy_data->current_directory_childs);
-	copy_data->current_directory_childs = NULL;
-	g_directory_foreach_child ((GFile *) copy_data->current_dir->data,
-				   FALSE,
-				   TRUE,
-				   "standard::name,standard::type",
-				   copy_data->cancellable,
-				   NULL,
-				   copy_data__copy_current_directory__for_each_file_cb,
-				   copy_data__copy_current_directory__childs_done_cb,
-				   copy_data);
+	copy_data->copied_size += g_file_info_get_size (source->info);
+	copy_data->current = copy_data->current->next;
+	copy_data__copy_current_file (copy_data);
 }
 
 
@@ -1066,6 +1113,15 @@ static void copy_data__copy_current_sidecar (CopyData *copy_data);
 
 
 static void
+copy_data__copy_next_sidecar (CopyData *copy_data)
+{
+	copy_data->current_source_sidecar = copy_data->current_source_sidecar->next;
+	copy_data->current_destination_sidecar = copy_data->current_destination_sidecar->next;
+	copy_data__copy_current_sidecar (copy_data);
+}
+
+
+static void
 copy_data__copy_current_sidecar_ready_cb (GObject      *source_object,
 				          GAsyncResult *result,
 				          gpointer      user_data)
@@ -1077,30 +1133,32 @@ copy_data__copy_current_sidecar_ready_cb (GObject      *source_object,
 			g_file_delete ((GFile *) copy_data->current_source_sidecar->data, copy_data->cancellable, NULL);
 	}
 
-	copy_data->current_source_sidecar = copy_data->current_source_sidecar->next;
-	copy_data->current_destination_sidecar = copy_data->current_destination_sidecar->next;
-	copy_data__copy_current_sidecar (copy_data);
+	copy_data__copy_next_sidecar (copy_data);
 }
 
 
-static void copy_data__copy_current_file (CopyData *copy_data);
-
-
 static void
 copy_data__copy_current_sidecar (CopyData *copy_data)
 {
 	GFile *source;
 	GFile *destination;
+	GFile *destination_parent;
 
 	if (copy_data->current_source_sidecar == NULL) {
-		copy_data->current_source = copy_data->current_source->next;
-		copy_data->current_destination = copy_data->current_destination->next;
-		copy_data__copy_current_file (copy_data);
+		copy_data__copy_next_file (copy_data);
 		return;
 	}
 
 	source = copy_data->current_source_sidecar->data;
+	if (! g_file_query_exists (source, copy_data->cancellable)) {
+		copy_data__copy_next_sidecar (copy_data);
+		return;
+	}
+
 	destination = copy_data->current_destination_sidecar->data;
+	destination_parent = g_file_get_parent (destination);
+	g_file_make_directory (destination_parent, copy_data->cancellable, NULL);
+
 	g_file_copy_async (source,
 			   destination,
 			   G_FILE_COPY_OVERWRITE,
@@ -1110,6 +1168,8 @@ copy_data__copy_current_sidecar (CopyData *copy_data)
 			   NULL,
 			   copy_data__copy_current_sidecar_ready_cb,
 			   copy_data);
+
+	g_object_unref (destination_parent);
 }
 
 
@@ -1118,10 +1178,9 @@ copy_data__copy_current_file_ready_cb (GObject      *source_object,
 				       GAsyncResult *result,
 				       gpointer      user_data)
 {
-	CopyData *copy_data = user_data;
-	GError   *error = NULL;
-	GFile    *source;
-	GFile    *destination;
+	CopyData    *copy_data = user_data;
+	GError      *error = NULL;
+	GthFileData *source;
 
 	if (! g_file_copy_finish ((GFile *) source_object, result, &error)) {
 		copy_data->done_callback (error, copy_data->user_data);
@@ -1129,11 +1188,11 @@ copy_data__copy_current_file_ready_cb (GObject      *source_object,
 		return;
 	}
 
-	source = (GFile *) copy_data->current_source->data;
-	destination = (GFile *) copy_data->current_destination->data;
-
-	if (copy_data->move)
-		g_file_delete (source, copy_data->cancellable, NULL);
+	source = (GthFileData *) copy_data->current->data;
+	if (g_hash_table_lookup (copy_data->source_hash, source->file) == NULL) {
+		copy_data__copy_next_file (copy_data);
+		return;
+	}
 
 	/* copy the metadata sidecars if requested */
 
@@ -1142,9 +1201,8 @@ copy_data__copy_current_file_ready_cb (GObject      *source_object,
 	copy_data->source_sidecars = NULL;
 	copy_data->destination_sidecars = NULL;
 	if (copy_data->flags && G_FILE_COPY_ALL_METADATA) {
-		gth_hook_invoke ("add-sidecars", source, &copy_data->source_sidecars);
-		gth_hook_invoke ("add-sidecars", destination, &copy_data->destination_sidecars);
-
+		gth_hook_invoke ("add-sidecars", source->file, &copy_data->source_sidecars);
+		gth_hook_invoke ("add-sidecars", copy_data->current_destination, &copy_data->destination_sidecars);
 		copy_data->source_sidecars = g_list_reverse (copy_data->source_sidecars);
 		copy_data->destination_sidecars = g_list_reverse (copy_data->destination_sidecars);
 	}
@@ -1160,85 +1218,88 @@ copy_data__copy_current_file_progress_cb (goffset  current_num_bytes,
                                           goffset  total_num_bytes,
                                           gpointer user_data)
 {
-	/*CopyData *copy_data = user_data;*/
+	CopyData *copy_data = user_data;
+	char     *s1;
+	char     *s2;
+	char     *details;
 
-	/* FIXME */
+	if (copy_data->progress_callback == NULL)
+		return;
+
+	s1 = g_format_size_for_display (copy_data->copied_size + current_num_bytes);
+	s2 = g_format_size_for_display (copy_data->tot_size);
+	/* This is a progress size indicator, for example: 230.4 MB of 512.8 MB */
+	details = g_strdup_printf (_("%s of %s"), s1, s2);
+
+	copy_data->progress_callback (NULL,
+				      copy_data->message,
+				      details,
+				      FALSE,
+				      (double) (copy_data->copied_size + current_num_bytes) / copy_data->tot_size,
+				      copy_data->progress_callback_data);
+
+	g_free (details);
+	g_free (s2);
+	g_free (s1);
 }
 
 
 static void
 copy_data__copy_current_file (CopyData *copy_data)
 {
-	GFile *source;
-	GFile *destination;
+	GthFileData *source;
 
-	if (copy_data->current_source == NULL) {
-		copy_data->current_dir = copy_data->dirs;
-		copy_data__copy_current_directory_to_destination (copy_data);
+	if (copy_data->current == NULL) {
+		copy_data__delete_source (copy_data);
 		return;
 	}
 
-	copy_data->n_current++;
+	_g_object_unref (copy_data->current_destination);
 
-	source = copy_data->current_source->data;
-	destination = copy_data->current_destination->data;
+	source = (GthFileData *) copy_data->current->data;
+	if (g_hash_table_lookup (copy_data->source_hash, source->file) != NULL) {
+		_g_object_unref (copy_data->source_base);
+		copy_data->source_base = g_file_get_parent (source->file);
+	}
+	copy_data->current_destination = get_destination_file (source->file, copy_data->source_base, copy_data->destination);
 
 	if (copy_data->progress_callback != NULL) {
-		char *details;
-		char *source_name;
-		char *destination_name;
+		GFile *destination_parent;
+		char  *destination_name;
 
-		source_name = _g_file_get_display_name (source);
-		destination_name = _g_file_get_display_name (destination);
-		details = g_strdup_printf (_("Copying %s to %s"), source_name, destination_name);
-		copy_data->progress_callback (NULL,
-					      _("Copying files"),
-					      details,
-					      FALSE,
-					      (double) copy_data->n_current / (copy_data->n_files + 1),
-					      copy_data->progress_callback_data);
+		g_free (copy_data->message);
+
+		destination_parent = g_file_get_parent (copy_data->current_destination);
+		destination_name = _g_file_get_display_name (destination_parent);
+		copy_data->message = g_strdup_printf (_("Copying \"%s\" to \"%s\""), g_file_info_get_display_name (source->info), destination_name);
 
-		g_free (details);
 		g_free (destination_name);
-		g_free (source_name);
+		g_object_unref (destination_parent);
 	}
 
-	g_file_copy_async (source,
-			   destination,
-			   copy_data->flags,
-			   copy_data->io_priority,
-			   copy_data->cancellable,
-			   copy_data__copy_current_file_progress_cb,
-			   copy_data,
-			   copy_data__copy_current_file_ready_cb,
-			   copy_data);
-}
-
-
-static void
-copy_data__copy_files_to_destination (CopyData *copy_data)
-{
-	GList *scan;
-
-	copy_data->n_files = g_list_length (copy_data->sources);
-	copy_data->n_current = 0;
-
-
-	copy_data->destinations = NULL;
-	for (scan = copy_data->sources; scan; scan = scan->next) {
-		GFile *source = scan->data;
-		char  *source_name;
-
-		source_name = g_file_get_basename (source);
-		copy_data->destinations = g_list_prepend (copy_data->destinations, g_file_get_child (copy_data->destination, source_name));
+	if (g_file_info_get_file_type (source->info) == G_FILE_TYPE_DIRECTORY) {
+		GError *error = NULL;
 
-		g_free (source_name);
+		if (! g_file_make_directory (copy_data->current_destination, copy_data->cancellable, &error)) {
+			if (! g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
+				copy_data->done_callback (error, copy_data->user_data);
+				copy_data_free (copy_data);
+				return;
+			}
+			g_clear_error (&error);
+		}
+		copy_data__copy_next_file (copy_data);
 	}
-	copy_data->destinations = g_list_reverse (copy_data->destinations);
-
-	copy_data->current_source = copy_data->sources;
-	copy_data->current_destination = copy_data->destinations;
-	copy_data__copy_current_file (copy_data);
+	else
+		g_file_copy_async (source->file,
+				   copy_data->current_destination,
+				   copy_data->flags,
+				   copy_data->io_priority,
+				   copy_data->cancellable,
+				   copy_data__copy_current_file_progress_cb,
+				   copy_data,
+				   copy_data__copy_current_file_ready_cb,
+				   copy_data);
 }
 
 
@@ -1256,24 +1317,20 @@ copy_files__sources_info_ready_cb (GList    *files,
 		return;
 	}
 
-	copy_data->sources = NULL;
-	copy_data->dirs = NULL;
-	for (scan = files; scan; scan = scan->next) {
-		GthFileData *file_data = scan->data;
+	copy_data->files = _g_object_list_ref (files);
+	copy_data->tot_size = 0;
+	copy_data->tot_files = 0;
+	for (scan = copy_data->files; scan; scan = scan->next) {
+		GthFileData *file_data = (GthFileData *) scan->data;
 
-		switch (g_file_info_get_file_type (file_data->info)) {
-		case G_FILE_TYPE_DIRECTORY:
-			copy_data->dirs = g_list_prepend (copy_data->dirs, g_object_ref (file_data->file));
-			break;
-		default:
-			copy_data->sources = g_list_prepend (copy_data->sources, g_object_ref (file_data->file));
-			break;
-		}
+		copy_data->tot_size += g_file_info_get_size (file_data->info);
+		copy_data->tot_files += 1;
 	}
-	copy_data->sources = g_list_reverse (copy_data->sources);
-	copy_data->dirs = g_list_reverse (copy_data->dirs);
 
-	copy_data__copy_files_to_destination (copy_data);
+	copy_data->copied_size = 0;
+	copy_data->copied_files = 0;
+	copy_data->current = copy_data->files;
+	copy_data__copy_current_file (copy_data);
 }
 
 
@@ -1286,22 +1343,27 @@ _g_copy_files_async (GList            *sources, /* GFile list */
 		     GCancellable     *cancellable,
 		     ProgressCallback  progress_callback,
 		     gpointer          progress_callback_data,
-		     CopyDoneCallback  done_callback,
+		     ReadyFunc         done_callback,
 		     gpointer          user_data)
 {
 	CopyData *copy_data;
+	GList    *scan;
 
 	copy_data = g_new0 (CopyData, 1);
 	copy_data->destination = g_object_ref (destination);
 	copy_data->move = move;
 	copy_data->flags = flags;
 	copy_data->io_priority = io_priority;
-	copy_data->cancellable = g_object_ref (cancellable);
+	copy_data->cancellable = _g_object_ref (cancellable);
 	copy_data->progress_callback = progress_callback;
 	copy_data->progress_callback_data = progress_callback_data;
 	copy_data->done_callback = done_callback;
 	copy_data->user_data = user_data;
 
+	copy_data->source_hash = g_hash_table_new_full ((GHashFunc) g_file_hash, (GEqualFunc) g_file_equal, (GDestroyNotify) g_object_unref, NULL);
+	for (scan = sources; scan; scan = scan->next)
+		g_hash_table_insert (copy_data->source_hash, g_object_ref (scan->data), GINT_TO_POINTER (1));
+
 	if (copy_data->progress_callback != NULL)
 		copy_data->progress_callback (NULL,
 					      _("Copying files"),
@@ -1310,11 +1372,13 @@ _g_copy_files_async (GList            *sources, /* GFile list */
 					      0.0,
 					      copy_data->progress_callback_data);
 
-	g_query_info_async (sources,
-			    G_FILE_ATTRIBUTE_STANDARD_TYPE,
-			    cancellable,
-			    copy_files__sources_info_ready_cb,
-			    copy_data);
+	_g_query_info_async (sources,
+			     TRUE,
+			     TRUE,
+			     "standard::name,standard::display-name,standard::type,standard::size",
+			     copy_data->cancellable,
+			     copy_files__sources_info_ready_cb,
+			     copy_data);
 }
 
 
@@ -1327,7 +1391,7 @@ _g_copy_file_async (GFile                 *source,
 		    GCancellable          *cancellable,
 		    ProgressCallback       progress_callback,
 		    gpointer               progress_callback_data,
-		    CopyDoneCallback       callback,
+		    ReadyFunc              callback,
 		    gpointer               user_data)
 {
 	GList *source_files;
@@ -1436,6 +1500,80 @@ _g_delete_files (GList     *file_list,
 }
 
 
+/* -- _g_delete_files_async -- */
+
+
+typedef struct {
+	gboolean      include_metadata;
+	GCancellable *cancellable;
+	ReadyFunc     callback;
+	gpointer      user_data;
+} DeleteData;
+
+
+static void
+delete_data_free (DeleteData *delete_data)
+{
+	_g_object_unref (delete_data->cancellable);
+	g_free (delete_data);
+}
+
+
+static void
+delete_files__info_ready_cb (GList    *files,
+			     GError   *error,
+			     gpointer  user_data)
+{
+	DeleteData *delete_data = user_data;
+
+	if (error == NULL) {
+		GList *file_list;
+		GList *scan;
+
+		file_list = _g_object_list_ref (files);
+		file_list = g_list_reverse (file_list);
+
+		for (scan = file_list; scan; scan = scan->next) {
+			GthFileData *file_data = scan->data;
+
+			if (! g_file_delete (file_data->file, delete_data->cancellable, &error))
+				break;
+		}
+
+		_g_object_list_unref (file_list);
+	}
+
+	delete_data->callback (error, delete_data->user_data);
+	delete_data_free (delete_data);
+}
+
+
+void
+_g_delete_files_async (GList        *file_list,
+		       gboolean      recursive,
+		       gboolean      include_metadata,
+		       GCancellable *cancellable,
+		       ReadyFunc     callback,
+		       gpointer      user_data)
+{
+	DeleteData *delete_data;
+
+	delete_data = g_new0 (DeleteData, 1);
+	delete_data->include_metadata = include_metadata;
+	delete_data->cancellable = _g_object_ref (cancellable);
+	delete_data->callback = callback;
+	delete_data->user_data = user_data;
+
+	_g_query_info_async (file_list,
+			     recursive,
+			     FALSE,
+			     GFILE_NAME_TYPE_ATTRIBUTES,
+			     delete_data->cancellable,
+			     delete_files__info_ready_cb,
+			     delete_data);
+}
+
+
 #define BUFFER_SIZE 4096
 
 
diff --git a/gthumb/gio-utils.h b/gthumb/gio-utils.h
index a466027..eca3690 100644
--- a/gthumb/gio-utils.h
+++ b/gthumb/gio-utils.h
@@ -44,14 +44,10 @@ typedef DirOp (*StartDirCallback)    (GFile       *directory,
 typedef void (*ForEachChildCallback) (GFile       *file,
 				      GFileInfo   *info,
 				      gpointer     user_data);
-typedef void (*ForEachDoneCallback)  (GError      *error,
-				      gpointer     data);
 typedef void (*ListReadyCallback)    (GList       *files,
 				      GList       *dirs,
 				      GError      *error,
 				      gpointer     user_data);
-typedef void (*CopyDoneCallback)     (GError      *error,
-				      gpointer     user_data);
 typedef void (*BufferReadyCallback)  (void        *buffer,
 				      gsize        count,
 				      GError      *error,
@@ -69,7 +65,7 @@ void   g_directory_foreach_child     (GFile                 *directory,
 				      GCancellable          *cancellable,
 				      StartDirCallback       start_dir_func,
 				      ForEachChildCallback   for_each_file_func,
-				      ForEachDoneCallback    done_func,
+				      ReadyFunc              done_func,
 				      gpointer               user_data);
 void   g_directory_list_async        (GFile                 *directory,
 				      const char            *base_dir,
@@ -84,15 +80,17 @@ void   g_directory_list_async        (GFile                 *directory,
 				      GCancellable          *cancellable,
 				      ListReadyCallback      done_func,
 				      gpointer               done_data);
-void   g_query_info_async            (GList                 *files,        /* GFile * list */
+void   _g_query_info_async           (GList                 *file_list,    /* GFile * list */
+				      gboolean               recursive,
+				      gboolean               follow_links,
 				      const char            *attributes,
 				      GCancellable          *cancellable,
-				      InfoReadyCallback      ready_func,
+				      InfoReadyCallback      ready_callback,
 				      gpointer               user_data);
 
 /* asynchronous copy functions */
 
-void     _g_dummy_file_op_async      (CopyDoneCallback       callback,
+void     _g_dummy_file_op_async      (ReadyFunc              callback,
 				      gpointer               user_data);
 void     _g_copy_files_async         (GList                 *sources,
 				      GFile                 *destination,
@@ -102,7 +100,7 @@ void     _g_copy_files_async         (GList                 *sources,
 				      GCancellable          *cancellable,
 				      ProgressCallback       progress_callback,
 				      gpointer               progress_callback_data,
-				      CopyDoneCallback       callback,
+				      ReadyFunc              callback,
 				      gpointer               user_data);
 void     _g_copy_file_async          (GFile                 *source,
 				      GFile                 *destination,
@@ -112,7 +110,7 @@ void     _g_copy_file_async          (GFile                 *source,
 				      GCancellable          *cancellable,
 				      ProgressCallback       progress_callback,
 				      gpointer               progress_callback_data,
-				      CopyDoneCallback       callback,
+				      ReadyFunc              callback,
 				      gpointer               user_data);
 gboolean _g_move_file                (GFile                 *source,
                                       GFile                 *destination,
@@ -124,8 +122,14 @@ gboolean _g_move_file                (GFile                 *source,
 gboolean _g_delete_files             (GList                 *file_list,
 				      gboolean               include_metadata,
 				      GError               **error);
+void     _g_delete_files_async       (GList                  *file_list,
+				      gboolean               recursive,
+				      gboolean               include_metadata,
+				      GCancellable          *cancellable,
+				      ReadyFunc              callback,
+				      gpointer               user_data);
 
-/* -- read/write/create file  -- */
+/* -- load/write/create file  -- */
 
 gboolean g_load_file_in_buffer       (GFile                 *file,
 				      void                 **buffer,
diff --git a/gthumb/glib-utils.c b/gthumb/glib-utils.c
index ff49f26..24f9918 100644
--- a/gthumb/glib-utils.c
+++ b/gthumb/glib-utils.c
@@ -35,6 +35,16 @@
 /* gobject utils*/
 
 
+gpointer
+_g_object_ref (gpointer object)
+{
+	if (object != NULL)
+		return g_object_ref (object);
+	else
+		return NULL;
+}
+
+
 void
 _g_object_unref (gpointer object)
 {
diff --git a/gthumb/glib-utils.h b/gthumb/glib-utils.h
index a817591..383ace1 100644
--- a/gthumb/glib-utils.h
+++ b/gthumb/glib-utils.h
@@ -30,8 +30,9 @@
 
 G_BEGIN_DECLS
 
+#define GFILE_NAME_TYPE_ATTRIBUTES "standard::name,standard::type"
 #define GFILE_DISPLAY_ATTRIBUTES "standard::display-name,standard::icon"
-#define GFILE_BASIC_ATTRIBUTES GFILE_DISPLAY_ATTRIBUTES ",standard::type"
+#define GFILE_BASIC_ATTRIBUTES GFILE_DISPLAY_ATTRIBUTES ",standard::name,standard::type"
 
 #define DEFINE_STANDARD_ATTRIBUTES(a) ( \
 	"standard::type," \
@@ -74,6 +75,7 @@ G_BEGIN_DECLS
 
 /* gobject utils*/
 
+gpointer      _g_object_ref                  (gpointer     object);
 void          _g_object_unref                (gpointer     object);
 GList *       _g_object_list_ref             (GList       *list);
 void          _g_object_list_unref           (GList       *list);
diff --git a/gthumb/gth-metadata-provider.c b/gthumb/gth-metadata-provider.c
index 124a9f0..5dbc14a 100644
--- a/gthumb/gth-metadata-provider.c
+++ b/gthumb/gth-metadata-provider.c
@@ -562,14 +562,15 @@ _g_query_all_metadata_async (GList             *files, /* GFile * list */
 
 	qam = g_new0 (QueryAllMetadata, 1);
 	qam->attributes = g_strdup (attributes);
-	if (cancellable != NULL)
-		qam->cancellable = g_object_ref (cancellable);
+	qam->cancellable = _g_object_ref (cancellable);
 	qam->ready_func = ready_func;
 	qam->user_data = user_data;
 
-	g_query_info_async (files,
-			    qam->attributes,
-			    qam->cancellable,
-			    qam_info_ready_cb,
-			    qam);
+	_g_query_info_async (files,
+			     FALSE,
+			     TRUE,
+			     qam->attributes,
+			     qam->cancellable,
+			     qam_info_ready_cb,
+			     qam);
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]