diff options
Diffstat (limited to 'eel/eel-preferences.c')
-rw-r--r-- | eel/eel-preferences.c | 1764 |
1 files changed, 1764 insertions, 0 deletions
diff --git a/eel/eel-preferences.c b/eel/eel-preferences.c new file mode 100644 index 00000000..7a24ed11 --- /dev/null +++ b/eel/eel-preferences.c @@ -0,0 +1,1764 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ + +/* eel-preferences.c - Preference peek/poke/notify implementation. + + Copyright (C) 1999, 2000 Eazel, Inc. + + The Mate 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. + + The Mate 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 the Mate Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Authors: Ramiro Estrugo <[email protected]> +*/ + +#include <config.h> +#include "eel-preferences.h" + +#include "eel-debug.h" +#include "eel-mateconf-extensions.h" +#include "eel-lib-self-check-functions.h" +#include "eel-enumeration.h" +#include "eel-glib-extensions.h" +#include "eel-string.h" +#include <mateconf/mateconf-client.h> +#include <mateconf/mateconf.h> +#include <gtk/gtk.h> + +/* An enumeration used for updating auto-storage variables in a type-specific way. + * FIXME: there is another enumeration like this in eel-global-preferences.c, + * used for different purposes but in a related way. Should we combine them? + */ +typedef enum +{ + PREFERENCE_BOOLEAN = 1, + PREFERENCE_INTEGER, + PREFERENCE_STRING, + PREFERENCE_STRING_ARRAY, + PREFERENCE_STRING_ARRAY_AS_QUARKS +} PreferenceType; + +/* + * PreferencesEntry: + * + * A structure to manage preference hash table nodes. + * Preferences are hash tables. The hash key is the preference name + * (a string). The hash value is a pointer of the following struct: + */ +typedef struct +{ + char *name; + char *description; + PreferenceType type; + gboolean invisible; + GList *callback_list; + GList *auto_storage_list; + int mateconf_connection_id; + char *enumeration_id; + MateConfValue *fallback; +} PreferencesEntry; + +/* + * PreferencesCallbackEntry: + * + * A structure to manage callback lists. A callback list is a GList. + * The callback_data in each list node is a pointer to the following + * struct: + */ +typedef struct +{ + EelPreferencesCallback callback; + gpointer callback_data; +} PreferencesCallbackEntry; + +static GHashTable *global_table = NULL; +static char *storage_path = NULL; +static gboolean initialized = FALSE; + +static void preferences_global_table_free (void); +static char * preferences_key_make (const char *name); +static void preferences_callback_entry_free (PreferencesCallbackEntry *callback_entry); +static void preferences_entry_update_auto_storage (PreferencesEntry *entry); +static PreferencesEntry *preferences_global_table_lookup_or_insert (const char *name); + +static int +preferences_mateconf_value_get_int (const MateConfValue *value) +{ + if (value == NULL) + { + return 0; + } + g_assert (value->type == MATECONF_VALUE_INT); + return mateconf_value_get_int (value); +} + +static gboolean +preferences_mateconf_value_get_bool (const MateConfValue *value) +{ + if (value == NULL) + { + return FALSE; + } + g_assert (value->type == MATECONF_VALUE_BOOL); + return mateconf_value_get_bool (value); +} + +static char * +preferences_mateconf_value_get_string (const MateConfValue *value) +{ + if (value == NULL) + { + return g_strdup (""); + } + g_assert (value->type == MATECONF_VALUE_STRING); + return g_strdup (mateconf_value_get_string (value)); +} + +static char ** +preferences_mateconf_value_get_string_array (const MateConfValue *value) +{ + GSList *slist, *l; + GPtrArray *result; + + if (value == NULL) + { + return NULL; + } + + g_assert (value->type == MATECONF_VALUE_LIST); + g_assert (mateconf_value_get_list_type (value) == MATECONF_VALUE_STRING); + + slist = eel_mateconf_value_get_string_list (value); + + result = g_ptr_array_new (); + for (l = slist; l != NULL; l = l->next) + { + g_ptr_array_add (result, l->data); + } + g_slist_free (slist); + g_ptr_array_add (result, NULL); + + return (char **) g_ptr_array_free (result, FALSE); +} + +static const char * +preferences_peek_storage_path (void) +{ + g_assert (storage_path != NULL); + + return storage_path; +} + +static void +preferences_set_storage_path (const char *new_storage_path) +{ + g_assert (eel_strlen (new_storage_path) > 0); + + /* Make sure the path is indeed different */ + if (eel_str_is_equal (new_storage_path, storage_path)) + { + return; + } + + /* Free the preference hash table */ + preferences_global_table_free (); + + /* Stop monitoring the old path */ + eel_mateconf_monitor_remove (storage_path); + + g_free (storage_path); + storage_path = g_strdup (new_storage_path); + + /* Start monitoring the new path */ + eel_mateconf_monitor_add (storage_path); +} + +static gboolean +preferences_is_initialized (void) +{ + return initialized; +} + +static MateConfValue * +preferences_get_value (const char *name) +{ + MateConfValue *result; + char *key; + PreferencesEntry *entry; + + g_assert (name != NULL); + g_assert (preferences_is_initialized ()); + + key = preferences_key_make (name); + result = eel_mateconf_get_value (key); + g_free (key); + + if (result == NULL) + { + entry = preferences_global_table_lookup_or_insert (name); + + if (entry->fallback) + result = mateconf_value_copy (entry->fallback); + } + + return result; +} + +/* If the preference name begind with a "/", we interpret + * it as a straight mateconf key. */ +static gboolean +preferences_preference_is_mateconf_key (const char *name) +{ + g_assert (name != NULL); + + if (eel_str_has_prefix (name, "/")) + { + return FALSE; + } + + return TRUE; +} + +static char * +preferences_key_make (const char *name) +{ + g_assert (name != NULL); + + if (!preferences_preference_is_mateconf_key (name)) + { + return g_strdup (name); + } + + /* Otherwise, we prefix it with the path */ + return g_strconcat (preferences_peek_storage_path (), "/", + name, NULL); +} + +/* Get default from schema or emergency fallback */ +static MateConfValue * +preferences_get_default_value (const char *name) +{ + MateConfValue *result; + PreferencesEntry *entry; + char *key; + + g_assert (name != NULL); + + key = preferences_key_make (name); + + result = eel_mateconf_get_default_value (key); + + g_free (key); + + if (result == NULL) + { + entry = preferences_global_table_lookup_or_insert (name); + if (entry && entry->fallback) + result = mateconf_value_copy (entry->fallback); + } + + return result; +} + +static int +preferences_callback_entry_compare (gconstpointer a, + gconstpointer b) +{ + const PreferencesCallbackEntry *a_entry = a; + const PreferencesCallbackEntry *b_entry = b; + + if (a_entry->callback < b_entry->callback) + { + return -1; + } + + if (a_entry->callback > b_entry->callback) + { + return +1; + } + + if (a_entry->callback_data < b_entry->callback_data) + { + return -1; + } + + if (a_entry->callback_data > b_entry->callback_data) + { + return +1; + } + + return 0; +} + +/* Public preferences functions */ + +gboolean +eel_preferences_get_is_invisible (const char *name) +{ + g_assert (name != NULL); + g_assert (preferences_is_initialized ()); + + return preferences_global_table_lookup_or_insert (name)->invisible; +} + +void +eel_preferences_set_is_invisible (const char *name, + gboolean is_invisible) +{ + g_return_if_fail (name != NULL); + g_return_if_fail (preferences_is_initialized ()); + + preferences_global_table_lookup_or_insert (name)->invisible = is_invisible; +} + +void +eel_preferences_set_boolean (const char *name, + gboolean boolean_value) +{ + char *key; + + g_return_if_fail (name != NULL); + g_return_if_fail (preferences_is_initialized ()); + + key = preferences_key_make (name); + eel_mateconf_set_boolean (key, boolean_value); + g_free (key); + + eel_mateconf_suggest_sync (); +} + +gboolean +eel_preferences_get_boolean (const char *name) +{ + gboolean result; + MateConfValue *value; + + g_return_val_if_fail (name != NULL, 0); + g_return_val_if_fail (preferences_is_initialized (), 0); + + value = preferences_get_value (name); + result = preferences_mateconf_value_get_bool (value); + eel_mateconf_value_free (value); + + return result; +} + +void +eel_preferences_set_integer (const char *name, + int int_value) +{ + char *key; + int old_value; + + g_return_if_fail (name != NULL); + g_return_if_fail (preferences_is_initialized ()); + + key = preferences_key_make (name); + old_value = eel_preferences_get_integer (name); + + if (int_value != old_value) + { + eel_mateconf_set_integer (key, int_value); + } + g_free (key); +} + +int +eel_preferences_get_integer (const char *name) +{ + int result; + MateConfValue *value; + + g_return_val_if_fail (name != NULL, 0); + g_return_val_if_fail (preferences_is_initialized (), 0); + + value = preferences_get_value (name); + result = preferences_mateconf_value_get_int (value); + eel_mateconf_value_free (value); + + return result; +} + +/* MateConf has no unsigned store, save as signed and cast */ +guint +eel_preferences_get_uint (const char *name) +{ + return (guint)eel_preferences_get_integer (name); +} +void +eel_preferences_set_uint (const char *name, + guint uint_value) +{ + eel_preferences_set_integer (name, (int)uint_value); +} + +guint +eel_preferences_get_enum (const char *name) +{ + guint ret_val; + char *str_value; + MateConfValue *value; + const EelEnumeration *enumeration; + PreferencesEntry *entry; + + g_return_val_if_fail (name != NULL, 0); + g_return_val_if_fail (preferences_is_initialized (), 0); + + entry = preferences_global_table_lookup_or_insert (name); + g_return_val_if_fail (entry != NULL, 0); + + enumeration = eel_enumeration_lookup (entry->enumeration_id); + + if (!enumeration) + { + g_warning ("No enum entry for '%s' (%s)", + name, entry->enumeration_id); + return 0; + } + + value = preferences_get_value (name); + if (value->type == MATECONF_VALUE_INT) /* compatibility path */ + { + ret_val = (guint)preferences_mateconf_value_get_int (value); + eel_mateconf_value_free (value); + return ret_val; + } + + str_value = preferences_mateconf_value_get_string (value); + eel_mateconf_value_free (value); + + if (str_value == NULL) + { + g_warning ("No key for '%s' at %s", str_value, name); + return 0; + } + + ret_val = eel_enumeration_get_value_for_name (enumeration, str_value); + + g_free (str_value); + + return ret_val; +} + +void +eel_preferences_set_enum (const char *name, + guint int_value) +{ + const char *str_value; + const EelEnumeration *enumeration; + PreferencesEntry *entry; + + g_return_if_fail (name != NULL); + g_return_if_fail (preferences_is_initialized ()); + + entry = preferences_global_table_lookup_or_insert (name); + g_return_if_fail (entry != NULL); + + enumeration = eel_enumeration_lookup (entry->enumeration_id); + + if (!enumeration) + { + g_warning ("No enum entry for '%s' (%s)", + name, entry->enumeration_id); + return; + } + + str_value = eel_enumeration_get_name_for_value (enumeration, int_value); + + if (str_value == NULL) + { + g_warning ("No enum match for '%d'", int_value); + return; + } + + eel_preferences_set (name, str_value); +} + +void +eel_preferences_set (const char *name, + const char *string_value) +{ + char *key; + char *old_value; + + g_return_if_fail (name != NULL); + g_return_if_fail (preferences_is_initialized ()); + + key = preferences_key_make (name); + old_value = eel_preferences_get (name); + + if (strcmp (string_value, old_value) != 0) + { + eel_mateconf_set_string (key, string_value); + } + g_free (key); + g_free (old_value); +} + +char * +eel_preferences_get (const char *name) +{ + char *result; + MateConfValue *value; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (preferences_is_initialized (), NULL); + + value = preferences_get_value (name); + result = preferences_mateconf_value_get_string (value); + eel_mateconf_value_free (value); + + return result; +} + +void +eel_preferences_set_string_array (const char *name, + char **strv_value) +{ + GSList *slist; + int i; + char *key; + + g_return_if_fail (name != NULL); + g_return_if_fail (preferences_is_initialized ()); + + slist = NULL; + if (strv_value != NULL) + { + for (i = 0; strv_value[i] != NULL; i++) + { + slist = g_slist_prepend (slist, strv_value[i]); + } + slist = g_slist_reverse (slist); + } + + key = preferences_key_make (name); + eel_mateconf_set_string_list (key, slist); + g_free (key); + + g_slist_free (slist); +} + +static gboolean +string_array_is_valid (char **strv, const char *enumeration_id) +{ + guint i; + gboolean res; + + g_assert (strv != NULL); + g_assert (enumeration_id != NULL); + + res = TRUE; + for (i = 0; strv[i] != NULL; i++) + { + const EelEnumeration *enumeration; + + enumeration = eel_enumeration_lookup (enumeration_id); + if (!enumeration) + { + res = FALSE; + break; + } + + if (!eel_enumeration_contains_name (enumeration, strv[i])) + { + res = FALSE; + break; + } + } + + return res; +} + +char ** +eel_preferences_get_string_array (const char *name) +{ + char **result; + MateConfValue *value; + PreferencesEntry *entry; + MateConfValue *default_value; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (preferences_is_initialized (), NULL); + + value = preferences_get_value (name); + result = preferences_mateconf_value_get_string_array (value); + eel_mateconf_value_free (value); + + entry = preferences_global_table_lookup_or_insert (name); + g_assert (entry != NULL); + + /* No enumeration_id so we're done */ + if (entry->enumeration_id == NULL) + { + return result; + } + + /* Do a sanity check on the validity of the values */ + if (string_array_is_valid (result, entry->enumeration_id)) + { + return result; + } + + /* Forget the bad value and use the default instead */ + g_strfreev (result); + + default_value = preferences_get_default_value (name); + if (default_value) + { + result = preferences_mateconf_value_get_string_array (default_value); + mateconf_value_free (default_value); + } + + return result; +} + +void +eel_preferences_unset (const char *name) +{ + char *key; + + g_return_if_fail (name != NULL); + g_return_if_fail (preferences_is_initialized ()); + + key = preferences_key_make (name); + + eel_mateconf_unset (key); + + g_free (key); +} + +gboolean +eel_preferences_key_is_writable (const char *name) +{ + gboolean result; + char *key; + + g_return_val_if_fail (name != NULL, 0); + g_return_val_if_fail (preferences_is_initialized (), 0); + + key = preferences_key_make (name); + result = eel_mateconf_key_is_writable (key); + g_free (key); + + return result; +} + +/** + * preferences_callback_entry_invoke_function + * + * A function that invokes a callback from the given struct. It is meant to be fed to + * g_list_foreach () + * @data: The list data privately maintained by the GList. + * @callback_data: The callback_data privately maintained by the GList. + **/ +static void +preferences_callback_entry_invoke_function (gpointer data, + gpointer callback_data) +{ + PreferencesCallbackEntry *callback_entry; + + g_assert (data != NULL); + + callback_entry = data; + + (* callback_entry->callback) (callback_entry->callback_data); +} + +static void +preferences_entry_invoke_callbacks (PreferencesEntry *entry) +{ + g_assert (entry != NULL); + + /* Update the auto storage preferences */ + if (entry->auto_storage_list != NULL) + { + preferences_entry_update_auto_storage (entry); + } + + /* Invoke callbacks for this entry if any */ + if (entry->callback_list != NULL) + { + g_list_foreach (entry->callback_list, + preferences_callback_entry_invoke_function, + NULL); + } +} + +static void +update_auto_string (gpointer data, gpointer callback_data) +{ + char **storage; + const char *value; + + g_assert (data != NULL); + g_assert (callback_data != NULL); + + storage = (char **)data; + value = (const char *)callback_data; + + g_free (*storage); + *(char **)storage = g_strdup (value); +} + +static void +update_auto_string_array (gpointer data, gpointer callback_data) +{ + char ***storage; + char **value; + + g_assert (data != NULL); + g_assert (callback_data != NULL); + + storage = (char ***)data; + value = (char **)callback_data; + + g_strfreev (*storage); + *(char ***)storage = value ? g_strdupv (value) : NULL; +} + +static void +update_auto_string_array_as_quarks (gpointer data, gpointer callback_data) +{ + GQuark **storage; + char **value; + int i = 0; + + g_assert (data != NULL); + g_assert (callback_data != NULL); + + storage = (GQuark **)data; + value = (char **)callback_data; + + g_free (*storage); + *storage = g_new (GQuark, g_strv_length (value) + 1); + + if (value != NULL) + { + for (i = 0; value[i] != NULL; ++i) + { + (*storage)[i] = g_quark_from_string (value[i]); + } + } + (*storage)[i] = 0; +} + +static void +update_auto_integer_or_boolean (gpointer data, gpointer callback_data) +{ + g_assert (data != NULL); + + *(int *)data = GPOINTER_TO_INT (callback_data); +} + +static void +preferences_entry_update_auto_storage (PreferencesEntry *entry) +{ + char *new_string_value; + char **new_string_array_value; + int new_int_value; + guint new_uint_value; + gboolean new_boolean_value; + + switch (entry->type) + { + case PREFERENCE_STRING: + if (entry->enumeration_id != NULL) + { + new_uint_value = eel_preferences_get_enum (entry->name); + g_list_foreach (entry->auto_storage_list, + update_auto_integer_or_boolean, + GINT_TO_POINTER (new_uint_value)); + } + else + { + new_string_value = eel_preferences_get (entry->name); + g_list_foreach (entry->auto_storage_list, + update_auto_string, + new_string_value); + g_free (new_string_value); + } + break; + case PREFERENCE_STRING_ARRAY: + new_string_array_value = eel_preferences_get_string_array (entry->name); + g_list_foreach (entry->auto_storage_list, + update_auto_string_array, + new_string_array_value); + g_strfreev (new_string_array_value); + break; + case PREFERENCE_STRING_ARRAY_AS_QUARKS: + new_string_array_value = eel_preferences_get_string_array (entry->name); + g_list_foreach (entry->auto_storage_list, + update_auto_string_array_as_quarks, + new_string_array_value); + g_strfreev (new_string_array_value); + break; + case PREFERENCE_INTEGER: + new_int_value = eel_preferences_get_integer (entry->name); + g_list_foreach (entry->auto_storage_list, + update_auto_integer_or_boolean, + GINT_TO_POINTER (new_int_value)); + break; + case PREFERENCE_BOOLEAN: + new_boolean_value = eel_preferences_get_boolean (entry->name); + g_list_foreach (entry->auto_storage_list, + update_auto_integer_or_boolean, + GINT_TO_POINTER (new_boolean_value)); + break; + default: + g_warning ("unexpected preferences type %d in preferences_entry_update_auto_storage", entry->type); + } +} + +static void +preferences_something_changed_notice (MateConfClient *client, + guint connection_id, + MateConfEntry *entry, + gpointer notice_data) +{ + g_assert (entry != NULL); + g_assert (entry->key != NULL); + g_assert (notice_data != NULL); + + preferences_entry_invoke_callbacks (notice_data); +} + +static void +preferences_entry_ensure_mateconf_connection (PreferencesEntry *entry) +{ + char *key; + + /* + * We install only one mateconf notification for each preference entry. + * Otherwise, we would invoke the installed callbacks more than once + * per registered callback. + */ + if (entry->mateconf_connection_id != EEL_MATECONF_UNDEFINED_CONNECTION) + { + return; + } + + g_assert (entry->name != NULL); + + key = preferences_key_make (entry->name); + + entry->mateconf_connection_id = eel_mateconf_notification_add (key, + preferences_something_changed_notice, + entry); + g_free (key); + + g_assert (entry->mateconf_connection_id != EEL_MATECONF_UNDEFINED_CONNECTION); +} + +/** + * preferences_entry_add_callback + * + * Add a callback to a pref node. Callbacks are fired whenever + * the pref value changes. + * @preferences_entry: The hash node. + * @callback: The user-supplied callback. + * @callback_data: The user-supplied closure. + **/ +static void +preferences_entry_add_callback (PreferencesEntry *entry, + EelPreferencesCallback callback, + gpointer callback_data) +{ + PreferencesCallbackEntry *callback_entry; + GList *l; + + g_assert (entry != NULL); + g_assert (callback != NULL); + + callback_entry = g_new0 (PreferencesCallbackEntry, 1); + callback_entry->callback = callback; + callback_entry->callback_data = callback_data; + + l = g_list_find_custom (entry->callback_list, callback_entry, preferences_callback_entry_compare); + if (l == NULL) + { + entry->callback_list = g_list_append (entry->callback_list, callback_entry); + preferences_entry_ensure_mateconf_connection (entry); + } + else + { + g_warning ("Trying to add a callback for %s that already exists.", entry->name); + } +} + +/** + * preferences_entry_add_auto_storage + * + * Add an auto-storage variable to a pref node. The variable will + * be updated to match the pref value whenever the pref + * the pref value changes. + * @preferences_entry: The hash node. + * @storage: The user-supplied location at which to store the value. + * @type: Which type of variable this is. + **/ +static void +preferences_entry_add_auto_storage (PreferencesEntry *entry, + gpointer storage, + PreferenceType type) +{ + g_assert (entry != NULL); + g_assert (storage != NULL); + g_assert (entry->type == 0 || entry->type == type); + if (g_list_find (entry->auto_storage_list, storage) != NULL) + { + g_warning ("Trying to add an auto storage for %s that already exists.", entry->name); + return; + } + + entry->type = type; + + entry->auto_storage_list = g_list_append (entry->auto_storage_list, storage); + + preferences_entry_ensure_mateconf_connection (entry); +} + +static void +preferences_entry_check_remove_connection (PreferencesEntry *entry) +{ + /* + * If there are no callbacks or auto-storage variables left in the entry, + * remove the mateconf notification. + */ + if (entry->callback_list != NULL || entry->auto_storage_list != NULL) + { + return; + } + + eel_mateconf_notification_remove (entry->mateconf_connection_id); + entry->mateconf_connection_id = EEL_MATECONF_UNDEFINED_CONNECTION; +} + +/** + * preferences_entry_remove_callback + * + * remove a callback from a pref entry. Both the callback and the callback_data must + * match in order for a callback to be removed from the entry. + * @preferences_entry: The hash entry. + * @callback: The user-supplied callback. + * @callback_data: The user-supplied closure. + **/ +static void +preferences_entry_remove_callback (PreferencesEntry *entry, + EelPreferencesCallback callback, + gpointer callback_data) +{ + PreferencesCallbackEntry cb_entry; + GList *l; + + g_assert (entry != NULL); + g_assert (callback != NULL); + + cb_entry.callback = callback; + cb_entry.callback_data = callback_data; + + l = g_list_find_custom (entry->callback_list, &cb_entry, preferences_callback_entry_compare); + if (l != NULL) + { + preferences_callback_entry_free (l->data); + entry->callback_list = g_list_delete_link (entry->callback_list, l); + preferences_entry_check_remove_connection (entry); + } + else + { + g_warning ("Trying to remove a callback for %s without adding it first.", entry->name); + } + + g_assert (g_list_find_custom (entry->callback_list, &cb_entry, preferences_callback_entry_compare) == NULL); +} + +/** + * preferences_entry_remove_auto_storage + * + * remove an auto-storage variable from a pref entry. + * @preferences_entry: The hash entry. + * @storage: The user-supplied location. + **/ +static void +preferences_entry_remove_auto_storage (PreferencesEntry *entry, + gpointer storage) +{ + GList *new_list; + const GList *node; + gpointer storage_in_entry; + + g_assert (entry != NULL); + g_assert (storage != NULL); + g_assert (entry->auto_storage_list != NULL); + + new_list = g_list_copy (entry->auto_storage_list); + + for (node = new_list; node != NULL; node = node->next) + { + storage_in_entry = node->data; + + g_assert (storage_in_entry != NULL); + + if (storage_in_entry == storage) + { + entry->auto_storage_list = g_list_remove (entry->auto_storage_list, + storage); + + switch (entry->type) + { + case PREFERENCE_STRING: + update_auto_string (storage, NULL); + break; + case PREFERENCE_STRING_ARRAY: + update_auto_string_array (storage, NULL); + break; + case PREFERENCE_STRING_ARRAY_AS_QUARKS: + update_auto_string_array_as_quarks (storage, NULL); + break; + case PREFERENCE_BOOLEAN: + case PREFERENCE_INTEGER: + update_auto_integer_or_boolean (storage, NULL); + break; + default: + g_warning ("unexpected preference type %d in preferences_entry_remove_auto_storage", entry->type); + } + } + } + + g_list_free (new_list); + + preferences_entry_check_remove_connection (entry); +} + +/** + * preferences_callback_entry_free + * + * Free a callback info struct. + * @preferences_callback_entry: The struct to free. + **/ +static void +preferences_callback_entry_free (PreferencesCallbackEntry *callback_entry) +{ + g_assert (callback_entry != NULL); + + callback_entry->callback = NULL; + callback_entry->callback_data = NULL; + + g_free (callback_entry); +} + +/** + * preferences_callback_entry_free_func + * + * A function that frees a callback info struct. It is meant to be fed to + * g_list_foreach () + * @data: The list data privately maintained by the GList. + * @callback_data: The callback_data privately maintained by the GList. + **/ +static void +preferences_callback_entry_free_func (gpointer data, + gpointer callback_data) +{ + g_assert (data != NULL); + + preferences_callback_entry_free (data); +} + +/** + * preferences_entry_free + * + * Free a preference hash node's members along with the node itself. + * @preferences_hash_node: The node to free. + **/ +static void +preferences_entry_free (PreferencesEntry *entry) +{ + g_assert (entry != NULL); + + eel_mateconf_notification_remove (entry->mateconf_connection_id); + entry->mateconf_connection_id = EEL_MATECONF_UNDEFINED_CONNECTION; + + g_list_free (entry->auto_storage_list); + eel_g_list_free_deep_custom (entry->callback_list, + preferences_callback_entry_free_func, + NULL); + + entry->auto_storage_list = NULL; + entry->callback_list = NULL; + + g_free (entry->name); + g_free (entry->description); + g_free (entry->enumeration_id); + + eel_mateconf_value_free (entry->fallback); + + g_free (entry); +} + +/** + * preferences_entry_free_func + * + * A function that frees a pref hash node. It is meant to be fed to + * g_hash_table_foreach () + * @key: The hash key privately maintained by the GHashTable. + * @value: The hash value privately maintained by the GHashTable. + * @callback_data: The callback_data privately maintained by the GHashTable. + **/ +static void +preferences_entry_free_func (gpointer key, + gpointer value, + gpointer callback_data) +{ + g_assert (value != NULL); + + preferences_entry_free (value); +} + +static void +preferences_global_table_free (void) +{ + if (global_table == NULL) + { + return; + } + + g_hash_table_foreach (global_table, preferences_entry_free_func, NULL); + g_hash_table_destroy (global_table); + global_table = NULL; + + g_free (storage_path); + storage_path = NULL; +} + +static void +preferences_uninitialize (void) +{ + initialized = FALSE; +} + +static GHashTable * +preferences_global_table_get_global (void) +{ + static gboolean at_exit_handler_added = FALSE; + + if (global_table == NULL) + { + global_table = g_hash_table_new (g_str_hash, g_str_equal); + + if (!at_exit_handler_added) + { + at_exit_handler_added = TRUE; + eel_debug_call_at_shutdown (preferences_global_table_free); + /* ensure that we catch calls to preferences functions after eel shutdown */ + eel_debug_call_at_shutdown (preferences_uninitialize); + } + } + + return global_table; +} + +static PreferencesEntry * +preferences_global_table_lookup (const char *name) +{ + g_assert (name != NULL); + g_assert (preferences_global_table_get_global () != NULL); + + return g_hash_table_lookup (preferences_global_table_get_global (), name); +} + +static PreferencesEntry * +preferences_global_table_insert (const char *name) +{ + PreferencesEntry *entry; + + g_assert (name != NULL); + g_assert (preferences_global_table_get_global () != NULL); + g_assert (preferences_global_table_lookup (name) == NULL); + + entry = g_new0 (PreferencesEntry, 1); + entry->name = g_strdup (name); + + g_hash_table_insert (preferences_global_table_get_global (), entry->name, entry); + + g_assert (entry == preferences_global_table_lookup (name)); + + return entry; +} + +static PreferencesEntry * +preferences_global_table_lookup_or_insert (const char *name) +{ + PreferencesEntry *entry; + + g_assert (name != NULL); + + entry = preferences_global_table_lookup (name); + + if (entry != NULL) + { + return entry; + } + + entry = preferences_global_table_insert (name); + g_assert (entry != NULL); + + return entry; +} + +void +eel_preferences_add_callback (const char *name, + EelPreferencesCallback callback, + gpointer callback_data) +{ + PreferencesEntry *entry; + + g_return_if_fail (name != NULL); + g_return_if_fail (callback != NULL); + g_return_if_fail (preferences_is_initialized ()); + + entry = preferences_global_table_lookup_or_insert (name); + g_assert (entry != NULL); + + preferences_entry_add_callback (entry, callback, callback_data); +} + +void +eel_preferences_add_auto_string (const char *name, + const char **storage) +{ + PreferencesEntry *entry; + char *value; + + g_return_if_fail (name != NULL); + g_return_if_fail (storage != NULL); + g_return_if_fail (preferences_is_initialized ()); + + entry = preferences_global_table_lookup_or_insert (name); + g_assert (entry != NULL); + + preferences_entry_add_auto_storage (entry, storage, PREFERENCE_STRING); + + value = eel_preferences_get (entry->name); + update_auto_string (storage, value); + g_free (value); +} + +void +eel_preferences_add_auto_string_array (const char *name, + char ***storage) +{ + PreferencesEntry *entry; + char **value; + + g_return_if_fail (name != NULL); + g_return_if_fail (storage != NULL); + g_return_if_fail (preferences_is_initialized ()); + + entry = preferences_global_table_lookup_or_insert (name); + g_assert (entry != NULL); + + preferences_entry_add_auto_storage (entry, storage, PREFERENCE_STRING_ARRAY); + + value = eel_preferences_get_string_array (entry->name); + update_auto_string_array (storage, value); + g_strfreev (value); +} + +void +eel_preferences_add_auto_string_array_as_quarks (const char *name, + GQuark **storage) +{ + PreferencesEntry *entry; + char **value; + + g_return_if_fail (name != NULL); + g_return_if_fail (storage != NULL); + g_return_if_fail (preferences_is_initialized ()); + + entry = preferences_global_table_lookup_or_insert (name); + g_assert (entry != NULL); + + preferences_entry_add_auto_storage (entry, storage, PREFERENCE_STRING_ARRAY_AS_QUARKS); + + value = eel_preferences_get_string_array (entry->name); + update_auto_string_array_as_quarks (storage, value); + g_strfreev (value); +} + +void +eel_preferences_add_auto_integer (const char *name, + int *storage) +{ + PreferencesEntry *entry; + int value; + + g_return_if_fail (name != NULL); + g_return_if_fail (storage != NULL); + g_return_if_fail (preferences_is_initialized ()); + + entry = preferences_global_table_lookup_or_insert (name); + g_assert (entry != NULL); + + preferences_entry_add_auto_storage (entry, storage, PREFERENCE_INTEGER); + + value = eel_preferences_get_integer (entry->name); + update_auto_integer_or_boolean (storage, GINT_TO_POINTER (value)); +} + + +void +eel_preferences_add_auto_enum (const char *name, + guint *storage) +{ + PreferencesEntry *entry; + guint value; + + g_return_if_fail (name != NULL); + g_return_if_fail (storage != NULL); + g_return_if_fail (preferences_is_initialized ()); + + entry = preferences_global_table_lookup_or_insert (name); + g_assert (entry != NULL); + g_assert (entry->enumeration_id != NULL); + + preferences_entry_add_auto_storage (entry, storage, PREFERENCE_STRING); + + value = eel_preferences_get_enum (entry->name); + update_auto_integer_or_boolean (storage, GINT_TO_POINTER (value)); +} + +void +eel_preferences_add_auto_boolean (const char *name, + gboolean *storage) +{ + PreferencesEntry *entry; + gboolean value; + + g_return_if_fail (name != NULL); + g_return_if_fail (storage != NULL); + g_return_if_fail (preferences_is_initialized ()); + + entry = preferences_global_table_lookup_or_insert (name); + g_assert (entry != NULL); + + preferences_entry_add_auto_storage (entry, storage, PREFERENCE_BOOLEAN); + + value = eel_preferences_get_boolean (entry->name); + update_auto_integer_or_boolean (storage, GINT_TO_POINTER (value)); +} + +void +eel_preferences_remove_auto_string (const char *name, + const char **storage) +{ + PreferencesEntry *entry; + + g_return_if_fail (name != NULL); + g_return_if_fail (storage != NULL); + g_return_if_fail (preferences_is_initialized ()); + + entry = preferences_global_table_lookup (name); + if (entry == NULL) + { + g_warning ("Trying to remove auto-string for %s without adding it first.", name); + return; + } + + preferences_entry_remove_auto_storage (entry, storage); +} + +void +eel_preferences_remove_auto_string_array (const char *name, + char ***storage) +{ + PreferencesEntry *entry; + + g_return_if_fail (name != NULL); + g_return_if_fail (storage != NULL); + g_return_if_fail (preferences_is_initialized ()); + + entry = preferences_global_table_lookup (name); + if (entry == NULL) + { + g_warning ("Trying to remove auto-string for %s without adding it first.", name); + return; + } + + preferences_entry_remove_auto_storage (entry, storage); +} + +void +eel_preferences_remove_auto_integer (const char *name, + int *storage) +{ + PreferencesEntry *entry; + + g_return_if_fail (name != NULL); + g_return_if_fail (storage != NULL); + g_return_if_fail (preferences_is_initialized ()); + + entry = preferences_global_table_lookup (name); + if (entry == NULL) + { + g_warning ("Trying to remove auto-integer for %s without adding it first.", name); + return; + } + + preferences_entry_remove_auto_storage (entry, storage); +} + +void +eel_preferences_remove_auto_boolean (const char *name, + gboolean *storage) +{ + PreferencesEntry *entry; + + g_return_if_fail (name != NULL); + g_return_if_fail (storage != NULL); + g_return_if_fail (preferences_is_initialized ()); + + entry = preferences_global_table_lookup (name); + if (entry == NULL) + { + g_warning ("Trying to remove auto-boolean for %s without adding it first.", name); + return; + } + + preferences_entry_remove_auto_storage (entry, storage); +} + +typedef struct +{ + char *name; + EelPreferencesCallback callback; + gpointer callback_data; +} WhileAliveData; + +static void +preferences_while_alive_disconnector (gpointer callback_data, GObject *where_object_was) +{ + WhileAliveData *data; + + g_assert (callback_data != NULL); + + data = callback_data; + + /* we might have survived an eel shutdown, which + * already cleared all the callbacks */ + if (preferences_is_initialized ()) + { + eel_preferences_remove_callback (data->name, + data->callback, + data->callback_data); + } + + g_free (data->name); + g_free (data); +} + +void +eel_preferences_add_callback_while_alive (const char *name, + EelPreferencesCallback callback, + gpointer callback_data, + GObject *alive_object) +{ + WhileAliveData *data; + + g_return_if_fail (name != NULL); + g_return_if_fail (callback != NULL); + g_return_if_fail (G_IS_OBJECT (alive_object)); + g_return_if_fail (preferences_is_initialized ()); + + data = g_new (WhileAliveData, 1); + data->name = g_strdup (name); + data->callback = callback; + data->callback_data = callback_data; + + eel_preferences_add_callback (name, callback, callback_data); + + g_object_weak_ref (alive_object, + preferences_while_alive_disconnector, + data); +} + +void +eel_preferences_remove_callback (const char *name, + EelPreferencesCallback callback, + gpointer callback_data) +{ + PreferencesEntry *entry; + + g_return_if_fail (name != NULL); + g_return_if_fail (callback != NULL); + g_return_if_fail (preferences_is_initialized ()); + + entry = preferences_global_table_lookup (name); + + if (entry == NULL) + { + g_warning ("Trying to remove a callback for %s without adding it first.", name); + return; + } + + preferences_entry_remove_callback (entry, callback, callback_data); +} + +void +eel_preferences_set_description (const char *name, + const char *description) +{ + PreferencesEntry *entry; + + g_return_if_fail (name != NULL); + g_return_if_fail (description != NULL); + g_return_if_fail (preferences_is_initialized ()); + + entry = preferences_global_table_lookup_or_insert (name); + g_assert (entry != NULL); + + g_free (entry->description); + entry->description = g_strdup (description); +} + +char * +eel_preferences_get_description (const char *name) +{ + PreferencesEntry *entry; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (preferences_is_initialized (), NULL); + + entry = preferences_global_table_lookup_or_insert (name); + + return g_strdup (entry->description ? entry->description : ""); +} + +void +eel_preferences_set_enumeration_id (const char *name, + const char *enumeration_id) +{ + PreferencesEntry *entry; + + g_return_if_fail (name != NULL); + g_return_if_fail (enumeration_id != NULL); + g_return_if_fail (preferences_is_initialized ()); + + entry = preferences_global_table_lookup_or_insert (name); + g_assert (entry != NULL); + + g_free (entry->enumeration_id); + entry->enumeration_id = g_strdup (enumeration_id); +} + +char * +eel_preferences_get_enumeration_id (const char *name) +{ + PreferencesEntry *entry; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (preferences_is_initialized (), NULL); + + entry = preferences_global_table_lookup_or_insert (name); + + return g_strdup (entry->enumeration_id); +} + +static void +preferences_set_emergency_fallback_stealing_value (const char *name, + MateConfValue *value) +{ + PreferencesEntry *entry; + + g_assert (name != NULL); + g_assert (preferences_is_initialized ()); + + entry = preferences_global_table_lookup_or_insert (name); + g_assert (entry != NULL); + + if (entry->fallback) + mateconf_value_free (entry->fallback); + entry->fallback = value; /* steal ownership of value */ +} + +void +eel_preferences_set_emergency_fallback_string (const char *name, + const char *value) +{ + MateConfValue *mateconf_value; + + g_return_if_fail (name != NULL); + g_return_if_fail (value != NULL); + + mateconf_value = mateconf_value_new (MATECONF_VALUE_STRING); + + mateconf_value_set_string (mateconf_value, value); + + preferences_set_emergency_fallback_stealing_value (name, mateconf_value); +} + +void +eel_preferences_set_emergency_fallback_integer (const char *name, + int value) +{ + MateConfValue *mateconf_value; + + g_return_if_fail (name != NULL); + + mateconf_value = mateconf_value_new (MATECONF_VALUE_INT); + + mateconf_value_set_int (mateconf_value, value); + + preferences_set_emergency_fallback_stealing_value (name, mateconf_value); +} + +void +eel_preferences_set_emergency_fallback_boolean (const char *name, + gboolean value) +{ + MateConfValue *mateconf_value; + + g_return_if_fail (name != NULL); + + mateconf_value = mateconf_value_new (MATECONF_VALUE_BOOL); + + mateconf_value_set_bool (mateconf_value, value); + + preferences_set_emergency_fallback_stealing_value (name, mateconf_value); +} + + +void +eel_preferences_set_emergency_fallback_string_array (const char *name, + char **value) +{ + MateConfValue *mateconf_value; + GSList *list; + int i; + + g_return_if_fail (name != NULL); + g_return_if_fail (value != NULL); + + mateconf_value = mateconf_value_new (MATECONF_VALUE_LIST); + mateconf_value_set_list_type (mateconf_value, MATECONF_VALUE_STRING); + + list = NULL; + for (i = 0; value[i] != NULL; ++i) + { + MateConfValue *v; + + v = mateconf_value_new (MATECONF_VALUE_STRING); + mateconf_value_set_string (v, value[i]); + + list = g_slist_prepend (list, v); + } + + mateconf_value_set_list_nocopy (mateconf_value, g_slist_reverse (list)); + + preferences_set_emergency_fallback_stealing_value (name, mateconf_value); +} + +MateConfValue* +eel_preferences_get_emergency_fallback (const char *name) +{ + PreferencesEntry *entry; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (preferences_is_initialized (), NULL); + + entry = preferences_global_table_lookup_or_insert (name); + + return entry->fallback ? mateconf_value_copy (entry->fallback) : NULL; +} + +gboolean +eel_preferences_monitor_directory (const char *directory) +{ + g_return_val_if_fail (preferences_is_initialized (), FALSE); + + return eel_mateconf_monitor_add (directory); +} + +gboolean +eel_preferences_is_visible (const char *name) +{ + g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (preferences_is_initialized (), FALSE); + + return !preferences_global_table_lookup_or_insert (name)->invisible; +} + +void +eel_preferences_init (const char *path) +{ + g_return_if_fail (eel_strlen (path) > 0); + + if (initialized) + { + return; + } + + initialized = TRUE; + + preferences_set_storage_path (path); +} + +#if !defined (EEL_OMIT_SELF_CHECK) + +#define CHECK_BOOLEAN(name__, value__) \ +G_STMT_START { \ + eel_preferences_set_boolean ((name__), (value__)); \ + EEL_CHECK_BOOLEAN_RESULT (eel_preferences_get_boolean (name__), (value__)); \ +} G_STMT_END + +#define CHECK_INTEGER(name__, value__) \ +G_STMT_START { \ + eel_preferences_set_integer ((name__), (value__)); \ + EEL_CHECK_INTEGER_RESULT (eel_preferences_get_integer (name__), (value__)); \ +} G_STMT_END + +#define CHECK_STRING(name__, value__) \ +G_STMT_START { \ + eel_preferences_set ((name__), (value__)); \ + EEL_CHECK_STRING_RESULT (eel_preferences_get (name__), (value__)); \ +} G_STMT_END + +void +eel_self_check_preferences (void) +{ + /* Disabled until I can debug why these seemingly harmless tests + * dont work. -re + */ +#if 0 + int original_user_level; + + original_user_level = eel_preferences_get_user_level (); + + EEL_CHECK_INTEGER_RESULT (eel_preferences_get_integer ("self-check/neverset/i"), 0); + EEL_CHECK_STRING_RESULT (eel_preferences_get ("self-check/neverset/s"), ""); + EEL_CHECK_BOOLEAN_RESULT (eel_preferences_get_boolean ("self-check/neverset/b"), FALSE); + + eel_preferences_set_user_level (0); + + /* FIXME: Fails if you add the commented-out lines. */ + /* CHECK_INTEGER ("self-check/i", 0); */ + CHECK_INTEGER ("self-check/i", 666); + /* CHECK_BOOLEAN ("self-check/b", FALSE); */ + CHECK_BOOLEAN ("self-check/b", TRUE); + /* CHECK_STRING ("self-check/s", ""); */ + CHECK_STRING ("self-check/s", "foo"); + + /* Restore the original user level */ + eel_preferences_set_user_level (original_user_level); +#endif +} + +#endif /* !EEL_OMIT_SELF_CHECK */ |