From 17d7c024d2d7aee48fadb811ce8c77cb20848b65 Mon Sep 17 00:00:00 2001 From: Thomas Liebetraut Date: Wed, 22 May 2013 01:55:30 +0200 Subject: [PATCH] Write support in prefs.js --- prefs.js | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/prefs.js b/prefs.js index 76c97cc..b2047ca 100644 --- a/prefs.js +++ b/prefs.js @@ -68,24 +68,44 @@ const TeaTimePrefsWidget = new Lang.Class({ this._tealist.set_column_types([GObject.TYPE_STRING, GObject.TYPE_INT, Gtk.Adjustment]); this._settings = getSettings(); + this._inhibitUpdate = true; this._settings.connect("changed::" + SETTINGS_TEALIST_KEY, Lang.bind(this, this._refresh)); this._initWindow(); this.vexpand = true; + this._inhibitUpdate = false; this._refresh(); + this._tealist.connect("row-changed", Lang.bind(this, this._save)); + this._tealist.connect("row-deleted", Lang.bind(this, this._save)); }, _initWindow: function() { this.treeview = new Gtk.TreeView({model: this._tealist, expand: true}); + this.treeview.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE); this.add(this.treeview); let teaname = new Gtk.TreeViewColumn({ title: _("Tea"), expand: true }); let renderer = new Gtk.CellRendererText({ editable: true }); + // When the renderer is done editing it's value, we first write + // the new value to the view's model, i.e. this._tealist. + // This makes life a little harder due to chaining of callbacks + // and the need for this._inhibitUpdate, but it feels a lot cleaner + // when the UI does not know about the config storage backend. + renderer.connect("edited", Lang.bind(this, function(renderer, pathString, newValue) { + let [store, iter] = this._tealist.get_iter(Gtk.TreePath.new_from_string(pathString)); + this._tealist.set(iter, [0], [newValue]); + })); teaname.pack_start(renderer, true); teaname.add_attribute(renderer, "text", 0); this.treeview.append_column(teaname); - let steeptime = new Gtk.TreeViewColumn({ title: _("Steep time") }); + let steeptime = new Gtk.TreeViewColumn({ title: _("Steep time"), min_width: 150 }); let spinrenderer = new Gtk.CellRendererSpin({ editable: true }); + // See comment above. + spinrenderer.connect("edited", Lang.bind(this, function(renderer, pathString, newValue) { + let [store, iter] = this._tealist.get_iter(Gtk.TreePath.new_from_string(pathString)); + this._tealist.set(iter, [1], [parseInt(newValue)]); + })); + steeptime.pack_start(spinrenderer, true); steeptime.add_attribute(spinrenderer, "adjustment", 2); steeptime.add_attribute(spinrenderer, "text", 1); @@ -96,14 +116,24 @@ const TeaTimePrefsWidget = new Lang.Class({ this.toolbar.get_style_context().add_class("inline-toolbar"); this.add(this.toolbar); this.addButton = new Gtk.ToolButton({ icon_name: "list-add-symbolic", use_action_appearance: false }); + this.addButton.connect("clicked", Lang.bind(this, this._addTea)); this.toolbar.insert(this.addButton, -1); this.removeButton = new Gtk.ToolButton({ icon_name: "list-remove-symbolic", use_action_appearance: false }); + this.removeButton.connect("clicked", Lang.bind(this, this._removeSelectedTea)); this.toolbar.insert(this.removeButton, -1); }, _refresh: function() { - this._tealist.clear(); - + // don't update the model if someone else is messing with the backend + if (this._inhibitUpdate) + return; + let list = this._settings.get_value(SETTINGS_TEALIST_KEY); + + // stop everyone from reacting to the changes we are about to produce + // in the model + this._inhibitUpdate = true; + + this._tealist.clear(); for (let i = 0; i < list.n_children(); ++i) { let item = list.get_child_value(i); let teaname = item.get_child_value(0).get_string()[0]; @@ -112,6 +142,51 @@ const TeaTimePrefsWidget = new Lang.Class({ let adj = new Gtk.Adjustment({ lower: 1, step_increment: 1, upper: 65535, value: time }); this._tealist.set(this._tealist.append(), [0, 1, 2], [teaname, time, adj]); } + + this._inhibitUpdate = false; + }, + _addTea: function() { + let adj = new Gtk.Adjustment({ lower: 1, step_increment: 1, upper: 65535, value: 1 }); + let item = this._tealist.append(); + this._tealist.set(item, [0, 1, 2], ["", 1, adj]); + this.treeview.set_cursor(this._tealist.get_path(item), this.treeview.get_column(0), true); + }, + _removeSelectedTea: function() { + let [selection, store] = this.treeview.get_selection().get_selected_rows(); + let iters = []; + for (let i = 0; i < selection.length; ++i) { + let [isSet, iter] = store.get_iter(selection[i]); + if (isSet) { + iters.push(iter); + } + } + // it's ok not to inhibit updates here as remove != change + iters.forEach(function(value, index, array) { + store.remove(value) } + ); + + this.treeview.get_selection().unselect_all(); + }, + _save: function(store, path_, iter_) { + // don't update the backend if someone else is messing with the model + if (this._inhibitUpdate) + return; + + let values = []; + this._tealist.foreach(function(store, path, iter) { + values.push(GLib.Variant.new_dict_entry( + GLib.Variant.new_string(store.get_value(iter, 0)), + GLib.Variant.new_uint32(store.get_value(iter, 1)))) + }); + let settingsValue = GLib.Variant.new_array(GLib.VariantType.new("{su}"), values); + + // all changes have happened through the UI, we can safely + // disable updating it here to avoid an infinite loop + this._inhibitUpdate = true; + + this._settings.set_value(SETTINGS_TEALIST_KEY, settingsValue); + + this._inhibitUpdate = false; } });