[model] Add work-in-progress GtkTreeModel implementation
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [model] Add work-in-progress GtkTreeModel implementation
- Date: Thu, 11 Mar 2010 06:35:40 +0000 (UTC)
commit f4b5b329e0265193234ac5ace3f32289e5495e35
Author: Ryan Lortie <desrt desrt ca>
Date: Thu Mar 11 01:34:51 2010 -0500
Add work-in-progress GtkTreeModel implementation
Makefile.am | 2 +-
configure.ac | 4 +
gtk/.gitignore | 3 +
gtk/Makefile.am | 8 ++
gtk/fs.vala | 173 ++++++++++++++++++++++++++++
gtk/mg.vala | 24 ++++
gtk/model-gtk.pc.in | 11 ++
gtk/model-gtk.vala | 319 +++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 543 insertions(+), 1 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 93547f5..ddccabc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,2 +1,2 @@
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc
-SUBDIRS = model docs
+SUBDIRS = model gtk docs
diff --git a/configure.ac b/configure.ac
index 12ca73b..47f4c0f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,11 +2,13 @@ AC_INIT(model, 0.2.0.1)
AM_INIT_AUTOMAKE
AM_SILENT_RULES([yes])
AC_PROG_LIBTOOL
+AM_PROG_VALAC
GTK_DOC_CHECK
AC_PROG_CC
PKG_CHECK_MODULES(introspection, gobject-introspection-1.0 >= 0.6.3)
PKG_CHECK_MODULES(gobject, gobject-2.0)
+PKG_CHECK_MODULES(gtk, gtk+-2.0)
AC_SUBST(girdir, ${datadir}/gir-1.0)
AC_SUBST(typelibdir, ${libdir}/girepository-1.0)
@@ -17,6 +19,8 @@ AC_SUBST(vapidir, ${datadir}/vala/vapi)
AC_OUTPUT([
model/Makefile
model/model.pc
+ gtk/Makefile
+ gtk/model-gtk.pc
docs/reference/model-docs.xml
docs/reference/Makefile
docs/Makefile
diff --git a/gtk/.gitignore b/gtk/.gitignore
new file mode 100644
index 0000000..7ce0e80
--- /dev/null
+++ b/gtk/.gitignore
@@ -0,0 +1,3 @@
+*.stamp
+*.c
+model-gtk
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
new file mode 100644
index 0000000..9c6fba4
--- /dev/null
+++ b/gtk/Makefile.am
@@ -0,0 +1,8 @@
+bin_PROGRAMS = model-gtk
+
+AM_VALAFLAGS = --vapidir ../model --pkg model --pkg gtk+-2.0 --pkg=posix
+AM_CFLAGS = $(gtk_CFLAGS) -I../model -Wno-bad-function-cast
+model_gtk_LDADD = $(gtk_LIBS) ../model/libmodel.la
+
+model_gtk_SOURCES = model-gtk.vala fs.vala mg.vala
+pkgconfig_DATA = model-gtk.pc
diff --git a/gtk/fs.vala b/gtk/fs.vala
new file mode 100644
index 0000000..23ab72e
--- /dev/null
+++ b/gtk/fs.vala
@@ -0,0 +1,173 @@
+namespace FS {
+ public class File : Model.SimpleDictionary {
+ internal string name;
+ FileMonitor? monitor;
+ GLib.File file;
+ ulong watch_id;
+
+ static void monitor_changed (FileMonitor monitor, GLib.File file,
+ GLib.File? other_file,
+ FileMonitorEvent event, File this_file) {
+ assert (this_file.monitor == monitor);
+
+ if ((event == FileMonitorEvent.CHANGED ||
+ event == FileMonitorEvent.ATTRIBUTE_CHANGED) &&
+ this_file.file.equal (file)) {
+ this_file.update_info.begin ();
+ }
+ }
+
+ async void update_info () {
+ try {
+ var info = yield file.query_info_async ("standard::size," +
+ "standard::content-type," +
+ "owner::user",
+ 0, 0, null);
+
+ set_integer ("size", (int) info.get_size (), true);
+ set_string ("type", info.get_content_type (), true);
+ set_string ("user", info.get_attribute_string ("owner::user"), true);
+
+ if (info.get_content_type () == "inode/directory") {
+ set ("contents", new Directory (file), true);
+ } else {
+ set ("contents", null, true);
+ }
+ } catch (Error e) {
+ /* file probably just disappeared in the meantime. do nothing */
+ }
+ }
+
+ ~File () {
+ if (watch_id != 0) {
+ SignalHandler.disconnect (monitor, watch_id);
+ }
+ }
+
+ public File (GLib.File file, FileMonitor? monitor) {
+ this.file = file;
+ this.name = (!) file.get_basename ();
+ this.monitor = monitor;
+
+ set_string ("name", name, false);
+ set ("contents", null);
+ set ("size", null);
+ set ("type", null);
+ set ("user", null);
+
+ update_info.begin ();
+
+ if (monitor != null) {
+ this.watch_id = Signal.connect (monitor, "changed",
+ (Callback) monitor_changed, this);
+ }
+ }
+ }
+
+ public class Directory : Model.AbstractSortedList
+ {
+ FileMonitor? monitor;
+ ulong watch_id;
+ GLib.File file;
+
+ ~Directory () {
+ if (watch_id != 0) {
+ SignalHandler.disconnect (monitor, watch_id);
+ }
+ }
+
+ override void free_key (Model.AbstractSortedList.Key key) {
+ }
+
+ override int compare (Model.AbstractSortedList.ConstKey a,
+ Model.AbstractSortedList.ConstKey b) {
+ return strcmp ((string) a, (string) b);
+ }
+
+ override void create_item (ulong index,
+ Model.AbstractSortedList.ConstKey key,
+ out Model.AbstractSortedList.Key new_key,
+ out Model.Object new_object) {
+ var child = new File (file.get_child ((string) key), monitor);
+ weak string name = child.name;
+ new_object = child;
+ new_key = (Model.AbstractSortedList.Key) name;
+ }
+
+ override void warning (ulong index, Model.AbstractSortedList.ConstKey key,
+ char mode, ulong current_index,
+ Model.Object current_value) {
+ assert_not_reached ();
+ }
+
+ static int compare_strings (void *a, void *b) {
+ string **a_str = a;
+ string **b_str = b;
+
+ return strcmp (*a_str, *b_str);
+ }
+
+ async void enumerate () {
+ string[] names = null;
+
+ try {
+ var enumerator =
+ yield file.enumerate_children_async ("standard::name", 0, 0, null);
+
+ do {
+ var files = yield enumerator.next_files_async (5, 0, null);
+ names = new string[0];
+
+ foreach (var file in files) {
+ names += file.get_name ();
+ }
+
+ Posix.qsort ((void *) names, names.length, sizeof (void *),
+ (Posix.compar_fn_t) compare_strings);
+
+ merge ("i", (Model.AbstractSortedList.ConstKey[]) names);
+ } while (names.length == 5);
+ } catch (Error e) {
+ }
+ }
+
+ static void monitor_changed (FileMonitor monitor, GLib.File file,
+ GLib.File? other_file,
+ FileMonitorEvent event, Directory this_dir) {
+ string mode = "";
+
+ assert (this_dir.monitor == monitor);
+
+ switch (event) {
+ case FileMonitorEvent.CREATED:
+ mode = "i";
+ break;
+ case FileMonitorEvent.DELETED:
+ mode = "d";
+ break;
+ default:
+ return;
+ }
+
+ string[] names = new string[] { file.get_basename () };
+ this_dir.merge (mode, (Model.AbstractSortedList.ConstKey[]) names);
+ }
+
+ public Directory (GLib.File file) {
+ Object ();
+
+ this.file = file;
+
+ try {
+ this.monitor = file.monitor_directory (0, null);
+ this.watch_id = Signal.connect (monitor, "changed",
+ (Callback) monitor_changed, this);
+ } catch (GLib.IOError e) {
+ this.monitor = null;
+ this.watch_id = 0;
+ }
+
+ enumerate.begin ();
+ }
+ }
+}
diff --git a/gtk/mg.vala b/gtk/mg.vala
new file mode 100644
index 0000000..3262cdc
--- /dev/null
+++ b/gtk/mg.vala
@@ -0,0 +1,24 @@
+Gtk.TreeView v;
+Model.List list;
+
+bool setup () {
+ v.set_model (new Model.GtkModel (list, {typeof(string),typeof(string)}, {"name", "type"}, "contents"));
+ return false;
+}
+
+void main (string[]args) {
+ Gtk.init (ref args);
+
+ var w = new Gtk.Window (Gtk.WindowType.TOPLEVEL);
+ list = new FS.Directory (File.new_for_path ("/home/desrt/Desktop/small"));
+ v = new Gtk.TreeView ();
+ v.insert_column_with_attributes (0, "name", new Gtk.CellRendererText (), "text", 0);
+ v.insert_column_with_attributes (0, "type", new Gtk.CellRendererText (), "text", 1);
+ Timeout.add (1000, setup);
+ var sc = new Gtk.ScrolledWindow (null, null);
+ sc.add (v);
+ w.add (sc);
+ w.show_all ();
+
+ Gtk.main ();
+}
diff --git a/gtk/model-gtk.pc.in b/gtk/model-gtk.pc.in
new file mode 100644
index 0000000..fb91d93
--- /dev/null
+++ b/gtk/model-gtk.pc.in
@@ -0,0 +1,11 @@
+prefix= prefix@
+exec_prefix= prefix@/bin
+libdir= prefix@/lib
+includedir= prefix@/include/model
+
+Name: model
+Description: a simple data model object
+Version: @PACKAGE_VERSION@
+Requires: gobject-2.0
+Libs: -L${libdir} -lmodel
+Cflags: -I${includedir}
diff --git a/gtk/model-gtk.vala b/gtk/model-gtk.vala
new file mode 100644
index 0000000..3377418
--- /dev/null
+++ b/gtk/model-gtk.vala
@@ -0,0 +1,319 @@
+namespace Model {
+ class ListTracker {
+ internal DictionaryTracker? first;
+ //DictionaryTracker[]? array;
+ internal int length;
+ ulong changed_id;
+
+ internal weak Model.DictionaryTracker? parent;
+ internal weak GtkModel model;
+ Model.List source;
+
+ DictionaryTracker **cached_child;
+ ulong cached_child_index;
+
+ void source_changed (Model.List source, ulong position, ulong deleted, ulong inserted, bool more) {
+ Gtk.TreePath path;
+ bool was_empty;
+
+ was_empty = length == 0;
+
+ if (parent != null) {
+ path = parent.get_tree_path ();
+ } else {
+ path = new Gtk.TreePath ();
+ }
+
+ /* seek to position */
+ if (cached_child_index > position) {
+ cached_child = null;
+ }
+
+ if (cached_child == null) {
+ cached_child = &first;
+
+ for (cached_child_index = 0; cached_child_index < position; cached_child_index++) {
+ cached_child = &((DictionaryTracker) (*cached_child)).next;
+ }
+ }
+
+ path.append_index ((int) position);
+
+ while (deleted --> 0) {
+ model.row_deleted (path);
+
+ var tmp = *cached_child;
+ *cached_child = (owned) ((DictionaryTracker) tmp).next;
+ delete tmp;
+ length--;
+ }
+
+ path.up ();
+
+ while (inserted --> 0) {
+ var dict = source.get_child (position) as Model.Dictionary;
+ Gtk.TreeIter iter;
+
+ *cached_child = new DictionaryTracker (this, dict, *cached_child);
+ path.append_index ((int) position++);
+
+ if (model.write_iter (out iter, *cached_child)) {
+ model.row_inserted (path, iter);
+ }
+
+ path.up ();
+ length++;
+ }
+
+ if (was_empty != (length == 0)) {
+ Gtk.TreeIter iter;
+
+ if (model.write_iter (out iter, parent)) {
+ model.row_has_child_toggled (path, iter);
+ }
+ }
+
+ if (more == false) {
+ cached_child = null;
+ }
+ }
+
+ internal ListTracker (GtkModel model, Model.List source, DictionaryTracker? parent = null) {
+ this.parent = parent;
+ this.source = source;
+ this.model = model;
+
+ length = (int) source.n_children ();
+ for (var i = length; i --> 0;) {
+ var dict = source.get_child (i) as Model.Dictionary;
+ first = new DictionaryTracker (this, dict, first);
+ }
+
+ changed_id = source.changed.connect (source_changed);
+ }
+
+ internal DictionaryTracker? get_nth (int n) {
+ var dt = first;
+
+ while (dt != null && n --> 0) {
+ dt = dt.next;
+ }
+
+ return dt;
+ }
+
+ internal DictionaryTracker? get_for_path (Gtk.TreePath path, int depth = 0) {
+ var item = get_nth (path.get_indices ()[depth++]);
+
+ if (item != null && depth < path.get_depth ()) {
+ if (item.children != null) {
+ item = item.children.get_for_path (path, depth);
+ } else {
+ item = null;
+ }
+ }
+
+ return item;
+ }
+
+ ~ListTracker () {
+ source.disconnect (changed_id);
+ }
+ }
+
+ class DictionaryTracker {
+ Model.Reference? children_reference;
+ internal ListTracker? children;
+
+ internal weak ListTracker siblings;
+ internal Model.Dictionary source;
+ weak GtkModel model;
+
+ internal DictionaryTracker? next;
+
+ void children_reference_changed (Model.Reference reference) {
+ var list = reference.get_value () as Model.List;
+
+ if (list != null) {
+ children = new ListTracker (model, list, this);
+ } else {
+ children = null;
+ }
+ }
+
+ public DictionaryTracker (ListTracker siblings, Model.Dictionary source, DictionaryTracker? next) {
+ this.model = siblings.model;
+ this.siblings = siblings;
+ this.source = source;
+ this.next = next;
+
+ if (model.children_key != null) {
+ children_reference = source.get_reference ((!) model.children_key);
+ children_reference.changed += children_reference_changed;
+ children_reference_changed ((!) children_reference);
+ }
+ }
+
+ internal int get_index () {
+ var chain = siblings.first;
+ var i = 0;
+
+ while (chain != this) {
+ chain = chain.next;
+ i++;
+ }
+
+ return i;
+ }
+
+ internal Gtk.TreePath get_tree_path () {
+ Gtk.TreePath path;
+
+ if (siblings.parent != null) {
+ path = siblings.parent.get_tree_path ();
+ } else {
+ path = new Gtk.TreePath ();
+ }
+
+ path.append_index (get_index ());
+
+ return path;
+ }
+ }
+
+ public class GtkModel : GLib.Object, Gtk.TreeModel {
+ internal string? children_key;
+ string[] keys;
+ Type[] types;
+
+ ListTracker root;
+ internal int stamp;
+
+ GLib.Type get_column_type (int index) {
+ return types[index];
+ }
+
+ Gtk.TreeModelFlags get_flags () {
+ Gtk.TreeModelFlags flags;
+
+ flags = Gtk.TreeModelFlags.ITERS_PERSIST;
+
+ if (this.children_key == null) {
+ flags |= Gtk.TreeModelFlags.LIST_ONLY;
+ }
+
+ return flags;
+ }
+
+ bool get_iter (out Gtk.TreeIter iter, Gtk.TreePath path) {
+ return write_iter (out iter, root.get_for_path (path));
+ }
+
+ int get_n_columns () {
+ return keys.length;
+ }
+
+ Gtk.TreePath get_path (Gtk.TreeIter iter) {
+ return iter_to_dt (iter).get_tree_path ();
+ }
+
+ void get_value (Gtk.TreeIter iter, int column, out GLib.Value value) {
+ var dt = iter_to_dt (iter);
+ var object = dt.source.get_value (keys[column]);
+
+ value.init (types[column]);
+
+ if (types[column] == typeof (string)) {
+ var string_object = object as Model.String;
+ if (string_object != null) {
+ value.set_string (string_object.get ());
+ }
+ } else if (types[column] == typeof (int)) {
+ var integer_object = object as Model.Integer;
+ if (integer_object != null) {
+ value.set_int (integer_object.get ());
+ }
+ } else if (types[column] == typeof (double)) {
+ var float_object = object as Model.Float;
+ if (float_object != null) {
+ value.set_double (float_object.get ());
+ }
+ } if (types[column] == typeof (bool)) {
+ var boolean_object = object as Model.Boolean;
+ if (boolean_object != null) {
+ value.set_boolean (boolean_object.get ());
+ }
+ }
+ }
+
+ DictionaryTracker iter_to_dt (Gtk.TreeIter iter) {
+ assert (iter.stamp == stamp);
+ assert (iter.user_data != null);
+
+ return (DictionaryTracker) iter.user_data;
+ }
+
+ ListTracker? iter_to_lt (Gtk.TreeIter? iter) {
+ if (iter != null) {
+ return iter_to_dt (iter).children;
+ } else {
+ return root;
+ }
+ }
+
+ internal bool write_iter (out Gtk.TreeIter iter, DictionaryTracker? dt) {
+ iter.stamp = (dt != null) ? stamp : 0;
+ iter.user_data = (void *) dt;
+
+ return dt != null;
+ }
+
+ bool iter_children (out Gtk.TreeIter iter, Gtk.TreeIter? parent) {
+ var lt = iter_to_lt (parent);
+ return write_iter (out iter, lt == null ? null : lt.first);
+ }
+
+ bool iter_has_child (Gtk.TreeIter iter) {
+ var lt = iter_to_lt (iter);
+ return lt != null && lt.length > 0;
+ }
+
+ int iter_n_children (Gtk.TreeIter? iter) {
+ var lt = iter_to_lt (iter);
+ return lt == null ? 0 : lt.length;
+ }
+
+ bool iter_next (ref Gtk.TreeIter iter) {
+ var dt = iter_to_dt (iter);
+ return write_iter (out iter, dt.next);
+ }
+
+ bool iter_nth_child (out Gtk.TreeIter iter, Gtk.TreeIter? parent, int n) {
+ var lt = iter_to_lt (parent);
+ return write_iter (out iter, lt == null ? null : lt.get_nth (n));
+ }
+
+ bool iter_parent (out Gtk.TreeIter iter, Gtk.TreeIter child) {
+ var dt = iter_to_dt (child);
+ return write_iter (out iter, dt.siblings.parent);
+ }
+
+ void ref_node (Gtk.TreeIter iter) {
+ }
+
+ void unref_node (Gtk.TreeIter iter) {
+ }
+
+ public GtkModel (Model.List list, Type[] types, string[] keys, string? children_key) {
+ this.types = types;
+ this.keys = keys;
+ this.children_key = children_key;
+
+ this.root = new ListTracker (this, list);
+
+ assert (types.length == keys.length);
+ }
+ }
+}
+
+// vim:ts=8 sw=8 noet:
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]