[evolution] Test drive EIOActivity with a simple asynchronous function.



commit aa66a17e401d73cbe394ed7f99bf73350e9b938b
Author: Matthew Barnes <mbarnes redhat com>
Date:   Fri Nov 6 13:33:55 2009 -0500

    Test drive EIOActivity with a simple asynchronous function.
    
    Rename e-fsutils to e-file-utils.  This is where we'll add asynchronous
    functions for common file I/O operations with EActivity integration.
    
    Start with e_file_replace_contents_async() (and corresponding finish()
    function).  This is a simple wrapper for g_file_replace_contents_async()
    which also returns an EActivity.  It replaces e_write_file_uri().
    
    Also redesign EIOActivity to -contain- a GAsyncResult rather than
    implement the interface for itself.  This is easier for now but I may
    change my mind again when I figure out how to tie centralized error
    reporting into the EActivity framework.

 e-util/Makefile.am                     |    4 +-
 e-util/e-file-utils.c                  |  281 ++++++++++++++++++++++++++++++++
 e-util/{e-fsutils.h => e-file-utils.h} |   26 +++-
 e-util/e-fsutils.c                     |  158 ------------------
 e-util/e-io-activity.c                 |  206 +++++++++++++++---------
 e-util/e-io-activity.h                 |   12 +-
 shell/e-shell-migrate.c                |    2 +-
 7 files changed, 441 insertions(+), 248 deletions(-)
---
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index bc5e7cd..ecc3220 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -24,8 +24,8 @@ eutilinclude_HEADERS = 				\
 	e-dialog-widgets.h			\
 	e-error.h				\
 	e-event.h				\
+	e-file-utils.h				\
 	e-folder-map.h				\
-	e-fsutils.h				\
 	e-html-utils.h				\
 	e-icon-factory.h			\
 	e-import.h				\
@@ -98,8 +98,8 @@ libeutil_la_SOURCES =				\
 	e-dialog-widgets.c			\
 	e-error.c				\
 	e-event.c				\
+	e-file-utils.c				\
 	e-folder-map.c				\
-	e-fsutils.c				\
 	e-html-utils.c				\
 	e-icon-factory.c			\
 	e-import.c				\
diff --git a/e-util/e-file-utils.c b/e-util/e-file-utils.c
new file mode 100644
index 0000000..f8adcc7
--- /dev/null
+++ b/e-util/e-file-utils.c
@@ -0,0 +1,281 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *		Michael Zucchi <notzed ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/* This isn't as portable as, say, the stuff in GNU coreutils.  But I care not for OSF1. */
+#ifdef HAVE_STATVFS
+# ifdef HAVE_SYS_STATVFS_H
+#  include <sys/statvfs.h>
+# endif
+#else
+#ifdef HAVE_STATFS
+# ifdef HAVE_SYS_PARAM_H
+#  include <sys/param.h>	/* bsd interface */
+# endif
+# ifdef HAVE_SYS_MOUNT_H
+#  include <sys/mount.h>
+# endif
+#endif
+#endif
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib/gi18n-lib.h>
+
+#include "e-file-utils.h"
+#include "e-io-activity.h"
+
+static void
+file_replace_contents_cb (GFile *file,
+                          GAsyncResult *result,
+                          EActivity *activity)
+{
+	gchar *new_etag;
+	gboolean success;
+	GError *error = NULL;
+
+	success = g_file_replace_contents_finish (
+		file, result, &new_etag, &error);
+
+	result = e_io_activity_get_async_result (E_IO_ACTIVITY (activity));
+
+	g_object_set_data_full (
+		G_OBJECT (result),
+		"__new_etag__", new_etag,
+		(GDestroyNotify) g_free);
+
+	g_simple_async_result_set_op_res_gboolean (
+		G_SIMPLE_ASYNC_RESULT (result), success);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (
+			G_SIMPLE_ASYNC_RESULT (result), error);
+		g_error_free (error);
+	}
+
+	e_activity_complete (activity);
+
+	g_object_unref (activity);
+}
+
+EActivity *
+e_file_replace_contents_async (GFile *file,
+                               const gchar *contents,
+                               gsize length,
+                               const gchar *etag,
+                               gboolean make_backup,
+                               GFileCreateFlags flags,
+                               GAsyncReadyCallback callback,
+                               gpointer user_data)
+{
+	EActivity *activity;
+	GSimpleAsyncResult *simple;
+	GCancellable *cancellable;
+	const gchar *format;
+	gchar *description;
+	gchar *basename;
+	gchar *filename;
+	gchar *hostname;
+	gchar *uri;
+
+	g_return_val_if_fail (G_IS_FILE (file), NULL);
+	g_return_val_if_fail (contents != NULL, NULL);
+
+	uri = g_file_get_uri (file);
+	filename = g_filename_from_uri (uri, &hostname, NULL);
+	basename = g_filename_display_basename (filename);
+
+	if (hostname != NULL) {
+		/* Translators: The string value is the basename of a file. */
+		format = _("Writing \"%s\"");
+		description = g_strdup_printf (format, basename);
+	} else {
+		/* Translators: The first string value is the basename of a
+		 * remote file, the second string value is the hostname. */
+		format = _("Writing \"%s\" to %s");
+		description = g_strdup_printf (format, basename, hostname);
+	}
+
+	cancellable = g_cancellable_new ();
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (file), callback, user_data,
+		e_file_replace_contents_async);
+
+	activity = e_io_activity_new (
+		description, G_ASYNC_RESULT (simple), cancellable);
+
+	g_file_replace_contents_async (
+		file, contents, length, etag,
+		make_backup, flags, cancellable,
+		(GAsyncReadyCallback) file_replace_contents_cb,
+		activity);
+
+	g_object_unref (cancellable);
+	g_object_unref (simple);
+
+	g_free (description);
+	g_free (basename);
+	g_free (filename);
+	g_free (hostname);
+	g_free (uri);
+
+	return activity;
+}
+
+gboolean
+e_file_replace_contents_finish (GFile *file,
+                                GAsyncResult *result,
+                                gchar **new_etag,
+                                GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (G_IS_FILE (file), FALSE);
+	g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		return FALSE;
+
+	if (new_etag != NULL)
+		*new_etag = g_object_steal_data (
+			G_OBJECT (result), "__new_etag__");
+
+	return TRUE;
+}
+
+/**
+ * e_fsutils_usage:
+ * @path:
+ *
+ * Calculate the amount of disk space used by a given path.
+ *
+ * Return value: The number of 1024 byte blocks used by the
+ * filesystem.
+ **/
+glong e_fsutils_usage(const gchar *inpath)
+{
+	GDir *dir;
+	const gchar *d;
+	long size = 0;
+	GSList *paths;
+
+	/* iterative, depth-first scan, because i can ... */
+	paths = g_slist_prepend(NULL, g_strdup(inpath));
+
+	while (paths) {
+		gchar *path = paths->data;
+
+		paths = g_slist_remove_link(paths, paths);
+
+		dir = g_dir_open(path, 0, NULL);
+		if (dir == NULL) {
+			g_free(path);
+			goto fail;
+		}
+
+		while ((d = g_dir_read_name(dir))) {
+			gchar *full_path;
+			struct stat st;
+
+			full_path = g_build_filename(path, d, NULL);
+			if (g_stat(full_path, &st) == -1) {
+				g_free(full_path);
+				g_dir_close(dir);
+				g_free(path);
+				goto fail;
+			} else if (S_ISDIR(st.st_mode)) {
+				paths = g_slist_prepend(paths, full_path);
+				full_path = NULL;
+			} else if (S_ISREG(st.st_mode)) {
+				/* This is in 512 byte blocks.  st_blksize is page size on linux,
+				   on *BSD it might be significant. */
+#ifndef G_OS_WIN32
+				size += st.st_blocks/2;
+#endif
+			}
+
+			g_free(full_path);
+		}
+
+		g_dir_close(dir);
+		g_free(path);
+	}
+
+	return size;
+
+fail:
+	g_slist_foreach(paths, (GFunc)g_free, NULL);
+	g_slist_free(paths);
+
+	return -1;
+}
+
+/**
+ * e_fsutils_avail:
+ * @path:
+ *
+ * Find the available disk space at the given path.
+ *
+ * Return value: -1 if it could not be determined, otherwise the
+ * number of disk blocks, expressed as system-independent, 1024 byte
+ * blocks.
+ **/
+glong
+e_fsutils_avail(const gchar *path)
+{
+#if defined(HAVE_STATVFS)
+	struct statvfs stfs;
+
+	if (statvfs(path, &stfs) == -1)
+		return -1;
+
+	/* Assumes that frsize === power of 2 */
+	if (stfs.f_frsize >= 1024)
+		return stfs.f_bavail * (stfs.f_frsize / 1024);
+	else
+		return stfs.f_bavail / (1024 / stfs.f_frsize);
+#elif defined(HAVE_STATFS)
+	struct statfs stfs;
+
+	if (statfs(path, &stfs) == -1)
+		return -1;
+
+	/* For BSD this isn't clear, it may be dependent on f_bsize */
+	return stfs.f_bavail / 2;
+#else
+	errno = ENOSYS;
+	return -1;
+#endif
+}
+
diff --git a/e-util/e-fsutils.h b/e-util/e-file-utils.h
similarity index 57%
rename from e-util/e-fsutils.h
rename to e-util/e-file-utils.h
index 6143e48..147b9b5 100644
--- a/e-util/e-fsutils.h
+++ b/e-util/e-file-utils.h
@@ -20,16 +20,30 @@
  *
  */
 
-#ifndef E_FSUTILS_H
-#define E_FSUTILS_H
+#ifndef E_FILE_UTILS_H
+#define E_FILE_UTILS_H
 
-#include <glib.h>
+#include <gio/gio.h>
+#include <e-util/e-activity.h>
 
 G_BEGIN_DECLS
 
-glong e_fsutils_usage(const gchar *path);
-glong e_fsutils_avail(const gchar *path);
+EActivity *	e_file_replace_contents_async	(GFile *file,
+						 const gchar *contents,
+						 gsize length,
+						 const gchar *etag,
+						 gboolean make_backup,
+						 GFileCreateFlags flags,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_file_replace_contents_finish	(GFile *file,
+						 GAsyncResult *result,
+						 gchar **new_etag,
+						 GError **error);
+
+glong		e_fsutils_usage			(const gchar *path);
+glong		e_fsutils_avail			(const gchar *path);
 
 G_END_DECLS
 
-#endif /* !E_FOLDER_MAP_H */
+#endif /* E_FILE_UTILS_H */
diff --git a/e-util/e-io-activity.c b/e-util/e-io-activity.c
index 086ee10..9569e42 100644
--- a/e-util/e-io-activity.c
+++ b/e-util/e-io-activity.c
@@ -26,26 +26,54 @@
 	((obj), E_TYPE_IO_ACTIVITY, EIOActivityPrivate))
 
 struct _EIOActivityPrivate {
-	GObject *source_object;
+	GAsyncResult *async_result;
 	GCancellable *cancellable;
-	GAsyncReadyCallback callback;
-	gpointer user_data;
 };
 
 enum {
 	PROP_0,
+	PROP_ASYNC_RESULT,
 	PROP_CANCELLABLE
 };
 
 static gpointer parent_class;
 
 static void
+io_activity_set_property (GObject *object,
+                          guint property_id,
+                          const GValue *value,
+                          GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ASYNC_RESULT:
+			e_io_activity_set_async_result (
+				E_IO_ACTIVITY (object),
+				g_value_get_object (value));
+			return;
+
+		case PROP_CANCELLABLE:
+			e_io_activity_set_cancellable (
+				E_IO_ACTIVITY (object),
+				g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
 io_activity_get_property (GObject *object,
                           guint property_id,
                           GValue *value,
                           GParamSpec *pspec)
 {
 	switch (property_id) {
+		case PROP_ASYNC_RESULT:
+			g_value_set_object (
+				value, e_io_activity_get_async_result (
+				E_IO_ACTIVITY (object)));
+			return;
+
 		case PROP_CANCELLABLE:
 			g_value_set_object (
 				value, e_io_activity_get_cancellable (
@@ -63,9 +91,9 @@ io_activity_dispose (GObject *object)
 
 	priv = E_IO_ACTIVITY_GET_PRIVATE (object);
 
-	if (priv->source_object != NULL) {
-		g_object_unref (priv->source_object);
-		priv->source_object = NULL;
+	if (priv->async_result != NULL) {
+		g_object_unref (priv->async_result);
+		priv->async_result = NULL;
 	}
 
 	if (priv->cancellable != NULL) {
@@ -80,55 +108,37 @@ io_activity_dispose (GObject *object)
 static void
 io_activity_cancelled (EActivity *activity)
 {
-	EIOActivityPrivate *priv;
-
-	priv = E_IO_ACTIVITY_GET_PRIVATE (activity);
+	EIOActivity *io_activity;
+	GCancellable *cancellable;
 
 	/* Chain up to parent's cancelled() method. */
 	E_ACTIVITY_CLASS (parent_class)->cancelled (activity);
 
-	g_cancellable_cancel (priv->cancellable);
+	io_activity = E_IO_ACTIVITY (activity);
+	cancellable = e_io_activity_get_cancellable (io_activity);
+
+	if (cancellable != NULL)
+		g_cancellable_cancel (cancellable);
 }
 
 static void
 io_activity_completed (EActivity *activity)
 {
-	EIOActivityPrivate *priv;
-
-	priv = E_IO_ACTIVITY_GET_PRIVATE (activity);
+	EIOActivity *io_activity;
+	GAsyncResult *async_result;
 
 	/* Chain up to parent's completed() method. */
 	E_ACTIVITY_CLASS (parent_class)->completed (activity);
 
-	/* Clear the function pointer after invoking it
-	 * to guarantee it will not be invoked again. */
-	if (priv->callback != NULL) {
-		priv->callback (
-			priv->source_object,
-			G_ASYNC_RESULT (activity),
-			priv->user_data);
-		priv->callback = NULL;
-	}
-}
-
-static gpointer
-io_activity_get_user_data (GAsyncResult *async_result)
-{
-	EIOActivityPrivate *priv;
+	io_activity = E_IO_ACTIVITY (activity);
+	async_result = e_io_activity_get_async_result (io_activity);
 
-	priv = E_IO_ACTIVITY_GET_PRIVATE (async_result);
-
-	return priv->user_data;
-}
-
-static GObject *
-io_activity_get_source_object (GAsyncResult *async_result)
-{
-	EIOActivityPrivate *priv;
-
-	priv = E_IO_ACTIVITY_GET_PRIVATE (async_result);
-
-	return priv->source_object;
+	/* We know how to invoke a GSimpleAsyncResult.  For any other
+	 * type of GAsyncResult the caller will have to take measures
+	 * to invoke it himself. */
+	if (G_IS_SIMPLE_ASYNC_RESULT (async_result))
+		g_simple_async_result_complete (
+			G_SIMPLE_ASYNC_RESULT (async_result));
 }
 
 static void
@@ -141,6 +151,7 @@ io_activity_class_init (EIOActivityClass *class)
 	g_type_class_add_private (class, sizeof (EIOActivityPrivate));
 
 	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = io_activity_set_property;
 	object_class->get_property = io_activity_get_property;
 	object_class->dispose = io_activity_dispose;
 
@@ -150,28 +161,31 @@ io_activity_class_init (EIOActivityClass *class)
 
 	g_object_class_install_property (
 		object_class,
+		PROP_ASYNC_RESULT,
+		g_param_spec_object (
+			"async-result",
+			"Asynchronous Result",
+			NULL,
+			G_TYPE_ASYNC_RESULT,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		object_class,
 		PROP_CANCELLABLE,
 		g_param_spec_object (
 			"cancellable",
 			"Cancellable",
 			NULL,
 			G_TYPE_CANCELLABLE,
-			G_PARAM_READABLE));
-}
-
-static void
-io_activity_iface_init (GAsyncResultIface *iface)
-{
-	iface->get_user_data = io_activity_get_user_data;
-	iface->get_source_object = io_activity_get_source_object;
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
 }
 
 static void
 io_activity_init (EIOActivity *io_activity)
 {
 	io_activity->priv = E_IO_ACTIVITY_GET_PRIVATE (io_activity);
-
-	io_activity->priv->cancellable = g_cancellable_new ();
 }
 
 GType
@@ -193,45 +207,57 @@ e_io_activity_get_type (void)
 			NULL   /* value_table */
 		};
 
-		static const GInterfaceInfo iface_info = {
-			(GInterfaceInitFunc) io_activity_iface_init,
-			(GInterfaceFinalizeFunc) NULL,
-			NULL   /* interface_data */
-		};
-
 		type = g_type_register_static (
 			E_TYPE_ACTIVITY, "EIOActivity", &type_info, 0);
-
-		g_type_add_interface_static (
-			type, G_TYPE_ASYNC_RESULT, &iface_info);
 	}
 
 	return type;
 }
 
 EActivity *
-e_io_activity_new (GObject *source_object,
-                   const gchar *primary_text,
-                   GAsyncReadyCallback callback,
-                   gpointer user_data)
+e_io_activity_new (const gchar *primary_text,
+                   GAsyncResult *async_result,
+                   GCancellable *cancellable)
 {
-	EActivity *activity;
-	EIOActivityPrivate *priv;
-
-	g_return_val_if_fail (G_IS_OBJECT (source_object), NULL);
 	g_return_val_if_fail (primary_text != NULL, NULL);
-	g_return_val_if_fail (callback != NULL, NULL);
 
-	activity = g_object_new (
-		E_TYPE_IO_ACTIVITY, "primary-text", primary_text, NULL);
+	if (async_result != NULL)
+		g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), NULL);
 
-	/* XXX Should these be construct properties? */
-	priv = E_IO_ACTIVITY_GET_PRIVATE (activity);
-	priv->source_object = g_object_ref (source_object);
-	priv->callback = callback;
-	priv->user_data = user_data;
+	if (cancellable != NULL)
+		g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), NULL);
 
-	return activity;
+	return g_object_new (
+		E_TYPE_IO_ACTIVITY,
+		"async-result", async_result, "cancellable",
+		cancellable, "primary-text", primary_text, NULL);
+}
+
+GAsyncResult *
+e_io_activity_get_async_result (EIOActivity *io_activity)
+{
+	g_return_val_if_fail (E_IS_IO_ACTIVITY (io_activity), NULL);
+
+	return io_activity->priv->async_result;
+}
+
+void
+e_io_activity_set_async_result (EIOActivity *io_activity,
+                                GAsyncResult *async_result)
+{
+	g_return_if_fail (E_IS_IO_ACTIVITY (io_activity));
+
+	if (async_result != NULL) {
+		g_return_if_fail (G_IS_ASYNC_RESULT (async_result));
+		g_object_ref (async_result);
+	}
+
+	if (io_activity->priv->async_result != NULL)
+		g_object_unref (io_activity->priv->async_result);
+
+	io_activity->priv->async_result = async_result;
+
+	g_object_notify (G_OBJECT (io_activity), "async-result");
 }
 
 GCancellable *
@@ -241,3 +267,29 @@ e_io_activity_get_cancellable (EIOActivity *io_activity)
 
 	return io_activity->priv->cancellable;
 }
+
+void
+e_io_activity_set_cancellable (EIOActivity *io_activity,
+                               GCancellable *cancellable)
+{
+	g_return_if_fail (E_IS_IO_ACTIVITY (io_activity));
+
+	if (cancellable != NULL) {
+		g_return_if_fail (G_IS_CANCELLABLE (cancellable));
+		g_object_ref (cancellable);
+	}
+
+	if (io_activity->priv->cancellable != NULL)
+		g_object_unref (io_activity->priv->cancellable);
+
+	io_activity->priv->cancellable = cancellable;
+
+	g_object_freeze_notify (G_OBJECT (io_activity));
+
+	e_activity_set_allow_cancel (
+		E_ACTIVITY (io_activity), (cancellable != NULL));
+
+	g_object_notify (G_OBJECT (io_activity), "cancellable");
+
+	g_object_thaw_notify (G_OBJECT (io_activity));
+}
diff --git a/e-util/e-io-activity.h b/e-util/e-io-activity.h
index c94953a..7734174 100644
--- a/e-util/e-io-activity.h
+++ b/e-util/e-io-activity.h
@@ -60,11 +60,15 @@ struct _EIOActivityClass {
 };
 
 GType		e_io_activity_get_type		(void);
-EActivity *	e_io_activity_new		(GObject *source_object,
-						 const gchar *primary_text,
-						 GAsyncReadyCallback callback,
-						 gpointer user_data);
+EActivity *	e_io_activity_new		(const gchar *primary_text,
+						 GAsyncResult *async_result,
+						 GCancellable *cancellable);
+GAsyncResult *	e_io_activity_get_async_result	(EIOActivity *io_activity);
+void		e_io_activity_set_async_result	(EIOActivity *io_activity,
+						 GAsyncResult *async_result);
 GCancellable *	e_io_activity_get_cancellable	(EIOActivity *io_activity);
+void		e_io_activity_set_cancellable	(EIOActivity *io_activity,
+						 GCancellable *cancellable);
 
 G_END_DECLS
 
diff --git a/shell/e-shell-migrate.c b/shell/e-shell-migrate.c
index a330d9d..d7c8312 100644
--- a/shell/e-shell-migrate.c
+++ b/shell/e-shell-migrate.c
@@ -29,7 +29,7 @@
 
 #include "e-util/e-bconf-map.h"
 #include "e-util/e-error.h"
-#include "e-util/e-fsutils.h"
+#include "e-util/e-file-utils.h"
 #include "e-util/e-util.h"
 
 #include "es-event.h"



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