[dconf] Make dconf-editor understand and display schemas



commit 68ee02e3654e13da7c0c53928ab2de2a36313f87
Author: Robert Ancell <robert ancell canonical com>
Date:   Tue Jun 15 17:33:08 2010 +1000

    Make dconf-editor understand and display schemas

 configure.ac             |    2 +
 editor/Makefile.am       |    8 +-
 editor/dconf-editor.vala |  231 ++++++++++++++-----------
 editor/dconf-model.vala  |  428 ++++++++++++++++++++++++++++++++++++++++++++++
 editor/dconf-schema.vala |  147 ++++++++++++++++
 editor/dconf-view.vala   |  201 ++++++++++++++++++++++
 editor/dconf.vapi        |    8 +-
 7 files changed, 918 insertions(+), 107 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 29b3dbd..f4633a4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -9,6 +9,8 @@ AC_PROG_CC
 
 PKG_CHECK_MODULES(gio, gio-2.0 >= 2.25.7)
 PKG_CHECK_MODULES(gtk, gtk+-2.0)
+PKG_CHECK_MODULES(gee, gee-1.0)
+PKG_CHECK_MODULES(libxml, libxml-2.0)
 
 AC_ARG_WITH(gio_modules_dir, [  --with-gio-modules-dir=PATH choose directory for the GIO module, [default=LIBDIR/gio/modules]], giomodulesdir="$withval", giomodulesdir=${libdir}/gio/modules)
 AC_SUBST(giomodulesdir)
diff --git a/editor/Makefile.am b/editor/Makefile.am
index 2d6a486..e2e77a6 100644
--- a/editor/Makefile.am
+++ b/editor/Makefile.am
@@ -1,6 +1,6 @@
 bin_PROGRAMS = dconf-editor
 
-AM_CFLAGS = $(gtk_CFLAGS) -I$(top_srcdir)/common -I$(top_srcdir)/client
-AM_VALAFLAGS = --pkg gtk+-2.0
-dconf_editor_LDADD = ../client/libdconf.la $(gtk_LIBS)
-dconf_editor_SOURCES = dconf-editor.vala dconf.vapi
+AM_CFLAGS = $(gtk_CFLAGS) $(gee_CFLAGS) $(libxml_CFLAGS) -I$(top_srcdir)/common -I$(top_srcdir)/client
+AM_VALAFLAGS = --pkg gee-1.0 --pkg gtk+-2.0 --pkg libxml-2.0
+dconf_editor_LDADD = ../client/libdconf.la $(gtk_LIBS) $(gee_LIBS) $(libxml_LIBS)
+dconf_editor_SOURCES = dconf-editor.vala dconf-model.vala dconf-schema.vala dconf-view.vala dconf.vapi
diff --git a/editor/dconf-editor.vala b/editor/dconf-editor.vala
index 127881a..e52057a 100644
--- a/editor/dconf-editor.vala
+++ b/editor/dconf-editor.vala
@@ -1,61 +1,18 @@
-public class ConfModel : Gtk.TreeStore
-{
-    public ConfModel()
-    {
-        set_column_types({typeof(string), typeof(string)});
-    }
-    
-    private Gtk.TreeIter? get_iter(string[] split_key, int length)
-    {
-        if (length == 1) // NOTE: Assumes key started with /
-            return null;
-
-        Gtk.TreeIter? parent = get_iter(split_key, length - 1);
-        string key = split_key[length - 1];
-
-        Gtk.TreeIter iter;
-        bool have_iter = iter_children(out iter, parent);
-        while (have_iter)
-        {
-            string name;
-            get(iter, 0, out name, -1);
-            if (name == key)
-                return iter;
-            if (name > key)
-            {
-                insert_before(out iter, parent, iter);
-                break;
-            }
-            have_iter = iter_next(ref iter);
-        }
-        if (!have_iter)
-            append(out iter, parent);
-
-        set(iter, 0, key, -1);
-
-        return iter;
-    }
-
-    public void add_key(string key)
-    {
-        string[] tokens = key.split("/", -1);    
-        Gtk.TreeIter? iter = get_iter(tokens, tokens.length);
-        set(iter, 1, key, -1);
-    }
-}
-
 public class EditorWindow : Gtk.Window
 {
-    public ConfModel model;
-
-    private DConf.Client client;
-    private Gtk.TreeView tree_view;
-    private Gtk.Label name_label;
-    private Gtk.Label value_label;
-
-    public EditorWindow(DConf.Client client)
+    public SettingsModel model;
+    public SchemaList schemas;
+
+    private Gtk.TreeView dir_tree_view;
+    private Gtk.TreeView key_tree_view;
+    private Gtk.Label schema_label;
+    private Gtk.Label summary_label;
+    private Gtk.Label description_label;
+    private Gtk.Label type_label;
+    private Gtk.Label default_label;
+
+    public EditorWindow()
     {
-        this.client = client;
         set_title("Configuration Editor");
         set_default_size(600, 300);
         set_border_width(6);
@@ -64,87 +21,163 @@ public class EditorWindow : Gtk.Window
         hbox.show();
         add(hbox);
 
-        model = new ConfModel();
+        model = new SettingsModel();
+        schemas = new SchemaList();
+        try
+        {
+            schemas.load_directory("/usr/share/glib-2.0/schemas");
+        } catch (Error e) {
+            warning("Failed to parse schemas: %s", e.message);
+        }
+
+        Gtk.ScrolledWindow scroll = new Gtk.ScrolledWindow(null, null);
+        scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
+        scroll.show();
+        hbox.pack_start(scroll, false, false, 0);
 
-        tree_view = new Gtk.TreeView();
-        tree_view.set_headers_visible(false);
-        tree_view.set_model(model);
-        tree_view.insert_column_with_attributes(-1, "Key", new Gtk.CellRendererText(), "text", 0, null);
-        tree_view.get_selection().changed.connect(key_selected_cb);
-        tree_view.show();
-        hbox.pack_start(tree_view, false, false, 0);
+        dir_tree_view = new DConfDirView();
+        dir_tree_view.set_model(model);
+        dir_tree_view.get_selection().changed.connect(dir_selected_cb); // FIXME: Put in view
+        dir_tree_view.show();
+        scroll.add(dir_tree_view);
 
         Gtk.VBox vbox = new Gtk.VBox(false, 6);
         vbox.show();
         hbox.pack_start(vbox, true, true, 0);
+        
+        scroll = new Gtk.ScrolledWindow(null, null);
+        scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
+        scroll.show();
+        vbox.pack_start(scroll, true, true, 0);
+
+        key_tree_view = new DConfKeyView();
+        key_tree_view.show();
+        key_tree_view.get_selection().changed.connect(key_selected_cb);
+        scroll.add(key_tree_view);
+
+        Gtk.Table schema_table = new Gtk.Table(0, 2, false);
+        schema_table.set_row_spacings(6);
+        schema_table.set_col_spacings(6);
+        schema_table.show();
+        vbox.pack_start(schema_table, false, true, 0);
+
+        schema_label = add_row(schema_table, 0, "Schema:");
+        summary_label = add_row(schema_table, 1, "Summary:");
+        description_label = add_row(schema_table, 2, "Description:");
+        type_label = add_row(schema_table, 3, "Type:");
+        default_label = add_row(schema_table, 4, "Default:");
+
+        /* Always select something */
+        Gtk.TreeIter iter;
+        if (model.get_iter_first(out iter))
+            dir_tree_view.get_selection().select_iter(iter);
+    }
+    
+    private Gtk.Label add_row(Gtk.Table table, int row, string label)
+    {
+        Gtk.Label name_label, value_label;
 
-        name_label = new Gtk.Label("");
+        name_label = new Gtk.Label(label);
         name_label.set_alignment(0.0f, 0.5f);
-        name_label.show();
-        vbox.pack_start(name_label, false, true, 0);
+        table.attach(name_label, 0, 1, row, row+1, Gtk.AttachOptions.FILL, Gtk.AttachOptions.FILL, 0, 0);
 
         value_label = new Gtk.Label("");
         value_label.set_alignment(0.0f, 0.5f);
+        table.attach_defaults(value_label, 1, 2, row, row+1);
+        
+        name_label.show();
         value_label.show();
-        vbox.pack_start(value_label, false, true, 0);
+
+        return value_label;
     }
     
-    private string? get_selected_key()
+    private void dir_selected_cb()
     {
+        KeyModel? key_model = null;
+
         Gtk.TreeIter iter;
-        if (!tree_view.get_selection().get_selected(null, out iter))
-            return null;
+        if (dir_tree_view.get_selection().get_selected(null, out iter))
+            key_model = model.get_directory(iter).key_model;
+
+        key_tree_view.set_model(key_model);
 
-        string key;
-        model.get(iter, 1, out key, -1);
-        return key;
+        /* Always select something */
+        if (key_model != null && key_model.get_iter_first(out iter))
+            key_tree_view.get_selection().select_iter(iter);
     }
-    
-    private void key_selected_cb()
+
+    private Key? get_selected_key()
     {
-        string? key = get_selected_key();
-        if (key == null)
+        Gtk.TreeIter iter;
+        Gtk.TreeModel model;
+        if (key_tree_view.get_selection().get_selected(out model, out iter))
         {
-            name_label.set_text("");
-            value_label.set_text("");
+            KeyModel key_model = (KeyModel) model;
+            return key_model.get_key(iter);
         }
         else
+            return null;
+    }
+
+    private string type_to_description(string type)
+    {
+        switch(type)
         {
-            GLib.Variant value = client.read(key);
-            name_label.set_text(key);
-            value_label.set_text(value == null ? "(unset)" : value.print(false));
+        case "i":
+           return "Integer";
+        case "b":
+           return "Boolean";
+        case "s":
+           return "String";
+        default:
+           return type;
         }
     }
+
+    private void key_selected_cb()
+    {
+        Key? key = get_selected_key();
+        string schema_name = "", summary = "", description = "", type = "", default_value = "";
+
+        if (key != null)
+        {
+            SchemaKey? schema = schemas.keys.get(key.full_name);
+            if (schema != null)
+            {
+                schema_name = schema.schema.id;
+                if (schema.summary != null)
+                    summary = schema.summary;
+                if (schema.description != null)
+                    description = schema.description;
+                type = type_to_description(schema.type);
+                default_value = schema.default_value;
+            }
+            else
+            {
+                schema_name = "No schema";
+            }
+        }
+
+        schema_label.set_text(schema_name);
+        summary_label.set_text(summary);
+        description_label.set_text(description);
+        type_label.set_text(type);
+        default_label.set_text(default_value);
+    }
 }
 
 class ConfigurationEditor
 {
-    private DConf.Client client;
     private EditorWindow window;
     
     public ConfigurationEditor()
     {
-        client = new DConf.Client("", true, null, null);
-        window = new EditorWindow(client);
+        window = new EditorWindow();
         window.destroy.connect(Gtk.main_quit);
         
-        read_keys("/");
-
         window.show();
     }
 
-    private void read_keys(string parent)
-    {
-        string[] keys = client.list(parent);
-        for (int i = 0; i < keys.length; i++)
-        {
-            if (DConf.is_rel_dir(keys[i]))
-                read_keys(parent + keys[i]);
-            else
-                window.model.add_key(parent + keys[i]);
-        }
-    }
-
     public static int main(string[] args)
     {
         Gtk.init(ref args);
diff --git a/editor/dconf-model.vala b/editor/dconf-model.vala
new file mode 100644
index 0000000..f17daff
--- /dev/null
+++ b/editor/dconf-model.vala
@@ -0,0 +1,428 @@
+public class Key : GLib.Object
+{
+    private DConf.Client client;
+
+    public string name;
+    public string full_name;
+
+    public Key? next;
+    
+    private Variant _value;
+    public Variant value
+    {
+        get { update_value(); return _value; }
+        set {
+            _value = value;
+            try
+            {
+                client.write(full_name, value, 0, null);
+            }
+            catch (GLib.Error e)
+            {
+            }
+        }
+    }
+
+    public Key(DConf.Client client, string name, string full_name)
+    {
+        this.client = client;
+        this.name = name;
+        this.full_name = full_name;
+    }
+    
+    private void update_value()
+    {
+        _value = client.read(full_name);
+    }
+}
+
+public class Directory : GLib.Object
+{
+    private DConf.Client client;
+
+    public string name;
+    public string full_name;
+
+    public Directory? parent;
+    
+    private KeyModel _key_model;
+    public KeyModel key_model
+    {
+        get { update_children(); if (_key_model == null) _key_model = new KeyModel(this); return _key_model; }
+        private set {}
+    }
+
+    private int _n_children;
+    public int n_children
+    {
+        get { update_children(); return _n_children; }
+        private set { _n_children = value; }
+    }
+    public Directory? _child;
+    public Directory? child
+    {
+        get { update_children(); return _child; }
+        private set { _child = value; }
+    }
+    public Directory? next;
+
+    private int _n_keys;
+    public int n_keys
+    {
+        get { update_children(); return _n_keys; }
+        private set { _n_keys = value; }
+    }
+    private Key? _keys;
+    public Key? keys
+    {
+        get { update_children(); return _keys; }
+        private set { _keys = value; }
+    }
+
+    private bool have_children;
+    
+    public Directory(DConf.Client client, string name, string full_name, Directory? parent = null)
+    {
+        this.client = client;
+        this.parent = parent;
+        this.name = name;
+        this.full_name = full_name;
+    }
+
+    private void update_children()
+    {
+        if (have_children)
+            return;
+        have_children = true;
+
+        Directory? last_directory = null;
+        Key? last_key = null;
+        string[] items = client.list(full_name);
+        _n_children = 0;
+        _n_keys = 0;
+        for (int i = 0; i < items.length; i++)
+        {
+            string item_name = full_name + items[i];
+
+            if (DConf.is_dir(item_name))
+            {
+                string dir_name = items[i][0:-1];
+
+                Directory directory = new Directory(client, dir_name, item_name, this);
+                if (last_directory == null)
+                   child = directory;
+                else
+                   last_directory.next = directory;
+                last_directory = directory;
+                _n_children++;
+            }
+            else
+            {
+                Key key = new Key(client, items[i], item_name);
+                if (last_key == null)
+                    keys = key;
+                else
+                    last_key.next = key;
+                last_key = key;
+                _n_keys++;
+            }
+        }
+    }
+}
+
+public class KeyModel: GLib.Object, Gtk.TreeModel/*, Gtk.TreeSortable*/
+{
+    private Directory directory;
+
+    construct {}
+
+    public KeyModel(Directory directory)
+    {
+        this.directory = directory;
+    }
+
+    public Gtk.TreeModelFlags get_flags()
+    {
+        return Gtk.TreeModelFlags.LIST_ONLY;
+    }
+
+    public int get_n_columns()
+    {
+        return 3;
+    }
+
+    public Type get_column_type(int index)
+    {
+        if (index == 0)
+            return typeof(Key);
+        else
+            return typeof(string);
+    }
+    
+    private void set_iter(out Gtk.TreeIter iter, Key key)
+    {
+        iter.stamp = 0;
+        iter.user_data = key;
+        iter.user_data2 = key;
+        iter.user_data3 = key;
+    }
+
+    public Key get_key(Gtk.TreeIter iter)
+    {
+        return (Key)iter.user_data;
+    }
+
+    public bool get_iter(out Gtk.TreeIter iter, Gtk.TreePath path)
+    {
+        if (path.get_depth() != 1)
+            return false;
+
+        return iter_nth_child(out iter, null, path.get_indices()[0]);
+    }
+
+    public Gtk.TreePath get_path(Gtk.TreeIter iter)
+    {
+        Gtk.TreePath path = new Gtk.TreePath();
+        Key key = directory.keys;
+        int index = 0;
+        while (key != get_key(iter))
+        {
+            key = key.next;
+            index++;
+        }
+        path.append_index(index);
+        return path;
+    }
+
+    public void get_value(Gtk.TreeIter iter, int column, out Value value)
+    {
+        if (column == 0)
+            value = get_key(iter);
+        else if (column == 1)
+            value = get_key(iter).name;
+        else
+            value = get_key(iter).value.print(false);
+    }
+
+    public bool iter_next(ref Gtk.TreeIter iter)
+    {
+        Key key = get_key(iter);
+        if (key.next == null)
+            return false;
+        set_iter(out iter, key.next);
+        return true;
+    }
+
+    public bool iter_children(out Gtk.TreeIter iter, Gtk.TreeIter? parent)
+    {
+        if (parent != null || directory.n_keys == 0)
+            return false;
+        set_iter(out iter, directory.keys);
+        return true;
+    }
+
+    public bool iter_has_child(Gtk.TreeIter iter)
+    {
+        return false;
+    }
+
+    public int iter_n_children(Gtk.TreeIter? iter)
+    {
+        if (iter == null)
+            return directory.n_keys;
+        else
+            return 0;
+    }
+
+    public bool iter_nth_child(out Gtk.TreeIter iter, Gtk.TreeIter? parent, int n)
+    {
+        if (parent != null)
+            return false;
+
+        Key key = directory.keys;
+        if (key == null)
+            return false;
+        for (int i = 0; i < n; i++)
+        {
+           key = key.next;
+           if (key == null)
+               return false;
+        }
+
+        set_iter(out iter, key);
+        return true;
+    }
+
+    public bool iter_parent(out Gtk.TreeIter iter, Gtk.TreeIter child)
+    {
+        return false;
+    }
+
+    public void ref_node(Gtk.TreeIter iter)
+    {
+        get_key(iter).ref();
+    }
+
+    public void unref_node(Gtk.TreeIter iter)
+    {
+        get_key(iter).unref();
+    }
+}
+
+public class SettingsModel: GLib.Object, Gtk.TreeModel
+{
+    private DConf.Client client;
+    private Directory root;
+
+    construct {}
+
+    public SettingsModel()
+    {
+        client = new DConf.Client("", true, null, null);
+        root = new Directory(client, "/", "/");
+    }
+
+    public Gtk.TreeModelFlags get_flags()
+    {
+        return 0;
+    }
+
+    public int get_n_columns()
+    {
+        return 2;
+    }
+
+    public Type get_column_type(int index)
+    {
+        if (index == 0)
+            return typeof(Directory);
+        else
+            return typeof(string);
+    }
+    
+    private void set_iter(out Gtk.TreeIter iter, Directory directory)
+    {
+        iter.stamp = 0;
+        iter.user_data = directory;
+        iter.user_data2 = directory;
+        iter.user_data3 = directory;
+    }
+
+    public Directory get_directory(Gtk.TreeIter? iter)
+    {
+        if (iter == null)
+            return root;
+        else
+            return (Directory)iter.user_data;
+    }
+
+    public bool get_iter(out Gtk.TreeIter iter, Gtk.TreePath path)
+    {
+        if (!iter_nth_child(out iter, null, path.get_indices()[0]))
+            return false;
+
+        for (int i = 1; i < path.get_depth(); i++)
+        {
+            Gtk.TreeIter parent = iter;
+            if (!iter_nth_child(out iter, parent, path.get_indices()[i]))
+                return false;
+        }
+
+        return true;
+    }
+
+    public Gtk.TreePath get_path(Gtk.TreeIter iter)
+    {
+        Gtk.TreePath path;
+        Gtk.TreeIter parent;
+        if (iter_parent(out parent, iter))
+            path = get_path(parent);
+        else
+            path = new Gtk.TreePath();
+
+        int index = 0;
+        Directory directory = get_directory(iter);
+        Directory d = directory.parent.child;
+        while (d != directory)
+        {
+            d = d.next;
+            index++;
+        }
+        path.append_index(index);
+
+        return path;
+    }
+
+    public void get_value(Gtk.TreeIter iter, int column, out Value value)
+    {
+        if (column == 0)
+            value = get_directory(iter);
+        else
+            value = get_directory(iter).name;
+    }
+
+    public bool iter_next(ref Gtk.TreeIter iter)
+    {
+        Directory directory = get_directory(iter);
+        if (directory.next == null)
+            return false;
+        set_iter(out iter, directory.next);
+        return true;
+    }
+
+    public bool iter_children(out Gtk.TreeIter iter, Gtk.TreeIter? parent)
+    {
+        Directory directory = get_directory(parent);
+        if (directory.n_children == 0)
+            return false;
+        set_iter(out iter, directory.child);
+        return true;
+    }
+
+    public bool iter_has_child(Gtk.TreeIter iter)
+    {
+        return get_directory(iter).n_children > 0;
+    }
+
+    public int iter_n_children(Gtk.TreeIter? iter)
+    {
+        return get_directory(iter).n_children;
+    }
+
+    public bool iter_nth_child(out Gtk.TreeIter iter, Gtk.TreeIter? parent, int n)
+    {
+        Directory directory = get_directory(parent);
+
+        directory = directory.child;
+        if (directory == null)
+            return false;       
+        for (int i = 0; i < n; i++)
+        {
+           directory = directory.next;
+           if (directory == null)
+               return false;
+        }
+
+        set_iter(out iter, directory);
+        return true;
+    }
+
+    public bool iter_parent(out Gtk.TreeIter iter, Gtk.TreeIter child)
+    {
+        Directory directory = get_directory(child);
+        if (directory.parent == root)
+            return false;
+        set_iter(out iter, directory.parent);
+        return true;
+    }
+
+    public void ref_node(Gtk.TreeIter iter)
+    {
+        get_directory(iter).ref();
+    }
+
+    public void unref_node(Gtk.TreeIter iter)
+    {
+        get_directory(iter).unref();
+    }
+}
diff --git a/editor/dconf-schema.vala b/editor/dconf-schema.vala
new file mode 100644
index 0000000..ee17e02
--- /dev/null
+++ b/editor/dconf-schema.vala
@@ -0,0 +1,147 @@
+using Gee;
+
+public class SchemaKey
+{
+    public Schema schema;
+    public string name;
+    public string type;
+    public string default_value;
+    public string? summary;
+    public string? description;
+    public string? gettext_domain;
+
+    public SchemaKey(Xml.Node* node, Schema schema, string? gettext_domain)
+    {
+        this.schema = schema;
+        this.gettext_domain = gettext_domain;
+
+        for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next)
+        {
+            if (prop->name == "name")
+                name = prop->children->content;
+            else if (prop->name == "type")
+                type = prop->children->content;
+            //else
+            //    ?
+        }
+
+        //if (name == null || type == null)
+        //    ?
+
+        for (Xml.Node* child = node->children; child != null; child = child->next)
+        {
+            if (child->name == "default")
+               default_value = child->children->content;
+            else if (child->name == "summary")
+               summary = child->children->content;
+            else if (child->name == "description")
+               description = child->children->content;
+            //else
+            //   ?
+        }
+        
+        //if (default_value == null)
+        //    ?
+    }
+}
+
+public class Schema
+{
+    public SchemaList list;
+    public string id;
+    public string? path;
+    public HashMap<string, SchemaKey> keys = new HashMap<string, SchemaKey>();
+
+    public Schema(SchemaList list, Xml.Node* node, string? gettext_domain)
+    {
+        this.list = list;
+
+        for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next)
+        {
+            if (prop->name == "id")
+                id = prop->children->content;
+            else if (prop->name == "path")
+                path = prop->children->content; // FIXME: Does the path have to end with '/'?
+            else if (prop->name == "gettext-domain")
+                gettext_domain = prop->children->content;
+            //else
+            //    ?
+        }
+
+        //if (id == null)
+        //    ?
+
+        for (Xml.Node* child = node->children; child != null; child = child->next)
+        {
+            if (child->name != "key")
+               continue;
+            SchemaKey key = new SchemaKey(child, this, gettext_domain);
+            keys.set(key.name, key);
+        }
+    }
+}
+
+public class SchemaList
+{
+    public ArrayList<Schema> schemas = new ArrayList<Schema>();
+    public HashMap<string, SchemaKey> keys = new HashMap<string, SchemaKey>();
+
+    public void parse_file(string path)
+    {
+        Xml.Doc* doc = Xml.Parser.parse_file(path);
+        if (doc == null)
+            return;
+
+        Xml.Node* root = doc->get_root_element();
+        if (root == null)
+            return;
+        if (root->name != "schemalist")
+            return;
+
+        string? gettext_domain = null;
+        for (Xml.Attr* prop = root->properties; prop != null; prop = prop->next)
+        {
+            if (prop->name == "gettext-domain")
+                gettext_domain = prop->children->content;
+        }
+
+        for (Xml.Node* node = root->children; node != null; node = node->next)
+        {
+            if (node->name != "schema")
+               continue;
+
+            Schema schema = new Schema(this, node, gettext_domain);
+            schemas.add(schema);
+            if (schema.path == null)
+            {
+                // FIXME: What to do here?
+                continue;
+            }
+            
+            foreach (var key in schema.keys.values)
+            {
+                string full_name = schema.path + key.name;
+                keys.set(full_name, key);
+            }
+        }
+
+        delete doc;
+    }
+
+    public void load_directory(string dir) throws Error
+    {
+        File directory = File.new_for_path(dir);
+        var i = directory.enumerate_children (FILE_ATTRIBUTE_STANDARD_NAME, 0, null);
+        FileInfo info;
+        while ((info = i.next_file (null)) != null) {
+            string name = info.get_name();
+
+            if (!name.has_suffix(".gschema.xml"))
+                continue;
+
+            string path = Path.build_filename(dir, name, null);
+            debug("Loading schema: %s", path);
+            parse_file(path);
+        }
+    }
+}
diff --git a/editor/dconf-view.vala b/editor/dconf-view.vala
new file mode 100644
index 0000000..8ec36e8
--- /dev/null
+++ b/editor/dconf-view.vala
@@ -0,0 +1,201 @@
+private class KeyValueRenderer: Gtk.CellRenderer
+{
+    private Gtk.CellRendererText text_renderer;
+    private Gtk.CellRendererToggle toggle_renderer;
+    private Gtk.CellEditable cell_editor;
+
+    private Key _key;
+    public Key key
+    {
+        get { return _key; }
+        set
+        {
+            _key = value;
+            if (_key.value.is_of_type(VariantType.BOOLEAN))
+                mode = Gtk.CellRendererMode.ACTIVATABLE;
+            else if (_key.value.is_of_type(VariantType.STRING) ||
+                     _key.value.is_of_type(VariantType.BYTE) ||
+                     _key.value.is_of_type(VariantType.INT16) ||
+                     _key.value.is_of_type(VariantType.UINT16) ||
+                     _key.value.is_of_type(VariantType.INT32) ||
+                     _key.value.is_of_type(VariantType.UINT32) ||
+                     _key.value.is_of_type(VariantType.INT64) ||
+                     _key.value.is_of_type(VariantType.UINT64) ||
+                     _key.value.is_of_type(VariantType.DOUBLE))
+                mode = Gtk.CellRendererMode.EDITABLE;
+            else
+                mode = Gtk.CellRendererMode.INERT;
+        }
+    }
+    
+    construct
+    {
+        text_renderer = new Gtk.CellRendererText();
+        toggle_renderer = new Gtk.CellRendererToggle();
+        toggle_renderer.xalign = 0f;
+    }
+
+    private Gtk.CellRenderer get_renderer()
+    {
+        if (key.value.is_of_type(VariantType.BOOLEAN))
+        {
+            toggle_renderer.active = key.value.get_boolean();
+            return toggle_renderer;
+        }
+        else
+        {
+            text_renderer.text = key.value.print(false);
+            return text_renderer;
+        }
+    }
+
+    public override void get_size(Gtk.Widget     widget,
+                                  Gdk.Rectangle? cell_area,
+                                  out int        x_offset,
+                                  out int        y_offset,
+                                  out int        width,
+                                  out int        height)
+    {
+        get_renderer().get_size(widget, cell_area, out x_offset, out y_offset, out width, out height);
+    }
+
+    public override void render(Gdk.Window    window,
+                                Gtk.Widget    widget,
+                                Gdk.Rectangle background_area,
+                                Gdk.Rectangle cell_area,
+                                Gdk.Rectangle expose_area,
+                                Gtk.CellRendererState flags)
+    {
+        get_renderer().render(window, widget, background_area, cell_area, expose_area, flags);
+    }
+
+    public override bool activate(Gdk.Event event,
+                                  Gtk.Widget widget,
+                                  string path,
+                                  Gdk.Rectangle background_area,
+                                  Gdk.Rectangle cell_area,
+                                  Gtk.CellRendererState flags)
+    {
+        key.value = new Variant.boolean(!key.value.get_boolean());
+        return true;
+    }
+    
+    private void editing_done_cb(Gtk.CellEditable cell_editable)
+    {
+        cell_editor = null;
+        // FIXME: Appears to be broken
+        /*if (cell_editable.editing_canceled)
+            return;*/
+
+        if (key.value.is_of_type(VariantType.STRING))
+        {
+            var entry = (Gtk.Entry)cell_editable;
+            key.value = new Variant.string(entry.get_text());
+            return;
+        }
+
+        var spin = (Gtk.SpinButton)cell_editable;
+        if (key.value.is_of_type(VariantType.BYTE))
+            key.value = new Variant.byte((uchar)spin.get_value_as_int());
+        else if (key.value.is_of_type(VariantType.INT16))
+            key.value = new Variant.int16((int16)spin.get_value_as_int());
+        else if (key.value.is_of_type(VariantType.UINT16))
+            key.value = new Variant.uint16((uint16)spin.get_value_as_int());
+        else if (key.value.is_of_type(VariantType.INT32))
+            key.value = new Variant.int32(spin.get_value_as_int());
+        else if (key.value.is_of_type(VariantType.UINT32))
+            key.value = new Variant.uint32(spin.get_value_as_int());
+        else if (key.value.is_of_type(VariantType.INT64))
+            key.value = new Variant.int64(spin.get_value_as_int());
+        else if (key.value.is_of_type(VariantType.UINT64))
+            key.value = new Variant.uint64(spin.get_value_as_int());
+        else if (key.value.is_of_type(VariantType.DOUBLE))
+            key.value = new Variant.double(spin.get_value());
+    }
+
+    public override unowned Gtk.CellEditable start_editing(Gdk.Event event,
+                                                           Gtk.Widget widget,
+                                                           string path,
+                                                           Gdk.Rectangle background_area,
+                                                           Gdk.Rectangle cell_area,
+                                                           Gtk.CellRendererState flags)
+    {
+        if (key.value.is_of_type(VariantType.STRING))
+        {
+            var entry = new Gtk.Entry();
+            entry.set_text(_key.value.get_string());
+            cell_editor = entry;
+        }
+        else if (key.value.is_of_type(VariantType.BYTE))
+        {
+            var spin = new Gtk.SpinButton.with_range(0, 255, 1);
+            spin.set_value(key.value.get_byte());
+            cell_editor = spin;
+        }
+        else if (key.value.is_of_type(VariantType.INT16))
+        {
+            var spin = new Gtk.SpinButton.with_range(int16.MIN, int16.MAX, 1);
+            spin.set_value(key.value.get_int16());
+            cell_editor = spin;
+        }
+        else if (key.value.is_of_type(VariantType.UINT16))
+        {
+            var spin = new Gtk.SpinButton.with_range(0, uint16.MAX, 1);
+            spin.set_value(key.value.get_uint16());
+            cell_editor = spin;
+        }
+        else if (key.value.is_of_type(VariantType.INT32))
+        {
+            var spin = new Gtk.SpinButton.with_range(int32.MIN, int32.MAX, 1);
+            spin.set_value(key.value.get_int32());
+            cell_editor = spin;
+        }
+        else if (key.value.is_of_type(VariantType.UINT32))
+        {
+            var spin = new Gtk.SpinButton.with_range(0, uint32.MAX, 1);
+            spin.set_value(key.value.get_uint32());
+            cell_editor = spin;
+        }
+        else if (key.value.is_of_type(VariantType.INT64))
+        {
+            var spin = new Gtk.SpinButton.with_range(int64.MIN, int64.MAX, 1);
+            spin.set_value(key.value.get_int64());
+            cell_editor = spin;
+        }
+        else if (key.value.is_of_type(VariantType.UINT64))
+        {
+            var spin = new Gtk.SpinButton.with_range(0, uint64.MAX, 1);
+            spin.set_value(key.value.get_uint64());
+            cell_editor = spin;
+        }
+        else if (key.value.is_of_type(VariantType.DOUBLE))
+        {
+            var spin = new Gtk.SpinButton.with_range(double.MIN, double.MAX, 1);
+            spin.set_value(key.value.get_uint64());
+            cell_editor = spin;
+        }
+        cell_editor.editing_done.connect(editing_done_cb);
+        cell_editor.show();
+        return cell_editor;
+    }
+}
+
+public class DConfDirView : Gtk.TreeView
+{
+    public DConfDirView()
+    {
+        set_headers_visible(false);
+        insert_column_with_attributes(-1, "Key", new Gtk.CellRendererText(), "text", 1, null);
+    }
+}
+
+public class DConfKeyView : Gtk.TreeView
+{
+    public DConfKeyView()
+    {
+        var column = new Gtk.TreeViewColumn.with_attributes("Name", new Gtk.CellRendererText(), "text", 1, null);
+        /*column.set_sort_column_id(1);*/
+        append_column(column);
+        insert_column_with_attributes(-1, "Value", new KeyValueRenderer(), "key", 0, null);
+    }
+}
diff --git a/editor/dconf.vapi b/editor/dconf.vapi
index 1a1f15b..52a49b4 100644
--- a/editor/dconf.vapi
+++ b/editor/dconf.vapi
@@ -19,11 +19,11 @@ namespace DConf {
 		public bool watch (string name) throws GLib.Error;
 		public async bool watch_async (string name);
 		public bool watch_finish (GLib.AsyncResult _result);
-		public bool write (string key, GLib.Variant value, uint64 sequence, GLib.Cancellable cancellable) throws GLib.Error;
-		public async bool write_async (string key, GLib.Variant value, GLib.Cancellable cancellable) throws GLib.Error;
+		public bool write (string key, GLib.Variant value, uint64 sequence, GLib.Cancellable? cancellable) throws GLib.Error;
+		public async bool write_async (string key, GLib.Variant value, GLib.Cancellable? cancellable) throws GLib.Error;
 		public bool write_finish (GLib.AsyncResult _result, uint64 sequence) throws GLib.Error;
-		public bool write_many (string prefix, string keys, out unowned GLib.Variant values, uint64 sequence, GLib.Cancellable cancellable) throws GLib.Error;
-		public async bool write_many_async (string prefix, string keys, out unowned GLib.Variant values, GLib.Cancellable cancellable) throws GLib.Error;
+		public bool write_many (string prefix, string keys, out unowned GLib.Variant values, uint64 sequence, GLib.Cancellable? cancellable) throws GLib.Error;
+		public async bool write_many_async (string prefix, string keys, out unowned GLib.Variant values, GLib.Cancellable? cancellable) throws GLib.Error;
 		public bool write_many_finish (GLib.AsyncResult _result, uint64 sequence) throws GLib.Error;
 	}
 	[CCode (cheader_filename = "dconf.h")]



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