list stuff

This commit is contained in:
Ryan Lortie
2010-09-13 13:19:09 -04:00
parent c7254e602d
commit b12b0a26c2
7 changed files with 454 additions and 104 deletions

View File

@@ -1558,6 +1558,11 @@ g_settings_sync
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
#endif
#endif

View File

@@ -32,11 +32,42 @@
G_TYPE_MEMORY_SETTINGS_BACKEND, \
GMemorySettingsBackend))
typedef struct
{
GHashTable/*<string, GVariant>*/ *values;
GHashTable/*<string, GHashTable<string, TablePair>*/ *lists;
} TablePair;
static void
table_pair_free (gpointer data)
{
TablePair *pair = data;
g_hash_table_unref (pair->values);
g_hash_table_unref (pair->list);
}
static TablePair *
table_pair_new (void)
{
TablePair *pair;
pair = g_slice_new (TablePair);
pair->values = g_hash_table_new (g_str_hash, g_str_equal, g_free,
(GDestroyNotify) g_variant_unref);
pair->lists = g_hash_table_new (g_str_hash, g_str_equal, g_free,
(GDestroyNotify) g_hash_table_unref);
return pair;
}
typedef GSettingsBackendClass GMemorySettingsBackendClass;
typedef struct
{
GSettingsBackend parent_instance;
GHashTable *table;
TablePair *root;
} GMemorySettingsBackend;
G_DEFINE_TYPE_WITH_CODE (GMemorySettingsBackend,
@@ -45,6 +76,90 @@ G_DEFINE_TYPE_WITH_CODE (GMemorySettingsBackend,
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, 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, 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 (*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,

View File

@@ -195,6 +195,7 @@ struct _GSettingsPrivate
gchar *path;
GDelayedSettingsBackend *delayed;
gboolean destroyed;
};
enum
@@ -465,6 +466,10 @@ g_settings_constructed (GObject *object)
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
@@ -1975,7 +1980,7 @@ g_settings_get_has_unapplied (GSettings *settings)
G_DELAYED_SETTINGS_BACKEND (settings->priv->backend));
}
/* Extra API (reset, sync, get_child, is_writable, list_*) {{{1 */
/* Extra API (reset, sync, get_child, is_writable) {{{1 */
/**
* g_settings_reset:
* @settings: a #GSettings object
@@ -2089,95 +2094,6 @@ g_settings_get_child (GSettings *settings,
return child;
}
/**
* g_settings_list_keys:
* @settings: a #GSettings object
* @returns: a list of the keys on @settings
*
* Introspects the list of keys on @settings.
*
* You should probably not be calling this function from "normal" code
* (since you should already know what keys are in your schema). This
* function is intended for introspection reasons.
*
* You should free the return value with g_strfreev() when you are done
* with it.
*/
gchar **
g_settings_list_keys (GSettings *settings)
{
const GQuark *keys;
gchar **strv;
gint n_keys;
gint i, j;
keys = g_settings_schema_list (settings->priv->schema, &n_keys);
strv = g_new (gchar *, n_keys + 1);
for (i = j = 0; i < n_keys; i++)
{
const gchar *key = g_quark_to_string (keys[i]);
if (!g_str_has_suffix (key, "/"))
strv[j++] = g_strdup (key);
}
strv[j] = NULL;
return strv;
}
/**
* g_settings_list_children:
* @settings: a #GSettings object
* @returns: a list of the children on @settings
*
* Gets the list of children on @settings.
*
* The list is exactly the list of strings for which it is not an error
* to call g_settings_get_child().
*
* For GSettings objects that are lists, this value can change at any
* time and you should connect to the "children-changed" signal to watch
* for those changes. Note that there is a race condition here: you may
* request a child after listing it only for it to have been destroyed
* in the meantime. For this reason, g_settings_get_chuld() may return
* %NULL even for a child that was listed by this function.
*
* For GSettings objects that are not lists, you should probably not be
* calling this function from "normal" code (since you should already
* know what children are in your schema). This function may still be
* useful there for introspection reasons, however.
*
* You should free the return value with g_strfreev() when you are done
* with it.
*/
gchar **
g_settings_list_children (GSettings *settings)
{
const GQuark *keys;
gchar **strv;
gint n_keys;
gint i, j;
keys = g_settings_schema_list (settings->priv->schema, &n_keys);
strv = g_new (gchar *, n_keys + 1);
for (i = j = 0; i < n_keys; i++)
{
const gchar *key = g_quark_to_string (keys[i]);
if (g_str_has_suffix (key, "/"))
{
gint length = strlen (key);
strv[j] = g_memdup (key, length);
strv[j][length - 1] = '\0';
j++;
}
}
strv[j] = NULL;
return strv;
}
/* Binding {{{1 */
typedef struct
{
@@ -2736,6 +2652,219 @@ g_settings_unbind (gpointer object,
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;
}
/**
* g_settings_list_keys:
* @settings: a #GSettings object
* @returns: a list of the keys on @settings
*
* Introspects the list of keys on @settings.
*
* You should probably not be calling this function from "normal" code
* (since you should already know what keys are in your schema). This
* function is intended for introspection reasons.
*
* You should free the return value with g_strfreev() when you are done
* with it.
*/
gchar **
g_settings_list_keys (GSettings *settings)
{
const GQuark *keys;
gchar **strv;
gint n_keys;
gint i, j;
keys = g_settings_schema_list (settings->priv->schema, &n_keys);
strv = g_new (gchar *, n_keys + 1);
for (i = j = 0; i < n_keys; i++)
{
const gchar *key = g_quark_to_string (keys[i]);
if (!g_str_has_suffix (key, "/"))
strv[j++] = g_strdup (key);
}
strv[j] = NULL;
return strv;
}
/**
* g_settings_list_children:
* @settings: a #GSettings object
* @returns: a list of the children on @settings
*
* Gets the list of children on @settings.
*
* The list is exactly the list of strings for which it is not an error
* to call g_settings_get_child().
*
* For GSettings objects that are lists, this value can change at any
* time and you should connect to the "children-changed" signal to watch
* for those changes. Note that there is a race condition here: you may
* request a child after listing it only for it to have been destroyed
* in the meantime. For this reason, g_settings_get_chuld() may return
* %NULL even for a child that was listed by this function.
*
* For GSettings objects that are not lists, you should probably not be
* calling this function from "normal" code (since you should already
* know what children are in your schema). This function may still be
* useful there for introspection reasons, however.
*
* You should free the return value with g_strfreev() when you are done
* with it.
*/
gchar **
g_settings_list_children (GSettings *settings)
{
const GQuark *keys;
gchar **strv;
gint n_keys;
gint i, j;
keys = g_settings_schema_list (settings->priv->schema, &n_keys);
strv = g_new (gchar *, n_keys + 1);
for (i = j = 0; i < n_keys; i++)
{
const gchar *key = g_quark_to_string (keys[i]);
if (g_str_has_suffix (key, "/"))
{
gint length = strlen (key);
strv[j] = g_memdup (key, length);
strv[j][length - 1] = '\0';
j++;
}
}
strv[j] = NULL;
return strv;
}
/* Epilogue {{{1 */
/* vim:set foldmethod=marker: */

View File

@@ -58,7 +58,10 @@ struct _GSettingsClass
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
@@ -79,11 +82,6 @@ GSettings * g_settings_new_with_backend (const g
GSettings * g_settings_new_with_backend_and_path (const gchar *schema,
GSettingsBackend *backend,
const gchar *path);
gchar ** g_settings_list_children (GSettings *settings);
gchar ** g_settings_list_keys (GSettings *settings);
gboolean g_settings_get_destroyed (GSettings *settings);
GPermission * g_settings_get_permission (GSettings *settings);
gboolean g_settings_set_value (GSettings *settings,
const gchar *key,
@@ -256,6 +254,23 @@ gpointer g_settings_get_mapped (GSettin
GSettingsGetMapping mapping,
gpointer user_data);
gchar ** g_settings_list_children (GSettings *settings);
gchar ** g_settings_list_keys (GSettings *settings);
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__ */

View File

@@ -1050,3 +1050,48 @@ g_settings_backend_sync_default (void)
if (class->sync)
class->sync (backend);
}
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);
}

View File

@@ -96,16 +96,23 @@ struct _GSettingsBackendClass
gchar ** (*list) (GSettingsBackend *backend,
const gchar *path,
const gchar * const *schema_items);
gchar * (*add) (GSettingsBackend *backend,
const gchar *path,
const gchar *hint);
gboolean (*remove) (GSettingsBackend *backend,
const gchar *path,
const gchar *name);
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[20];
gpointer padding[18];
};
struct _GSettingsBackend

View File

@@ -41,12 +41,24 @@ typedef struct
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 @@ void g_settings_backend_subscribe (GSettin
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
GMainContext * g_settings_backend_get_active_context (void);
G_GNUC_INTERNAL