Merge branch 'master' into wip/gsettings-list
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 2783df7..fc4b590 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1606,6 +1606,11 @@
g_settings_list_keys
g_settings_list_children
g_settings_get_mapped
+g_settings_add_child
+g_settings_can_add_child
+g_settings_can_remove_child
+g_settings_remove_child
+g_settings_get_destroyed
g_settings_get_range
g_settings_range_check
#endif
diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c
index f61bb8b..ad904e4 100644
--- a/gio/gkeyfilesettingsbackend.c
+++ b/gio/gkeyfilesettingsbackend.c
@@ -19,6 +19,7 @@
*
* Authors: Vincent Untz <vuntz@gnome.org>
* Ryan Lortie <desrt@desrt.ca>
+ * Rodrigo Moya <rodrigo@gnome.org>
*/
#include "config.h"
@@ -369,6 +370,14 @@
return g_object_ref (kfsb->permission);
}
+static gchar **
+g_keyfile_settings_backend_list (GSettingsBackend *backend,
+ const gchar *path,
+ const gchar * const *schema_items)
+{
+ GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
+}
+
static void
keyfile_to_tree (GKeyfileSettingsBackend *kfsb,
GTree *tree,
@@ -536,6 +545,7 @@
class->reset = g_keyfile_settings_backend_reset;
class->get_writable = g_keyfile_settings_backend_get_writable;
class->get_permission = g_keyfile_settings_backend_get_permission;
+ class->list = g_keyfile_settings_backend_list;
/* No need to implement subscribed/unsubscribe: the only point would be to
* stop monitoring the file when there's no GSettings anymore, which is no
* big win. */
diff --git a/gio/gmemorysettingsbackend.c b/gio/gmemorysettingsbackend.c
index 5d747b5..eceebc1 100644
--- a/gio/gmemorysettingsbackend.c
+++ b/gio/gmemorysettingsbackend.c
@@ -16,7 +16,8 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
- * Author: Ryan Lortie <desrt@desrt.ca>
+ * Authors: Ryan Lortie <desrt@desrt.ca>
+ * Rodrigo Moya <rodrigo@gnome.org>
*/
#include "config.h"
@@ -44,6 +45,90 @@
g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME,
g_define_type_id, "memory", 10))
+static GHashTable *
+g_memory_settings_backend_get_table (GHashTable *table,
+ const gchar **key_ptr)
+{
+ const gchar *key = *key_ptr;
+ gchar *prefix;
+ gchar *name;
+ gint i, j;
+
+ for (i = 2; key[i - 2] != ':' || key[i - 1] != '/'; i++)
+ if (key[i - 2] == '\0')
+ /* no :/ in the string -- pass through */
+ return table;
+
+ for (j = i + 1; key[j - 1] != '/'; j++)
+ if (key[j - 1] == '\0')
+ /* string of form /some/path:/child -- definitely invalid */
+ return NULL;
+
+ /* We now have this situation:
+ *
+ * /some/path:/child/item
+ * ^0 ^i ^j
+ *
+ * So we can split the string into 3 parts:
+ *
+ * prefix = /some/path:/
+ * child = child/
+ * *key_ptr = item
+ */
+ prefix = g_strndup (key, i);
+ name = g_strndup (key + i, j - i);
+
+ /* name is either like 'foo' or ':foo'
+ *
+ * If it doesn't start with a colon, it's in the schema.
+ */
+ if (name[0] == ':')
+ {
+ table = g_hash_table_lookup (table, prefix);
+ if (table)
+ table = g_hash_table_lookup (table, name);
+ }
+ else
+ {
+ gpointer value;
+
+ if (!g_hash_table_lookup_extended (table, prefix, NULL, &value))
+ g_hash_table_insert (table, prefix,
+ value = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) g_hash_table_unref));
+
+ table = value;
+
+ if (!g_hash_table_lookup_extended (table, prefix, NULL, &value))
+ g_hash_table_insert (table, prefix,
+ value = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) g_hash_table_unref));
+ }
+
+ if (key[i] == ':' && key[i + 1] == '/')
+ {
+ gchar *prefix;
+ gchar *name;
+ gint j;
+
+ i += 2;
+
+ for (j = i; key[j] != '/'; j++)
+ /* found a path of the form /a:/b
+ * which is never a valid spot for a key.
+ */
+ if (key[j] == '\0')
+ return NULL;
+
+ name = g_strndup ((const gchar *) key + i, j - i);
+ prefix = g_strndup (key, i);
+ table = g_hash_table_lookup (table, prefix);
+ g_free (prefix);
+ }
+
+ return table;
+}
+
static GVariant *
g_memory_settings_backend_read (GSettingsBackend *backend,
const gchar *key,
@@ -52,11 +137,16 @@
{
GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
GVariant *value;
+ GHashTable *table;
if (default_value)
return NULL;
- value = g_hash_table_lookup (memory->table, key);
+ table = g_memory_settings_backend_get_table (memory->table, &key);
+ if (!table)
+ return NULL;
+
+ value = g_hash_table_lookup (table, key);
if (value != NULL)
g_variant_ref (value);
@@ -72,8 +162,13 @@
{
GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
GVariant *old_value;
+ GHashTable *table;
- old_value = g_hash_table_lookup (memory->table, key);
+ table = g_memory_settings_backend_get_table (memory->table, &key);
+ if (!table)
+ return FALSE;
+
+ old_value = g_hash_table_lookup (table, key);
g_variant_ref_sink (value);
if (old_value == NULL || !g_variant_equal (value, old_value))
@@ -93,11 +188,16 @@
gpointer data)
{
GMemorySettingsBackend *memory = data;
+ GHashTable *table;
+
+ table = g_memory_settings_backend_get_table (memory->table, &key);
+ if (!table)
+ return FALSE;
if (value != NULL)
- g_hash_table_insert (memory->table, g_strdup (key), g_variant_ref (value));
+ g_hash_table_insert (table, g_strdup (key), g_variant_ref (value));
else
- g_hash_table_remove (memory->table, key);
+ g_hash_table_remove (table, key);
return FALSE;
}
@@ -119,10 +219,15 @@
gpointer origin_tag)
{
GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+ GHashTable *table;
- if (g_hash_table_lookup (memory->table, key))
+ table = g_memory_settings_backend_get_table (memory->table, &key);
+ if (!table)
+ return;
+
+ if (g_hash_table_lookup (table, key))
{
- g_hash_table_remove (memory->table, key);
+ g_hash_table_remove (table, key);
g_settings_backend_changed (backend, key, origin_tag);
}
}
@@ -141,6 +246,92 @@
return g_simple_permission_new (TRUE);
}
+static gchar **
+g_memory_settings_backend_list (GSettingsBackend *backend,
+ const gchar *path,
+ const gchar * const *schema_items)
+{
+ gchar **result = NULL;
+ GHashTable *table;
+ GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+
+ table = g_memory_settings_backend_get_table (memory->table, &path);
+ if (table != NULL)
+ {
+ GHashTableIter iter;
+ gchar *key_name;
+ guint i;
+ GSList *list = NULL;
+
+ while (g_hash_table_iter_next (&iter, (gpointer *) &key_name, NULL))
+ {
+ gboolean in_schema_items = FALSE;
+
+ for (i = 0; i < g_strv_length ((gchar **) schema_items); i++)
+ {
+ if (g_str_equal (key_name, schema_items[i]))
+ {
+ /* It's in both lists, so return it */
+ list = g_slist_append (list, key_name);
+ in_schema_items = TRUE;
+ }
+ }
+ if (!in_schema_items)
+ {
+ /* Has been added by the user */
+ list = g_slist_append (list, key_name);
+ }
+ }
+
+ result = g_new0 (gchar *, g_slist_length (list) + 1);
+ i = 0;
+ while (list != NULL)
+ {
+ result[i] = g_strdup ((const gchar *) list->data);
+ i++;
+
+ list = g_slist_remove (list, list->data);
+ }
+
+ result[i] = NULL;
+ }
+
+ return result;
+}
+
+static gboolean
+g_memory_settings_backend_can_insert (GSettingsBackend *backend,
+ const gchar *path)
+{
+ return FALSE;
+}
+
+static gboolean
+g_memory_settings_backend_can_remove (GSettingsBackend *backend,
+ const gchar *path,
+ const gchar *id)
+{
+ return FALSE;
+}
+
+static gboolean
+g_memory_settings_backend_insert (GSettingsBackend *backend,
+ const gchar *path,
+ gint index_,
+ const gchar *prefix,
+ gchar **name)
+{
+ return FALSE;
+}
+
+static gboolean
+g_memory_settings_backend_remove (GSettingsBackend *backend,
+ const gchar *path,
+ const gchar *id)
+{
+ return FALSE;
+}
+
static void
g_memory_settings_backend_finalize (GObject *object)
{
@@ -171,6 +362,11 @@
backend_class->reset = g_memory_settings_backend_reset;
backend_class->get_writable = g_memory_settings_backend_get_writable;
backend_class->get_permission = g_memory_settings_backend_get_permission;
+ backend_class->list = g_memory_settings_backend_list;
+ backend_class->can_insert = g_memory_settings_backend_can_insert;
+ backend_class->can_remove = g_memory_settings_backend_can_remove;
+ backend_class->insert = g_memory_settings_backend_insert;
+ backend_class->remove = g_memory_settings_backend_remove;
object_class->finalize = g_memory_settings_backend_finalize;
}
diff --git a/gio/gsettings.c b/gio/gsettings.c
index ec48006..c9a23b7 100644
--- a/gio/gsettings.c
+++ b/gio/gsettings.c
@@ -213,6 +213,7 @@
gchar *path;
GDelayedSettingsBackend *delayed;
+ gboolean destroyed;
};
enum
@@ -496,6 +497,10 @@
settings->priv->main_context);
g_settings_backend_subscribe (settings->priv->backend,
settings->priv->path);
+
+ settings->priv->destroyed =
+ !g_settings_backend_check (settings->priv->backend,
+ settings->priv->path);
}
static void
@@ -2936,6 +2941,130 @@
g_object_set_qdata (object, binding_quark, NULL);
}
+/* Children (add, remove, can_{add,remove}, list, get_destroyed) {{{1 */
+
+/**
+ * g_settings_add_child:
+ * @settings: a list #GSettings
+ * @prefix: the prefix of the new child name
+ * @name: return location for the new child name
+ * @returns: %TRUE if the child was added
+ *
+ * Adds a child to a list.
+ *
+ * The child will be created with a new unique name that starts with
+ * @prefix. For example, if @prefix is "item" then the child's name may
+ * end up looking like "item0", "item1", or so on. No particular format
+ * is specified -- only that the resulting name will have the given
+ * prefix.
+ *
+ * If the creation was successful then @name (if non-%NULL) is set to
+ * the name of the new child and %TRUE is returned.
+ *
+ * If the creation fails then %FALSE is returned and @name is untouched.
+ **/
+gboolean
+g_settings_add_child (GSettings *settings,
+ const gchar *prefix,
+ gchar **name)
+{
+ g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
+ g_return_val_if_fail (prefix != NULL, FALSE);
+
+ return g_settings_backend_insert (settings->priv->backend,
+ settings->priv->path,
+ -1, prefix, name);
+}
+
+/**
+ * g_settings_remove_child:
+ * @settings: a list #GSettings
+ * @id: the id of the child to remove
+ * @returns: %TRUE if the child was successfully removed
+ *
+ * Removes a child from the list.
+ *
+ * In the case that the removal was successful then %TRUE is returned.
+ * In the case that it failed, %FALSE is returned.
+ *
+ * The return value of this function is not particularly useful, since
+ * it is not specified if the non-operation resulting from the request
+ * to remove a non-existent child is considered to be a success (and
+ * this situation can easily arise as a race condition). Most usually
+ * you will actually probably want to ignore the return value here and
+ * just monitor the "children-changed" signal and make changes to your
+ * user interface accordingly.
+ **/
+gboolean
+g_settings_remove_child (GSettings *settings,
+ const gchar *id)
+{
+ g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
+ g_return_val_if_fail (id != NULL, FALSE);
+
+ return g_settings_backend_remove (settings->priv->backend,
+ settings->priv->path,
+ id);
+}
+
+/**
+ * g_settings_can_add_child:
+ * @settings: a #GSettings object
+ * @returns: %TRUE if a call to g_settings_add_child() would succeed
+ *
+ * Checks if it is valid to add children to @settings.
+ **/
+gboolean
+g_settings_can_add_child (GSettings *settings)
+{
+ g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
+
+ return g_settings_backend_can_insert (settings->priv->backend,
+ settings->priv->path);
+}
+
+/**
+ * g_settings_can_remove_child:
+ * @settings: a #GSettings object
+ * @id: the identifier of an existing child
+ * @returns: %TRUE if a call to g_settings_remove_child() would succeed
+ *
+ * Checks if it is valid to remove @id from @settings.
+ *
+ * The return value of this function is unspecified for the case that
+ * @id does not exist.
+ **/
+gboolean
+g_settings_can_remove_child (GSettings *settings,
+ const gchar *id)
+{
+ g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
+
+ return g_settings_backend_can_remove (settings->priv->backend,
+ settings->priv->path, id);
+}
+
+/**
+ * g_settings_get_destroyed:
+ * @settings: a #GSettings
+ * @returns: %TRUE if @settings has been destroyed
+ *
+ * Checks if @settings has been destroyed.
+ *
+ * If @settings is a member of a list (or a child thereof) and has since
+ * been removed from the list then it will be considered as having been
+ * destroyed. The "notify" signal will be emitted on the "destroyed"
+ * property and this function will return %TRUE thereafter.
+ *
+ * A destroyed #GSettings object is never revived and nearly all
+ * operations performed on it will be meaningless.
+ **/
+gboolean
+g_settings_get_destroyed (GSettings *settings)
+{
+ return settings->priv->destroyed;
+}
+
/* Epilogue {{{1 */
/* vim:set foldmethod=marker: */
diff --git a/gio/gsettings.h b/gio/gsettings.h
index 324ad36..f30c44a 100644
--- a/gio/gsettings.h
+++ b/gio/gsettings.h
@@ -58,7 +58,10 @@
const GQuark *keys,
gint n_keys);
- gpointer padding[20];
+ void (*can_remove_changed) (GSettings *settings);
+ void (*children_changed) (GSettings *settings);
+
+ gpointer padding[18];
};
struct _GSettings
@@ -260,6 +263,20 @@
GSettingsGetMapping mapping,
gpointer user_data);
+gboolean g_settings_can_add_child (GSettings *settings);
+
+gboolean g_settings_can_remove_child (GSettings *settings,
+ const gchar *id);
+
+gboolean g_settings_add_child (GSettings *settings,
+ const gchar *prefix,
+ gchar **name);
+
+gboolean g_settings_remove_child (GSettings *settings,
+ const gchar *id);
+
+gboolean g_settings_get_destroyed (GSettings *settings);
+
G_END_DECLS
#endif /* __G_SETTINGS_H__ */
diff --git a/gio/gsettingsbackend.c b/gio/gsettingsbackend.c
index 24a6f9a..ee0a93f 100644
--- a/gio/gsettingsbackend.c
+++ b/gio/gsettingsbackend.c
@@ -1030,3 +1030,57 @@
if (class->sync)
class->sync (backend);
}
+
+gchar **
+g_settings_backend_list (GSettingsBackend *backend,
+ const gchar *path,
+ const gchar * const *schema_items)
+{
+ return G_SETTINGS_BACKEND_GET_CLASS (backend)
+ ->list (backend, path, schema_items);
+}
+
+gboolean
+g_settings_backend_can_insert (GSettingsBackend *backend,
+ const gchar *path)
+{
+ return G_SETTINGS_BACKEND_GET_CLASS (backend)
+ ->can_insert (backend, path);
+}
+
+gboolean
+g_settings_backend_can_remove (GSettingsBackend *backend,
+ const gchar *path,
+ const gchar *id)
+{
+ return G_SETTINGS_BACKEND_GET_CLASS (backend)
+ ->can_remove (backend, path, id);
+}
+
+gboolean
+g_settings_backend_insert (GSettingsBackend *backend,
+ const gchar *path,
+ gint index_,
+ const gchar *prefix,
+ gchar **name)
+{
+ return G_SETTINGS_BACKEND_GET_CLASS (backend)
+ ->insert (backend, path, index_, prefix, name);
+}
+
+gboolean
+g_settings_backend_remove (GSettingsBackend *backend,
+ const gchar *path,
+ const gchar *id)
+{
+ return G_SETTINGS_BACKEND_GET_CLASS (backend)
+ ->remove (backend, path, id);
+}
+
+gboolean
+g_settings_backend_check (GSettingsBackend *backend,
+ const gchar *path)
+{
+ return G_SETTINGS_BACKEND_GET_CLASS (backend)
+ ->check (backend, path);
+}
diff --git a/gio/gsettingsbackend.h b/gio/gsettingsbackend.h
index e674187..2854529 100644
--- a/gio/gsettingsbackend.h
+++ b/gio/gsettingsbackend.h
@@ -93,7 +93,26 @@
GPermission * (*get_permission) (GSettingsBackend *backend,
const gchar *path);
- gpointer padding[24];
+ gchar ** (*list) (GSettingsBackend *backend,
+ const gchar *path,
+ const gchar * const *schema_items);
+ gboolean (*check) (GSettingsBackend *backend,
+ const gchar *path);
+ gboolean (*can_insert) (GSettingsBackend *backend,
+ const gchar *path);
+ gboolean (*can_remove) (GSettingsBackend *backend,
+ const gchar *path,
+ const gchar *id);
+ gboolean (*insert) (GSettingsBackend *backend,
+ const gchar *path,
+ gint index_,
+ const gchar *prefix,
+ gchar **name);
+ gboolean (*remove) (GSettingsBackend *backend,
+ const gchar *path,
+ const gchar *id);
+
+ gpointer padding[18];
};
struct _GSettingsBackend
diff --git a/gio/gsettingsbackendinternal.h b/gio/gsettingsbackendinternal.h
index e009b31..8a423e0 100644
--- a/gio/gsettingsbackendinternal.h
+++ b/gio/gsettingsbackendinternal.h
@@ -41,12 +41,24 @@
const gchar *prefix,
const gchar * const *names,
gpointer origin_tag);
+
void (* writable_changed) (GObject *target,
GSettingsBackend *backend,
const gchar *key);
void (* path_writable_changed) (GObject *target,
GSettingsBackend *backend,
const gchar *path);
+
+ void (* children_changed) (GObject *target,
+ GSettingsBackend *backend,
+ const gchar *path,
+ gpointer origin_tag);
+ void (* can_add_changed) (GObject *target,
+ GSettingsBackend *backend,
+ const gchar *path);
+ void (* can_remove_changed) (GObject *target,
+ GSettingsBackend *backend,
+ const gchar *path);
} GSettingsListenerVTable;
G_GNUC_INTERNAL
@@ -91,6 +103,28 @@
G_GNUC_INTERNAL
GPermission * g_settings_backend_get_permission (GSettingsBackend *backend,
const gchar *path);
+
+G_GNUC_INTERNAL
+gboolean g_settings_backend_check (GSettingsBackend *backend,
+ const gchar *path);
+G_GNUC_INTERNAL
+gboolean g_settings_backend_insert (GSettingsBackend *backend,
+ const gchar *path,
+ gint index,
+ const gchar *prefix,
+ gchar **name);
+G_GNUC_INTERNAL
+gboolean g_settings_backend_remove (GSettingsBackend *backend,
+ const gchar *path,
+ const gchar *id);
+G_GNUC_INTERNAL
+gboolean g_settings_backend_can_insert (GSettingsBackend *backend,
+ const gchar *path);
+G_GNUC_INTERNAL
+gboolean g_settings_backend_can_remove (GSettingsBackend *backend,
+ const gchar *path,
+ const gchar *id);
+
G_GNUC_INTERNAL
void g_settings_backend_sync_default (void);