summaryrefslogtreecommitdiffstats
path: root/src/terminal-settings-list.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/terminal-settings-list.cc')
-rw-r--r--src/terminal-settings-list.cc930
1 files changed, 930 insertions, 0 deletions
diff --git a/src/terminal-settings-list.cc b/src/terminal-settings-list.cc
new file mode 100644
index 0000000..8907643
--- /dev/null
+++ b/src/terminal-settings-list.cc
@@ -0,0 +1,930 @@
+/*
+ * Copyright © 2013 Christian Persch
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "terminal-settings-list.hh"
+#include "terminal-client-utils.hh"
+
+#include <string.h>
+#include <uuid.h>
+
+#define G_SETTINGS_ENABLE_BACKEND
+#include <gio/gsettingsbackend.h>
+
+#include "terminal-type-builtins.hh"
+#include "terminal-settings-utils.hh"
+#include "terminal-schemas.hh"
+#include "terminal-debug.hh"
+#include "terminal-libgsystem.hh"
+
+struct _TerminalSettingsList {
+ GSettings parent;
+
+ GSettingsBackend* settings_backend;
+ GSettingsSchemaSource* schema_source;
+ char *path;
+ char *child_schema_id;
+
+ char **uuids;
+ char *default_uuid;
+
+ GHashTable *children;
+
+ TerminalSettingsListFlags flags;
+};
+
+struct _TerminalSettingsListClass {
+ GSettingsClass parent;
+
+ void (* children_changed) (TerminalSettingsList *list);
+ void (* default_changed) (TerminalSettingsList *list);
+};
+
+enum {
+ PROP_SCHEMA_SOURCE = 1,
+ PROP_CHILD_SCHEMA_ID,
+ PROP_FLAGS
+};
+
+enum {
+ SIGNAL_CHILDREN_CHANGED,
+ SIGNAL_DEFAULT_CHANGED,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS];
+
+static void
+strv_printerr (char **strv)
+{
+ char **p;
+
+ if (strv == nullptr) {
+ g_printerr ("(null)");
+ return;
+ }
+
+ for (p = strv; *p; p++)
+ g_printerr ("%s'%s'", p != strv ? ", " : "", *p);
+}
+
+static char **
+strv_sort (char **strv)
+{
+ // FIXMEchpe
+ return strv;
+}
+
+static gboolean
+strv_equal (char **a,
+ char **b)
+{
+ char **e, **f;
+
+ if (a == nullptr || b == nullptr)
+ return a == b;
+
+ for (e = a, f = b; *e && *f; e++, f++) {
+ if (!g_str_equal (*e, *f))
+ return FALSE;
+ }
+
+ return *e == *f;
+}
+
+static int
+strv_find (char **strv,
+ const char *str)
+{
+ int i;
+
+ if (strv == nullptr || str == nullptr)
+ return -1;
+
+ for (i = 0; strv[i]; i++) {
+ if (!g_str_equal (strv[i], str))
+ continue;
+
+ return i;
+ }
+
+ return -1;
+}
+
+#if defined(TERMINAL_SERVER) || defined(TERMINAL_PREFERENCES)
+
+static char **
+strv_dupv_insert (char **strv,
+ const char *str)
+{
+ char **nstrv, **p, **q;
+
+ if (strv == nullptr) {
+ char *s[2] = { (char *) str, nullptr };
+ return g_strdupv (s);
+ }
+
+ /* Is it already in the list? */
+ for (p = strv; *p; p++)
+ if (g_str_equal (*p, str))
+ return g_strdupv (strv);
+
+ /* Not found; append */
+ nstrv = g_new (char *, (p - strv) + 2);
+ for (p = strv, q = nstrv; *p; p++, q++)
+ *q = g_strdup (*p);
+ *q++ = g_strdup (str);
+ *q = nullptr;
+
+ return strv_sort (nstrv);
+}
+
+static char **
+strv_dupv_remove (char **strv,
+ const char *str)
+{
+ char **nstrv, **p, **q;
+
+ if (strv == nullptr)
+ return nullptr;
+
+ nstrv = g_strdupv (strv);
+ for (p = q = nstrv; *p; p++) {
+ if (!g_str_equal (*p, str))
+ *q++ = *p;
+ else
+ g_free (*p);
+ }
+ *q = nullptr;
+
+ return nstrv;
+}
+
+#endif /* TERMINAL_SERVER || TERMINAL_PREFERENCES */
+
+gboolean
+terminal_settings_list_valid_uuid (const char *str)
+{
+ uuid_t u;
+
+ if (str == nullptr)
+ return FALSE;
+
+ return uuid_parse ((char *) str, u) == 0;
+}
+
+#if defined(TERMINAL_SERVER) || defined(TERMINAL_PREFERENCES)
+
+static char *
+new_list_entry (void)
+{
+ uuid_t u;
+ char name[37];
+
+ uuid_generate (u);
+ uuid_unparse (u, name);
+
+ return g_strdup (name);
+}
+
+#endif /* TERMINAL_SERVER || TERMINAL_PREFERENCES */
+
+static gboolean
+validate_list (TerminalSettingsList *list,
+ char **entries)
+{
+ gboolean allow_empty = (list->flags & TERMINAL_SETTINGS_LIST_FLAG_ALLOW_EMPTY) != 0;
+ guint i;
+
+ if (entries == nullptr)
+ return allow_empty;
+
+ for (i = 0; entries[i]; i++) {
+ if (!terminal_settings_list_valid_uuid (entries[i]))
+ return FALSE;
+ }
+
+ return (i > 0) || allow_empty;
+}
+
+static gboolean
+list_map_func (GVariant *value,
+ gpointer *result,
+ gpointer user_data)
+{
+ TerminalSettingsList *list = (TerminalSettingsList*)user_data;
+ gs_strfreev char **entries;
+
+ entries = strv_sort (g_variant_dup_strv (value, nullptr));
+
+ if (validate_list (list, entries)) {
+ gs_transfer_out_value(result, &entries);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static char*
+path_new (TerminalSettingsList *list,
+ char const* uuid)
+{
+ return g_strdup_printf ("%s:%s/", list->path, uuid);
+}
+
+static GSettings *
+terminal_settings_list_ref_child_internal (TerminalSettingsList *list,
+ const char *uuid)
+{
+ GSettings *child;
+ gs_free char *path = nullptr;
+
+ if (strv_find (list->uuids, uuid) == -1)
+ return nullptr;
+
+ _terminal_debug_print (TERMINAL_DEBUG_SETTINGS_LIST,
+ "%s UUID %s\n", G_STRFUNC, uuid);
+
+ child = (GSettings*)g_hash_table_lookup (list->children, uuid);
+ if (child)
+ goto done;
+
+ path = path_new (list, uuid);
+ child = terminal_g_settings_new_with_path(list->settings_backend,
+ list->schema_source,
+ list->child_schema_id,
+ path);
+ g_hash_table_insert (list->children, g_strdup (uuid), child /* adopted */);
+
+ done:
+ return (GSettings*)g_object_ref(child);
+}
+
+#if defined(TERMINAL_SERVER) || defined(TERMINAL_PREFERENCES)
+
+static char *
+terminal_settings_list_add_child_internal (TerminalSettingsList *list,
+ const char *uuid,
+ const char *name)
+{
+
+ auto const new_uuid = new_list_entry();
+ _terminal_debug_print (TERMINAL_DEBUG_SETTINGS_LIST,
+ "%s NEW UUID %s\n", G_STRFUNC, new_uuid);
+
+ gs_free auto path = path_new(list, uuid);
+ gs_free auto new_path = path_new(list, new_uuid);
+
+ auto tree = terminal_g_settings_backend_create_tree();
+ terminal_g_settings_backend_clone_schema(list->settings_backend,
+ list->schema_source,
+ list->child_schema_id,
+ path,
+ new_path,
+ tree);
+ if (name) {
+ g_tree_insert(tree,
+ g_strconcat(new_path, TERMINAL_PROFILE_VISIBLE_NAME_KEY, nullptr), // transfer
+ g_variant_take_ref(g_variant_new_string(name))); // transfer
+ }
+
+#ifdef ENABLE_DEBUG
+ _TERMINAL_DEBUG_IF(TERMINAL_DEBUG_SETTINGS_LIST) {
+ g_printerr("Cloning schema %s from %s -> %s\n", list->child_schema_id, path, new_path);
+ terminal_g_settings_backend_print_tree(tree);
+ }
+#endif
+
+ auto const tag = &list;
+ (void)terminal_g_settings_backend_write_tree(list->settings_backend, tree, tag);
+ g_tree_unref(tree);
+
+ gs_strfreev auto new_uuids = strv_dupv_insert(list->uuids, new_uuid);
+ g_settings_set_strv(&list->parent, TERMINAL_SETTINGS_LIST_LIST_KEY,
+ (char const* const*)new_uuids);
+
+ return new_uuid;
+}
+
+static void
+terminal_settings_list_remove_child_internal (TerminalSettingsList *list,
+ const char *uuid)
+{
+ gs_strfreev char **new_uuids;
+
+ _terminal_debug_print (TERMINAL_DEBUG_SETTINGS_LIST,
+ "%s UUID %s\n", G_STRFUNC, uuid);
+
+ new_uuids = strv_dupv_remove (list->uuids, uuid);
+
+ if ((new_uuids == nullptr || new_uuids[0] == nullptr) &&
+ (list->flags & TERMINAL_SETTINGS_LIST_FLAG_ALLOW_EMPTY) == 0)
+ return;
+
+ g_settings_set_strv (&list->parent, TERMINAL_SETTINGS_LIST_LIST_KEY, (const char * const *) new_uuids);
+
+ if (list->default_uuid != nullptr &&
+ g_str_equal (list->default_uuid, uuid))
+ g_settings_set_string (&list->parent, TERMINAL_SETTINGS_LIST_DEFAULT_KEY, "");
+
+ /* Now we unset all keys under the child */
+ gs_free auto path = path_new(list, uuid);
+ terminal_g_settings_backend_erase_path(list->settings_backend,
+ list->schema_source,
+ list->child_schema_id,
+ path);
+}
+
+#endif /* TERMINAL_SERVER || TERMINAL_PREFERENCES */
+
+static void
+terminal_settings_list_update_list (TerminalSettingsList *list)
+{
+ char **uuids, *uuid;
+ GSettings *child;
+ GHashTable *new_children;
+ guint i;
+ gboolean changed;
+
+ uuids = (char**)g_settings_get_mapped (&list->parent,
+ TERMINAL_SETTINGS_LIST_LIST_KEY,
+ list_map_func, list);
+
+ _TERMINAL_DEBUG_IF (TERMINAL_DEBUG_SETTINGS_LIST) {
+ g_printerr ("%s: current UUIDs [", G_STRFUNC);
+ strv_printerr (list->uuids);
+ g_printerr ("]\n new UUIDs [");
+ strv_printerr (uuids);
+ g_printerr ("]\n");
+ }
+
+ if (strv_equal (uuids, list->uuids) &&
+ ((list->flags & TERMINAL_SETTINGS_LIST_FLAG_HAS_DEFAULT) == 0 ||
+ strv_find (list->uuids, list->default_uuid) != -1)) {
+ g_strfreev (uuids);
+ return;
+ }
+
+ new_children = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_object_unref);
+
+ if (uuids) {
+ for (i = 0; uuids[i] != nullptr; i++) {
+ uuid = uuids[i];
+
+ child = (GSettings*)g_hash_table_lookup (list->children, uuid);
+
+ if (child) {
+ g_object_ref (child);
+ g_hash_table_remove (list->children, uuid);
+ g_hash_table_insert (new_children, g_strdup (uuid), child /* adopted */);
+ }
+ }
+
+ changed = !strv_equal (uuids, list->uuids);
+ } else {
+ changed = g_strv_length (list->uuids) != 0;
+ }
+
+ g_hash_table_unref (list->children);
+ list->children = new_children;
+
+ g_strfreev (list->uuids);
+ list->uuids = uuids; /* adopts */
+
+ if (changed)
+ g_signal_emit (list, signals[SIGNAL_CHILDREN_CHANGED], 0);
+}
+
+static void
+terminal_settings_list_update_default (TerminalSettingsList *list)
+{
+ if ((list->flags & TERMINAL_SETTINGS_LIST_FLAG_HAS_DEFAULT) == 0)
+ return;
+
+ g_free (list->default_uuid);
+ list->default_uuid = g_settings_get_string (&list->parent,
+ TERMINAL_SETTINGS_LIST_DEFAULT_KEY);
+
+ _terminal_debug_print (TERMINAL_DEBUG_SETTINGS_LIST,
+ "%s new default UUID %s\n", G_STRFUNC, list->default_uuid);
+
+ g_signal_emit (list, signals[SIGNAL_DEFAULT_CHANGED], 0);
+}
+
+G_DEFINE_TYPE (TerminalSettingsList, terminal_settings_list, G_TYPE_SETTINGS);
+
+static void
+terminal_settings_list_changed (GSettings *list_settings,
+ const char *key)
+{
+ TerminalSettingsList *list = TERMINAL_SETTINGS_LIST (list_settings);
+
+ _terminal_debug_print (TERMINAL_DEBUG_SETTINGS_LIST,
+ "%s key %s", G_STRFUNC, key ? key : "(null)");
+
+ if (key == nullptr ||
+ g_str_equal (key, TERMINAL_SETTINGS_LIST_LIST_KEY)) {
+ terminal_settings_list_update_list (list);
+ terminal_settings_list_update_default (list);
+ }
+
+ if (key == nullptr)
+ return;
+
+ if (g_str_equal (key, TERMINAL_SETTINGS_LIST_DEFAULT_KEY)) {
+ terminal_settings_list_update_default (list);
+ }
+}
+
+static void
+terminal_settings_list_init (TerminalSettingsList *list)
+{
+ list->flags = TERMINAL_SETTINGS_LIST_FLAG_NONE;
+}
+
+static void
+terminal_settings_list_constructed (GObject *object)
+{
+ TerminalSettingsList *list = TERMINAL_SETTINGS_LIST (object);
+
+ G_OBJECT_CLASS (terminal_settings_list_parent_class)->constructed (object);
+
+ g_object_get(object, "backend", &list->settings_backend, nullptr);
+ g_assert(list->settings_backend);
+ g_assert(list->schema_source);
+
+ g_assert (list->schema_source != nullptr);
+ g_assert (list->child_schema_id != nullptr);
+
+ g_object_get (object, "path", &list->path, nullptr);
+
+ list->children = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_object_unref);
+
+ terminal_settings_list_changed (&list->parent, nullptr);
+}
+
+static void
+terminal_settings_list_finalize (GObject *object)
+{
+ TerminalSettingsList *list = TERMINAL_SETTINGS_LIST (object);
+
+ g_free (list->path);
+ g_free (list->child_schema_id);
+ g_strfreev (list->uuids);
+ g_free (list->default_uuid);
+ g_hash_table_unref (list->children);
+ g_settings_schema_source_unref(list->schema_source);
+ g_clear_object(&list->settings_backend);
+
+ G_OBJECT_CLASS (terminal_settings_list_parent_class)->finalize (object);
+}
+
+static void
+terminal_settings_list_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ TerminalSettingsList *list = TERMINAL_SETTINGS_LIST (object);
+
+ switch (prop_id) {
+ case PROP_SCHEMA_SOURCE: {
+ auto const schema_source = reinterpret_cast<GSettingsSchemaSource*>(g_value_get_boxed(value));
+ list->schema_source = g_settings_schema_source_ref(schema_source);
+ break;
+ }
+ case PROP_CHILD_SCHEMA_ID:
+ list->child_schema_id = g_value_dup_string (value);
+ break;
+ case PROP_FLAGS:
+ list->flags = TerminalSettingsListFlags(g_value_get_flags (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+terminal_settings_list_class_init (TerminalSettingsListClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GSettingsClass *settings_class = G_SETTINGS_CLASS (klass);
+
+ object_class->set_property = terminal_settings_list_set_property;
+ object_class->constructed = terminal_settings_list_constructed;
+ object_class->finalize = terminal_settings_list_finalize;
+
+ /**
+ * TerminalSettingsList:child-schema-id:
+ *
+ * The name of the schema of the children of this list.
+ */
+ g_object_class_install_property (object_class, PROP_SCHEMA_SOURCE,
+ g_param_spec_boxed("schema-source", nullptr, nullptr,
+ G_TYPE_SETTINGS_SCHEMA_SOURCE,
+ GParamFlags(G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS)));
+
+ /**
+ * TerminalSettingsList:child-schema-id:
+ *
+ * The name of the schema of the children of this list.
+ */
+ g_object_class_install_property (object_class, PROP_CHILD_SCHEMA_ID,
+ g_param_spec_string ("child-schema-id", nullptr, nullptr,
+ nullptr,
+ GParamFlags(G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS)));
+
+ /**
+ * TerminalSettingsList:flags:
+ *
+ * Flags from #TerminalSettingsListFlags.
+ */
+ g_object_class_install_property (object_class, PROP_FLAGS,
+ g_param_spec_flags ("flags", nullptr,nullptr,
+ TERMINAL_TYPE_SETTINGS_LIST_FLAGS,
+ TERMINAL_SETTINGS_LIST_FLAG_NONE,
+ GParamFlags(G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS)));
+
+ /**
+ * TerminalSettingsList::children-changed:
+ * @list: the object on which the signal was emitted
+ *
+ * The "children-changed" signal is emitted when the list of children
+ * has potentially changed.
+ */
+ signals[SIGNAL_CHILDREN_CHANGED] =
+ g_signal_new ("children-changed", TERMINAL_TYPE_SETTINGS_LIST,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TerminalSettingsListClass, children_changed),
+ nullptr, nullptr,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * TerminalSettingsList::default-changed:
+ * @list: the object on which the signal was emitted
+ *
+ * The "default-changed" signal is emitted when the default child
+ * has potentially changed.
+ */
+ signals[SIGNAL_DEFAULT_CHANGED] =
+ g_signal_new ("default-changed", TERMINAL_TYPE_SETTINGS_LIST,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TerminalSettingsListClass, default_changed),
+ nullptr, nullptr,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ settings_class->changed = terminal_settings_list_changed;
+}
+
+
+/**
+ * terminal_settings_list_new:
+ * @backend: (nullable): a #GSettingsBackend, or %NULL
+ * @schema_source: a #GSettingsSchemaSource
+ * @path: the settings path for the list
+ * @schema_id: the schema of the list, equal to or derived from "org.gnome.Terminal.SettingsList"
+ * @child_schema_id: the schema of the list children
+ * @flags: list flags
+ *
+ * Returns: (transfer full): the newly created #TerminalSettingsList
+ */
+TerminalSettingsList *
+terminal_settings_list_new (GSettingsBackend* backend,
+ GSettingsSchemaSource* schema_source,
+ const char *path,
+ const char *schema_id,
+ const char *child_schema_id,
+ TerminalSettingsListFlags flags)
+{
+ g_return_val_if_fail (backend == nullptr || G_IS_SETTINGS_BACKEND (backend), nullptr);
+ g_return_val_if_fail (schema_source != nullptr, nullptr);
+ g_return_val_if_fail (path != nullptr, nullptr);
+ g_return_val_if_fail (schema_id != nullptr, nullptr);
+ g_return_val_if_fail (child_schema_id != nullptr, nullptr);
+ g_return_val_if_fail (g_str_has_suffix (path, ":/"), nullptr);
+
+ gs_unref_settings_schema auto schema =
+ g_settings_schema_source_lookup(schema_source, schema_id, true);
+ g_assert_nonnull(schema);
+
+ return reinterpret_cast<TerminalSettingsList*>(g_object_new (TERMINAL_TYPE_SETTINGS_LIST,
+ "backend", backend,
+ "schema-source", schema_source,
+ "settings-schema", schema,
+ "child-schema-id", child_schema_id,
+ "path", path,
+ "flags", flags,
+ nullptr));
+}
+
+/**
+ * terminal_settings_list_dupv_children:
+ * @list: a #TerminalSettingsList
+ *
+ * Returns: (transfer full): the UUIDs of the children in the settings list, or %nullptr
+ * if the list is empty
+ */
+char **
+terminal_settings_list_dupv_children (TerminalSettingsList *list)
+{
+ g_return_val_if_fail (TERMINAL_IS_SETTINGS_LIST (list), nullptr);
+
+ return g_strdupv (list->uuids);
+}
+
+/**
+ * terminal_settings_list_dup_default_child:
+ * @list: a #TerminalSettingsList
+ *
+ * Returns: (transfer full): the UUID of the default child in the settings list
+ */
+char *
+terminal_settings_list_dup_default_child (TerminalSettingsList *list)
+{
+ g_return_val_if_fail (TERMINAL_IS_SETTINGS_LIST (list), nullptr);
+
+ if ((list->flags & TERMINAL_SETTINGS_LIST_FLAG_HAS_DEFAULT) == 0)
+ return nullptr;
+
+ if ((strv_find (list->uuids, list->default_uuid)) != -1)
+ return g_strdup (list->default_uuid);
+
+ /* Just randomly designate the first child as default, but don't write that
+ * to the settings.
+ */
+ if (list->uuids == nullptr || list->uuids[0] == nullptr) {
+ g_warn_if_fail ((list->flags & TERMINAL_SETTINGS_LIST_FLAG_ALLOW_EMPTY));
+ return nullptr;
+ }
+
+ return g_strdup (list->uuids[0]);
+}
+
+/**
+ * terminal_settings_list_has_child:
+ * @list: a #TerminalSettingsList
+ * @uuid: the UUID of a list child
+ *
+ * Returns: %TRUE iff the child with @uuid exists
+ */
+gboolean
+terminal_settings_list_has_child (TerminalSettingsList *list,
+ const char *uuid)
+{
+ g_return_val_if_fail (TERMINAL_IS_SETTINGS_LIST (list), FALSE);
+ g_return_val_if_fail (terminal_settings_list_valid_uuid (uuid), FALSE);
+
+ return strv_find (list->uuids, uuid) != -1;
+}
+
+/**
+ * terminal_settings_list_ref_child:
+ * @list: a #TerminalSettingsList
+ * @uuid: the UUID of a list child
+ *
+ * Returns the child #GSettings for the list child with UUID @uuid, or %nullptr
+ * if @list has no such child.
+ *
+ * Returns: (transfer full): a reference to the #GSettings for the child, or %nullptr
+ */
+GSettings *
+terminal_settings_list_ref_child (TerminalSettingsList *list,
+ const char *uuid)
+{
+ g_return_val_if_fail (TERMINAL_IS_SETTINGS_LIST (list), nullptr);
+ g_return_val_if_fail (terminal_settings_list_valid_uuid (uuid), nullptr);
+
+ return terminal_settings_list_ref_child_internal (list, uuid);
+}
+
+/**
+ * terminal_settings_list_ref_children:
+ * @list: a #TerminalSettingsList
+ *
+ * Returns the list of children #GSettings or @list.
+ *
+ * Returns: (transfer full): a list of child #GSettings of @list
+ */
+GList *
+terminal_settings_list_ref_children (TerminalSettingsList *list)
+{
+ GList *l;
+ guint i;
+
+ g_return_val_if_fail (TERMINAL_IS_SETTINGS_LIST (list), nullptr);
+
+ if (list->uuids == nullptr)
+ return nullptr;
+
+ l = nullptr;
+ for (i = 0; list->uuids[i]; i++)
+ l = g_list_prepend (l, terminal_settings_list_ref_child (list, list->uuids[i]));
+
+ return g_list_reverse (l);
+}
+
+/**
+ * terminal_settings_list_ref_default_child:
+ * @list: a #TerminalSettingsList
+ *
+ * Returns the default child #GSettings for the list, or %nullptr if @list has no
+ * children.
+ *
+ * Returns: (transfer full): a reference to the #GSettings for the default child, or %nullptr
+ */
+GSettings *
+terminal_settings_list_ref_default_child (TerminalSettingsList *list)
+{
+ gs_free char *uuid = nullptr;
+
+ g_return_val_if_fail (TERMINAL_IS_SETTINGS_LIST (list), nullptr);
+
+ uuid = terminal_settings_list_dup_default_child (list);
+ if (uuid == nullptr)
+ return nullptr;
+
+ return terminal_settings_list_ref_child_internal (list, uuid);
+}
+
+#if defined(TERMINAL_SERVER) || defined(TERMINAL_PREFERENCES)
+
+/**
+ * terminal_settings_list_add_child:
+ * @list: a #TerminalSettingsList
+ * @name: the name of the new profile
+ *
+ * Adds a new child to the list, and returns a reference to its #GSettings.
+ *
+ * Returns: (transfer full): the UUID of new child
+ */
+char *
+terminal_settings_list_add_child (TerminalSettingsList *list,
+ const char *name)
+{
+ g_return_val_if_fail (TERMINAL_IS_SETTINGS_LIST (list), nullptr);
+
+ return terminal_settings_list_add_child_internal (list, nullptr, name);
+}
+
+/**
+ * terminal_settings_list_clone_child:
+ * @list: a #TerminalSettingsList
+ * @uuid: the UUID of the child to clone
+ * @name: the name of the new child
+ *
+ * Adds a new child to the list, and returns a reference to its #GSettings.
+ * All keys of the new child will have the same value as @uuid's.
+ *
+ * Returns: (transfer full): the UUID of new child
+ */
+char *
+terminal_settings_list_clone_child (TerminalSettingsList *list,
+ const char *uuid,
+ const char *name)
+{
+ g_return_val_if_fail (TERMINAL_IS_SETTINGS_LIST (list), nullptr);
+ g_return_val_if_fail (terminal_settings_list_valid_uuid (uuid), nullptr);
+
+ return terminal_settings_list_add_child_internal (list, uuid, name);
+}
+
+/**
+ * terminal_settings_list_remove_child:
+ * @list: a #TerminalSettingsList
+ * @uuid: the UUID of a list child
+ *
+ * Removes the child with UUID @uuid from the list.
+ */
+void
+terminal_settings_list_remove_child (TerminalSettingsList *list,
+ const char *uuid)
+{
+ g_return_if_fail (TERMINAL_IS_SETTINGS_LIST (list));
+ g_return_if_fail (terminal_settings_list_valid_uuid (uuid));
+
+ terminal_settings_list_remove_child_internal (list, uuid);
+}
+
+#endif /* TERMINAL_SERVER || TERMINAL_PREFERENCES */
+
+/**
+ * terminal_settings_list_dup_uuid_from_child:
+ * @list: a #TerminalSettingsList
+ * @child: a #GSettings of a child in the list
+ *
+ * Returns the UUID of @child in the list, or %nullptr if @child is not in the list.
+ *
+ * Returns: (transfer full): the UUID of the child in the settings list, or %nullptr
+ */
+char *
+terminal_settings_list_dup_uuid_from_child (TerminalSettingsList *list,
+ GSettings *child)
+{
+ gs_free char *path;
+ char *p;
+
+ g_return_val_if_fail (TERMINAL_IS_SETTINGS_LIST (list), nullptr);
+
+ g_object_get (child, "path", &path, nullptr);
+ g_return_val_if_fail (g_str_has_prefix (path, list->path), nullptr);
+
+ p = path + strlen (list->path);
+ g_return_val_if_fail (p[0] == ':', nullptr);
+ p++;
+ g_return_val_if_fail (strlen (p) == 37, nullptr);
+ g_return_val_if_fail (p[36] == '/', nullptr);
+ p[36] = '\0';
+ g_assert (terminal_settings_list_valid_uuid (p));
+
+ return g_strdup (p);
+}
+
+/**
+ * terminal_settings_list_get_set_default_child:
+ * @list: a #TerminalSettingsList
+ * @uuid: the UUID of a child in the list
+ *
+ * Sets @uuid as the default child.
+ */
+void
+terminal_settings_list_set_default_child (TerminalSettingsList *list,
+ const char *uuid)
+{
+ g_return_if_fail (TERMINAL_IS_SETTINGS_LIST (list));
+ g_return_if_fail (terminal_settings_list_valid_uuid (uuid));
+
+ if (!terminal_settings_list_has_child (list, uuid))
+ return;
+
+ g_settings_set_string (&list->parent, TERMINAL_SETTINGS_LIST_DEFAULT_KEY, uuid);
+}
+
+/**
+ * terminal_settings_list_foreach_child:
+ * @list: a #TerminalSettingsList
+ * @callback: a #TerminalSettingsListForeachFunc
+ * @user_data: user data for @callback
+ *
+ * Calls @callback for each child of @list.
+ *
+ * NOTE: No changes to @list must be made from @callback.
+ */
+void
+terminal_settings_list_foreach_child (TerminalSettingsList *list,
+ TerminalSettingsListForeachFunc callback,
+ gpointer user_data)
+{
+ g_return_if_fail (TERMINAL_IS_SETTINGS_LIST (list));
+ g_return_if_fail (callback);
+
+ for (char **p = list->uuids; *p; p++) {
+ const char *uuid = *p;
+ gs_unref_object GSettings *child = terminal_settings_list_ref_child_internal (list, uuid);
+ if (child != nullptr)
+ callback (list, uuid, child, user_data);
+ }
+}
+
+/**
+ * terminal_settings_list_foreach_child:
+ * @list: a #TerminalSettingsList
+ *
+ * Returns: the number of children of @list.
+ */
+guint
+terminal_settings_list_get_n_children (TerminalSettingsList *list)
+{
+ g_return_val_if_fail (TERMINAL_IS_SETTINGS_LIST (list), 0);
+
+ return g_hash_table_size (list->children);
+}