[gnome-terminal] all: New settings list implementation



commit 9266b411f01ae1e90045cafd48fcad905c12c644
Author: Christian Persch <chpe gnome org>
Date:   Wed Feb 6 20:41:51 2013 +0100

    all: New settings list implementation
    
    Split the settings list implementation out into its own class.

 configure.ac                          |    1 +
 src/Makefile.am                       |   30 +-
 src/client.c                          |   24 +-
 src/migration.c                       |  102 ++--
 src/org.gnome.Terminal.gschema.xml.in |   28 +-
 src/profile-editor.c                  |    8 +-
 src/terminal-app.c                    |  361 ++------------
 src/terminal-app.h                    |   10 +-
 src/terminal-debug.c                  |   15 +-
 src/terminal-debug.h                  |   15 +-
 src/terminal-enums.h                  |    6 +
 src/terminal-gdbus.c                  |    6 +-
 src/terminal-options.c                |   24 +-
 src/terminal-options.h                |    4 +
 src/terminal-prefs.c                  |   84 ++--
 src/terminal-profile-utils.c          |  276 -----------
 src/terminal-profile-utils.h          |   43 --
 src/terminal-profiles-list.c          |  258 ++++++++++
 src/terminal-profiles-list.h          |   53 ++
 src/terminal-schemas.h                |    9 +-
 src/terminal-settings-list.c          |  881 +++++++++++++++++++++++++++++++++
 src/terminal-settings-list.h          |   74 +++
 src/terminal-window.c                 |   24 +-
 23 files changed, 1541 insertions(+), 795 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index cb462c4..ddb482d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -127,6 +127,7 @@ if test "$enable_migration" = "yes"; then
    gio-2.0 >= $GIO_REQUIRED
    vte$VTE_PC_VERSION >= $VTE_REQUIRED
    gconf-2.0 >= $GCONF_REQUIRED
+   dconf >= $DCONF_REQUIRED
    uuid])
   AC_DEFINE([ENABLE_MIGRATION],[1],[Define to 1 to enable prefs migration from GConf to GSettings])
 fi
diff --git a/src/Makefile.am b/src/Makefile.am
index f736f81..da5179c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -52,9 +52,11 @@ gnome_terminal_server_SOURCES = \
 	terminal-notebook.h \
 	terminal-prefs.c \
 	terminal-prefs.h \
-	terminal-profile-utils.c \
-	terminal-profile-utils.h \
+	terminal-profiles-list.c \
+	terminal-profiles-list.h \
 	terminal-schemas.h \
+	terminal-settings-list.c \
+	terminal-settings-list.h \
 	terminal-screen.c \
 	terminal-screen.h \
 	terminal-screen-container.c \
@@ -149,16 +151,22 @@ gnome_terminal_client_SOURCES = \
 	client.c \
 	terminal-client-utils.c \
 	terminal-client-utils.h \
+	terminal-debug.c \
+	terminal-debug.h \
 	terminal-defines.h \
 	terminal-intl.h \
-	terminal-profile-utils.c \
-	terminal-profile-utils.h \
+	terminal-profiles-list.c \
+	terminal-profiles-list.h \
 	terminal-schemas.h \
+	terminal-settings-list.c \
+	terminal-settings-list.h \
 	$(NULL)
 
 nodist_gnome_terminal_client_SOURCES = \
 	terminal-gdbus-generated.c \
 	terminal-gdbus-generated.h \
+	terminal-type-builtins.c \
+	terminal-type-builtins.h \
 	$(NULL)
 
 gnome_terminal_client_CPPFLAGS = \
@@ -193,14 +201,18 @@ gnome_terminal_SOURCES = \
 	terminal-intl.h \
 	terminal-options.c \
 	terminal-options.h \
-	terminal-profile-utils.c \
-	terminal-profile-utils.h \
+	terminal-profiles-list.c \
+	terminal-profiles-list.h \
 	terminal-schemas.h \
+	terminal-settings-list.c \
+	terminal-settings-list.h \
 	$(NULL)
 
 nodist_gnome_terminal_SOURCES = \
 	terminal-gdbus-generated.c \
 	terminal-gdbus-generated.h \
+	terminal-type-builtins.c \
+	terminal-type-builtins.h \
 	$(NULL)
 
 gnome_terminal_CPPFLAGS = \
@@ -256,7 +268,13 @@ libexec_PROGRAMS += gnome-terminal-migration
 
 gnome_terminal_migration_SOURCES = \
 	migration.c \
+	terminal-debug.c \
+	terminal-debug.h \
+	terminal-profiles-list.c \
+	terminal-profiles-list.h \
 	terminal-schemas.h \
+	terminal-settings-list.c \
+	terminal-settings-list.h \
 	$(NULL)
 
 nodist_gnome_terminal_migration_SOURCES = \
diff --git a/src/client.c b/src/client.c
index 3bd07a1..92be7fd 100644
--- a/src/client.c
+++ b/src/client.c
@@ -44,9 +44,11 @@
 #include "terminal-gdbus-generated.h"
 #include "terminal-defines.h"
 #include "terminal-client-utils.h"
-#include "terminal-profile-utils.h"
+#include "terminal-profiles-list.h"
+#include "terminal-debug.h"
 
 static gboolean quiet = FALSE;
+static TerminalSettingsList *profiles_list = NULL;
 
 static void _printerr (const char *format, ...) G_GNUC_PRINTF (1, 2);
 
@@ -127,6 +129,15 @@ modify_argv0_for_command (gint *argc, gchar **argv[], const gchar *command)
   g_free (program_name);
 }
 
+static TerminalSettingsList *
+ensure_profiles_list (void)
+{
+  if (profiles_list == NULL)
+    profiles_list = terminal_profiles_list_new ();
+
+  return profiles_list;
+}
+
 typedef struct
 {
   char       *server_app_id;
@@ -309,7 +320,7 @@ option_profile_cb (const gchar *option_name,
     return FALSE;
   }
 
-  data->profile = terminal_profile_util_get_profile_by_uuid (value, error);
+  data->profile = terminal_profiles_list_dup_uuid (ensure_profiles_list (), value, error);
   return data->profile != NULL;
 }
 
@@ -748,9 +759,9 @@ complete (int *argcp,
     {
       char **profiles, **p;
 
-      profiles = terminal_profile_util_list_profiles ();
+      profiles = terminal_settings_list_dupv_children (ensure_profiles_list ());
       if (profiles == NULL)
-        return FALSE;
+        return TRUE;
 
       for (p = profiles; *p; p++)
         g_print ("%s\n", *p);
@@ -790,6 +801,8 @@ main (gint argc, gchar *argv[])
   g_type_init ();
 #endif
 
+  _terminal_debug_init ();
+
   ret = EXIT_FAILURE;
 
   if (argc < 2)
@@ -827,5 +840,8 @@ main (gint argc, gchar *argv[])
     }
 
  out:
+
+  g_clear_object (&profiles_list);
+
   return ret;
 }
diff --git a/src/migration.c b/src/migration.c
index ee6b53f..1e3ae19 100644
--- a/src/migration.c
+++ b/src/migration.c
@@ -24,17 +24,21 @@
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <gconf/gconf-client.h>
+#include <dconf.h>
 #include <vte/vte.h>
-#include <uuid.h>
 
 #include "terminal-schemas.h"
+#include "terminal-profiles-list.h"
 #include "terminal-type-builtins.h"
+#include "terminal-debug.h"
 
+static gboolean clean = FALSE;
 static gboolean dry_run = FALSE;
 static gboolean force = FALSE;
 static gboolean verbose = FALSE;
 
 static const GOptionEntry options[] = {
+  { "clean", 0, 0, G_OPTION_ARG_NONE, &clean, NULL, NULL },
   { "dry-run", 0, 0, G_OPTION_ARG_NONE, &dry_run, NULL, NULL },
   { "force", 0, 0, G_OPTION_ARG_NONE, &force, NULL, NULL },
   { "verbose", 0, 0, G_OPTION_ARG_NONE, &verbose, NULL, NULL },
@@ -43,6 +47,8 @@ static const GOptionEntry options[] = {
 
 #define ERROR_DOMAIN (g_intern_static_string ("gnome-terminal-migration-error"))
 
+#define TERMINAL_PATH_PREFIX "/org/gnome/terminal/"
+
 enum {
   ERROR_GENERIC
 };
@@ -306,28 +312,42 @@ migrate_global_prefs (GSettings *settings,
   return TRUE;
 }
 
-static char *
-migrate_profile (GConfClient *client,
-                 GSettings *global_settings,
+static void
+do_clean (void)
+{
+  DConfClient *client;
+
+  if (verbose)
+    g_printerr ("Cleaning...\n");
+
+#ifdef HAVE_DCONF_1_2
+  client = dconf_client_new (NULL, NULL, NULL, NULL);
+  dconf_client_write (client, TERMINAL_PATH_PREFIX, NULL, NULL, NULL, NULL);
+#else /* modern DConf */
+  client = dconf_client_new ();
+  dconf_client_write_sync (client, TERMINAL_PATH_PREFIX, NULL, NULL, NULL, NULL);
+#endif
+  g_object_unref (client);
+}
+
+static void
+migrate_profile (TerminalSettingsList *list,
+                 GConfClient *client,
                  const char *gconf_id,
-                 gboolean is_default)
+                 const char *default_gconf_id)
 {
   GSettings *settings;
-  char *path;
+  char *child_name, *path;
   const char *name;
-  uuid_t u;
-  char str[37];
-
-  uuid_generate (u);
-  uuid_unparse (u, str);
-
-  path = g_strdup_printf (TERMINAL_PROFILES_PATH_PREFIX ":%s/", str);
-  if (verbose)
-    g_printerr ("Migrating profile \"%s\" to \"%s\" is-default %s\n",
-                gconf_id, path, is_default ? "true" : "false");
 
-  settings = g_settings_new_with_path (TERMINAL_PROFILE_SCHEMA, path);
-  g_free (path);
+  if (g_strcmp0 (gconf_id, default_gconf_id) == 0) {
+    /* Re-use the default list child */
+    settings = terminal_settings_list_ref_default_child (list);
+  } else {
+    child_name = terminal_settings_list_add_child (list);
+    settings = terminal_settings_list_ref_child (list, child_name);
+    g_free (child_name);
+  }
 
   path = gconf_concat_dir_and_key (GCONF_PROFILES_PREFIX, gconf_id);
 
@@ -422,26 +442,20 @@ migrate_profile (GConfClient *client,
 
   g_free (path);
   g_object_unref (settings);
-
-  return g_strdup (str);
 }
 
 static gboolean
 migrate_profiles (GSettings *global_settings,
                   GError **error)
 {
+  TerminalSettingsList *list;
   GConfClient *client;
   GConfValue *value, *dvalue;
   GSList *l;
-  GPtrArray *profile_uuids;
-  char *uuid;
-  const char *profile, *default_profile, *default_uuid;
-  gboolean is_default;
+  const char *default_profile;
 
   client = gconf_client_get_default ();
 
-  profile_uuids = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
-
   dvalue = gconf_client_get (client, GCONF_GLOBAL_PREFIX "/default_profile", NULL);
   if (dvalue != NULL &&
       dvalue->type == GCONF_VALUE_STRING)
@@ -449,19 +463,16 @@ migrate_profiles (GSettings *global_settings,
   else
     default_profile = NULL;
 
-  default_uuid = NULL;
+  list = terminal_profiles_list_new ();
+
   value = gconf_client_get (client, GCONF_GLOBAL_PREFIX "/profile_list", NULL);
   if (value != NULL &&
       value->type == GCONF_VALUE_LIST &&
       gconf_value_get_list_type (value) == GCONF_VALUE_STRING) {
     for (l = gconf_value_get_list (value); l != NULL; l = l->next) {
-      profile = gconf_value_get_string (l->data);
-
-      is_default = g_strcmp0 (profile, default_profile) == 0;
-      uuid = migrate_profile (client, global_settings, profile, is_default);
-      g_ptr_array_add (profile_uuids, uuid);
-      if (is_default)
-        default_uuid = uuid;
+      migrate_profile (list, client,
+                       gconf_value_get_string (l->data),
+                       default_profile);
     }
   }
 
@@ -480,27 +491,13 @@ migrate_profiles (GSettings *global_settings,
     g_free (path);
   }
 
-  /* Only write profile list if there were any profiles migrated */
-  if (profile_uuids->len) {
-    g_ptr_array_add (profile_uuids, NULL);
-    g_settings_set_strv (global_settings, TERMINAL_SETTING_PROFILES_KEY,
-                         (const char * const *) profile_uuids->pdata);
-
-    /* The GConf setting might be corrupt, with the default profile pointing to a profile
-     * that doesn't actually exist. In this case, just pick the first profile as default.
-     */
-    if (default_uuid == NULL)
-      default_uuid = (const char *) profile_uuids->pdata[0];
-
-    g_settings_set_string (global_settings, TERMINAL_SETTING_DEFAULT_PROFILE_KEY, default_uuid);
-  }
-
   if (value)
     gconf_value_free (value);
   if (dvalue)
     gconf_value_free (dvalue);
   g_object_unref (client);
-  g_ptr_array_free (profile_uuids, TRUE);
+
+  g_object_unref (list);
 
   return TRUE;
 }
@@ -608,6 +605,8 @@ main (int argc,
   g_type_init ();
 #endif
 
+  _terminal_debug_init ();
+
   context = g_option_context_new ("");
   g_option_context_add_main_entries (context, options, NULL);
 
@@ -628,6 +627,9 @@ main (int argc,
       goto out;
   }
 
+  if (clean)
+    do_clean ();
+
   if (!migrate (global_settings, &error)) {
     g_printerr ("Error: %s\n", error->message);
     g_error_free (error);
diff --git a/src/org.gnome.Terminal.gschema.xml.in b/src/org.gnome.Terminal.gschema.xml.in
index 4181396..819a03d 100644
--- a/src/org.gnome.Terminal.gschema.xml.in
+++ b/src/org.gnome.Terminal.gschema.xml.in
@@ -65,6 +65,26 @@
     <value nick='underline' value='2'/>
   </enum>
 
+  <!-- SettingsList base schema -->
+
+  <schema id="org.gnome.Terminal.SettingsList">
+    <key name="list" type="as">
+      <default>[]</default>
+    </key>
+    <key name="default" type="s">
+      <default>''</default>
+    </key>
+  </schema>
+
+  <!-- Profiles list schema -->
+
+  <schema id="org.gnome.Terminal.ProfilesList" 
+          extends="org.gnome.Terminal.SettingsList"
+          path="/org/gnome/terminal/legacy/profiles:/">
+    <override name="list">['b1dcc9dd-5262-4d8d-a863-c897e6d979b9']</override>
+    <override name="default">'b1dcc9dd-5262-4d8d-a863-c897e6d979b9'</override>
+  </schema>
+
   <!-- A terminal profile -->
 
   <schema id="org.gnome.Terminal.Legacy.Profile">
@@ -505,13 +525,7 @@
       <_summary>Whether to show the menubar in new windows</_summary>
     </key>
 
-    <key name="profiles" type="as">
-      <default>['b1dcc9dd-5262-4d8d-a863-c897e6d979b9']</default>
-    </key>
-
-    <key name="default-profile" type="s">
-      <default>'b1dcc9dd-5262-4d8d-a863-c897e6d979b9'</default>
-    </key>
+   <!-- <child name="profiles" schema="org.gnome.Terminal.ProfilesList" /> -->
 
     <key name="schema-version" type="u">
       <default>0</default>
diff --git a/src/profile-editor.c b/src/profile-editor.c
index 3302cad..8537576 100644
--- a/src/profile-editor.c
+++ b/src/profile-editor.c
@@ -25,13 +25,14 @@
 #include <glib.h>
 #include <gio/gio.h>
 
+#include "terminal-app.h"
 #include "terminal-enums.h"
 #include "terminal-intl.h"
 #include "profile-editor.h"
 #include "terminal-schemas.h"
 #include "terminal-type-builtins.h"
 #include "terminal-util.h"
-#include "terminal-profile-utils.h"
+#include "terminal-profiles-list.h"
 
 typedef struct _TerminalColorScheme TerminalColorScheme;
 
@@ -674,6 +675,7 @@ terminal_profile_edit (GSettings  *profile,
                        GtkWindow  *transient_parent,
                        const char *widget_name)
 {
+  TerminalSettingsList *profiles_list;
   GtkBuilder *builder;
   GError *error = NULL;
   GtkWidget *editor, *w;
@@ -691,6 +693,8 @@ terminal_profile_edit (GSettings  *profile,
       return;
     }
 
+  profiles_list = terminal_app_get_profiles_list (terminal_app_get ());
+
   builder = gtk_builder_new ();
   gtk_builder_add_from_resource (builder, "/org/gnome/terminal/ui/profile-preferences.ui", &error);
   g_assert_no_error (error);
@@ -716,7 +720,7 @@ terminal_profile_edit (GSettings  *profile,
   gtk_widget_add_events (w, GDK_BUTTON_PRESS_MASK | GDK_SCROLL_MASK);
   g_signal_connect (w, "scroll-event", G_CALLBACK (scroll_event_cb), NULL);
 
-  uuid = terminal_profile_util_get_profile_uuid (profile);
+  uuid = terminal_settings_list_dup_uuid_from_child (profiles_list, profile);
   gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (builder, "profile-uuid")),
                       uuid);
   g_free (uuid);
diff --git a/src/terminal-app.c b/src/terminal-app.c
index 6d51603..bfe5ca9 100644
--- a/src/terminal-app.c
+++ b/src/terminal-app.c
@@ -32,7 +32,7 @@
 #include "terminal-screen.h"
 #include "terminal-screen-container.h"
 #include "terminal-window.h"
-#include "terminal-profile-utils.h"
+#include "terminal-profiles-list.h"
 #include "terminal-util.h"
 #include "profile-editor.h"
 #include "terminal-encoding.h"
@@ -46,9 +46,6 @@
 #include <stdlib.h>
 #include <time.h>
 
-#include <uuid.h>
-#include <dconf.h>
-
 #define DESKTOP_INTERFACE_SETTINGS_SCHEMA       "org.gnome.desktop.interface"
 
 #define SYSTEM_PROXY_SETTINGS_SCHEMA            "org.gnome.system.proxy"
@@ -65,7 +62,6 @@
 struct _TerminalAppClass {
   GtkApplicationClass parent_class;
 
-  void (* profile_list_changed) (TerminalApp *app);
   void (* encoding_list_changed) (TerminalApp *app);
 };
 
@@ -75,22 +71,18 @@ struct _TerminalApp
 
   GDBusObjectManagerServer *object_manager;
 
-  GtkWidget *new_profile_dialog;
-
-  GHashTable *profiles_hash;
+  TerminalSettingsList *profiles_list;
 
   GHashTable *encodings;
   gboolean encodings_locked;
 
   GSettings *global_settings;
-  GSettings *profiles_settings;
   GSettings *desktop_interface_settings;
   GSettings *system_proxy_settings;
 };
 
 enum
 {
-  PROFILE_LIST_CHANGED,
   ENCODING_LIST_CHANGED,
   LAST_SIGNAL
 };
@@ -147,272 +139,50 @@ maybe_migrate_settings (TerminalApp *app)
 #endif /* ENABLE_MIGRATION */
 }
 
-static char **
-strv_insert (char **strv,
-             char *str)
-{
-  guint i;
-
-  for (i = 0; strv[i]; i++)
-    if (strcmp (strv[i], str) == 0)
-      return strv;
-
-  /* Not found; append */
-  strv = g_realloc_n (strv, i + 2, sizeof (char *));
-  strv[i++] = str;
-  strv[i] = NULL;
-
-  return strv;
-}
-
-static char **
-strv_remove (char **strv,
-             char *str)
-{
-  char **a, **b;
-
-  a = b = strv;
-  while (*a) {
-    if (strcmp (*a, str) != 0)
-      *b++ = *a;
-    a++;
-  }
-  *b = NULL;
-
-  return strv;
-}
-
-static GSettings * /* ref */
-profile_clone (TerminalApp *app,
-               GSettings *base_profile)
+void
+terminal_app_new_profile (TerminalApp *app,
+                          GSettings   *base_profile,
+                          GtkWindow   *transient_parent)
 {
   GSettings *profile;
-  uuid_t u;
-  char str[37];
-  char *new_path;
-  char **profiles;
-
-  uuid_generate (u);
-  uuid_unparse (u, str);
-  new_path = g_strdup_printf (TERMINAL_PROFILES_PATH_PREFIX ":%s/", str);
-
-  if (base_profile)
-    {
-      static const char * const keys[] = {
-        TERMINAL_PROFILE_ALLOW_BOLD_KEY,
-        TERMINAL_PROFILE_AUDIBLE_BELL_KEY,
-        TERMINAL_PROFILE_BACKGROUND_COLOR_KEY,
-        TERMINAL_PROFILE_BACKSPACE_BINDING_KEY,
-        TERMINAL_PROFILE_BOLD_COLOR_KEY,
-        TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG_KEY,
-        TERMINAL_PROFILE_CURSOR_BLINK_MODE_KEY,
-        TERMINAL_PROFILE_CURSOR_SHAPE_KEY,
-        TERMINAL_PROFILE_CUSTOM_COMMAND_KEY,
-        TERMINAL_PROFILE_DEFAULT_SIZE_COLUMNS_KEY,
-        TERMINAL_PROFILE_DEFAULT_SIZE_ROWS_KEY,
-        TERMINAL_PROFILE_DELETE_BINDING_KEY,
-        TERMINAL_PROFILE_ENCODING,
-        TERMINAL_PROFILE_EXIT_ACTION_KEY,
-        TERMINAL_PROFILE_FONT_KEY,
-        TERMINAL_PROFILE_FOREGROUND_COLOR_KEY,
-        TERMINAL_PROFILE_LOGIN_SHELL_KEY,
-        TERMINAL_PROFILE_NAME_KEY,
-        TERMINAL_PROFILE_PALETTE_KEY,
-        TERMINAL_PROFILE_SCROLLBACK_LINES_KEY,
-        TERMINAL_PROFILE_SCROLLBACK_UNLIMITED_KEY,
-        TERMINAL_PROFILE_SCROLLBAR_POLICY_KEY,
-        TERMINAL_PROFILE_SCROLL_ON_KEYSTROKE_KEY,
-        TERMINAL_PROFILE_SCROLL_ON_OUTPUT_KEY,
-        TERMINAL_PROFILE_TITLE_MODE_KEY,
-        TERMINAL_PROFILE_TITLE_KEY,
-        TERMINAL_PROFILE_UPDATE_RECORDS_KEY,
-        TERMINAL_PROFILE_USE_CUSTOM_COMMAND_KEY,
-        TERMINAL_PROFILE_USE_CUSTOM_DEFAULT_SIZE_KEY,
-        TERMINAL_PROFILE_USE_SKEY_KEY,
-        TERMINAL_PROFILE_USE_SYSTEM_FONT_KEY,
-        TERMINAL_PROFILE_USE_THEME_COLORS_KEY,
-        /* TERMINAL_PROFILE_VISIBLE_NAME_KEY, */
-        TERMINAL_PROFILE_WORD_CHARS_KEY,
-      };
-      DConfClient *client;
-#ifndef HAVE_DCONF_1_2
-      DConfChangeset *changeset;
-#endif
-      char *base_path;
-      guint i;
-
-      g_object_get (base_profile, "path", &base_path, NULL);
-
-#ifdef HAVE_DCONF_1_2
-      client = dconf_client_new (NULL, NULL, NULL, NULL);
-#else
-      client = dconf_client_new ();
-      changeset = dconf_changeset_new ();
-#endif
+  char *base_uuid, *uuid;
 
-      for (i = 0; i < G_N_ELEMENTS (keys); i++)
-        {
-          GVariant *value;
-          char *p;
-
-          p = g_strconcat (base_path, keys[i], NULL);
-#ifdef HAVE_DCONF_1_2
-          value = dconf_client_read_no_default (client, p);
-#else
-          value = dconf_client_read (client, p);
-#endif
-          g_free (p);
-
-          if (value)
-            {
-              p = g_strconcat (new_path, keys[i], NULL);
-#ifdef HAVE_DCONF_1_2
-              dconf_client_write (client, p, value, NULL, NULL, NULL);
-#else
-              dconf_changeset_set (changeset, p, value);
-#endif
-              g_free (p);
-              g_variant_unref (value);
-            }
-        }
-
-#ifndef HAVE_DCONF_1_2
-      dconf_client_change_sync (client, changeset, NULL, NULL, NULL);
-      g_object_unref (changeset);
-#endif
-      g_object_unref (client);
-      g_free (base_path);
-    }
-
-  profile = g_settings_new_with_path (TERMINAL_PROFILE_SCHEMA, new_path);
-  g_free (new_path);
-
-  if (base_profile)
-    {
-      const char *base_name;
-      char *new_name;
-
-      g_settings_get (base_profile, TERMINAL_PROFILE_VISIBLE_NAME_KEY, "&s", &base_name);
-      new_name = g_strdup_printf ("%s (Cloned)", base_name);
-      g_settings_set_string (profile, TERMINAL_PROFILE_VISIBLE_NAME_KEY, new_name);
-      g_free (new_name);
-    }
+  if (base_profile) {
+    base_uuid = terminal_settings_list_dup_uuid_from_child (app->profiles_list, base_profile);
+    uuid = terminal_settings_list_clone_child (app->profiles_list, base_uuid);
+    g_free (base_uuid);
+  } else {
+    uuid = terminal_settings_list_add_child (app->profiles_list);
+  }
 
-  /* Store the new UUID in the list of profiles, and add the profile to the hash table.
-   * We'll get a changed signal for the profile list key, but that will result in a no-op.
-   */
-  g_hash_table_insert (app->profiles_hash, g_strdup (str) /* adopted */, profile /* adopted */);
-  g_signal_emit (app, signals[PROFILE_LIST_CHANGED], 0);
+  if (uuid == NULL)
+    return;
 
-  g_settings_get (app->global_settings, TERMINAL_SETTING_PROFILES_KEY, "^a&s", &profiles);
-  profiles = strv_insert (profiles, str);
-  g_settings_set_strv (app->global_settings, TERMINAL_SETTING_PROFILES_KEY, (const char * const *) profiles);
-  g_free (profiles);
+  profile = terminal_settings_list_ref_child (app->profiles_list, uuid);
+  g_free (uuid);
+  if (profile == NULL)
+    return;
 
-  return g_object_ref (profile);
+  terminal_profile_edit (profile, transient_parent, "profile-name-entry");
+  g_object_unref (profile);
 }
 
 void
 terminal_app_remove_profile (TerminalApp *app,
                              GSettings *profile)
 {
-  char *uuid, *path;
-  char **profiles;
-  DConfClient *client;
-
-  uuid = terminal_profile_util_get_profile_uuid (profile);
-  g_object_get (profile, "path", &path, NULL);
-
-  g_settings_get (app->global_settings, TERMINAL_SETTING_PROFILES_KEY, "^a&s", &profiles);
-  profiles = strv_remove (profiles, uuid);
-  g_settings_set_strv (app->global_settings, TERMINAL_SETTING_PROFILES_KEY, (const char * const *) profiles);
-  g_free (profiles);
-
-  /* unset all keys under the profile's path */
-#ifdef HAVE_DCONF_1_2
-  client = dconf_client_new (NULL, NULL, NULL, NULL);
-  dconf_client_write (client, path, NULL, NULL, NULL, NULL);
-#else /* modern DConf */
-  client = dconf_client_new ();
-  dconf_client_write_sync (client, path, NULL, NULL, NULL, NULL);
-#endif
-  g_object_unref (client);
+  char *uuid;
 
+  uuid = terminal_settings_list_dup_uuid_from_child (app->profiles_list, profile);
+  terminal_settings_list_remove_child (app->profiles_list, uuid);
   g_free (uuid);
-  g_free (path);
 }
 
 gboolean
 terminal_app_can_remove_profile (TerminalApp *app,
                                  GSettings *profile)
 {
-  return g_hash_table_size (app->profiles_hash) > 1;
-}
-
-void
-terminal_app_get_profiles_iter (TerminalApp *app,
-                                GHashTableIter *iter)
-{
-  g_hash_table_iter_init (iter, app->profiles_hash);
-}
-
-static void
-terminal_app_profile_list_changed_cb (GSettings *settings,
-                                      const char *key,
-                                      TerminalApp *app)
-{
-  char **profiles, *default_profile;
-  guint i;
-  GHashTable *new_profiles;
-  gboolean changed = FALSE;
-
-  /* Use get_mapped so we can be sure never to get valid profile names, and
-   * never an empty profile list, since the schema defines one profile.
-   */
-  profiles = terminal_profile_util_get_profiles (app->global_settings);
-  g_settings_get (app->global_settings, TERMINAL_SETTING_DEFAULT_PROFILE_KEY,
-                  "&s", &default_profile);
-
-  new_profiles = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                        (GDestroyNotify) g_free,
-                                        (GDestroyNotify) g_object_unref);
-
-  for (i = 0; profiles[i] != NULL; i++) {
-    const char *name = profiles[i];
-    GSettings *profile;
-
-    if (app->profiles_hash)
-      profile = g_hash_table_lookup (app->profiles_hash, name);
-    else
-      profile = NULL;
-
-    if (profile) {
-      g_object_ref (profile);
-      g_hash_table_remove (app->profiles_hash, name);
-    } else {
-      char *path;
-      path = g_strdup_printf (TERMINAL_PROFILES_PATH_PREFIX ":%s/", name);
-      profile = g_settings_new_with_path (TERMINAL_PROFILE_SCHEMA, path);
-      g_free (path);
-      changed = TRUE;
-    }
-
-    g_hash_table_insert (new_profiles, g_strdup (name) /* adopted */, profile /* adopted */);
-  }
-  g_strfreev (profiles);
-
-  g_assert (g_hash_table_size (new_profiles) > 0);
-
-  if (app->profiles_hash == NULL ||
-      g_hash_table_size (app->profiles_hash) > 0)
-    changed = TRUE;
-
-  if (app->profiles_hash != NULL)
-    g_hash_table_unref (app->profiles_hash);
-  app->profiles_hash = new_profiles;
-
-  if (changed)
-    g_signal_emit (app, signals[PROFILE_LIST_CHANGED], 0);
+  return TRUE;
 }
 
 static int
@@ -472,18 +242,6 @@ terminal_app_encoding_list_notify_cb (GSettings   *settings,
   g_signal_emit (app, signals[ENCODING_LIST_CHANGED], 0);
 }
 
-void
-terminal_app_new_profile (TerminalApp *app,
-                          GSettings   *base_profile,
-                          GtkWindow   *transient_parent)
-{
-  GSettings *new_profile;
-
-  new_profile = profile_clone (app, base_profile);
-  terminal_profile_edit (new_profile, transient_parent, "profile-name-entry");
-  g_object_unref (new_profile);
-}
-
 /* App menu callbacks */
 
 static void
@@ -592,11 +350,7 @@ terminal_app_init (TerminalApp *app)
   maybe_migrate_settings (app);
 
   /* Get the profiles */
-  terminal_app_profile_list_changed_cb (app->global_settings, NULL, app);
-  g_signal_connect (app->global_settings,
-                    "changed::" TERMINAL_SETTING_PROFILES_KEY,
-                    G_CALLBACK (terminal_app_profile_list_changed_cb),
-                    app);
+  app->profiles_list = terminal_profiles_list_new ();
 
   /* Get the encodings */
   app->encodings = terminal_encodings_get_builtins ();
@@ -619,11 +373,6 @@ terminal_app_finalize (GObject *object)
                                         app);
   g_hash_table_destroy (app->encodings);
 
-  g_signal_handlers_disconnect_by_func (app->global_settings,
-                                        G_CALLBACK (terminal_app_profile_list_changed_cb),
-                                        app);
-  g_hash_table_unref (app->profiles_hash);
-
   g_object_unref (app->global_settings);
   g_object_unref (app->desktop_interface_settings);
   g_object_unref (app->system_proxy_settings);
@@ -694,20 +443,11 @@ terminal_app_class_init (TerminalAppClass *klass)
   g_application_class->dbus_register = terminal_app_dbus_register;
   g_application_class->dbus_unregister = terminal_app_dbus_unregister;
 
-  signals[PROFILE_LIST_CHANGED] =
-    g_signal_new (I_("profile-list-changed"),
-                  G_OBJECT_CLASS_TYPE (object_class),
-                  G_SIGNAL_RUN_LAST,
-                  G_STRUCT_OFFSET (TerminalAppClass, profile_list_changed),
-                  NULL, NULL,
-                  g_cclosure_marshal_VOID__VOID,
-                  G_TYPE_NONE, 0);
-
   signals[ENCODING_LIST_CHANGED] =
     g_signal_new (I_("encoding-list-changed"),
                   G_OBJECT_CLASS_TYPE (object_class),
                   G_SIGNAL_RUN_LAST,
-                  G_STRUCT_OFFSET (TerminalAppClass, profile_list_changed),
+                  G_STRUCT_OFFSET (TerminalAppClass, encoding_list_changed),
                   NULL, NULL,
                   g_cclosure_marshal_VOID__VOID,
                   G_TYPE_NONE, 0);
@@ -792,49 +532,14 @@ terminal_app_edit_encodings (TerminalApp     *app,
 }
 
 /**
- * terminal_profile_get_list:
+ * terminal_app_get_profiles_list:
  *
- * Returns: a #GList containing all profile #GSettings objects.
- *   The content of the list is owned by the backend and
- *   should not be modified or freed. Use g_list_free() when done
- *   using the list.
+ * Returns: (transfer none): returns the singleton profiles list #TerminalSettingsList
  */
-GList*
-terminal_app_get_profile_list (TerminalApp *app)
+TerminalSettingsList *
+terminal_app_get_profiles_list (TerminalApp *app)
 {
-  g_return_val_if_fail (TERMINAL_IS_APP (app), NULL);
-
-  return g_list_sort (g_hash_table_get_values (app->profiles_hash), terminal_profile_util_profiles_compare);
-}
-
-/**
- * terminal_app_get_profile_by_uuid:
- * @app:
- * @uuid:
- * @error:
- *
- * Returns: (transfer none): the #GSettings for the profile identified by @uuid
- */
-GSettings *
-terminal_app_ref_profile_by_uuid (TerminalApp *app,
-                                  const char  *uuid,
-                                  GError **error)
-{
-  GSettings *profile = NULL;
-
-  if (uuid == NULL)
-    g_settings_get (app->global_settings, TERMINAL_SETTING_DEFAULT_PROFILE_KEY, "&s", &uuid);
-
-  profile = g_hash_table_lookup (app->profiles_hash, uuid);
-
-  if (profile == NULL)
-    {
-      g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
-                   "Profile \"%s\" does not exist", uuid);
-      return NULL;
-    }
-
-  return g_object_ref (profile);
+  return app->profiles_list;
 }
 
 GHashTable *
diff --git a/src/terminal-app.h b/src/terminal-app.h
index 7911695..67d86c3 100644
--- a/src/terminal-app.h
+++ b/src/terminal-app.h
@@ -23,6 +23,7 @@
 
 #include "terminal-encoding.h"
 #include "terminal-screen.h"
+#include "terminal-profiles-list.h"
 
 G_BEGIN_DECLS
 
@@ -84,14 +85,7 @@ void terminal_app_edit_preferences (TerminalApp     *app,
 void terminal_app_edit_encodings   (TerminalApp     *app,
                                     GtkWindow       *transient_parent);
 
-GList* terminal_app_get_profile_list (TerminalApp *app);
-
-void terminal_app_get_profiles_iter (TerminalApp *app,
-                                     GHashTableIter *iter);
-
-GSettings* terminal_app_ref_profile_by_uuid (TerminalApp *app,
-                                             const char  *uuid,
-                                             GError **error);
+TerminalSettingsList *terminal_app_get_profiles_list (TerminalApp *app);
 
 TerminalEncoding *terminal_app_ensure_encoding (TerminalApp *app,
                                                 const char *charset);
diff --git a/src/terminal-debug.c b/src/terminal-debug.c
index e350a36..4657291 100644
--- a/src/terminal-debug.c
+++ b/src/terminal-debug.c
@@ -28,13 +28,14 @@ _terminal_debug_init(void)
 {
 #ifdef GNOME_ENABLE_DEBUG
   const GDebugKey keys[] = {
-    { "accels",    TERMINAL_DEBUG_ACCELS    },
-    { "encodings", TERMINAL_DEBUG_ENCODINGS },
-    { "server",    TERMINAL_DEBUG_SERVER   },
-    { "geometry",  TERMINAL_DEBUG_GEOMETRY  },
-    { "mdi",       TERMINAL_DEBUG_MDI       },
-    { "processes", TERMINAL_DEBUG_PROCESSES },
-    { "profile",   TERMINAL_DEBUG_PROFILE   }
+    { "accels",        TERMINAL_DEBUG_ACCELS        },
+    { "encodings",     TERMINAL_DEBUG_ENCODINGS     },
+    { "server",        TERMINAL_DEBUG_SERVER        },
+    { "geometry",      TERMINAL_DEBUG_GEOMETRY      },
+    { "mdi",           TERMINAL_DEBUG_MDI           },
+    { "processes",     TERMINAL_DEBUG_PROCESSES     },
+    { "profile",       TERMINAL_DEBUG_PROFILE       },
+    { "settings-list", TERMINAL_DEBUG_SETTINGS_LIST },
   };
 
   _terminal_debug_flags = g_parse_debug_string (g_getenv ("GNOME_TERMINAL_DEBUG"),
diff --git a/src/terminal-debug.h b/src/terminal-debug.h
index 6c26eca..b5bb792 100644
--- a/src/terminal-debug.h
+++ b/src/terminal-debug.h
@@ -25,13 +25,14 @@
 G_BEGIN_DECLS
 
 typedef enum {
-  TERMINAL_DEBUG_ACCELS     = 1 << 0,
-  TERMINAL_DEBUG_ENCODINGS  = 1 << 1,
-  TERMINAL_DEBUG_SERVER    = 1 << 2,
-  TERMINAL_DEBUG_GEOMETRY   = 1 << 3,
-  TERMINAL_DEBUG_MDI        = 1 << 4,
-  TERMINAL_DEBUG_PROCESSES  = 1 << 5,
-  TERMINAL_DEBUG_PROFILE    = 1 << 6
+  TERMINAL_DEBUG_ACCELS        = 1 << 0,
+  TERMINAL_DEBUG_ENCODINGS     = 1 << 1,
+  TERMINAL_DEBUG_SERVER        = 1 << 2,
+  TERMINAL_DEBUG_GEOMETRY      = 1 << 3,
+  TERMINAL_DEBUG_MDI           = 1 << 4,
+  TERMINAL_DEBUG_PROCESSES     = 1 << 5,
+  TERMINAL_DEBUG_PROFILE       = 1 << 6,
+  TERMINAL_DEBUG_SETTINGS_LIST = 1 << 7
 } TerminalDebugFlags;
 
 void _terminal_debug_init(void);
diff --git a/src/terminal-enums.h b/src/terminal-enums.h
index 4ea6e7a..d0a7635 100644
--- a/src/terminal-enums.h
+++ b/src/terminal-enums.h
@@ -39,6 +39,12 @@ typedef enum
   TERMINAL_EXIT_HOLD
 } TerminalExitAction;
 
+typedef enum {
+  TERMINAL_SETTINGS_LIST_FLAG_NONE = 0,
+  TERMINAL_SETTINGS_LIST_FLAG_HAS_DEFAULT = 1 << 0,
+  TERMINAL_SETTINGS_LIST_FLAG_ALLOW_EMPTY = 1 << 1
+} TerminalSettingsListFlags;
+
 G_END_DECLS
 
 #endif /* TERMINAL_ENUMS_H */
diff --git a/src/terminal-gdbus.c b/src/terminal-gdbus.c
index 9d3eab7..6f702cc 100644
--- a/src/terminal-gdbus.c
+++ b/src/terminal-gdbus.c
@@ -352,6 +352,7 @@ terminal_factory_impl_create_instance (TerminalFactory *factory,
                                        GVariant *options)
 {
   TerminalApp *app = terminal_app_get ();
+  TerminalSettingsList *profiles_list;
   GDBusObjectManagerServer *object_manager;
   TerminalWindow *window;
   TerminalScreen *screen;
@@ -372,8 +373,9 @@ terminal_factory_impl_create_instance (TerminalFactory *factory,
   if (!g_variant_lookup (options, "profile", "&s", &profile_uuid))
     profile_uuid = NULL;
 
-  profile = terminal_app_ref_profile_by_uuid (app, profile_uuid, &err);
-  if (profile == NULL) 
+  profiles_list = terminal_app_get_profiles_list (app);
+  profile = terminal_profiles_list_ref_profile_by_uuid (profiles_list, profile_uuid, &err);
+  if (profile == NULL)
     {
       g_dbus_method_invocation_return_gerror (invocation, err);
       g_error_free (err);
diff --git a/src/terminal-options.c b/src/terminal-options.c
index 6262eda..f2a54b6 100644
--- a/src/terminal-options.c
+++ b/src/terminal-options.c
@@ -31,12 +31,20 @@
 #include "terminal-screen.h"
 #include "terminal-app.h"
 #include "terminal-intl.h"
-#include "terminal-profile-utils.h"
 #include "terminal-util.h"
 #include "terminal-version.h"
 
 static GOptionContext *get_goption_context (TerminalOptions *options);
 
+static TerminalSettingsList *
+terminal_options_ensure_profiles_list (TerminalOptions *options)
+{
+  if (options->profiles_list == NULL)
+    options->profiles_list = terminal_profiles_list_new ();
+
+  return options->profiles_list;
+}
+
 static char *
 terminal_util_key_file_get_string_unescape (GKeyFile *key_file,
                                             const char *group,
@@ -296,7 +304,8 @@ option_profile_cb (const gchar *option_name,
   TerminalOptions *options = data;
   char *profile;
 
-  profile = terminal_profile_util_get_profile_by_uuid_or_name (value, error);
+  profile = terminal_profiles_list_dup_uuid_or_name (terminal_options_ensure_profiles_list (options),
+                                                     value, error);
   if (profile == NULL)
     return FALSE;
 
@@ -325,7 +334,8 @@ option_profile_id_cb (const gchar *option_name,
   TerminalOptions *options = data;
   char *profile;
 
-  profile = terminal_profile_util_get_profile_by_uuid (value, error);
+  profile = terminal_profiles_list_dup_uuid (terminal_options_ensure_profiles_list (options),
+                                             value, error);
   if (profile == NULL)
     return FALSE;
 
@@ -355,7 +365,8 @@ option_window_callback (const gchar *option_name,
   TerminalOptions *options = data;
   char *profile;
 
-  profile = terminal_profile_util_get_profile_by_uuid (value, error);
+  profile = terminal_profiles_list_dup_uuid (terminal_options_ensure_profiles_list (options),
+                                             value, error);
   if (profile == NULL)
     return FALSE;
 
@@ -373,7 +384,8 @@ option_tab_callback (const gchar *option_name,
   TerminalOptions *options = data;
   char *profile;
 
-  profile = terminal_profile_util_get_profile_by_uuid (value, error);
+  profile = terminal_profiles_list_dup_uuid (terminal_options_ensure_profiles_list (options),
+                                             value, error);
   if (profile == NULL)
     return FALSE;
 
@@ -985,6 +997,8 @@ terminal_options_free (TerminalOptions *options)
   g_free (options->sm_client_id);
   g_free (options->sm_config_prefix);
 
+  g_clear_object (&options->profiles_list);
+
   g_slice_free (TerminalOptions, options);
 }
 
diff --git a/src/terminal-options.h b/src/terminal-options.h
index 9cab113..709c6d3 100644
--- a/src/terminal-options.h
+++ b/src/terminal-options.h
@@ -24,6 +24,8 @@
 
 #include <glib.h>
 
+#include "terminal-profiles-list.h"
+
 G_BEGIN_DECLS
 
 #define TERMINAL_CONFIG_VERSION             (1) /* Bump this for any changes */
@@ -58,6 +60,8 @@ enum
 
 typedef struct
 {
+  TerminalSettingsList *profiles_list; /* may be NULL */
+
   char    *server_app_id;
   gboolean remote_arguments;
   char    *startup_id;
diff --git a/src/terminal-prefs.c b/src/terminal-prefs.c
index 07335ce..4408865 100644
--- a/src/terminal-prefs.c
+++ b/src/terminal-prefs.c
@@ -32,10 +32,11 @@
 #include "terminal-intl.h"
 #include "terminal-schemas.h"
 #include "terminal-util.h"
-#include "terminal-profile-utils.h"
+#include "terminal-profiles-list.h"
 #include "terminal-encoding.h"
 
 typedef struct {
+  TerminalSettingsList *profiles_list;
   GtkWidget *dialog;
   GtkWindow *parent;
 
@@ -44,7 +45,7 @@ typedef struct {
   GtkWidget *manage_profiles_edit_button;
   GtkWidget *manage_profiles_clone_button;
   GtkWidget *manage_profiles_delete_button;
-  GtkWidget *manage_profiles_default_menu;
+  GtkWidget *profiles_default_combo;
 
   GtkListStore *encoding_base_store;
   GtkTreeModel *encodings_model;
@@ -82,7 +83,7 @@ profile_cell_data_func (GtkTreeViewColumn *tree_column,
                         GtkCellRenderer *cell,
                         GtkTreeModel *tree_model,
                         GtkTreeIter *iter,
-                        gpointer user_data)
+                        PrefData *data)
 {
   GSettings *profile;
   const char *text;
@@ -91,7 +92,7 @@ profile_cell_data_func (GtkTreeViewColumn *tree_column,
 
   gtk_tree_model_get (tree_model, iter, (int) COL_PROFILE, &profile, (int) -1);
   g_settings_get (profile, TERMINAL_PROFILE_VISIBLE_NAME_KEY, "&s", &text);
-  uuid = terminal_profile_util_get_profile_uuid (profile);
+  uuid = terminal_settings_list_dup_uuid_from_child (data->profiles_list, profile);
 
   g_value_init (&value, G_TYPE_STRING);
   g_value_take_string (&value,
@@ -117,7 +118,7 @@ profile_sort_func (GtkTreeModel *model,
   gtk_tree_model_get (model, a, (int) COL_PROFILE, &profile_a, (int) -1);
   gtk_tree_model_get (model, b, (int) COL_PROFILE, &profile_b, (int) -1);
 
-  retval = terminal_profile_util_profiles_compare (profile_a, profile_b);
+  retval = terminal_profiles_compare (profile_a, profile_b);
 
   g_object_unref (profile_a);
   g_object_unref (profile_b);
@@ -126,14 +127,14 @@ profile_sort_func (GtkTreeModel *model,
 }
 
 static /* ref */ GtkTreeModel *
-profile_liststore_new (GSettings *selected_profile,
+profile_liststore_new (PrefData *data,
+                       GSettings *selected_profile,
                        GtkTreeIter *selected_profile_iter,
                        gboolean *selected_profile_iter_set)
 {
   GtkListStore *store;
   GtkTreeIter iter;
-  GHashTableIter ht_iter;
-  gpointer value;
+  GList *list, *l;
 
   G_STATIC_ASSERT (NUM_PROFILE_COLUMNS == 1);
   store = gtk_list_store_new (NUM_PROFILE_COLUMNS, G_TYPE_SETTINGS);
@@ -141,10 +142,10 @@ profile_liststore_new (GSettings *selected_profile,
   if (selected_profile_iter)
     *selected_profile_iter_set = FALSE;
 
-  terminal_app_get_profiles_iter (terminal_app_get (), &ht_iter);
-  while (g_hash_table_iter_next (&ht_iter, NULL, &value))
+  list = terminal_settings_list_ref_children (data->profiles_list);
+  for (l = list; l != NULL; l = l->next)
     {
-      GSettings *profile = (GSettings *) value;
+      GSettings *profile = (GSettings *) l->data;
 
       gtk_list_store_insert_with_values (store, &iter, 0,
                                          (int) COL_PROFILE, profile,
@@ -157,6 +158,8 @@ profile_liststore_new (GSettings *selected_profile,
         }
     }
 
+  g_list_free_full (list, (GDestroyNotify) g_object_unref);
+
   /* Now turn on sorting */
   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store),
                                    COL_PROFILE,
@@ -169,9 +172,8 @@ profile_liststore_new (GSettings *selected_profile,
 }
 
 static /* ref */ GSettings*
-profile_combo_box_ref_selected (GtkWidget *widget)
+profile_combo_box_ref_selected (GtkComboBox *combo)
 {
-  GtkComboBox *combo = GTK_COMBO_BOX (widget);
   GSettings *profile;
   GtkTreeIter iter;
 
@@ -185,17 +187,18 @@ profile_combo_box_ref_selected (GtkWidget *widget)
 }
 
 static void
-profile_combo_box_refill (GtkWidget *combo_widget)
+profile_combo_box_refill (PrefData *data)
 {
-  GtkComboBox *combo = GTK_COMBO_BOX (combo_widget);
+  GtkComboBox *combo = GTK_COMBO_BOX (data->profiles_default_combo);
   GtkTreeIter iter;
   gboolean iter_set;
   GSettings *selected_profile;
   GtkTreeModel *model;
 
-  selected_profile = profile_combo_box_ref_selected (combo_widget);
+  selected_profile = profile_combo_box_ref_selected (combo);
 
-  model = profile_liststore_new (selected_profile,
+  model = profile_liststore_new (data,
+                                 selected_profile,
                                  &iter,
                                  &iter_set);
   gtk_combo_box_set_model (combo, model);
@@ -227,10 +230,11 @@ profile_combo_box_new (PrefData *data)
   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
   gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo), renderer,
                                       (GtkCellLayoutDataFunc) profile_cell_data_func,
-                                      NULL, NULL);
+                                      data, NULL);
 
-  default_profile = terminal_app_ref_profile_by_uuid (terminal_app_get (), NULL, NULL);
-  model = profile_liststore_new (default_profile,
+  default_profile = terminal_settings_list_ref_default_child (data->profiles_list);
+  model = profile_liststore_new (data,
+                                 default_profile,
                                  &iter,
                                  &iter_set);
   gtk_combo_box_set_model (combo, model);
@@ -242,9 +246,6 @@ profile_combo_box_new (PrefData *data)
   if (default_profile)
     g_object_unref (default_profile);
 
-  g_signal_connect (terminal_app_get (), "profile-list-changed",
-                    G_CALLBACK (profile_combo_box_refill), combo);
-
   gtk_widget_show (combo_widget);
   return combo_widget;
 }
@@ -253,19 +254,18 @@ static void
 profile_combo_box_changed_cb (GtkWidget *widget,
                               PrefData *data)
 {
-  GSettings *profile, *settings;
+  GSettings *profile;
   char *uuid;
 
-  profile = profile_combo_box_ref_selected (widget);
+  profile = profile_combo_box_ref_selected (GTK_COMBO_BOX (data->profiles_default_combo));
   if (!profile)
     return;
 
-  uuid = terminal_profile_util_get_profile_uuid (profile);
-  settings = terminal_app_get_global_settings (terminal_app_get ());
-  g_settings_set_string (settings, TERMINAL_SETTING_DEFAULT_PROFILE_KEY, uuid);
+  uuid = terminal_settings_list_dup_uuid_from_child (data->profiles_list, profile);
+  g_object_unref (profile);
 
+  terminal_settings_list_set_default_child (data->profiles_list, uuid);
   g_free (uuid);
-  g_object_unref (profile);
 }
 
 static GSettings *
@@ -295,7 +295,8 @@ profile_list_treeview_refill (PrefData *data)
   GtkTreeModel *model;
 
   selected_profile = profile_list_ref_selected (data);
-  model = profile_liststore_new (selected_profile,
+  model = profile_liststore_new (data,
+                                 selected_profile,
                                  &iter,
                                  &iter_set);
   gtk_tree_view_set_model (tree_view, model);
@@ -349,7 +350,7 @@ profile_list_treeview_new (PrefData *data)
   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), renderer, TRUE);
   gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer,
                                       (GtkCellLayoutDataFunc) profile_cell_data_func,
-                                      NULL, NULL);
+                                      data, NULL);
   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
                                GTK_TREE_VIEW_COLUMN (column));
 
@@ -572,10 +573,12 @@ prefs_dialog_destroy_cb (GtkWidget *widget,
 {
   TerminalApp *app = terminal_app_get ();
 
-  g_signal_handlers_disconnect_by_func (app, G_CALLBACK (profile_list_treeview_refill), data);
-  g_signal_handlers_disconnect_by_func (app, G_CALLBACK (profile_combo_box_refill), data);
+  g_signal_handlers_disconnect_by_func (data->profiles_list, G_CALLBACK (profile_combo_box_refill), data);
+  g_signal_handlers_disconnect_by_func (data->profiles_list, G_CALLBACK (profile_list_treeview_refill), data);
+
   g_signal_handlers_disconnect_by_func (app, G_CALLBACK (encodings_list_changed_cb), data);
 
+  /* Don't run this handler again */
   g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (prefs_dialog_destroy_cb), data);
   g_free (data);
 }
@@ -603,6 +606,7 @@ terminal_prefs_show_preferences (GtkWindow *transient_parent,
 
   data = g_new0 (PrefData, 1);
   data->parent = transient_parent;
+  data->profiles_list = terminal_app_get_profiles_list (app);
 
   terminal_util_load_builder_resource ("/org/gnome/terminal/ui/preferences.ui",
                                        "preferences-dialog",
@@ -662,7 +666,7 @@ terminal_prefs_show_preferences (GtkWindow *transient_parent,
   g_signal_connect (selection, "changed", G_CALLBACK (profile_list_selection_changed_cb), data);
 
   profile_list_treeview_refill (data);
-  g_signal_connect_swapped (app, "profile-list-changed",
+  g_signal_connect_swapped (data->profiles_list, "children-changed",
                             G_CALLBACK (profile_list_treeview_refill), data);
 
   gtk_container_add (GTK_CONTAINER (tree_view_container), GTK_WIDGET (data->manage_profiles_list));
@@ -681,15 +685,17 @@ terminal_prefs_show_preferences (GtkWindow *transient_parent,
                     G_CALLBACK (profile_list_delete_button_clicked_cb),
                     data);
 
-  data->manage_profiles_default_menu = profile_combo_box_new (data);
-  g_signal_connect (data->manage_profiles_default_menu, "changed",
+  data->profiles_default_combo = profile_combo_box_new (data);
+  g_signal_connect_swapped (data->profiles_list, "children-changed",
+                            G_CALLBACK (profile_combo_box_refill), data);
+  g_signal_connect (data->profiles_default_combo, "changed",
                     G_CALLBACK (profile_combo_box_changed_cb), data);
 
-  gtk_box_pack_start (GTK_BOX (default_hbox), data->manage_profiles_default_menu, FALSE, FALSE, 0);
-  gtk_widget_show (data->manage_profiles_default_menu);
+  gtk_box_pack_start (GTK_BOX (default_hbox), data->profiles_default_combo, FALSE, FALSE, 0);
+  gtk_widget_show (data->profiles_default_combo);
 
   // FIXMEchpe
-  gtk_label_set_mnemonic_widget (GTK_LABEL (default_label), data->manage_profiles_default_menu);
+  gtk_label_set_mnemonic_widget (GTK_LABEL (default_label), data->profiles_default_combo);
 
   /* Encodings tab */
 
diff --git a/src/terminal-profiles-list.c b/src/terminal-profiles-list.c
new file mode 100644
index 0000000..284cfd5
--- /dev/null
+++ b/src/terminal-profiles-list.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright  2001, 2002 Havoc Pennington
+ * Copyright  2002 Red Hat, Inc.
+ * Copyright  2002 Sun Microsystems
+ * Copyright  2003 Mariano Suarez-Alvarez
+ * Copyright  2011, 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-profiles-list.h"
+#include "terminal-schemas.h"
+
+#include <string.h>
+#include <uuid.h>
+
+/* Counts occurrences of @str in @strv */
+static guint
+strv_contains (char **strv,
+               const char *str,
+               guint *idx)
+{
+  guint n, i;
+
+  if (strv == NULL)
+    return 0;
+
+  n = 0;
+  for (i = 0; strv[i]; i++) {
+    if (strcmp (strv[i], str) == 0) {
+      n++;
+      if (idx)
+        *idx = i;
+    }
+  }
+
+  return n;
+}
+
+/**
+ * terminal_profiles_list_new:
+ *
+ * Returns: (transfer full): a new #TerminalSettingsList for the profiles list
+ */
+TerminalSettingsList *
+terminal_profiles_list_new (void)
+{
+  return terminal_settings_list_new (TERMINAL_PROFILES_PATH_PREFIX,
+                                     TERMINAL_PROFILES_LIST_SCHEMA,
+                                     TERMINAL_PROFILE_SCHEMA,
+                                     TERMINAL_SETTINGS_LIST_FLAG_HAS_DEFAULT);
+}
+
+static void
+get_profile_names (TerminalSettingsList *list,
+                   char ***profilesp,
+                   char ***namesp)
+{
+  GSettings *profile;
+  char **profiles, **names;
+  guint i, n;
+
+  *profilesp = profiles = terminal_settings_list_dupv_children (list);
+
+  n = g_strv_length (profiles);
+  *namesp = names = g_new0 (char *, n + 1);
+  for (i = 0; i < n; i++) {
+    profile = terminal_settings_list_ref_child (list, profiles[i]);
+    names[i] = g_settings_get_string (profile, TERMINAL_PROFILE_VISIBLE_NAME_KEY);
+    g_object_unref (profile);
+  }
+
+  names[n] = NULL;
+}
+
+/**
+ * terminal_profiles_list_get_children:
+ * @list:
+ *
+ * Returns: (transfer full):
+ */
+GList *
+terminal_profiles_list_ref_children (TerminalSettingsList *list)
+{
+  return g_list_sort (terminal_settings_list_ref_children (list),
+                      terminal_profiles_compare);
+}
+
+/**
+ * terminal_profiles_list_dup_uuid:
+ * @list:
+ * @uuid: (allow-none):
+ * @error:
+ *
+ * Returns: (transfer full): the UUID of the profile specified by @uuid, or %NULL
+ */
+char *
+terminal_profiles_list_dup_uuid (TerminalSettingsList *list,
+                                 const char *uuid,
+                                 GError **error)
+{
+  char *rv;
+
+  if (uuid == NULL) {
+    rv = terminal_settings_list_dup_default_child (list);
+    if (rv == NULL)
+      goto err;
+    return rv;
+  }
+
+  if (terminal_settings_list_has_child (list, uuid))
+    return g_strdup (uuid);
+
+ err:
+  g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+               "No profile with UUID \"%s\" exists", uuid);
+  return NULL;
+}
+
+/**
+ * terminal_profiles_list_ref_profile_by_uuid_or_name:
+ * @list:
+ * @uuid:
+ * @error:
+ *
+ * Returns: (transfer full): the profile #GSettings specified by @uuid, or %NULL
+ */
+GSettings *
+terminal_profiles_list_ref_profile_by_uuid (TerminalSettingsList *list,
+                                            const char *uuid,
+                                            GError **error)
+{
+  char *profile_uuid;
+  GSettings *profile;
+
+  profile_uuid = terminal_profiles_list_dup_uuid (list, uuid, error);
+  if (profile_uuid == NULL)
+    return NULL;
+
+  profile = terminal_settings_list_ref_child (list, profile_uuid);
+  g_free (profile_uuid);
+  g_assert (profile != NULL);
+  return profile;
+}
+
+/**
+ * terminal_profiles_list_get_profile_by_uuid:
+ * @list:
+ * @uuid: (allow-none):
+ * @error:
+ *
+ * Returns: (transfer full): the UUID of the profile specified by @uuid, or %NULL
+ */
+char *
+terminal_profiles_list_dup_uuid_or_name (TerminalSettingsList *list,
+                                         const char *uuid_or_name,
+                                         GError **error)
+{
+  char **profiles, **profile_names;
+  char *rv;
+  guint n, i;
+
+  rv = terminal_profiles_list_dup_uuid (list, uuid_or_name, NULL);
+  if (rv != NULL)
+    return rv;
+
+  /* Not found as UUID; try finding a profile with this string as 'visible-name' */
+  get_profile_names (list, &profiles, &profile_names);
+  n = strv_contains (profile_names, uuid_or_name, &i);
+
+  if (n == 0) {
+    g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+                 "No profile with UUID or name \"%s\" exists", uuid_or_name);
+    rv = NULL;
+  } else if (n != 1) {
+    g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+                 "No profile with UUID \"%s\" found and name is ambiguous", uuid_or_name);
+    rv = NULL;
+  } else {
+    rv = g_strdup (profiles[i]);
+  }
+
+  g_strfreev (profiles);
+  g_strfreev (profile_names);
+
+  return rv;
+}
+
+/**
+ * terminal_profiles_list_ref_profile_by_uuid_or_name:
+ * @list:
+ * @uuid:
+ * @error:
+ *
+ * Returns: (transfer full): the profile #GSettings specified by @uuid, or %NULL
+ */
+GSettings *
+terminal_profiles_list_ref_profile_by_uuid_or_name (TerminalSettingsList *list,
+                                                    const char *uuid_or_name,
+                                                    GError **error)
+{
+  char *uuid;
+  GSettings *profile;
+
+  uuid = terminal_profiles_list_dup_uuid_or_name (list, uuid_or_name, error);
+  if (uuid == NULL)
+    return NULL;
+
+  profile = terminal_settings_list_ref_child (list, uuid);
+  g_free (uuid);
+  g_assert (profile != NULL);
+  return profile;
+}
+
+int
+terminal_profiles_compare (gconstpointer pa,
+                           gconstpointer pb)
+{
+  GSettings *a = (GSettings *) pa;
+  GSettings *b = (GSettings *) pb;
+  const char *na, *nb;
+  char *patha, *pathb;
+  int result;
+
+  if (pa == pb)
+    return 0;
+  if (pa == NULL)
+    return 1;
+  if (pb == NULL)
+    return -1;
+
+  g_settings_get (a, TERMINAL_PROFILE_VISIBLE_NAME_KEY, "&s", &na);
+  g_settings_get (b, TERMINAL_PROFILE_VISIBLE_NAME_KEY, "&s", &nb);
+  result =  g_utf8_collate (na, nb);
+  if (result != 0)
+    return result;
+
+  g_object_get (a, "path", &patha, NULL);
+  g_object_get (b, "path", &pathb, NULL);
+  result = strcmp (patha, pathb);
+  g_free (patha);
+  g_free (pathb);
+
+  return result;
+}
diff --git a/src/terminal-profiles-list.h b/src/terminal-profiles-list.h
new file mode 100644
index 0000000..42ed11c
--- /dev/null
+++ b/src/terminal-profiles-list.h
@@ -0,0 +1,53 @@
+/*
+ * 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/>.
+ */
+
+#ifndef TERMINAL_PROFILES_LIST_H
+#define TERMINAL_PROFILES_LIST_H
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "terminal-settings-list.h"
+
+G_BEGIN_DECLS
+
+TerminalSettingsList *terminal_profiles_list_new (void);
+
+GList *terminal_profiles_list_ref_children (TerminalSettingsList *list);
+
+char *terminal_profiles_list_dup_uuid (TerminalSettingsList *list,
+                                       const char *uuid,
+                                       GError **error);
+
+GSettings *terminal_profiles_list_ref_profile_by_uuid (TerminalSettingsList *list,
+                                                       const char *uuid,
+                                                       GError **error);
+
+char *terminal_profiles_list_dup_uuid_or_name (TerminalSettingsList *list,
+                                               const char *uuid_or_name,
+                                               GError **error);
+
+GSettings *terminal_profiles_list_ref_profile_by_uuid_or_name (TerminalSettingsList *list,
+                                                               const char *uuid_or_name,
+                                                               GError **error);
+
+int terminal_profiles_compare (gconstpointer pa,
+                               gconstpointer pb);
+
+G_END_DECLS
+
+#endif /* TERMINAL_PROFILES_LIST_H */
diff --git a/src/terminal-schemas.h b/src/terminal-schemas.h
index f31a035..45386a1 100644
--- a/src/terminal-schemas.h
+++ b/src/terminal-schemas.h
@@ -22,11 +22,13 @@
 
 G_BEGIN_DECLS
 
-#define TERMINAL_SCHEMA_VERSION         (1u)
+#define TERMINAL_SCHEMA_VERSION         (2u)
 
 #define TERMINAL_KEYBINDINGS_SCHEMA     "org.gnome.Terminal.Legacy.Keybindings"
 #define TERMINAL_PROFILE_SCHEMA         "org.gnome.Terminal.Legacy.Profile"
 #define TERMINAL_SETTING_SCHEMA         "org.gnome.Terminal.Legacy.Settings"
+#define TERMINAL_SETTINGS_LIST_SCHEMA   "org.gnome.Terminal.SettingsList"
+#define TERMINAL_PROFILES_LIST_SCHEMA   "org.gnome.Terminal.ProfilesList"
 
 #define TERMINAL_PROFILE_ALLOW_BOLD_KEY                 "allow-bold"
 #define TERMINAL_PROFILE_AUDIBLE_BELL_KEY               "audible-bell"
@@ -64,14 +66,15 @@ G_BEGIN_DECLS
 #define TERMINAL_PROFILE_WORD_CHARS_KEY                 "word-chars"
 
 #define TERMINAL_SETTING_CONFIRM_CLOSE_KEY              "confirm-close"
-#define TERMINAL_SETTING_DEFAULT_PROFILE_KEY            "default-profile"
 #define TERMINAL_SETTING_DEFAULT_SHOW_MENUBAR_KEY       "default-show-menubar"
 #define TERMINAL_SETTING_ENABLE_MENU_BAR_ACCEL_KEY      "menu-accelerator-enabled"
 #define TERMINAL_SETTING_ENABLE_MNEMONICS_KEY           "mnemonics-enabled"
-#define TERMINAL_SETTING_PROFILES_KEY                   "profiles"
 #define TERMINAL_SETTING_ENCODINGS_KEY                  "encodings"
 #define TERMINAL_SETTING_SCHEMA_VERSION                 "schema-version"
 
+#define TERMINAL_SETTINGS_LIST_LIST_KEY                 "list"
+#define TERMINAL_SETTINGS_LIST_DEFAULT_KEY              "default"
+
 #define TERMINAL_PROFILES_PATH_PREFIX   "/org/gnome/terminal/legacy/profiles:/"
 
 G_END_DECLS
diff --git a/src/terminal-settings-list.c b/src/terminal-settings-list.c
new file mode 100644
index 0000000..c2934fa
--- /dev/null
+++ b/src/terminal-settings-list.c
@@ -0,0 +1,881 @@
+/*
+ * 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.h"
+
+#include <string.h>
+#include <uuid.h>
+#include <dconf.h>
+
+#include "terminal-type-builtins.h"
+#include "terminal-schemas.h"
+#include "terminal-debug.h"
+
+struct _TerminalSettingsList {
+  GSettings parent;
+
+  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_CHILD_SCHEMA_ID = 1,
+  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 == NULL) {
+    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 == NULL || b == NULL)
+    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 == NULL || str == NULL)
+    return -1;
+
+  for (i = 0; strv[i]; i++) {
+    if (!g_str_equal (strv[i], str))
+      continue;
+
+    return i;
+  }
+
+  return -1;
+}
+
+static char **
+strv_dupv_insert (char **strv,
+                  const char *str)
+{
+  char **nstrv, **p, **q;
+
+  if (strv == NULL) {
+    char *s[2] = { (char *) str, NULL };
+    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 = NULL;
+
+  return strv_sort (nstrv);
+}
+
+static char **
+strv_dupv_remove (char **strv,
+                  const char *str)
+{
+  char **nstrv, **p, **q;
+
+  if (strv == NULL)
+    return NULL;
+
+  nstrv = g_strdupv (strv);
+  for (p = q = nstrv; *p; p++) {
+    if (!g_str_equal (*p, str))
+      *q++ = *p;
+    else
+      g_free (*p);
+  }
+  *q = NULL;
+
+  return nstrv;
+}
+
+static gboolean
+is_valid_uuid (const char *str)
+{
+  uuid_t u;
+
+  if (str == NULL)
+    return FALSE;
+
+  return uuid_parse ((char *) str, u) == 0;
+}
+
+static char *
+new_list_entry (void)
+{
+  uuid_t u;
+  char name[37];
+
+  uuid_generate (u);
+  uuid_unparse (u, name);
+
+  return g_strdup (name);
+}
+
+static gboolean
+validate_list (TerminalSettingsList *list,
+               char **entries)
+{
+  gboolean allow_empty = (list->flags & TERMINAL_SETTINGS_LIST_FLAG_ALLOW_EMPTY) != 0;
+  guint i;
+
+  if (entries == NULL)
+    return allow_empty;
+
+  for (i = 0; entries[i]; i++) {
+    if (!is_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 = user_data;
+  char **entries;
+
+  entries = strv_sort (g_variant_dup_strv (value, NULL));
+
+  if (validate_list (list, entries)) {
+    *result = entries;
+    return TRUE;
+  }
+
+  g_strfreev (entries);
+  return FALSE;
+}
+
+static char *
+path_new (TerminalSettingsList *list,
+          const char *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;
+  char *path;
+
+  if (strv_find (list->uuids, uuid) == -1)
+    return NULL;
+
+  _terminal_debug_print (TERMINAL_DEBUG_SETTINGS_LIST,
+                         "%s UUID %s\n", G_STRFUNC, uuid);
+
+  child = g_hash_table_lookup (list->children, uuid);
+  if (child)
+    goto done;
+
+  path = path_new (list, uuid);
+  child = g_settings_new_with_path (list->child_schema_id, path);
+  g_hash_table_insert (list->children, g_strdup (uuid), child /* adopted */);
+  g_free (path);
+
+ done:
+  return g_object_ref (child);
+}
+
+static char *
+clone_child (TerminalSettingsList *list,
+             const char *uuid)
+{
+  char *new_uuid;
+  char *path, *new_path;
+  char **keys, *key;
+  guint i;
+  GVariant *value;
+  DConfClient *client;
+#ifndef HAVE_DCONF_1_2
+  DConfChangeset *changeset;
+#endif
+
+  new_uuid = new_list_entry ();
+
+  _terminal_debug_print (TERMINAL_DEBUG_SETTINGS_LIST,
+                         "%s UUID %s NEW UUID %s \n", G_STRFUNC, uuid ? uuid : "(null)", new_uuid);
+
+  path = path_new (list, uuid);
+  new_path = path_new (list, new_uuid);
+
+#ifdef HAVE_DCONF_1_2
+  client = dconf_client_new (NULL, NULL, NULL, NULL);
+#else
+  client = dconf_client_new ();
+  changeset = dconf_changeset_new ();
+#endif
+
+  /* FIXME: this is beyond ugly. Need API on GSettingsSchema to list all the keys! */
+  {
+    GSettings *dummy = g_settings_new_with_path (list->child_schema_id, "/foo");
+    keys = g_settings_list_keys (dummy);
+    g_object_unref (dummy);
+  }
+
+  for (i = 0; keys[i]; i++) {
+    key = g_strconcat (path, keys[i], NULL);
+#ifdef HAVE_DCONF_1_2
+    value = dconf_client_read_no_default (client, key);
+#else
+    value = dconf_client_read (client, key);
+#endif
+    g_free (key);
+
+    if (value) {
+      key = g_strconcat (new_path, keys[i], NULL);
+#ifdef HAVE_DCONF_1_2
+      dconf_client_write (client, key, value, NULL, NULL, NULL);
+#else
+      dconf_changeset_set (changeset, key, value);
+#endif
+      g_free (key);
+      g_variant_unref (value);
+    }
+  }
+
+#ifndef HAVE_DCONF_1_2
+  dconf_client_change_sync (client, changeset, NULL, NULL, NULL);
+  g_object_unref (changeset);
+#endif
+  g_object_unref (client);
+  g_free (path);
+  g_free (new_path);
+
+  return new_uuid;
+}
+
+static char *
+terminal_settings_list_add_child_internal (TerminalSettingsList *list,
+                                           const char *uuid)
+{
+  char *new_uuid;
+  char **new_uuids;
+
+  if (uuid)
+    new_uuid = clone_child (list, uuid);
+  else
+    new_uuid = new_list_entry ();
+
+  _terminal_debug_print (TERMINAL_DEBUG_SETTINGS_LIST,
+                         "%s NEW UUID %s\n", G_STRFUNC, new_uuid);
+
+  new_uuids = strv_dupv_insert (list->uuids, new_uuid);
+  g_settings_set_strv (&list->parent, TERMINAL_SETTINGS_LIST_LIST_KEY,
+                       (const char * const *) new_uuids);
+  g_strfreev (new_uuids);
+
+  return new_uuid;
+}
+
+static void
+terminal_settings_list_remove_child_internal (TerminalSettingsList *list,
+                                              const char *uuid)
+{
+  char **new_uuids, *path;
+  DConfClient *client;
+
+  _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 == NULL || new_uuids[0] == NULL) &&
+      (list->flags & TERMINAL_SETTINGS_LIST_FLAG_ALLOW_EMPTY) == 0) {
+    g_strfreev (new_uuids);
+    return;
+  }
+
+  g_settings_set_strv (&list->parent, TERMINAL_SETTINGS_LIST_LIST_KEY, (const char * const *) new_uuids);
+  g_strfreev (new_uuids);
+
+  if (list->default_uuid != NULL &&
+      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 */
+  path = path_new (list, uuid);
+
+#ifdef HAVE_DCONF_1_2
+  client = dconf_client_new (NULL, NULL, NULL, NULL);
+  dconf_client_write (client, path, NULL, NULL, NULL, NULL);
+#else /* modern DConf */
+  client = dconf_client_new ();
+  dconf_client_write_sync (client, path, NULL, NULL, NULL, NULL);
+#endif
+  g_object_unref (client);
+
+  g_free (path);
+}
+
+static void
+terminal_settings_list_update_list (TerminalSettingsList *list)
+{
+  char **uuids, *uuid;
+  GSettings *child;
+  GHashTable *new_children;
+  guint i;
+  gboolean changed;
+
+  uuids = 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] != NULL; i++) {
+      uuid = uuids[i];
+
+      child = 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 == NULL || 
+      g_str_equal (key, TERMINAL_SETTINGS_LIST_LIST_KEY)) {
+    terminal_settings_list_update_list (list);
+    terminal_settings_list_update_default (list);
+  }
+
+  if (key == NULL)
+    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_assert (list->child_schema_id != NULL);
+
+  g_object_get (object, "path", &list->path, NULL);
+
+  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, NULL);
+}
+
+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_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_CHILD_SCHEMA_ID:
+      list->child_schema_id = g_value_dup_string (value);
+      break;
+    case PROP_FLAGS:
+      list->flags = 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_CHILD_SCHEMA_ID,
+                                   g_param_spec_string ("child-schema-id", NULL,NULL,
+                                                        NULL,
+                                                        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", NULL,NULL,
+                                                       TERMINAL_TYPE_SETTINGS_LIST_FLAGS,
+                                                       TERMINAL_SETTINGS_LIST_FLAG_NONE,
+                                                       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),
+                  NULL, NULL,
+                  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),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
+
+  settings_class->changed = terminal_settings_list_changed;
+}
+
+
+/**
+ * terminal_settings_list_new:
+ * @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 (const char *path,
+                            const char *schema_id,
+                            const char *child_schema_id,
+                            TerminalSettingsListFlags flags)
+{
+  g_return_val_if_fail (path != NULL, NULL);
+  g_return_val_if_fail (schema_id != NULL, NULL);
+  g_return_val_if_fail (child_schema_id != NULL, NULL);
+  g_return_val_if_fail (g_str_has_suffix (path, ":/"), NULL);
+
+  return g_object_new (TERMINAL_TYPE_SETTINGS_LIST,
+                       "schema-id", schema_id,
+                       "child-schema-id", child_schema_id,
+                       "path", path,
+                       "flags", flags,
+                       NULL);
+}
+
+/**
+ * terminal_settings_list_dupv_children:
+ * @list: a #TerminalSettingsList
+ *
+ * Returns: (transfer full): the UUIDs of the children in the settings list, or %NULL
+ *  if the list is empty
+ */
+char **
+terminal_settings_list_dupv_children (TerminalSettingsList *list)
+{
+  g_return_val_if_fail (TERMINAL_IS_SETTINGS_LIST (list), NULL);
+
+  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), NULL);
+
+  if ((list->flags & TERMINAL_SETTINGS_LIST_FLAG_HAS_DEFAULT) == 0)
+    return NULL;
+
+  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 dconf.
+   */
+  if (list->uuids == NULL || list->uuids[0] == NULL) {
+    g_warn_if_fail ((list->flags & TERMINAL_SETTINGS_LIST_FLAG_ALLOW_EMPTY));
+    return NULL;
+  }
+
+  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 (is_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 %NULL
+ *   if @list has no such child.
+ *
+ * Returns: (transfer full): a reference to the #GSettings for the child, or %NULL
+ */
+GSettings *
+terminal_settings_list_ref_child (TerminalSettingsList *list,
+                                  const char *uuid)
+{
+  g_return_val_if_fail (TERMINAL_IS_SETTINGS_LIST (list), NULL);
+  g_return_val_if_fail (is_valid_uuid (uuid), NULL);
+
+  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), NULL);
+
+  if (list->uuids == NULL)
+    return NULL;
+
+  l = NULL;
+  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 %NULL if @list has no
+ *   children.
+ *
+ * Returns: (transfer full): a reference to the #GSettings for the default child, or %NULL
+ */
+GSettings *
+terminal_settings_list_ref_default_child (TerminalSettingsList *list)
+{
+  char *uuid;
+
+  g_return_val_if_fail (TERMINAL_IS_SETTINGS_LIST (list), NULL);
+
+  uuid = terminal_settings_list_dup_default_child (list);
+  if (uuid == NULL)
+    return NULL;
+
+  return terminal_settings_list_ref_child_internal (list, uuid);
+}
+
+/**
+ * terminal_settings_list_add_child:
+ * @list: a #TerminalSettingsList
+ *
+ * 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)
+{
+  g_return_val_if_fail (TERMINAL_IS_SETTINGS_LIST (list), NULL);
+
+  return terminal_settings_list_add_child_internal (list, NULL);
+}
+
+/**
+ * terminal_settings_list_clone_child:
+ * @list: a #TerminalSettingsList
+ * @uuid: the UUID of the child to clone
+ *
+ * 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)
+{
+  g_return_val_if_fail (TERMINAL_IS_SETTINGS_LIST (list), NULL);
+  g_return_val_if_fail (is_valid_uuid (uuid), NULL);
+
+  return terminal_settings_list_add_child_internal (list, uuid);
+}
+
+/**
+ * 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 (is_valid_uuid (uuid));
+
+  terminal_settings_list_remove_child_internal (list, uuid);
+}
+
+/**
+ * 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 %NULL if @child is in the list.
+ *
+ * Returns: (transfer full): the UUID of the child in the settings list, or %NULL
+ */
+char *
+terminal_settings_list_dup_uuid_from_child (TerminalSettingsList *list,
+                                            GSettings *child)
+{
+  char *path, *p;
+
+  g_return_val_if_fail (TERMINAL_IS_SETTINGS_LIST (list), NULL);
+
+  g_object_get (child, "path", &path, NULL);
+  g_return_val_if_fail (g_str_has_prefix (path, list->path), NULL);
+
+  p = path + strlen (list->path);
+  g_return_val_if_fail (p[0] == ':', NULL);
+  p++;
+  g_return_val_if_fail (strlen (p) == 37, NULL);
+  p[36] = '\0';
+  g_return_val_if_fail (is_valid_uuid (p), NULL);
+
+  p = g_strdup (p);
+  g_free (path);
+  return 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 (is_valid_uuid (uuid));
+
+  if (!terminal_settings_list_has_child (list, uuid))
+    return;
+
+  g_settings_set_string (&list->parent, TERMINAL_SETTINGS_LIST_DEFAULT_KEY, uuid);
+}
diff --git a/src/terminal-settings-list.h b/src/terminal-settings-list.h
new file mode 100644
index 0000000..ff00c99
--- /dev/null
+++ b/src/terminal-settings-list.h
@@ -0,0 +1,74 @@
+/*
+ * 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/>.
+ */
+
+#ifndef TERMINAL_SETTINGS_LIST_H
+#define TERMINAL_SETTINGS_LIST_H
+
+#include <gio/gio.h>
+
+#include "terminal-enums.h"
+
+G_BEGIN_DECLS
+
+#define TERMINAL_TYPE_SETTINGS_LIST            (terminal_settings_list_get_type ())
+#define TERMINAL_SETTINGS_LIST(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), TERMINAL_TYPE_SETTINGS_LIST, TerminalSettingsList))
+#define TERMINAL_SETTINGS_LIST_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TERMINAL_TYPE_SETTINGS_LIST, TerminalSettingsListClass))
+#define TERMINAL_IS_SETTINGS_LIST(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), TERMINAL_TYPE_SETTINGS_LIST))
+#define TERMINAL_IS_SETTINGS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TERMINAL_TYPE_SETTINGS_LIST))
+#define TERMINAL_SETTINGS_LIST_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TERMINAL_TYPE_SETTINGS_LIST, TerminalSettingsListClass))
+
+typedef struct _TerminalSettingsList      TerminalSettingsList;
+typedef struct _TerminalSettingsListClass TerminalSettingsListClass;
+
+GType terminal_settings_list_get_type (void);
+
+TerminalSettingsList *terminal_settings_list_new (const char *path,
+                                                  const char *schema_id,
+                                                  const char *child_schema_id,
+                                                  TerminalSettingsListFlags flags);
+
+char **terminal_settings_list_dupv_children (TerminalSettingsList *list);
+
+GList *terminal_settings_list_ref_children (TerminalSettingsList *list);
+
+gboolean terminal_settings_list_has_child (TerminalSettingsList *list,
+                                           const char *uuid);
+
+GSettings *terminal_settings_list_ref_child (TerminalSettingsList *list,
+                                             const char *uuid);
+
+char *terminal_settings_list_add_child (TerminalSettingsList *list);
+
+char *terminal_settings_list_clone_child (TerminalSettingsList *list,
+                                          const char *uuid);
+
+void terminal_settings_list_remove_child (TerminalSettingsList *list,
+                                          const char *uuid);
+
+char *terminal_settings_list_dup_uuid_from_child (TerminalSettingsList *list,
+                                                  GSettings *child);
+
+GSettings *terminal_settings_list_ref_default_child (TerminalSettingsList *list);
+
+char *terminal_settings_list_dup_default_child (TerminalSettingsList *list);
+
+void terminal_settings_list_set_default_child (TerminalSettingsList *list,
+                                               const char *uuid);
+
+G_END_DECLS
+
+#endif /* TERMINAL_SETTINGS_LIST_H */
diff --git a/src/terminal-window.c b/src/terminal-window.c
index 8162132..638e67d 100644
--- a/src/terminal-window.c
+++ b/src/terminal-window.c
@@ -511,6 +511,7 @@ terminal_window_update_set_profile_menu (TerminalWindow *window)
   GSettings *active_profile;
   GtkActionGroup *action_group;
   GtkAction *action;
+  TerminalSettingsList *profiles_list;
   GList *profiles, *p;
   GSList *group;
   guint n;
@@ -531,11 +532,13 @@ terminal_window_update_set_profile_menu (TerminalWindow *window)
       priv->profiles_action_group = NULL;
     }
 
-  profiles = terminal_app_get_profile_list (terminal_app_get ());
+  profiles_list = terminal_app_get_profiles_list (terminal_app_get ());
+  profiles = terminal_profiles_list_ref_children (profiles_list);
 
   action = gtk_action_group_get_action (priv->action_group, "TerminalProfiles");
   single_profile = !profiles || profiles->next == NULL; /* list length <= 1 */
   gtk_action_set_sensitive (action, !single_profile);
+
   if (profiles == NULL)
     return;
 
@@ -595,7 +598,7 @@ terminal_window_update_set_profile_menu (TerminalWindow *window)
                              GTK_UI_MANAGER_MENUITEM, FALSE);
     }
 
-  g_list_free (profiles);
+  g_list_free_full (profiles, (GDestroyNotify) g_object_unref);
 }
 
 static void
@@ -629,6 +632,7 @@ terminal_window_update_new_terminal_menus (TerminalWindow *window)
   TerminalWindowPrivate *priv = window->priv;
   GtkActionGroup *action_group;
   GtkAction *action;
+  TerminalSettingsList *profiles_list;
   GList *profiles, *p;
   guint n;
   gboolean have_single_profile;
@@ -648,7 +652,9 @@ terminal_window_update_new_terminal_menus (TerminalWindow *window)
       priv->new_terminal_action_group = NULL;
     }
 
-  profiles = terminal_app_get_profile_list (terminal_app_get ());
+  profiles_list = terminal_app_get_profiles_list (terminal_app_get ());
+  profiles = terminal_profiles_list_ref_children (profiles_list);
+
   have_single_profile = !profiles || !profiles->next;
 
   action = gtk_action_group_get_action (priv->action_group, "FileNewTab");
@@ -658,7 +664,7 @@ terminal_window_update_new_terminal_menus (TerminalWindow *window)
 
   if (have_single_profile)
     {
-      g_list_free (profiles);
+      g_list_free_full (profiles, (GDestroyNotify) g_object_unref);
       return;
     }
 
@@ -703,7 +709,7 @@ terminal_window_update_new_terminal_menus (TerminalWindow *window)
       ++n;
     }
 
-  g_list_free (profiles);
+  g_list_free_full (profiles, (GDestroyNotify) g_object_unref);
 }
 
 static void
@@ -1596,7 +1602,7 @@ terminal_window_screen_changed (GtkWidget *widget,
 }
 
 static void
-terminal_window_profile_list_changed_cb (TerminalApp *app,
+terminal_window_profile_list_changed_cb (TerminalSettingsList *profiles_list,
                                          TerminalWindow *window)
 {
   terminal_window_update_set_profile_menu (window);
@@ -1799,6 +1805,7 @@ terminal_window_init (TerminalWindow *window)
     };
   TerminalWindowPrivate *priv;
   TerminalApp *app;
+  TerminalSettingsList *profiles_list;
   GtkActionGroup *action_group;
   GtkAction *action;
   GtkUIManager *manager;
@@ -1916,8 +1923,9 @@ terminal_window_init (TerminalWindow *window)
   priv->tabs_menu = terminal_tabs_menu_new (window);
 
   app = terminal_app_get ();
-  terminal_window_profile_list_changed_cb (app, window);
-  g_signal_connect (app, "profile-list-changed",
+  profiles_list = terminal_app_get_profiles_list (app);
+  terminal_window_profile_list_changed_cb (profiles_list, window);
+  g_signal_connect (profiles_list, "children-changed",
                     G_CALLBACK (terminal_window_profile_list_changed_cb), window);
   
   terminal_window_encoding_list_changed_cb (app, window);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]