Ryujinx-GtkSharp/gtk/NodeStore.cs

564 lines
15 KiB
C#
Raw Normal View History

// NodeStore.cs - Tree store implementation for TreeView.
//
// Author: Mike Kestner <mkestner@novell.com>
//
// Copyright (c) 2003-2005 Novell, Inc.
//
// 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 {
class IDHashtable : Hashtable {
class IDComparer : IComparer {
public int Compare (object x, object y)
{
if ((int) x == (int) y)
return 0;
else
return 1;
}
}
class IDHashCodeProvider : IHashCodeProvider {
public int GetHashCode (object o)
{
return (int) o;
}
}
public IDHashtable () : base (new IDHashCodeProvider (), new IDComparer ()) {}
}
[GLib.CDeclCallback]
delegate int GetFlagsDelegate ();
[GLib.CDeclCallback]
delegate int GetNColumnsDelegate ();
[GLib.CDeclCallback]
delegate IntPtr GetColumnTypeDelegate (int col);
[GLib.CDeclCallback]
delegate bool GetNodeDelegate (out int node_idx, IntPtr path);
[GLib.CDeclCallback]
delegate IntPtr GetPathDelegate (int node_idx);
[GLib.CDeclCallback]
delegate void GetValueDelegate (int node_idx, int col, ref GLib.Value val);
[GLib.CDeclCallback]
delegate bool NextDelegate (ref int node_idx);
[GLib.CDeclCallback]
delegate bool ChildrenDelegate (out int child, int parent);
[GLib.CDeclCallback]
delegate bool HasChildDelegate (int node_idx);
[GLib.CDeclCallback]
delegate int NChildrenDelegate (int node_idx);
[GLib.CDeclCallback]
delegate bool NthChildDelegate (out int child, int parent, int n);
[GLib.CDeclCallback]
delegate bool ParentDelegate (out int parent, int child);
[StructLayout(LayoutKind.Sequential)]
struct TreeModelIfaceDelegates {
public GetFlagsDelegate get_flags;
public GetNColumnsDelegate get_n_columns;
public GetColumnTypeDelegate get_column_type;
public GetNodeDelegate get_node;
public GetPathDelegate get_path;
public GetValueDelegate get_value;
public NextDelegate next;
public ChildrenDelegate children;
public HasChildDelegate has_child;
public NChildrenDelegate n_children;
public NthChildDelegate nth_child;
public ParentDelegate parent;
}
Hashtable node_hash = new IDHashtable ();
GLib.GType[] ctypes;
MemberInfo [] getters;
int n_cols;
bool list_only = false;
ArrayList nodes = new ArrayList ();
TreeModelIfaceDelegates tree_model_iface;
int get_flags_cb ()
{
TreeModelFlags result = TreeModelFlags.ItersPersist;
if (list_only)
result |= TreeModelFlags.ListOnly;
return (int) result;
}
int get_n_columns_cb ()
{
return n_cols;
}
IntPtr get_column_type_cb (int col)
{
try {
return ctypes [col].Val;
} catch (Exception e) {
GLib.ExceptionManager.RaiseUnhandledException (e, false);
}
return IntPtr.Zero;
}
bool get_node_cb (out int node_idx, IntPtr path)
{
try {
if (path == IntPtr.Zero)
throw new ArgumentNullException ("path");
TreePath treepath = new TreePath (path);
node_idx = -1;
ITreeNode node = GetNodeAtPath (treepath);
if (node == null)
return false;
node_idx = node.ID;
node_hash [node.ID] = node;
return true;
} catch (Exception e) {
GLib.ExceptionManager.RaiseUnhandledException (e, false);
}
node_idx = -1;
return false;
}
IntPtr get_path_cb (int node_idx)
{
try {
ITreeNode node = node_hash [node_idx] as ITreeNode;
if (node == null) throw new Exception ("Invalid Node ID");
return GetPath (node).Handle;
} catch (Exception e) {
GLib.ExceptionManager.RaiseUnhandledException (e, false);
}
return IntPtr.Zero;
}
[DllImport("libgobject-2.0-0.dll")]
static extern void g_value_init (ref GLib.Value val, IntPtr type);
void get_value_cb (int node_idx, int col, ref GLib.Value val)
{
try {
ITreeNode node = node_hash [node_idx] as ITreeNode;
if (node == null)
return;
g_value_init (ref val, ctypes [col].Val);
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;
} catch (Exception e) {
GLib.ExceptionManager.RaiseUnhandledException (e, false);
}
}
bool next_cb (ref int node_idx)
{
try {
ITreeNode node = node_hash [node_idx] as ITreeNode;
if (node == null)
return false;
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];
}
node_hash [node.ID] = node;
node_idx = node.ID;
return true;
} catch (Exception e) {
GLib.ExceptionManager.RaiseUnhandledException (e, false);
}
return false;
}
bool children_cb (out int child_idx, int parent)
{
try {
child_idx = -1;
ITreeNode node;
if (parent == -1) {
if (Nodes.Count <= 0)
return false;
node = Nodes [0] as ITreeNode;
child_idx = node.ID;
node_hash [node.ID] = node;
return true;
}
node = node_hash [parent] as ITreeNode;
if (node == null || node.ChildCount <= 0)
return false;
ITreeNode child = node [0];
node_hash [child.ID] = child;
child_idx = child.ID;
return true;
} catch (Exception e) {
GLib.ExceptionManager.RaiseUnhandledException (e, false);
}
child_idx = -1;
return false;
}
bool has_child_cb (int node_idx)
{
try {
ITreeNode node = node_hash [node_idx] as ITreeNode;
if (node == null || node.ChildCount <= 0)
return false;
return true;
} catch (Exception e) {
GLib.ExceptionManager.RaiseUnhandledException (e, false);
}
return false;
}
int n_children_cb (int node_idx)
{
try {
if (node_idx == -1)
return Nodes.Count;
ITreeNode node = node_hash [node_idx] as ITreeNode;
if (node == null || node.ChildCount <= 0)
return 0;
return node.ChildCount;
} catch (Exception e) {
GLib.ExceptionManager.RaiseUnhandledException (e, false);
}
return 0;
}
bool nth_child_cb (out int child_idx, int parent, int n)
{
child_idx = -1;
try {
ITreeNode node;
if (parent == -1) {
if (Nodes.Count <= n)
return false;
node = Nodes [n] as ITreeNode;
child_idx = node.ID;
node_hash [node.ID] = node;
return true;
}
node = node_hash [parent] as ITreeNode;
if (node == null || node.ChildCount <= n)
return false;
ITreeNode child = node [n];
node_hash [child.ID] = child;
child_idx = child.ID;
return true;
} catch (Exception e) {
GLib.ExceptionManager.RaiseUnhandledException (e, false);
}
return false;
}
bool parent_cb (out int parent_idx, int child)
{
parent_idx = -1;
try {
ITreeNode node = node_hash [child] as ITreeNode;
if (node == null || node.Parent == null)
return false;
node_hash [node.Parent.ID] = node.Parent;
parent_idx = node.Parent.ID;
return true;
} catch (Exception e) {
GLib.ExceptionManager.RaiseUnhandledException (e, false);
}
return false;
}
[DllImport("gtksharpglue-2")]
static extern void gtksharp_node_store_set_tree_model_callbacks (IntPtr raw, ref TreeModelIfaceDelegates cbs);
private void BuildTreeModelIface ()
{
tree_model_iface.get_flags = new GetFlagsDelegate (get_flags_cb);
tree_model_iface.get_n_columns = new GetNColumnsDelegate (get_n_columns_cb);
tree_model_iface.get_column_type = new GetColumnTypeDelegate (get_column_type_cb);
tree_model_iface.get_node = new GetNodeDelegate (get_node_cb);
tree_model_iface.get_path = new GetPathDelegate (get_path_cb);
tree_model_iface.get_value = new GetValueDelegate (get_value_cb);
tree_model_iface.next = new NextDelegate (next_cb);
tree_model_iface.children = new ChildrenDelegate (children_cb);
tree_model_iface.has_child = new HasChildDelegate (has_child_cb);
tree_model_iface.n_children = new NChildrenDelegate (n_children_cb);
tree_model_iface.nth_child = new NthChildDelegate (nth_child_cb);
tree_model_iface.parent = new ParentDelegate (parent_cb);
gtksharp_node_store_set_tree_model_callbacks (Handle, ref tree_model_iface);
}
public NodeStore (Type node_type) : base (IntPtr.Zero)
{
CreateNativeObject (new string [0], new GLib.Value [0]);
ScanType (node_type);
BuildTreeModelIface ();
}
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);
ctypes = new GLib.GType [minfos.Count];
getters = new MemberInfo [minfos.Count];
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;
}
}
}
private IList Nodes {
get {
return nodes as IList;
}
}
[DllImport("gtksharpglue-2")]
static extern void gtksharp_node_store_emit_row_changed (IntPtr handle, IntPtr path, int node_idx);
private void changed_cb (object o, EventArgs args)
{
ITreeNode node = o as ITreeNode;
gtksharp_node_store_emit_row_changed (Handle, get_path_cb (node.ID), node.ID);
}
[DllImport("gtksharpglue-2")]
static extern void gtksharp_node_store_emit_row_inserted (IntPtr handle, IntPtr path, int node_idx);
private void EmitRowInserted (ITreeNode node)
{
gtksharp_node_store_emit_row_inserted (Handle, get_path_cb (node.ID), node.ID);
for (int i = 0; i < node.ChildCount; i++)
EmitRowInserted (node [i]);
}
private void child_added_cb (object o, ITreeNode child)
{
AddNodeInternal (child);
EmitRowInserted (child);
}
[DllImport("gtksharpglue-2")]
static extern void gtksharp_node_store_emit_row_deleted (IntPtr handle, IntPtr path);
[DllImport("gtksharpglue-2")]
static extern void gtksharp_node_store_emit_row_has_child_toggled (IntPtr handle, IntPtr path, int node_idx);
private void RemoveNodeInternal (ITreeNode node)
{
node_hash.Remove (node.ID);
for (int i = 0; i < node.ChildCount; i++)
RemoveNodeInternal (node [i]);
}
private void child_deleted_cb (object o, ITreeNode child, int idx)
{
ITreeNode node = o as ITreeNode;
TreePath path = new TreePath (get_path_cb (node.ID));
TreePath child_path = path.Copy ();
child_path.AppendIndex (idx);
RemoveNodeInternal (child);
gtksharp_node_store_emit_row_deleted (Handle, child_path.Handle);
if (node.ChildCount <= 0)
gtksharp_node_store_emit_row_has_child_toggled (Handle, get_path_cb (node.ID), node.ID);
}
private void AddNodeInternal (ITreeNode node)
{
node_hash [node.ID] = 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);
RemoveNodeInternal (node);
TreePath path = new TreePath ();
path.AppendIndex (idx);
gtksharp_node_store_emit_row_deleted (Handle, path.Handle);
}
public void Clear ()
{
while (nodes.Count > 0)
RemoveNode ((ITreeNode)nodes [0]);
}
private ITreeNode GetNodeAtPath (TreePath path)
{
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;
}
public ITreeNode GetNode (TreePath path)
{
if (path == null)
throw new ArgumentNullException ();
return GetNodeAtPath (path);
}
internal ITreeNode GetNode (TreeIter iter)
{
return node_hash [(int) iter.UserData] as ITreeNode;
}
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);
Automatic memory management for opaque types [#49565] * glib/Opaque.cs (Owned): new property saying whether or not gtk# owns the memory. (Opaque): Set Owned to true in the void ctor and false in the IntPtr one. (GetOpaque): add a new overload that can also create opaques, a la GLib.Object.GetObject. (Ref, Unref, Free): empty virtual methods to be overridden by subclasses. (set_Raw): Unref() and possibly Free() the old value, Ref() the new one. (~Opaque, Dispose): set Raw to IntPtr.Zero (triggering Free/Unref if needed) * parser/gapi2xml.pl (addReturnElem): if the method is named Copy and returns a pointer, set the "owned" attribute on the return-type. * */*-api.raw: Regen * generator/HandleBase.cs (FromNative): Add new FromNative/FromNativeReturn overloads that takes a "bool owned" param. Implement the 1-arg FromNative and FromNativeReturn in terms of that. * generator/ObjectBase.cs (FromNative): Implement HandleBase's new overload. Use the two-arg version of GLib.Object.GetObject when "owned" is true. * generator/OpaqueGen.cs (Generate): Pull out Ref, Unref, and Free/Destroy/Dispose methods and handle them specially by overriding Opaque.Ref, .Unref, and .Free appropriately. (If any of the methods are marked deprecated, output a deprecated do-nothing method as well, to save us from having to write all those deprecated methods by hand.) (FromNative): use GetOpaque, passing "owned". * generator/ReturnValue.cs (FromNative): if the value is a HandleBase, pass Owned to its FromNative(). * generator/Parameters.cs (Owned): new property (for use on out params) (FromNative): Call FromNative() on the generatable, handling Owned in the case of HandleBase. * generator/ManagedCallString.cs: * generator/MethodBody.cs: * generator/Signal.cs: use param.FromNative() rather than param.Generatable.FromNative(), to get ownership right. * */*.metadata: Mark opaque ref/unref/free methods deprecated (except where we were hiding them before). Add "owned" attributes to return values and out params as needed. * pango/AttrIterator.custom (GetFont): work around a memory-management oddity of the underlying method. * pango/AttrFontDesc.cs (AttrFontDesc): copy the passed-in FontDescriptor, since the attribute will assume ownership of it. * gtk/TreeView.custom (GetPathAtPos): set the "owned" flag on the returned TreePaths. * gtk/TargetList.custom: Remove refcounting stuff, which is now handled automatically * gtk/NodeStore.cs (GetPath): clear the Owned flag on the created TreePath so that the underlying structure doesn't get freed when the function returns * gtkhtml/HTMLStream.custom (Destroy): hide this and then reimplement it by hand to keep OpaqueGen from using it in Dispose(), since calling it after an HTMLStream.Close() will result in a crash. svn path=/trunk/gtk-sharp/; revision=47928
2005-08-02 20:45:21 +02:00
path.Owned = false;
return path;
}
internal TreeIter GetIter (ITreeNode node)
{
TreeIter iter = new TreeIter ();
iter.UserData = new IntPtr (node.ID);
return iter;
}
[DllImport("gtksharpglue-2")]
static extern IntPtr gtksharp_node_store_get_type ();
public static new GLib.GType GType {
get {
return new GLib.GType (gtksharp_node_store_get_type ());
}
}
public IEnumerator GetEnumerator ()
{
return nodes.GetEnumerator ();
}
}
}