summaryrefslogtreecommitdiff
path: root/gtk/NodeStore.cs
diff options
context:
space:
mode:
authorMirco Bauer <meebey@meebey.net>2012-09-30 23:54:59 +0200
committerMirco Bauer <meebey@meebey.net>2012-09-30 23:54:59 +0200
commit645346c05f224451a381b0f417fa8397344d13f9 (patch)
tree0481a90a6608c0e33caad7a3f70e562bcf09f368 /gtk/NodeStore.cs
Imported Upstream version 2.99.0~git20120905.446f2e7
Diffstat (limited to 'gtk/NodeStore.cs')
-rw-r--r--gtk/NodeStore.cs470
1 files changed, 470 insertions, 0 deletions
diff --git a/gtk/NodeStore.cs b/gtk/NodeStore.cs
new file mode 100644
index 0000000..308db17
--- /dev/null
+++ b/gtk/NodeStore.cs
@@ -0,0 +1,470 @@
+// NodeStore.cs - Tree store implementation for TreeView.
+//
+// Author: Mike Kestner <mkestner@novell.com>
+//
+// Copyright (c) 2003-2005 Novell, Inc.
+// Copyright (c) 2009 Christian Hoff
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of version 2 of the Lesser GNU General
+// Public License as published by the Free Software Foundation.
+//
+// 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this program; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA 02111-1307, USA.
+
+
+namespace Gtk {
+
+ using System;
+ using System.Collections;
+ using System.Reflection;
+ using System.Runtime.InteropServices;
+
+ public class NodeStore : GLib.Object, IEnumerable {
+
+ NodeStoreImplementor implementor;
+
+ public NodeStore (Type node_type)
+ {
+ implementor = new NodeStoreImplementor (node_type);
+ }
+
+ internal TreeModelAdapter Adapter {
+ get { return new TreeModelAdapter (implementor); }
+ }
+
+ internal TreeIter GetIter (ITreeNode node)
+ {
+ return implementor.GetIter (node);
+ }
+
+ internal TreePath GetPath (ITreeNode node)
+ {
+ return implementor.GetPath (node);
+ }
+
+ public ITreeNode GetNode (TreePath path)
+ {
+ return implementor.GetNode (path);
+ }
+
+ public void AddNode (ITreeNode node)
+ {
+ implementor.AddNode (node);
+ }
+
+ public void AddNode (ITreeNode node, int position)
+ {
+ implementor.AddNode (node, position);
+ }
+
+ public void RemoveNode (ITreeNode node)
+ {
+ implementor.RemoveNode (node);
+ }
+
+ public void Clear ()
+ {
+ implementor.Clear ();
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ return implementor.GetEnumerator ();
+ }
+
+ internal class NodeStoreImplementor : GLib.Object, TreeModelImplementor, IEnumerable {
+ TreeModelAdapter model_adapter;
+ GLib.GType[] ctypes;
+ MemberInfo [] getters;
+ int n_cols;
+ bool list_only = false;
+ ArrayList nodes = new ArrayList ();
+
+ public readonly int Stamp;
+
+ public NodeStoreImplementor (Type node_type)
+ {
+ // Create a random stamp for the iters
+ Random RandomStampGen = new Random ();
+ this.Stamp = RandomStampGen.Next (int.MinValue, int.MaxValue);
+
+ ScanType (node_type);
+
+ model_adapter = new Gtk.TreeModelAdapter (this);
+ }
+
+ void ScanType (Type type)
+ {
+ TreeNodeAttribute tna = (TreeNodeAttribute) Attribute.GetCustomAttribute (type, typeof (TreeNodeAttribute), false);
+ if (tna != null)
+ list_only = tna.ListOnly;
+
+ ArrayList minfos = new ArrayList ();
+
+ foreach (PropertyInfo pi in type.GetProperties ())
+ foreach (TreeNodeValueAttribute attr in pi.GetCustomAttributes (typeof (TreeNodeValueAttribute), false))
+ minfos.Add (pi);
+
+ foreach (FieldInfo fi in type.GetFields ())
+ foreach (TreeNodeValueAttribute attr in fi.GetCustomAttributes (typeof (TreeNodeValueAttribute), false))
+ minfos.Add (fi);
+
+ n_cols = minfos.Count;
+ ctypes = new GLib.GType [n_cols];
+ getters = new MemberInfo [n_cols];
+
+ foreach (MemberInfo mi in minfos) {
+ foreach (TreeNodeValueAttribute attr in mi.GetCustomAttributes (typeof (TreeNodeValueAttribute), false)) {
+ int col = attr.Column;
+
+ if (getters [col] != null)
+ throw new Exception (String.Format ("You have two TreeNodeValueAttributes with the Column={0}", col));
+
+ getters [col] = mi;
+ Type t = mi is PropertyInfo ? ((PropertyInfo) mi).PropertyType : ((FieldInfo) mi).FieldType;
+ ctypes [col] = (GLib.GType) t;
+ }
+ }
+ }
+
+ public TreeModelFlags Flags {
+ get {
+ TreeModelFlags result = TreeModelFlags.ItersPersist;
+ if (list_only)
+ result |= TreeModelFlags.ListOnly;
+ return result;
+ }
+ }
+
+ public int NColumns {
+ get {
+ return n_cols;
+ }
+ }
+
+ public GLib.GType GetColumnType (int col)
+ {
+ return ctypes [col];
+ }
+
+#region Gtk.TreePath handling
+ internal TreePath GetPath (ITreeNode node)
+ {
+ TreePath path = new TreePath ();
+ int idx;
+
+ while (node.Parent != null) {
+ idx = node.Parent.IndexOf (node);
+ if (idx < 0) throw new Exception ("Badly formed tree");
+ path.PrependIndex (idx);
+ node = node.Parent;
+ }
+ idx = Nodes.IndexOf (node);
+ if (idx < 0) throw new Exception ("Node not found in Nodes list");
+ path.PrependIndex (idx);
+
+ path.Owned = false;
+ return path;
+ }
+
+ public ITreeNode GetNode (TreePath path)
+ {
+ if (path == null)
+ throw new ArgumentNullException ();
+
+ int[] indices = path.Indices;
+
+ if (indices[0] >= Nodes.Count)
+ return null;
+
+ ITreeNode node = Nodes [indices [0]] as ITreeNode;
+ int i;
+ for (i = 1; i < path.Depth; i++) {
+ if (indices [i] >= node.ChildCount)
+ return null;
+
+ node = node [indices [i]];
+ }
+
+ return node;
+ }
+#endregion
+
+#region Gtk.TreeIter handling
+ ArrayList gc_handles = new ArrayList ();
+
+ protected override void Dispose (bool disposing)
+ {
+ // Free all the GCHandles pointing to the iters since they won't be garbage collected
+ foreach (System.Runtime.InteropServices.GCHandle handle in gc_handles)
+ handle.Free ();
+
+ base.Dispose (disposing);
+ }
+
+ internal void GetIter (ITreeNode node, ref TreeIter iter)
+ {
+ if (node == null)
+ throw new ArgumentNullException ("node");
+
+ iter.Stamp = this.Stamp;
+ GCHandle gch = GCHandle.Alloc (node);
+ iter.UserData = (IntPtr) gch;
+ gc_handles.Add (gch);
+ }
+
+ public TreeIter GetIter (ITreeNode node)
+ {
+ Gtk.TreeIter result = Gtk.TreeIter.Zero;
+ GetIter (node, ref result);
+
+ return result;
+ }
+
+ public ITreeNode GetNode (TreeIter iter)
+ {
+ if (iter.Stamp != this.Stamp)
+ throw new InvalidOperationException (String.Format ("iter belongs to a different model; it's stamp is not equal to the stamp of this model({0})", this.Stamp.ToString ()));
+
+ System.Runtime.InteropServices.GCHandle gch = (System.Runtime.InteropServices.GCHandle) iter.UserData;
+ return gch.Target as ITreeNode;
+ }
+
+ void TreeModelImplementor.RefNode (Gtk.TreeIter iter) { }
+ void TreeModelImplementor.UnrefNode (Gtk.TreeIter iter) { }
+#endregion
+
+ public bool GetIter (out TreeIter iter, TreePath path)
+ {
+ if (path == null)
+ throw new ArgumentNullException ("path");
+
+ ITreeNode node = GetNode (path);
+ if (node == null) {
+ iter = TreeIter.Zero;
+ return false;
+ } else {
+ iter = GetIter (node);
+ return true;
+ }
+ }
+
+ public Gtk.TreePath GetPath (TreeIter iter)
+ {
+ return GetPath (GetNode (iter));
+ }
+
+ public void GetValue (Gtk.TreeIter iter, int col, ref GLib.Value val)
+ {
+ ITreeNode node = GetNode (iter);
+ val.Init (ctypes [col]);
+
+ object col_val;
+ if (getters [col] is PropertyInfo)
+ col_val = ((PropertyInfo) getters [col]).GetValue (node, null);
+ else
+ col_val = ((FieldInfo) getters [col]).GetValue (node);
+ val.Val = col_val;
+ }
+
+ public bool IterNext (ref TreeIter iter)
+ {
+ ITreeNode node = GetNode (iter);
+
+ int idx;
+ if (node.Parent == null)
+ idx = Nodes.IndexOf (node);
+ else
+ idx = node.Parent.IndexOf (node);
+
+ if (idx < 0) throw new Exception ("Node not found in Nodes list");
+
+ if (node.Parent == null) {
+ if (++idx >= Nodes.Count)
+ return false;
+ node = Nodes [idx] as ITreeNode;
+ } else {
+ if (++idx >= node.Parent.ChildCount)
+ return false;
+ node = node.Parent [idx];
+ }
+
+ GetIter (node, ref iter);
+ return true;
+ }
+
+ public bool IterPrevious (ref TreeIter iter)
+ {
+ ITreeNode node = GetNode (iter);
+
+ int idx;
+ if (node.Parent == null)
+ idx = Nodes.IndexOf (node);
+ else
+ idx = node.Parent.IndexOf (node);
+
+ if (idx < 0) throw new Exception ("Node not found in Nodes list");
+ else if (idx == 0) return false;
+ node = node.Parent == null ? Nodes [idx - 1] as ITreeNode : node.Parent [idx - 1];
+ GetIter (node, ref iter);
+ return true;
+ }
+
+ public bool IterChildren (out Gtk.TreeIter first_child, Gtk.TreeIter parent)
+ {
+ first_child = Gtk.TreeIter.Zero;
+
+ if (parent.Equals (TreeIter.Zero)) {
+ if (Nodes.Count <= 0)
+ return false;
+ first_child = GetIter (Nodes [0] as ITreeNode);
+ } else {
+ ITreeNode node = GetNode (parent);
+ if (node.ChildCount <= 0)
+ return false;
+
+ first_child = GetIter (node [0]);
+ }
+ return true;
+ }
+
+ public bool IterHasChild (Gtk.TreeIter iter)
+ {
+ return IterNChildren (iter) > 0;
+ }
+
+ public int IterNChildren (Gtk.TreeIter iter)
+ {
+ if (iter.Equals (TreeIter.Zero))
+ return Nodes.Count;
+ else
+ return GetNode (iter).ChildCount;
+ }
+
+ public bool IterNthChild (out Gtk.TreeIter child, Gtk.TreeIter parent, int n)
+ {
+ child = TreeIter.Zero;
+
+ if (parent.Equals (TreeIter.Zero)) {
+ if (Nodes.Count <= n)
+ return false;
+ child = GetIter (Nodes [n] as ITreeNode);
+ } else {
+ ITreeNode parent_node = GetNode (parent);
+ if (parent_node.ChildCount <= n)
+ return false;
+ child = GetIter (parent_node [n]);
+ }
+ return true;
+ }
+
+ public bool IterParent (out Gtk.TreeIter parent, Gtk.TreeIter child)
+ {
+ parent = TreeIter.Zero;
+ ITreeNode child_node = GetNode (child);
+
+ if (child_node.Parent == null)
+ return false;
+ else {
+ parent = GetIter (child_node.Parent);
+ return true;
+ }
+ }
+
+ private IList Nodes {
+ get {
+ return nodes as IList;
+ }
+ }
+
+ private void changed_cb (object o, EventArgs args)
+ {
+ ITreeNode node = o as ITreeNode;
+ model_adapter.EmitRowChanged (GetPath (node), GetIter (node));
+ }
+
+ private void EmitRowInserted (ITreeNode node)
+ {
+ model_adapter.EmitRowInserted (GetPath (node), GetIter (node));
+ for (int i = 0; i < node.ChildCount; i++)
+ EmitRowInserted (node [i]);
+ }
+
+ private void child_added_cb (object sender, ITreeNode child)
+ {
+ AddNodeInternal (child);
+ EmitRowInserted (child);
+ }
+
+ private void child_deleted_cb (object sender, ITreeNode child, int idx)
+ {
+ ITreeNode node = sender as ITreeNode;
+
+ TreePath path = GetPath (node);
+ TreePath child_path = path.Copy ();
+ child_path.AppendIndex (idx);
+
+ model_adapter.EmitRowDeleted (child_path);
+
+ if (node.ChildCount <= 0)
+ model_adapter.EmitRowHasChildToggled (GetPath (node), GetIter (node));
+ }
+
+ private void AddNodeInternal (ITreeNode node)
+ {
+ node.Changed += new EventHandler (changed_cb);
+ node.ChildAdded += new TreeNodeAddedHandler (child_added_cb);
+ node.ChildRemoved += new TreeNodeRemovedHandler (child_deleted_cb);
+
+ for (int i = 0; i < node.ChildCount; i++)
+ AddNodeInternal (node [i]);
+ }
+
+ public void AddNode (ITreeNode node)
+ {
+ nodes.Add (node);
+ AddNodeInternal (node);
+ EmitRowInserted (node);
+ }
+
+ public void AddNode (ITreeNode node, int position)
+ {
+ nodes.Insert (position, node);
+ AddNodeInternal (node);
+ EmitRowInserted (node);
+ }
+
+ public void RemoveNode (ITreeNode node)
+ {
+ int idx = nodes.IndexOf (node);
+ if (idx < 0)
+ return;
+ nodes.Remove (node);
+
+ TreePath path = new TreePath ();
+ path.AppendIndex (idx);
+
+ model_adapter.EmitRowDeleted (path);
+ }
+
+ public void Clear ()
+ {
+ while (nodes.Count > 0)
+ RemoveNode ((ITreeNode)nodes [0]);
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ return nodes.GetEnumerator ();
+ }
+ }
+ }
+}