From 24b0c3d20d4ce21e9072d58e8bb70e10f82e3f46 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Tue, 22 Apr 2008 14:57:43 +0000 Subject: Move these files to e-util. 2008-04-22 Matthew Barnes * composer/gconf-bridge.c: * composer/gconf-bridge.h: Move these files to e-util. * composer/Makefile.am: * composer/e-composer-private.h: * mail/em-composer-prefs.c: Adapt. * e-util/Makefile.am: Add gconf-bridge.[ch] from composer. svn path=/trunk/; revision=35399 --- composer/ChangeLog | 10 + composer/Makefile.am | 4 +- composer/e-composer-private.h | 2 +- composer/gconf-bridge.c | 1249 ----------------------------------------- composer/gconf-bridge.h | 117 ---- e-util/ChangeLog | 5 + e-util/Makefile.am | 2 + e-util/gconf-bridge.c | 1249 +++++++++++++++++++++++++++++++++++++++++ e-util/gconf-bridge.h | 117 ++++ mail/ChangeLog | 5 + mail/em-composer-prefs.c | 2 +- 11 files changed, 1391 insertions(+), 1371 deletions(-) delete mode 100644 composer/gconf-bridge.c delete mode 100644 composer/gconf-bridge.h create mode 100644 e-util/gconf-bridge.c create mode 100644 e-util/gconf-bridge.h diff --git a/composer/ChangeLog b/composer/ChangeLog index bd6c1a3037..9d0dfb7e5b 100644 --- a/composer/ChangeLog +++ b/composer/ChangeLog @@ -1,3 +1,13 @@ +2008-04-22 Matthew Barnes + + * gconf-bridge.c: + * gconf-bridge.h: + Move these files to e-util. + + * Makefile.am: + * e-composer-private.h: + Adapt. + 2008-04-17 Milan Crha ** Part of fix for bug #526739 diff --git a/composer/Makefile.am b/composer/Makefile.am index 562687a1d4..5b777f5525 100644 --- a/composer/Makefile.am +++ b/composer/Makefile.am @@ -47,9 +47,7 @@ libcomposer_la_SOURCES = \ e-composer-text-header.c \ e-composer-text-header.h \ e-msg-composer.c \ - e-msg-composer.h \ - gconf-bridge.c \ - gconf-bridge.h + e-msg-composer.h uidir = $(evolutionuidir) ui_DATA = evolution-composer.ui diff --git a/composer/e-composer-private.h b/composer/e-composer-private.h index 5cea10af89..5381dac80d 100644 --- a/composer/e-composer-private.h +++ b/composer/e-composer-private.h @@ -24,7 +24,6 @@ #include #include -#include "gconf-bridge.h" #include @@ -32,6 +31,7 @@ #include "e-composer-actions.h" #include "e-composer-autosave.h" #include "e-composer-header-table.h" +#include "e-util/gconf-bridge.h" #define E_MSG_COMPOSER_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ diff --git a/composer/gconf-bridge.c b/composer/gconf-bridge.c deleted file mode 100644 index 49754cfe96..0000000000 --- a/composer/gconf-bridge.c +++ /dev/null @@ -1,1249 +0,0 @@ -/* - * (C) 2005 OpenedHand Ltd. - * - * Author: Jorn Baayen - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include - -#include -#include -#include - -#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; - - char *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; - - char *key_prefix; - - GtkWindow *window; - gulong configure_event_id; - gulong unmap_id; - guint sync_timeout_id; -} WindowBinding; - -typedef struct { - BindingType type; - guint id; - - char *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); - -#if !HAVE_DECL_GCONF_VALUE_COMPARE /* Not in headers in GConf < 2.13 */ -int gconf_value_compare (const GConfValue *value_a, - const GConfValue *value_b); -#endif - -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 float 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, - (float) 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 char *key, - GObject *object, - const char *prop, - gboolean delayed_sync) -{ - GParamSpec *pspec; - PropBinding *binding; - char *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; -} - -/* 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) { - int width, height; - char *key; - GdkWindowState state; - - state = gdk_window_get_state (GTK_WIDGET (binding->window)->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) { - int x, y; - char *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 1000 /* Delay before syncing new window - dimensions to GConf, in ms */ - -/* Called when the window han been resized or moved */ -static gboolean -window_binding_configure_event_cb (GtkWindow *window, - GdkEventConfigure *event, - WindowBinding *binding) -{ - /* Schedule a sync */ - if (binding->sync_timeout_id == 0) { - binding->sync_timeout_id = - g_timeout_add (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) -{ - 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); - - 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() */ - - 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 char *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) { - char *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)) { - gtk_window_maximize (window); - } - gconf_value_free (maximized_val); - } - } - - if (bind_pos) { - char *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->configure_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->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 char *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; - int 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) { - char *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 char *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-inserted", - G_CALLBACK - (list_store_binding_store_changed_cb), - binding); - binding->row_deleted_id = - g_signal_connect_swapped (list_store, "row-inserted", - G_CALLBACK - (list_store_binding_store_changed_cb), - binding); - binding->rows_reordered_id = - g_signal_connect_swapped (list_store, "row-inserted", - 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) { - char *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, - 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/composer/gconf-bridge.h b/composer/gconf-bridge.h deleted file mode 100644 index aa7bfaefb8..0000000000 --- a/composer/gconf-bridge.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * (C) 2005 OpenedHand Ltd. - * - * Author: Jorn Baayen - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __GCONF_BRIDGE_H__ -#define __GCONF_BRIDGE_H__ - -#include -#include -#include - -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 char *key, - GObject *object, - const char *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) - -guint gconf_bridge_bind_window (GConfBridge *bridge, - const char *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 char *key, - GtkListStore *list_store); - -void gconf_bridge_unbind (GConfBridge *bridge, - guint binding_id); - -G_END_DECLS - -#endif /* __GCONF_BRIDGE_H__ */ diff --git a/e-util/ChangeLog b/e-util/ChangeLog index 73220b574c..023c708dcc 100644 --- a/e-util/ChangeLog +++ b/e-util/ChangeLog @@ -1,3 +1,8 @@ +2008-04-22 Matthew Barnes + + * Makefile.am: + Add gconf-bridge.[ch] from composer. + 2008-04-18 Matthew Barnes ** Fixes bug #528817 diff --git a/e-util/Makefile.am b/e-util/Makefile.am index 3677d841d4..3d4c495b8a 100644 --- a/e-util/Makefile.am +++ b/e-util/Makefile.am @@ -115,6 +115,8 @@ libeutil_la_SOURCES = \ e-util-labels.c \ e-util-private.h \ e-xml-utils.c \ + gconf-bridge.c \ + gconf-bridge.h \ $(PLATFORM_SOURCES) MARSHAL_GENERATED = e-util-marshal.c e-util-marshal.h diff --git a/e-util/gconf-bridge.c b/e-util/gconf-bridge.c new file mode 100644 index 0000000000..49754cfe96 --- /dev/null +++ b/e-util/gconf-bridge.c @@ -0,0 +1,1249 @@ +/* + * (C) 2005 OpenedHand Ltd. + * + * Author: Jorn Baayen + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include +#include +#include + +#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; + + char *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; + + char *key_prefix; + + GtkWindow *window; + gulong configure_event_id; + gulong unmap_id; + guint sync_timeout_id; +} WindowBinding; + +typedef struct { + BindingType type; + guint id; + + char *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); + +#if !HAVE_DECL_GCONF_VALUE_COMPARE /* Not in headers in GConf < 2.13 */ +int gconf_value_compare (const GConfValue *value_a, + const GConfValue *value_b); +#endif + +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 float 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, + (float) 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 char *key, + GObject *object, + const char *prop, + gboolean delayed_sync) +{ + GParamSpec *pspec; + PropBinding *binding; + char *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; +} + +/* 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) { + int width, height; + char *key; + GdkWindowState state; + + state = gdk_window_get_state (GTK_WIDGET (binding->window)->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) { + int x, y; + char *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 1000 /* Delay before syncing new window + dimensions to GConf, in ms */ + +/* Called when the window han been resized or moved */ +static gboolean +window_binding_configure_event_cb (GtkWindow *window, + GdkEventConfigure *event, + WindowBinding *binding) +{ + /* Schedule a sync */ + if (binding->sync_timeout_id == 0) { + binding->sync_timeout_id = + g_timeout_add (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) +{ + 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); + + 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() */ + + 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 char *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) { + char *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)) { + gtk_window_maximize (window); + } + gconf_value_free (maximized_val); + } + } + + if (bind_pos) { + char *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->configure_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->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 char *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; + int 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) { + char *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 char *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-inserted", + G_CALLBACK + (list_store_binding_store_changed_cb), + binding); + binding->row_deleted_id = + g_signal_connect_swapped (list_store, "row-inserted", + G_CALLBACK + (list_store_binding_store_changed_cb), + binding); + binding->rows_reordered_id = + g_signal_connect_swapped (list_store, "row-inserted", + 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) { + char *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, + 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/e-util/gconf-bridge.h b/e-util/gconf-bridge.h new file mode 100644 index 0000000000..aa7bfaefb8 --- /dev/null +++ b/e-util/gconf-bridge.h @@ -0,0 +1,117 @@ +/* + * (C) 2005 OpenedHand Ltd. + * + * Author: Jorn Baayen + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GCONF_BRIDGE_H__ +#define __GCONF_BRIDGE_H__ + +#include +#include +#include + +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 char *key, + GObject *object, + const char *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) + +guint gconf_bridge_bind_window (GConfBridge *bridge, + const char *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 char *key, + GtkListStore *list_store); + +void gconf_bridge_unbind (GConfBridge *bridge, + guint binding_id); + +G_END_DECLS + +#endif /* __GCONF_BRIDGE_H__ */ diff --git a/mail/ChangeLog b/mail/ChangeLog index c90b054d0b..19d902e9ee 100644 --- a/mail/ChangeLog +++ b/mail/ChangeLog @@ -1,3 +1,8 @@ +2008-04-22 Matthew Barnes + + * em-composer-prefs.c: + gconf-bridge.h moved to e-util. + 2008-04-21 Milan Crha ** Fix for bug #323402 diff --git a/mail/em-composer-prefs.c b/mail/em-composer-prefs.c index 230d32526c..118bfb6a3c 100644 --- a/mail/em-composer-prefs.c +++ b/mail/em-composer-prefs.c @@ -32,10 +32,10 @@ #include "e-util/e-signature.h" #include "e-util/e-signature-list.h" +#include "e-util/gconf-bridge.h" #include "em-composer-prefs.h" #include "composer/e-msg-composer.h" -#include "composer/gconf-bridge.h" #include -- cgit