[evolution-data-server/email-factory-3-4: 5/14] Add basic libemail-utils



commit b270cd0724bd937f351cf4df34b7a6244bfb5286
Author: Srinivasa Ragavan <sragavan gnome org>
Date:   Mon Oct 10 15:06:02 2011 +0530

    Add basic libemail-utils

 mail/libemail-utils/Makefile.am         |    8 +-
 mail/libemail-utils/e-signature-utils.c |  342 ++++++++
 mail/libemail-utils/e-signature-utils.h |   40 +
 mail/libemail-utils/gconf-bridge.c      | 1364 +++++++++++++++++++++++++++++++
 mail/libemail-utils/gconf-bridge.h      |  134 +++
 mail/libemail-utils/mail-mt.h           |  102 +++
 6 files changed, 1988 insertions(+), 2 deletions(-)
---
diff --git a/mail/libemail-utils/Makefile.am b/mail/libemail-utils/Makefile.am
index 8cd90dd..d39d328 100644
--- a/mail/libemail-utils/Makefile.am
+++ b/mail/libemail-utils/Makefile.am
@@ -28,7 +28,9 @@ libemail_utils_la_SOURCES = 	\
 		e-account-utils.c	\
 		mail-mt.c	\
 		e-signature.c	\
-		e-signature-list.c
+		e-signature-list.c	\
+		e-signature-utils.c	\
+		gconf-bridge.c
 
 
 libmailutilsincludedir = $(includedir)/libemail-utils
@@ -37,7 +39,9 @@ libmailutilsinclude_HEADERS = 	\
 		e-account-utils.h	\
 		mail-mt.h	\
 		e-signature.h	\
-		e-signature-list.h
+		e-signature-list.h	\
+		e-signature-utils.h	\
+		gconf-bridge.h
 
 
 pkgconfigdir = $(libdir)/pkgconfig
diff --git a/mail/libemail-utils/e-signature-utils.c b/mail/libemail-utils/e-signature-utils.c
new file mode 100644
index 0000000..a7eea3e
--- /dev/null
+++ b/mail/libemail-utils/e-signature-utils.c
@@ -0,0 +1,342 @@
+/*
+ * e-signature-utils.c
+ *
+ * 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/>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-signature-utils.h"
+
+#include <errno.h>
+#include <camel/camel.h>
+#include <glib/gstdio.h>
+#include <gconf/gconf-client.h>
+
+#ifndef G_OS_WIN32
+#include <sys/wait.h>
+#endif
+
+#include <libedataserver/e-data-server-util.h>
+
+static ESignatureList *global_signature_list;
+
+ESignatureList *
+e_get_signature_list (void)
+{
+	if (G_UNLIKELY (global_signature_list == NULL)) {
+		GConfClient *client;
+
+		client = gconf_client_get_default ();
+		global_signature_list = e_signature_list_new (client);
+		g_object_unref (client);
+	}
+
+	g_return_val_if_fail (global_signature_list != NULL, NULL);
+
+	return global_signature_list;
+}
+
+ESignature *
+e_get_signature_by_name (const gchar *name)
+{
+	ESignatureList *signature_list;
+
+	g_return_val_if_fail (name != NULL, NULL);
+
+	signature_list = e_get_signature_list ();
+
+	return e_signature_list_find_by_name (signature_list, name);
+}
+
+ESignature *
+e_get_signature_by_uid (const gchar *uid)
+{
+	ESignatureList *signature_list;
+
+	g_return_val_if_fail (uid != NULL, NULL);
+
+	signature_list = e_get_signature_list ();
+
+	return e_signature_list_find_by_uid (signature_list, uid);
+}
+
+gchar *
+e_create_signature_file (GError **error)
+{
+	const gchar *data_dir;
+	gchar basename[32];
+	gchar *filename;
+	gchar *pathname;
+	gint32 ii;
+
+	data_dir = e_get_user_data_dir ();
+	pathname = g_build_filename (data_dir, "signatures", NULL);
+	filename = NULL;
+
+	if (g_mkdir_with_parents (pathname, 0700) < 0) {
+		g_set_error (
+			error, G_FILE_ERROR,
+			g_file_error_from_errno (errno),
+			"%s: %s", pathname, g_strerror (errno));
+		g_free (pathname);
+		return NULL;
+	}
+
+	for (ii = 0; ii < G_MAXINT32; ii++) {
+
+		g_snprintf (
+			basename, sizeof (basename),
+			"signature-%" G_GINT32_FORMAT, ii);
+
+		g_free (filename);
+		filename = g_build_filename (pathname, basename, NULL);
+
+		if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+			gint fd;
+
+			fd = g_creat (filename, 0600);
+			if (fd >= 0) {
+				close (fd);
+				break;
+			}
+
+			/* If we failed once we're probably going
+			 * to continue failing, so just give up. */
+			g_set_error (
+				error, G_FILE_ERROR,
+				g_file_error_from_errno (errno),
+				"%s: %s", filename, g_strerror (errno));
+			g_free (filename);
+			filename = NULL;
+			break;
+		}
+	}
+
+	/* If there are actually G_MAXINT32 signature files, the
+	 * most recent signature file we be overwritten.  Sorry. */
+
+	return filename;
+}
+
+gchar *
+e_read_signature_file (ESignature *signature,
+                       gboolean convert_to_html,
+                       GError **error)
+{
+	CamelStream *input_stream;
+	CamelStream *output_stream;
+	GByteArray *buffer;
+	const gchar *filename;
+	gboolean is_html;
+	gchar *content;
+	gsize length;
+	gint fd;
+
+	g_return_val_if_fail (E_IS_SIGNATURE (signature), NULL);
+
+	filename = e_signature_get_filename (signature);
+	is_html = e_signature_get_is_html (signature);
+
+	fd = g_open (filename, O_RDONLY, 0);
+	if (fd < 0) {
+		g_set_error (
+			error, G_FILE_ERROR,
+			g_file_error_from_errno (errno),
+			"%s: %s", filename, g_strerror (errno));
+		return NULL;
+	}
+
+	input_stream = camel_stream_fs_new_with_fd (fd);
+
+	if (!is_html && convert_to_html) {
+		CamelStream *filtered_stream;
+		CamelMimeFilter *filter;
+		gint32 flags;
+
+		filtered_stream =
+			camel_stream_filter_new (input_stream);
+		g_object_unref (input_stream);
+
+		flags =
+			CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT |
+			CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
+			CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES |
+			CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES;
+		filter = camel_mime_filter_tohtml_new (flags, 0);
+		camel_stream_filter_add (
+			CAMEL_STREAM_FILTER (filtered_stream), filter);
+		g_object_unref (filter);
+
+		input_stream = filtered_stream;
+	}
+
+	buffer = g_byte_array_new ();
+	output_stream = camel_stream_mem_new ();
+	camel_stream_mem_set_byte_array (
+		CAMEL_STREAM_MEM (output_stream), buffer);
+	camel_stream_write_to_stream (input_stream, output_stream, NULL, NULL);
+	g_object_unref (output_stream);
+	g_object_unref (input_stream);
+
+	/* Make sure the buffer is nul-terminated. */
+	length = (gsize) buffer->len;
+	g_byte_array_append (buffer, (guint8 *) "", 1);
+	content = (gchar *) g_byte_array_free (buffer, FALSE);
+
+	/* Signatures are saved as UTF-8, but we still need to check that
+	 * the signature is valid UTF-8 because the user may be opening
+	 * a signature file that is in his/her locale character set.  If
+	 * it's not in UTF-8 then try converting from the current locale. */
+	if (!g_utf8_validate (content, length, NULL)) {
+		gchar *utf8;
+
+		utf8 = g_locale_to_utf8 (content, length, NULL, NULL, error);
+		g_free (content);
+		content = utf8;
+	}
+
+	return content;
+}
+
+gchar *
+e_run_signature_script (const gchar *filename)
+{
+	/* FIXME Make this cross-platform, prefer GLib functions over
+	 *       POSIX, and report errors via GError instead of dumping
+	 *       messages to the terminal where users won't see them. */
+
+#ifndef G_OS_WIN32
+	gint in_fds[2];
+	pid_t pid;
+
+	g_return_val_if_fail (filename != NULL, NULL);
+
+	if (pipe (in_fds) == -1) {
+		g_warning (
+			"Failed to create pipe to '%s': %s",
+			filename, g_strerror (errno));
+		return NULL;
+	}
+
+	pid = fork ();
+
+	/* Child Process */
+	if (pid == 0) {
+		gint maxfd, ii;
+
+		close (in_fds[0]);
+		if (dup2 (in_fds[1], STDOUT_FILENO) < 0)
+			_exit (255);
+		close (in_fds[1]);
+
+		setsid ();
+
+		maxfd = sysconf (_SC_OPEN_MAX);
+		for (ii = 3; ii < maxfd; ii++) {
+			if (ii == STDIN_FILENO)
+				continue;
+			if (ii == STDOUT_FILENO)
+				continue;
+			if (ii == STDERR_FILENO)
+				continue;
+			fcntl (ii, F_SETFD, FD_CLOEXEC);
+		}
+
+		execlp ("/bin/sh", "/bin/sh", "-c", filename, NULL);
+
+		g_warning (
+			"Could not execute '%s': %s",
+			filename, g_strerror (errno));
+
+		_exit (255);
+
+	/* Parent Process */
+	} else if (pid > 0) {
+		CamelStream *output_stream;
+		CamelStream *input_stream;
+		GByteArray *buffer;
+		gchar *content;
+		gsize length;
+		gint result;
+		gint status;
+
+		close (in_fds[1]);
+
+		buffer = g_byte_array_new ();
+		output_stream = camel_stream_mem_new ();
+		camel_stream_mem_set_byte_array (
+			CAMEL_STREAM_MEM (output_stream), buffer);
+
+		input_stream = camel_stream_fs_new_with_fd (in_fds[0]);
+		camel_stream_write_to_stream (
+			input_stream, output_stream, NULL, NULL);
+		g_object_unref (input_stream);
+
+		g_object_unref (output_stream);
+
+		/* Make sure the buffer is nul-terminated. */
+		length = (gsize) buffer->len;
+		g_byte_array_append (buffer, (guchar *) "", 1);
+		content = (gchar *) g_byte_array_free (buffer, FALSE);
+
+		/* Signature scripts are supposed to generate UTF-8 content,
+		 * but because users are known to never read the manual, we
+		 * try to do our best if the content isn't valid UTF-8 by
+		 * assuming that the content is in the user's locale
+		 * character set. */
+		if (!g_utf8_validate (content, length, NULL)) {
+			gchar *utf8;
+
+			/* XXX Should pass a GError here. */
+			utf8 = g_locale_to_utf8 (
+				content, length, NULL, NULL, NULL);
+			g_free (content);
+			content = utf8;
+		}
+
+		/* Wait for the script process to terminate. */
+		result = waitpid (pid, &status, 0);
+
+		if (result == -1 && errno == EINTR) {
+			/* Child process is hanging... */
+			kill (pid, SIGTERM);
+			sleep (1);
+			result = waitpid (pid, &status, WNOHANG);
+			if (result == 0) {
+				/* ...still hanging, set phasers to KILL. */
+				kill (pid, SIGKILL);
+				sleep (1);
+				waitpid (pid, &status, WNOHANG);
+			}
+		}
+
+		return content;
+
+	/* Forking Failed */
+	} else {
+		g_warning (
+			"Failed to create child process '%s': %s",
+			filename, g_strerror (errno));
+		close (in_fds[0]);
+		close (in_fds[1]);
+	}
+#endif
+
+	return NULL;
+}
diff --git a/mail/libemail-utils/e-signature-utils.h b/mail/libemail-utils/e-signature-utils.h
new file mode 100644
index 0000000..a642a13
--- /dev/null
+++ b/mail/libemail-utils/e-signature-utils.h
@@ -0,0 +1,40 @@
+/*
+ * e-signature-utils.h
+ *
+ * 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/>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+#ifndef E_SIGNATURE_UTILS_H
+#define E_SIGNATURE_UTILS_H
+
+#include <gtk/gtk.h>
+#include <libemail-utils/e-signature.h>
+#include <libemail-utils/e-signature-list.h>
+
+G_BEGIN_DECLS
+
+ESignatureList *e_get_signature_list		(void);
+ESignature *	e_get_signature_by_name		(const gchar *name);
+ESignature *	e_get_signature_by_uid		(const gchar *uid);
+gchar *		e_create_signature_file		(GError **error);
+gchar *		e_read_signature_file		(ESignature *signature,
+						 gboolean convert_to_html,
+						 GError **error);
+gchar *		e_run_signature_script		(const gchar *filename);
+
+G_END_DECLS
+
+#endif /* E_SIGNATURE_UTILS_H */
diff --git a/mail/libemail-utils/gconf-bridge.c b/mail/libemail-utils/gconf-bridge.c
new file mode 100644
index 0000000..3de48b6
--- /dev/null
+++ b/mail/libemail-utils/gconf-bridge.c
@@ -0,0 +1,1364 @@
+/*
+ * (C) 2005 OpenedHand Ltd.
+ *
+ * Author: Jorn Baayen <jorn openedhand com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "gconf-bridge.h"
+
+struct _GConfBridge {
+        GConfClient *client;
+
+        GHashTable *bindings;
+};
+
+/* The data structures for the different kinds of bindings */
+typedef enum {
+        BINDING_PROP,
+        BINDING_WINDOW,
+        BINDING_LIST_STORE
+} BindingType;
+
+typedef struct {
+        BindingType type;
+        guint id;
+
+        gboolean delayed_mode;
+
+        gchar *key;
+        guint val_notify_id;
+        GSList *val_changes; /* List of changes made to GConf value,
+                                that have not received change notification
+                                yet. */
+
+        GObject *object;
+        GParamSpec *prop;
+        gulong prop_notify_id;
+
+        guint sync_timeout_id; /* Used in delayed mode */
+} PropBinding;
+
+typedef struct {
+        BindingType type;
+        guint id;
+
+        gboolean bind_size;
+        gboolean bind_pos;
+
+        gchar *key_prefix;
+
+        GtkWindow *window;
+        gulong configure_event_id;
+        gulong window_state_event_id;
+        gulong unmap_id;
+        guint sync_timeout_id;
+} WindowBinding;
+
+typedef struct {
+        BindingType type;
+        guint id;
+
+        gchar *key;
+        guint val_notify_id;
+        GSList *val_changes; /* List of changes made to GConf value,
+                                that have not received change notification
+                                yet. */
+
+        GtkListStore *list_store;
+        guint row_inserted_id;
+        guint row_changed_id;
+        guint row_deleted_id;
+        guint rows_reordered_id;
+
+        guint sync_idle_id;
+} ListStoreBinding;
+
+/* Some trickery to be able to treat the data structures generically */
+typedef union {
+        BindingType      type;
+
+        PropBinding      prop_binding;
+        WindowBinding    window_binding;
+        ListStoreBinding list_store_binding;
+} Binding;
+
+/* Function prototypes */
+static void
+unbind (Binding *binding);
+
+static GConfBridge *bridge = NULL; /* Global GConfBridge object */
+
+/* Free up all resources allocated by the GConfBridge. Called on exit. */
+static void
+destroy_bridge (void)
+{
+	g_hash_table_destroy (bridge->bindings);
+	g_object_unref (bridge->client);
+
+	g_free (bridge);
+}
+
+/**
+ * gconf_bridge_get
+ *
+ * Returns the #GConfBridge. This is a singleton object.
+ *
+ * Return value: The #GConfBridge.
+ **/
+GConfBridge *
+gconf_bridge_get (void)
+{
+	if (bridge)
+		return bridge;
+
+	gconf_bridge_install_default_error_handler ();
+
+	bridge = g_new (GConfBridge, 1);
+
+	bridge->client = gconf_client_get_default ();
+	bridge->bindings = g_hash_table_new_full (NULL, NULL, NULL,
+						  (GDestroyNotify) unbind);
+
+	g_atexit (destroy_bridge);
+
+	return bridge;
+}
+
+/**
+ * gconf_bridge_get_client
+ * @bridge: A #GConfBridge
+ *
+ * Returns the #GConfClient used by @bridge. This is the same #GConfClient
+ * as returned by gconf_client_get_default().
+ *
+ * Return value: A #GConfClient.
+ **/
+GConfClient *
+gconf_bridge_get_client (GConfBridge *bridge)
+{
+	g_return_val_if_fail (bridge != NULL, NULL);
+
+	return bridge->client;
+}
+
+/* Generate an ID for a new binding */
+static guint
+new_id (void)
+{
+	static guint id_counter = 0;
+
+	id_counter++;
+
+	return id_counter;
+}
+
+/*
+ * Property bindings
+ */
+
+/* Syncs a value from GConf to an object property */
+static void
+prop_binding_sync_pref_to_prop (PropBinding *binding,
+                                GConfValue *pref_value)
+{
+	GValue src_value, value;
+
+        /* Make sure we don't enter an infinite synchronizing loop */
+	g_signal_handler_block (binding->object, binding->prop_notify_id);
+
+	memset (&src_value, 0, sizeof (GValue));
+
+        /* First, convert GConfValue to GValue */
+	switch (pref_value->type) {
+	case GCONF_VALUE_STRING:
+		g_value_init (&src_value, G_TYPE_STRING);
+		g_value_set_string (&src_value,
+				    gconf_value_get_string (pref_value));
+		break;
+	case GCONF_VALUE_INT:
+		g_value_init (&src_value, G_TYPE_INT);
+		g_value_set_int (&src_value,
+				 gconf_value_get_int (pref_value));
+		break;
+	case GCONF_VALUE_BOOL:
+		g_value_init (&src_value, G_TYPE_BOOLEAN);
+		g_value_set_boolean (&src_value,
+				     gconf_value_get_bool (pref_value));
+		break;
+	case GCONF_VALUE_FLOAT:
+		g_value_init (&src_value, G_TYPE_FLOAT);
+		g_value_set_float (&src_value,
+				   gconf_value_get_float (pref_value));
+		break;
+	default:
+                g_warning ("prop_binding_sync_pref_to_prop: Unhandled value "
+                           "type '%d'.\n", pref_value->type);
+
+		return;
+	}
+
+        /* Then convert to the type expected by the object, if necessary */
+	memset (&value, 0, sizeof (GValue));
+	g_value_init (&value,
+		      G_PARAM_SPEC_VALUE_TYPE (binding->prop));
+
+	if (src_value.g_type != value.g_type) {
+		if (!g_value_transform (&src_value, &value)) {
+                        g_warning ("prop_binding_sync_pref_to_prop: Failed to "
+                                   "transform a \"%s\" to a \"%s\".",
+				   g_type_name (src_value.g_type),
+				   g_type_name (value.g_type));
+
+			goto done;
+		}
+
+		g_object_set_property (binding->object,
+				       binding->prop->name, &value);
+	} else {
+		g_object_set_property (binding->object,
+				       binding->prop->name, &src_value);
+	}
+
+done:
+	g_value_unset (&src_value);
+	g_value_unset (&value);
+
+	g_signal_handler_unblock (binding->object, binding->prop_notify_id);
+}
+
+/* Syncs an object property to GConf */
+static void
+prop_binding_sync_prop_to_pref (PropBinding *binding)
+{
+	GValue value;
+	GConfValue *gconf_value;
+
+	memset (&value, 0, sizeof (GValue));
+
+	g_value_init (&value,
+		      G_PARAM_SPEC_VALUE_TYPE (binding->prop));
+	g_object_get_property (binding->object,
+			       binding->prop->name,
+			       &value);
+
+	switch (value.g_type) {
+	case G_TYPE_STRING:
+		gconf_value = gconf_value_new (GCONF_VALUE_STRING);
+		gconf_value_set_string (gconf_value,
+					g_value_get_string (&value));
+		break;
+	case G_TYPE_INT:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_int (&value));
+		break;
+	case G_TYPE_UINT:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_uint (&value));
+		break;
+	case G_TYPE_LONG:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_long (&value));
+		break;
+	case G_TYPE_ULONG:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_ulong (&value));
+		break;
+	case G_TYPE_INT64:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_int64 (&value));
+		break;
+	case G_TYPE_UINT64:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_uint64 (&value));
+		break;
+	case G_TYPE_CHAR:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_char (&value));
+		break;
+	case G_TYPE_UCHAR:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_uchar (&value));
+		break;
+	case G_TYPE_ENUM:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_enum (&value));
+		break;
+	case G_TYPE_BOOLEAN:
+		gconf_value = gconf_value_new (GCONF_VALUE_BOOL);
+		gconf_value_set_bool (gconf_value,
+				      g_value_get_boolean (&value));
+		break;
+	case G_TYPE_DOUBLE:
+		gconf_value = gconf_value_new (GCONF_VALUE_FLOAT);
+#ifdef HAVE_CORBA_GCONF
+                /* FIXME we cast to a gfloat explicitly as CORBA GConf
+                 * uses doubles in its API, but treats them as floats
+                 * when transporting them over CORBA. See #322837 */
+		gconf_value_set_float (gconf_value,
+				       (gfloat) g_value_get_double (&value));
+#else
+		gconf_value_set_float (gconf_value,
+				       g_value_get_double (&value));
+#endif
+		break;
+	case G_TYPE_FLOAT:
+		gconf_value = gconf_value_new (GCONF_VALUE_FLOAT);
+		gconf_value_set_float (gconf_value,
+				       g_value_get_float (&value));
+		break;
+	default:
+		if (g_type_is_a (value.g_type, G_TYPE_ENUM)) {
+			gconf_value = gconf_value_new (GCONF_VALUE_INT);
+			gconf_value_set_int (gconf_value,
+					     g_value_get_enum (&value));
+		} else {
+                        g_warning ("prop_binding_sync_prop_to_pref: "
+                                   "Unhandled value type '%s'.\n",
+				   g_type_name (value.g_type));
+
+			goto done;
+		}
+
+		break;
+	}
+
+        /* Set to GConf */
+	gconf_client_set (bridge->client, binding->key, gconf_value, NULL);
+
+        /* Store until change notification comes in, so that we are able
+         * to ignore it */
+	binding->val_changes = g_slist_append (binding->val_changes,
+					       gconf_value);
+
+done:
+	g_value_unset (&value);
+}
+
+/* Called when a GConf value bound to an object property has changed */
+static void
+prop_binding_pref_changed (GConfClient *client,
+                           guint cnxn_id,
+                           GConfEntry *entry,
+                           gpointer user_data)
+{
+	GConfValue *gconf_value;
+	PropBinding *binding;
+	GSList *l;
+
+	gconf_value = gconf_entry_get_value (entry);
+	if (!gconf_value)
+		return; /* NULL means that the value has been unset */
+
+	binding = (PropBinding *) user_data;
+
+        /* Check that this notification is not caused by sync_prop_to_pref() */
+	l = g_slist_find_custom (binding->val_changes,
+				 gconf_value,
+				 (GCompareFunc) gconf_value_compare);
+	if (l) {
+		gconf_value_free (l->data);
+
+		binding->val_changes = g_slist_delete_link
+			(binding->val_changes, l);
+
+		return;
+	}
+
+	prop_binding_sync_pref_to_prop (binding, gconf_value);
+}
+
+/* Performs a scheduled prop-to-pref sync for a prop binding in
+ * delay mode */
+static gboolean
+prop_binding_perform_scheduled_sync (PropBinding *binding)
+{
+	prop_binding_sync_prop_to_pref (binding);
+
+	binding->sync_timeout_id = 0;
+
+	g_object_unref (binding->object);
+
+	return FALSE;
+}
+
+#define PROP_BINDING_SYNC_DELAY 100 /* Delay for bindings with "delayed"
+                                       set to TRUE, in ms */
+
+/* Called when an object property has changed */
+static void
+prop_binding_prop_changed (GObject *object,
+                           GParamSpec *param_spec,
+                           PropBinding *binding)
+{
+	if (binding->delayed_mode) {
+                /* Just schedule a sync */
+		if (binding->sync_timeout_id == 0) {
+                        /* We keep a reference on the object as long as
+                         * we haven't synced yet to make sure we don't
+                         * lose any data */
+			g_object_ref (binding->object);
+
+			binding->sync_timeout_id =
+				g_timeout_add
+					(PROP_BINDING_SYNC_DELAY,
+					 (GSourceFunc)
+					    prop_binding_perform_scheduled_sync,
+					 binding);
+		}
+	} else {
+                /* Directly sync */
+		prop_binding_sync_prop_to_pref (binding);
+	}
+}
+
+/* Called when an object is destroyed */
+static void
+prop_binding_object_destroyed (gpointer user_data,
+                               GObject *where_the_object_was)
+{
+	PropBinding *binding;
+
+	binding = (PropBinding *) user_data;
+	binding->object = NULL; /* Don't do anything with the object
+                                 * at unbind() */
+
+	g_hash_table_remove (bridge->bindings,
+			     GUINT_TO_POINTER (binding->id));
+}
+
+/**
+ * gconf_bridge_bind_property_full
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be bound
+ * @object: A #GObject
+ * @prop: The property of @object to be bound
+ * @delayed_sync: TRUE if there should be a delay between property changes
+ * and syncs to GConf. Set to TRUE when binding to a rapidly-changing
+ * property, for example the "value" property on a #GtkAdjustment.
+ *
+ * Binds @key to @prop, causing them to have the same value at all times.
+ *
+ * The types of @key and @prop should be compatible. Floats and doubles, and
+ * ints, uints, longs, unlongs, int64s, uint64s, chars, uchars and enums
+ * can be matched up. Booleans and strings can only be matched to their
+ * respective types.
+ *
+ * On calling this function the current value of @key will be set to @prop.
+ *
+ * Return value: The ID of the new binding.
+ **/
+guint
+gconf_bridge_bind_property_full (GConfBridge *bridge,
+                                 const gchar *key,
+                                 GObject *object,
+                                 const gchar *prop,
+                                 gboolean delayed_sync)
+{
+	GParamSpec *pspec;
+	PropBinding *binding;
+	gchar *signal;
+	GConfValue *val;
+
+	g_return_val_if_fail (bridge != NULL, 0);
+	g_return_val_if_fail (key != NULL, 0);
+	g_return_val_if_fail (G_IS_OBJECT (object), 0);
+	g_return_val_if_fail (prop != NULL, 0);
+
+        /* First, try to fetch the propertys GParamSpec off the object */
+	pspec = g_object_class_find_property
+				(G_OBJECT_GET_CLASS (object), prop);
+	if (G_UNLIKELY (pspec == NULL)) {
+                g_warning ("gconf_bridge_bind_property_full: A property \"%s\" "
+                           "was not found. Please make sure you are passing "
+                           "the right property name.", prop);
+
+		return 0;
+	}
+
+        /* GParamSpec found: All good, create new binding. */
+	binding = g_new (PropBinding, 1);
+
+	binding->type = BINDING_PROP;
+	binding->id = new_id ();
+	binding->delayed_mode = delayed_sync;
+	binding->val_changes = NULL;
+	binding->key = g_strdup (key);
+	binding->object = object;
+	binding->prop = pspec;
+	binding->sync_timeout_id = 0;
+
+        /* Watch GConf key */
+	binding->val_notify_id =
+		gconf_client_notify_add (bridge->client, key,
+					 prop_binding_pref_changed,
+					 binding, NULL, NULL);
+
+        /* Connect to property change notifications */
+        signal = g_strconcat ("notify::", prop, NULL);
+	binding->prop_notify_id =
+		g_signal_connect (object, signal,
+				  G_CALLBACK (prop_binding_prop_changed),
+				  binding);
+	g_free (signal);
+
+        /* Sync object to value from GConf, if set */
+	val = gconf_client_get (bridge->client, key, NULL);
+	if (val) {
+		prop_binding_sync_pref_to_prop (binding, val);
+		gconf_value_free (val);
+	}
+
+        /* Handle case where watched object gets destroyed */
+	g_object_weak_ref (object,
+			   prop_binding_object_destroyed, binding);
+
+        /* Insert binding */
+	g_hash_table_insert (bridge->bindings,
+			     GUINT_TO_POINTER (binding->id), binding);
+
+        /* Done */
+	return binding->id;
+}
+
+static void
+prop_binding_block_cb (gpointer hkey,
+                       PropBinding *binding,
+                       const gchar *key)
+{
+	g_return_if_fail (binding != NULL);
+	g_return_if_fail (key != NULL);
+
+	if (binding->type == BINDING_PROP && binding->key &&
+		g_ascii_strcasecmp (binding->key, key) == 0)
+		g_signal_handler_block (
+			binding->object, binding->prop_notify_id);
+}
+
+static void
+prop_binding_unblock_cb (gpointer hkey,
+                         PropBinding *binding,
+                         const gchar *key)
+{
+	g_return_if_fail (binding != NULL);
+	g_return_if_fail (key != NULL);
+
+	if (binding->type == BINDING_PROP && binding->key &&
+		g_ascii_strcasecmp (binding->key, key) == 0)
+		g_signal_handler_unblock (
+			binding->object, binding->prop_notify_id);
+}
+
+void
+gconf_bridge_block_property_bindings (GConfBridge *bridge,
+                                      const gchar *key)
+{
+	g_return_if_fail (bridge != NULL);
+	g_return_if_fail (key != NULL);
+
+	g_hash_table_foreach (
+		bridge->bindings, (GHFunc)
+		prop_binding_block_cb, (gpointer) key);
+}
+
+void
+gconf_bridge_unblock_property_bindings (GConfBridge *bridge,
+                                        const gchar *key)
+{
+	g_return_if_fail (bridge != NULL);
+	g_return_if_fail (key != NULL);
+
+	g_hash_table_foreach (
+		bridge->bindings, (GHFunc)
+		prop_binding_unblock_cb, (gpointer) key);
+}
+
+/* Unbinds a property binding */
+static void
+prop_binding_unbind (PropBinding *binding)
+{
+	if (binding->delayed_mode && binding->sync_timeout_id > 0) {
+                /* Perform any scheduled syncs */
+		g_source_remove (binding->sync_timeout_id);
+
+                /* The object will still be around as we have
+                 * a reference */
+		prop_binding_perform_scheduled_sync (binding);
+	}
+
+	gconf_client_notify_remove (bridge->client,
+				    binding->val_notify_id);
+	g_free (binding->key);
+
+	while (binding->val_changes) {
+		gconf_value_free (binding->val_changes->data);
+
+		binding->val_changes = g_slist_delete_link
+			(binding->val_changes, binding->val_changes);
+	}
+
+        /* The object might have been destroyed .. */
+	if (binding->object) {
+		g_signal_handler_disconnect (binding->object,
+					     binding->prop_notify_id);
+
+		g_object_weak_unref (binding->object,
+				     prop_binding_object_destroyed, binding);
+	}
+}
+
+/*
+ * Window bindings
+ */
+
+/* Performs a scheduled dimensions-to-prefs sync for a window binding */
+static gboolean
+window_binding_perform_scheduled_sync (WindowBinding *binding)
+{
+	if (binding->bind_size) {
+		gint width, height;
+		gchar *key;
+		GdkWindowState state;
+		GdkWindow *window;
+
+		window = gtk_widget_get_window (GTK_WIDGET (binding->window));
+		state = gdk_window_get_state (window);
+
+		if (state & GDK_WINDOW_STATE_MAXIMIZED) {
+                        key = g_strconcat (binding->key_prefix, "_maximized", NULL);
+			gconf_client_set_bool (bridge->client, key, TRUE, NULL);
+			g_free (key);
+		} else {
+			gtk_window_get_size (binding->window, &width, &height);
+
+                        key = g_strconcat (binding->key_prefix, "_width", NULL);
+			gconf_client_set_int (bridge->client, key, width, NULL);
+			g_free (key);
+
+                        key = g_strconcat (binding->key_prefix, "_height", NULL);
+			gconf_client_set_int (bridge->client, key, height, NULL);
+			g_free (key);
+
+                        key = g_strconcat (binding->key_prefix, "_maximized", NULL);
+			gconf_client_set_bool (bridge->client, key, FALSE, NULL);
+			g_free (key);
+		}
+	}
+
+	if (binding->bind_pos) {
+		gint x, y;
+		gchar *key;
+
+		gtk_window_get_position (binding->window, &x, &y);
+
+                key = g_strconcat (binding->key_prefix, "_x", NULL);
+		gconf_client_set_int (bridge->client, key, x, NULL);
+		g_free (key);
+
+                key = g_strconcat (binding->key_prefix, "_y", NULL);
+		gconf_client_set_int (bridge->client, key, y, NULL);
+		g_free (key);
+	}
+
+	binding->sync_timeout_id = 0;
+
+	return FALSE;
+}
+
+#define WINDOW_BINDING_SYNC_DELAY 1 /* Delay before syncing new window
+                                          dimensions to GConf, in s */
+
+/* Called when the window han been resized or moved */
+static gboolean
+window_binding_configure_event_cb (GtkWindow *window,
+                                   GdkEventConfigure *event,
+                                   WindowBinding *binding)
+{
+        /* re-postpone by cancel of the previous request */
+	if (binding->sync_timeout_id > 0)
+		g_source_remove (binding->sync_timeout_id);
+
+        /* Schedule a sync */
+	binding->sync_timeout_id = g_timeout_add_seconds (WINDOW_BINDING_SYNC_DELAY,
+		(GSourceFunc) window_binding_perform_scheduled_sync,
+		binding);
+
+	return FALSE;
+}
+
+/* Called when the window state is being changed */
+static gboolean
+window_binding_state_event_cb (GtkWindow *window,
+                               GdkEventWindowState *event,
+                               WindowBinding *binding)
+{
+	if (binding->sync_timeout_id > 0)
+		g_source_remove (binding->sync_timeout_id);
+
+	if (event
+	    && (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) != 0
+	    && (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) == 0) {
+		/* the window was restored from a maximized state; make sure its
+		 * width and height is the one user stored before maximization */
+		gint width, height;
+
+		width = GPOINTER_TO_INT (g_object_get_data (
+			G_OBJECT (window), "binding-premax-width"));
+		height = GPOINTER_TO_INT (g_object_get_data (
+			G_OBJECT (window), "binding-premax-height"));
+
+		if (width && height) {
+			gtk_window_resize (window, width, height);
+
+			/* Do this only once, as it is restored
+			 * after loading maximized state. */
+			g_object_set_data (
+				G_OBJECT (window),
+				"binding-premax-width", NULL);
+			g_object_set_data (
+				G_OBJECT (window),
+				"binding-premax-height", NULL);
+		}
+	}
+
+	window_binding_perform_scheduled_sync (binding);
+
+	return FALSE;
+}
+
+/* Called when the window is being unmapped */
+static gboolean
+window_binding_unmap_cb (GtkWindow *window,
+                         WindowBinding *binding)
+{
+        /* Force sync */
+	if (binding->sync_timeout_id > 0)
+		g_source_remove (binding->sync_timeout_id);
+
+        /* XXX It's too late to record the window position.
+         *     gtk_window_get_position() will report (0, 0). */
+	binding->bind_pos = FALSE;
+
+	window_binding_perform_scheduled_sync (binding);
+
+	return FALSE;
+}
+
+/* Called when a window is destroyed */
+static void
+window_binding_window_destroyed (gpointer user_data,
+                                 GObject *where_the_object_was)
+{
+	WindowBinding *binding;
+
+	binding = (WindowBinding *) user_data;
+	binding->window = NULL; /* Don't do anything with the window
+                                 * at unbind() */
+
+	if (binding->sync_timeout_id > 0)
+		g_source_remove (binding->sync_timeout_id);
+
+	g_hash_table_remove (bridge->bindings,
+			     GUINT_TO_POINTER (binding->id));
+}
+
+/**
+ * gconf_bridge_bind_window
+ * @bridge: A #GConfBridge
+ * @key_prefix: The prefix of the GConf keys
+ * @window: A #GtkWindow
+ * @bind_size: TRUE to bind the size of @window
+ * @bind_pos: TRUE to bind the position of @window
+ *
+ * On calling this function @window will be resized to the values
+ * specified by "@key_prefix<!-- -->_width" and "@key_prefix<!-- -->_height"
+ * and maximixed if "@key_prefix<!-- -->_maximized is TRUE if
+ * @bind_size is TRUE, and moved to the values specified by
+ * "@key_prefix<!-- -->_x" and "@key_prefix<!-- -->_y" if @bind_pos is TRUE.
+ * The respective GConf values will be updated when the window is resized
+ * and/or moved.
+ *
+ * Return value: The ID of the new binding.
+ **/
+guint
+gconf_bridge_bind_window (GConfBridge *bridge,
+                          const gchar *key_prefix,
+                          GtkWindow *window,
+                          gboolean bind_size,
+                          gboolean bind_pos)
+{
+	WindowBinding *binding;
+
+	g_return_val_if_fail (bridge != NULL, 0);
+	g_return_val_if_fail (key_prefix != NULL, 0);
+	g_return_val_if_fail (GTK_IS_WINDOW (window), 0);
+
+        /* Create new binding. */
+	binding = g_new (WindowBinding, 1);
+
+	binding->type = BINDING_WINDOW;
+	binding->id = new_id ();
+	binding->bind_size = bind_size;
+	binding->bind_pos = bind_pos;
+	binding->key_prefix = g_strdup (key_prefix);
+	binding->window = window;
+	binding->sync_timeout_id = 0;
+
+        /* Set up GConf keys & sync window to GConf values */
+	if (bind_size) {
+		gchar *key;
+		GConfValue *width_val, *height_val, *maximized_val;
+
+                key = g_strconcat (key_prefix, "_width", NULL);
+		width_val = gconf_client_get (bridge->client, key, NULL);
+		g_free (key);
+
+                key = g_strconcat (key_prefix, "_height", NULL);
+		height_val = gconf_client_get (bridge->client, key, NULL);
+		g_free (key);
+
+                key = g_strconcat (key_prefix, "_maximized", NULL);
+		maximized_val = gconf_client_get (bridge->client, key, NULL);
+		g_free (key);
+
+		if (width_val && height_val) {
+			gtk_window_resize (window,
+					   gconf_value_get_int (width_val),
+					   gconf_value_get_int (height_val));
+
+			gconf_value_free (width_val);
+			gconf_value_free (height_val);
+		} else if (width_val) {
+			gconf_value_free (width_val);
+		} else if (height_val) {
+			gconf_value_free (height_val);
+		}
+
+		if (maximized_val) {
+			if (gconf_value_get_bool (maximized_val)) {
+				/* Maximize is not done immediately, but to
+				 * count with proper window size, resize it
+				 * before. The previous size is restored
+				 * after the maximization is changed,
+				 * in window_binding_state_event_cb(). */
+				gint width = 0, height = 0;
+				GdkScreen *screen;
+
+				gtk_window_get_size (window, &width, &height);
+				g_object_set_data (
+					G_OBJECT (window),
+					"binding-premax-width",
+					GINT_TO_POINTER (width));
+				g_object_set_data (
+					G_OBJECT (window),
+					"binding-premax-height",
+					GINT_TO_POINTER (height));
+
+				screen = gtk_window_get_screen (window);
+				gtk_window_resize (window,
+					gdk_screen_get_width (screen),
+					gdk_screen_get_height (screen));
+
+				gtk_window_maximize (window);
+			}
+			gconf_value_free (maximized_val);
+		}
+	}
+
+	if (bind_pos) {
+		gchar *key;
+		GConfValue *x_val, *y_val;
+
+                key = g_strconcat (key_prefix, "_x", NULL);
+		x_val = gconf_client_get (bridge->client, key, NULL);
+		g_free (key);
+
+                key = g_strconcat (key_prefix, "_y", NULL);
+		y_val = gconf_client_get (bridge->client, key, NULL);
+		g_free (key);
+
+		if (x_val && y_val) {
+			gtk_window_move (window,
+					 gconf_value_get_int (x_val),
+					 gconf_value_get_int (y_val));
+
+			gconf_value_free (x_val);
+			gconf_value_free (y_val);
+		} else if (x_val) {
+			gconf_value_free (x_val);
+		} else if (y_val) {
+			gconf_value_free (y_val);
+		}
+	}
+
+        /* Connect to window size change notifications */
+	binding->configure_event_id =
+		g_signal_connect (window,
+                                  "configure-event",
+				  G_CALLBACK
+					(window_binding_configure_event_cb),
+				  binding);
+
+	binding->window_state_event_id =
+		g_signal_connect (window,
+                                  "window_state_event",
+				  G_CALLBACK
+					(window_binding_state_event_cb),
+				  binding);
+	binding->unmap_id =
+		g_signal_connect (window,
+                                  "unmap",
+				  G_CALLBACK (window_binding_unmap_cb),
+				  binding);
+
+        /* Handle case where window gets destroyed */
+	g_object_weak_ref (G_OBJECT (window),
+			   window_binding_window_destroyed, binding);
+
+        /* Insert binding */
+	g_hash_table_insert (bridge->bindings,
+			     GUINT_TO_POINTER (binding->id), binding);
+
+        /* Done */
+	return binding->id;
+}
+
+/* Unbinds a window binding */
+static void
+window_binding_unbind (WindowBinding *binding)
+{
+	if (binding->sync_timeout_id > 0)
+		g_source_remove (binding->sync_timeout_id);
+
+	g_free (binding->key_prefix);
+
+        /* The window might have been destroyed .. */
+	if (binding->window) {
+		g_signal_handler_disconnect (binding->window,
+					     binding->configure_event_id);
+		g_signal_handler_disconnect (binding->window,
+					     binding->window_state_event_id);
+		g_signal_handler_disconnect (binding->window,
+					     binding->unmap_id);
+
+		g_object_weak_unref (G_OBJECT (binding->window),
+				     window_binding_window_destroyed, binding);
+	}
+}
+
+/*
+ * List store bindings
+ */
+
+/* Fills a GtkListStore with the string list from @value */
+static void
+list_store_binding_sync_pref_to_store (ListStoreBinding *binding,
+                                       GConfValue *value)
+{
+	GSList *list, *l;
+	GtkTreeIter iter;
+
+        /* Make sure we don't enter an infinite synchronizing loop */
+	g_signal_handler_block (binding->list_store,
+				binding->row_inserted_id);
+	g_signal_handler_block (binding->list_store,
+				binding->row_deleted_id);
+
+	gtk_list_store_clear (binding->list_store);
+
+	list = gconf_value_get_list (value);
+	for (l = list; l; l = l->next) {
+		GConfValue *l_value;
+		const gchar *string;
+
+		l_value = (GConfValue *) l->data;
+		string = gconf_value_get_string (l_value);
+
+		gtk_list_store_insert_with_values (binding->list_store,
+						   &iter, -1,
+						   0, string,
+						   -1);
+	}
+
+	g_signal_handler_unblock (binding->list_store,
+				  binding->row_inserted_id);
+	g_signal_handler_unblock (binding->list_store,
+				  binding->row_deleted_id);
+}
+
+/* Sets a GConf value to the contents of a GtkListStore */
+static gboolean
+list_store_binding_sync_store_to_pref (ListStoreBinding *binding)
+{
+	GtkTreeModel *tree_model;
+	GtkTreeIter iter;
+	GSList *list;
+	gint res;
+	GConfValue *gconf_value;
+
+	tree_model = GTK_TREE_MODEL (binding->list_store);
+
+        /* Build list */
+	list = NULL;
+	res = gtk_tree_model_get_iter_first (tree_model, &iter);
+	while (res) {
+		gchar *string;
+		GConfValue *tmp_value;
+
+		gtk_tree_model_get (tree_model, &iter,
+				    0, &string, -1);
+
+		tmp_value = gconf_value_new (GCONF_VALUE_STRING);
+		gconf_value_set_string (tmp_value, string);
+
+		list = g_slist_append (list, tmp_value);
+
+		res = gtk_tree_model_iter_next (tree_model, &iter);
+	}
+
+        /* Create value */
+	gconf_value = gconf_value_new (GCONF_VALUE_LIST);
+	gconf_value_set_list_type (gconf_value, GCONF_VALUE_STRING);
+	gconf_value_set_list_nocopy (gconf_value, list);
+
+        /* Set */
+	gconf_client_set (bridge->client, binding->key, gconf_value, NULL);
+
+        /* Store until change notification comes in, so that we are able
+         * to ignore it */
+	binding->val_changes = g_slist_append (binding->val_changes,
+					       gconf_value);
+
+	binding->sync_idle_id = 0;
+
+	g_object_unref (binding->list_store);
+
+	return FALSE;
+}
+
+/* Pref changed: sync */
+static void
+list_store_binding_pref_changed (GConfClient *client,
+                                 guint cnxn_id,
+                                 GConfEntry *entry,
+                                 gpointer user_data)
+{
+	GConfValue *gconf_value;
+	ListStoreBinding *binding;
+	GSList *l;
+
+	gconf_value = gconf_entry_get_value (entry);
+	if (!gconf_value)
+		return; /* NULL means that the value has been unset */
+
+	binding = (ListStoreBinding *) user_data;
+
+        /* Check that this notification is not caused by
+         * sync_store_to_pref() */
+	l = g_slist_find_custom (binding->val_changes,
+				 gconf_value,
+				 (GCompareFunc) gconf_value_compare);
+	if (l) {
+		gconf_value_free (l->data);
+
+		binding->val_changes = g_slist_delete_link
+			(binding->val_changes, l);
+
+		return;
+	}
+
+	list_store_binding_sync_pref_to_store (binding, gconf_value);
+}
+
+/* Called when an object is destroyed */
+static void
+list_store_binding_store_destroyed (gpointer user_data,
+                                    GObject *where_the_object_was)
+{
+	ListStoreBinding *binding;
+
+	binding = (ListStoreBinding *) user_data;
+	binding->list_store = NULL; /* Don't do anything with the store
+                                     * at unbind() */
+
+	g_hash_table_remove (bridge->bindings,
+			     GUINT_TO_POINTER (binding->id));
+}
+
+/* List store changed: Sync */
+static void
+list_store_binding_store_changed_cb (ListStoreBinding *binding)
+{
+	if (binding->sync_idle_id == 0) {
+		g_object_ref (binding->list_store);
+
+		binding->sync_idle_id = g_idle_add
+			((GSourceFunc) list_store_binding_sync_store_to_pref,
+			 binding);
+	}
+}
+
+/**
+ * gconf_bridge_bind_string_list_store
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be bound
+ * @list_store: A #GtkListStore
+ *
+ * On calling this function single string column #GtkListStore @list_store
+ * will be kept synchronized with the GConf string list value pointed to by
+ * @key. On calling this function @list_store will be populated with the
+ * strings specified by the value of @key.
+ *
+ * Return value: The ID of the new binding.
+ **/
+guint
+gconf_bridge_bind_string_list_store (GConfBridge *bridge,
+                                     const gchar *key,
+                                     GtkListStore *list_store)
+{
+	GtkTreeModel *tree_model;
+	gboolean have_one_column, is_string_column;
+	ListStoreBinding *binding;
+	GConfValue *val;
+
+	g_return_val_if_fail (bridge != NULL, 0);
+	g_return_val_if_fail (key != NULL, 0);
+	g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), 0);
+
+        /* Check list store suitability */
+	tree_model = GTK_TREE_MODEL (list_store);
+	have_one_column = (gtk_tree_model_get_n_columns (tree_model) == 1);
+	is_string_column = (gtk_tree_model_get_column_type
+					(tree_model, 0) == G_TYPE_STRING);
+	if (G_UNLIKELY (!have_one_column || !is_string_column)) {
+                g_warning ("gconf_bridge_bind_string_list_store: Only "
+                           "GtkListStores with exactly one string column are "
+                           "supported.");
+
+		return 0;
+	}
+
+        /* Create new binding. */
+	binding = g_new (ListStoreBinding, 1);
+
+	binding->type = BINDING_LIST_STORE;
+	binding->id = new_id ();
+	binding->key = g_strdup (key);
+	binding->val_changes = NULL;
+	binding->list_store = list_store;
+	binding->sync_idle_id = 0;
+
+        /* Watch GConf key */
+	binding->val_notify_id =
+		gconf_client_notify_add (bridge->client, key,
+					 list_store_binding_pref_changed,
+					 binding, NULL, NULL);
+
+        /* Connect to ListStore change notifications */
+	binding->row_inserted_id =
+                g_signal_connect_swapped (list_store, "row-inserted",
+					  G_CALLBACK
+					  (list_store_binding_store_changed_cb),
+					  binding);
+	binding->row_changed_id =
+                g_signal_connect_swapped (list_store, "row-changed",
+					  G_CALLBACK
+					  (list_store_binding_store_changed_cb),
+					  binding);
+	binding->row_deleted_id =
+                g_signal_connect_swapped (list_store, "row-deleted",
+					  G_CALLBACK
+					  (list_store_binding_store_changed_cb),
+					  binding);
+	binding->rows_reordered_id =
+                g_signal_connect_swapped (list_store, "rows-reordered",
+					  G_CALLBACK
+					  (list_store_binding_store_changed_cb),
+					  binding);
+
+        /* Sync object to value from GConf, if set */
+	val = gconf_client_get (bridge->client, key, NULL);
+	if (val) {
+		list_store_binding_sync_pref_to_store (binding, val);
+		gconf_value_free (val);
+	}
+
+        /* Handle case where watched object gets destroyed */
+	g_object_weak_ref (G_OBJECT (list_store),
+			   list_store_binding_store_destroyed, binding);
+
+        /* Insert binding */
+	g_hash_table_insert (bridge->bindings,
+			     GUINT_TO_POINTER (binding->id), binding);
+
+        /* Done */
+	return binding->id;
+}
+
+/* Unbinds a list store binding */
+static void
+list_store_binding_unbind (ListStoreBinding *binding)
+{
+        /* Perform any scheduled syncs */
+	if (binding->sync_idle_id > 0) {
+		g_source_remove (binding->sync_idle_id);
+
+                /* The store will still be around as we added a reference */
+		list_store_binding_sync_store_to_pref (binding);
+	}
+
+	g_free (binding->key);
+
+	while (binding->val_changes) {
+		gconf_value_free (binding->val_changes->data);
+
+		binding->val_changes = g_slist_delete_link
+			(binding->val_changes, binding->val_changes);
+	}
+
+        /* The store might have been destroyed .. */
+	if (binding->list_store) {
+		g_signal_handler_disconnect (binding->list_store,
+					     binding->row_inserted_id);
+		g_signal_handler_disconnect (binding->list_store,
+					     binding->row_changed_id);
+		g_signal_handler_disconnect (binding->list_store,
+					     binding->row_deleted_id);
+		g_signal_handler_disconnect (binding->list_store,
+					     binding->rows_reordered_id);
+
+		g_object_weak_unref (G_OBJECT (binding->list_store),
+				     list_store_binding_store_destroyed,
+				     binding);
+	}
+}
+
+/*
+ * Generic unbinding
+ */
+
+/* Unbinds a binding */
+static void
+unbind (Binding *binding)
+{
+        /* Call specialized unbinding function */
+	switch (binding->type) {
+	case BINDING_PROP:
+		prop_binding_unbind ((PropBinding *) binding);
+		break;
+	case BINDING_WINDOW:
+		window_binding_unbind ((WindowBinding *) binding);
+		break;
+	case BINDING_LIST_STORE:
+		list_store_binding_unbind ((ListStoreBinding *) binding);
+		break;
+	default:
+                g_warning ("Unknown binding type '%d'\n", binding->type);
+		break;
+	}
+
+	g_free (binding);
+}
+
+/**
+ * gconf_bridge_unbind
+ * @bridge: A #GConfBridge
+ * @binding_id: The ID of the binding to be removed
+ *
+ * Removes the binding with ID @binding_id.
+ **/
+void
+gconf_bridge_unbind (GConfBridge *bridge,
+                     guint binding_id)
+{
+	g_return_if_fail (bridge != NULL);
+	g_return_if_fail (binding_id > 0);
+
+        /* This will trigger the hash tables value destruction
+         * function, which will take care of further cleanup */
+	g_hash_table_remove (bridge->bindings,
+			     GUINT_TO_POINTER (binding_id));
+}
+
+/*
+ * Error handling
+ */
+
+/* This is the same dialog as used in eel */
+static void
+error_handler (GConfClient *client,
+               GError *error)
+{
+	static gboolean shown_dialog = FALSE;
+
+        g_warning ("GConf error:\n  %s", error->message);
+
+	if (!shown_dialog) {
+		gchar *message;
+		GtkWidget *dlg;
+
+                message = g_strdup_printf (_("GConf error: %s"),
+					   error->message);
+		dlg = gtk_message_dialog_new (NULL, 0,
+					      GTK_MESSAGE_ERROR,
+					      GTK_BUTTONS_OK,
+                                              "%s",
+					      message);
+		g_free (message);
+
+		gtk_message_dialog_format_secondary_text
+			(GTK_MESSAGE_DIALOG (dlg),
+                         _("All further errors shown only on terminal."));
+                gtk_window_set_title (GTK_WINDOW (dlg), "");
+
+		gtk_dialog_run (GTK_DIALOG (dlg));
+
+		gtk_widget_destroy (dlg);
+
+		shown_dialog = TRUE;
+	}
+}
+
+/**
+ * gconf_bridge_install_default_error_handler
+ *
+ * Sets up the default error handler. Any unhandled GConf errors will
+ * automatically be handled by presenting the user an error dialog.
+ **/
+void
+gconf_bridge_install_default_error_handler (void)
+{
+	gconf_client_set_global_default_error_handler (error_handler);
+}
diff --git a/mail/libemail-utils/gconf-bridge.h b/mail/libemail-utils/gconf-bridge.h
new file mode 100644
index 0000000..371f117
--- /dev/null
+++ b/mail/libemail-utils/gconf-bridge.h
@@ -0,0 +1,134 @@
+/*
+ * (C) 2005 OpenedHand Ltd.
+ *
+ * Author: Jorn Baayen <jorn openedhand com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GCONF_BRIDGE_H__
+#define __GCONF_BRIDGE_H__
+
+#include <gconf/gconf-client.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+void gconf_bridge_install_default_error_handler (void);
+
+typedef struct _GConfBridge GConfBridge;
+
+GConfBridge *gconf_bridge_get                    (void);
+
+GConfClient *gconf_bridge_get_client             (GConfBridge  *bridge);
+
+guint        gconf_bridge_bind_property_full     (GConfBridge  *bridge,
+                                                  const gchar   *key,
+                                                  GObject      *object,
+                                                  const gchar   *prop,
+                                                  gboolean      delayed_sync);
+
+/**
+ * gconf_bridge_bind_property
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be bound
+ * @object: A #GObject
+ * @prop: The property of @object to be bound
+ *
+ * Binds @key to @prop without delays, causing them to have the same value at all times. See
+ * #gconf_bridge_bind_property_full for more details.
+ *
+ **/
+#define gconf_bridge_bind_property(bridge, key, object, prop) \
+        gconf_bridge_bind_property_full ((bridge), (key), \
+                                         (object), (prop), FALSE)
+
+/**
+ * gconf_bridge_bind_property_delayed
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be bound
+ * @object: A #GObject
+ * @prop: The property of @object to be bound
+ *
+ * Binds @key to @prop with a delay, causing them to have the same value at all
+ * times. See #gconf_bridge_bind_property_full for more details.
+ **/
+#define gconf_bridge_bind_property_delayed(bridge, key, object, prop) \
+        gconf_bridge_bind_property_full ((bridge), (key), \
+                                         (object), (prop), TRUE)
+
+/**
+ * gconf_bridge_block_property_bindings
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be blocked
+ *
+ * Blocks property bindings for @key. To unblock it call #gconf_bridge_unblock_property_bindings.
+ **/
+void         gconf_bridge_block_property_bindings (GConfBridge  *bridge, const gchar *key);
+
+/**
+ * gconf_bridge_unblock_property_bindings
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be unblocked
+ *
+ * Unblocks property bindings for @key, these should be previously blocked with #gconf_bridge_unblock_property_bindings.
+ **/
+void         gconf_bridge_unblock_property_bindings (GConfBridge  *bridge, const gchar *key);
+
+guint        gconf_bridge_bind_window            (GConfBridge  *bridge,
+                                                  const gchar   *key_prefix,
+                                                  GtkWindow    *window,
+                                                  gboolean      bind_size,
+                                                  gboolean      bind_pos);
+
+/**
+ * gconf_bridge_bind_window_size
+ * @bridge: A #GConfBridge
+ * @key_prefix: The prefix of the GConf keys
+ * @window: A #GtkWindow
+ *
+ * On calling this function @window will be resized to the values specified by
+ * "@key_prefix<!-- -->_width" and "@key_prefix<!-- -->_height".  The respective
+ * GConf values will be updated when the window is resized. See
+ * #gconf_bridge_bind_window for more details.
+ **/
+#define gconf_bridge_bind_window_size(bridge, key_prefix, window) \
+        gconf_bridge_bind_window ((bridge), (key_prefix), (window), TRUE, FALSE)
+
+/**
+ * gconf_bridge_bind_window_pos
+ * @bridge: A #GConfBridge
+ * @key_prefix: The prefix of the GConf keys
+ * @window: A #GtkWindow
+ *
+ * On calling this function @window will be moved to the values specified by
+ * "@key_prefix<!-- -->_x" and "@key_prefix<!-- -->_y". The respective GConf
+ * values will be updated when the window is moved. See
+ * #gconf_bridge_bind_window for more details.
+ **/
+#define gconf_bridge_bind_window_pos(bridge, key_prefix, window) \
+        gconf_bridge_bind_window ((bridge), (key_prefix), (window), FALSE, TRUE)
+
+guint        gconf_bridge_bind_string_list_store (GConfBridge  *bridge,
+                                                  const gchar   *key,
+                                                  GtkListStore *list_store);
+
+void         gconf_bridge_unbind                 (GConfBridge  *bridge,
+                                                  guint         binding_id);
+
+G_END_DECLS
+
+#endif /* __GCONF_BRIDGE_H__ */
diff --git a/mail/libemail-utils/mail-mt.h b/mail/libemail-utils/mail-mt.h
new file mode 100644
index 0000000..0d0c470
--- /dev/null
+++ b/mail/libemail-utils/mail-mt.h
@@ -0,0 +1,102 @@
+/*
+ * 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)
+ *
+ */
+
+#ifndef _MAIL_MT
+#define _MAIL_MT
+
+#include <camel/camel.h>
+
+typedef struct _MailMsg MailMsg;
+typedef struct _MailMsgInfo MailMsgInfo;
+
+typedef gchar *	(*MailMsgDescFunc)		(MailMsg *msg);
+typedef void	(*MailMsgExecFunc)		(MailMsg *msg,
+						 GCancellable *cancellable,
+						 GError **error);
+typedef void	(*MailMsgDoneFunc)		(MailMsg *msg);
+typedef void	(*MailMsgFreeFunc)		(MailMsg *msg);
+typedef void	(*MailMsgDispatchFunc)		(gpointer msg);
+
+struct _MailMsg {
+	MailMsgInfo *info;
+	volatile gint ref_count;
+	guint seq;			/* seq number for synchronisation */
+	gint priority;			/* priority (default = 0) */
+	GCancellable *cancellable;
+	GError *error;			/* up to the caller to use this */
+};
+
+struct _MailMsgInfo {
+	gsize size;
+	MailMsgDescFunc desc;
+	MailMsgExecFunc exec;
+	MailMsgDoneFunc done;
+	MailMsgFreeFunc free;
+};
+
+/* setup ports */
+void mail_msg_init (void);
+
+gboolean mail_in_main_thread (void);
+
+/* allocate a new message */
+gpointer mail_msg_new (MailMsgInfo *info);
+gpointer mail_msg_ref (gpointer msg);
+void mail_msg_unref (gpointer msg);
+void mail_msg_check_error (gpointer msg);
+void mail_msg_cancel (guint msgid);
+gboolean mail_msg_active (void);
+
+/* dispatch a message */
+void mail_msg_main_loop_push (gpointer msg);
+void mail_msg_unordered_push (gpointer msg);
+void mail_msg_fast_ordered_push (gpointer msg);
+void mail_msg_slow_ordered_push (gpointer msg);
+
+/* To implement the stop button */
+GHook * mail_cancel_hook_add (GHookFunc func, gpointer data);
+void mail_cancel_hook_remove (GHook *hook);
+void mail_cancel_all (void);
+
+/* request a string/password */
+gchar *mail_get_password (CamelService *service, const gchar *prompt,
+			 gboolean secret, gboolean *cache);
+
+void mail_mt_set_backend (gchar *backend);
+
+/* Call a function in the GUI thread, wait for it to return, type is
+ * the marshaller to use.  FIXME This thing is horrible, please put
+ * it out of its misery. */
+typedef enum {
+	MAIL_CALL_p_p,
+	MAIL_CALL_p_pp,
+	MAIL_CALL_p_ppp,
+	MAIL_CALL_p_pppp,
+	MAIL_CALL_p_ppppp,
+	MAIL_CALL_p_ppippp
+} mail_call_t;
+
+typedef gpointer (*MailMainFunc)();
+
+gpointer mail_call_main (mail_call_t type, MailMainFunc func, ...);
+
+#endif /* _MAIL_MT */



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