2003-11-04 23:20:15 +00:00
// NodeStore.cs - Tree store implementation for TreeView.
//
2005-01-03 18:37:08 +00:00
// Author: Mike Kestner <mkestner@novell.com>
2003-11-04 23:20:15 +00:00
//
2005-01-03 18:37:08 +00:00
// Copyright (c) 2003-2005 Novell, Inc.
2009-03-04 20:33:12 +00:00
// Copyright (c) 2009 Christian Hoff
2004-06-25 18:42:19 +00:00
//
// 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.
2003-11-04 23:20:15 +00:00
namespace Gtk {
using System ;
using System.Collections ;
using System.Reflection ;
using System.Runtime.InteropServices ;
2005-04-26 13:45:27 +00:00
public class NodeStore : GLib . Object , IEnumerable {
2009-03-16 21:05:08 +00:00
NodeStoreImplementor implementor ;
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
public NodeStore ( Type node_type )
{
2009-03-16 21:05:08 +00:00
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 ) ;
2009-03-04 20:33:12 +00:00
}
2009-03-16 21:05:08 +00:00
public void RemoveNode ( ITreeNode node )
{
implementor . RemoveNode ( node ) ;
}
public void Clear ( )
{
implementor . Clear ( ) ;
}
public IEnumerator GetEnumerator ( )
{
return implementor . GetEnumerator ( ) ;
}
2009-03-04 20:33:12 +00:00
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 ;
}
2003-11-04 23:20:15 +00:00
}
}
2009-03-04 20:33:12 +00:00
public TreeModelFlags Flags {
get {
TreeModelFlags result = TreeModelFlags . ItersPersist ;
if ( list_only )
result | = TreeModelFlags . ListOnly ;
return result ;
}
}
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
public int NColumns {
get {
return n_cols ;
}
}
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
public GLib . GType GetColumnType ( int col )
{
return ctypes [ col ] ;
}
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
#region Gtk . TreePath handling
internal TreePath GetPath ( ITreeNode node )
{
TreePath path = new TreePath ( ) ;
int idx ;
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
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 ;
2007-03-09 14:22:43 +00:00
}
2009-03-04 20:33:12 +00:00
public ITreeNode GetNode ( TreePath path )
{
if ( path = = null )
throw new ArgumentNullException ( ) ;
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
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 ( ) ;
public override void Dispose ( )
{
// 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 ( ) ;
2003-11-18 20:13:39 +00:00
2009-03-04 20:33:12 +00:00
base . Dispose ( ) ;
}
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
internal void GetIter ( ITreeNode node , ref TreeIter iter )
{
2007-03-09 14:22:43 +00:00
if ( node = = null )
2009-03-04 20:33:12 +00:00
throw new ArgumentNullException ( "node" ) ;
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
iter . Stamp = this . Stamp ;
GCHandle gch = GCHandle . Alloc ( node ) ;
iter . UserData = ( IntPtr ) gch ;
gc_handles . Add ( gch ) ;
2007-03-09 14:22:43 +00:00
}
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
public TreeIter GetIter ( ITreeNode node )
{
Gtk . TreeIter result = Gtk . TreeIter . Zero ;
GetIter ( node , ref result ) ;
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
return result ;
2007-03-09 14:22:43 +00:00
}
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
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 ] ) ;
2003-11-04 23:20:15 +00:00
2007-03-09 14:22:43 +00:00
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 ;
}
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
public bool IterNext ( ref TreeIter iter )
{
ITreeNode node = GetNode ( iter ) ;
2007-03-09 14:22:43 +00:00
int idx ;
if ( node . Parent = = null )
idx = Nodes . IndexOf ( node ) ;
else
idx = node . Parent . IndexOf ( node ) ;
2009-03-04 20:33:12 +00:00
2007-03-09 14:22:43 +00:00
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 ] ;
}
2009-03-04 20:33:12 +00:00
GetIter ( node , ref iter ) ;
2007-03-09 14:22:43 +00:00
return true ;
2003-11-04 23:20:15 +00:00
}
2009-03-04 20:33:12 +00:00
public bool IterChildren ( out Gtk . TreeIter first_child , Gtk . TreeIter parent )
{
first_child = Gtk . TreeIter . Zero ;
2007-03-09 14:22:43 +00:00
2009-03-04 20:33:12 +00:00
if ( parent . Equals ( TreeIter . Zero ) ) {
2007-03-09 14:22:43 +00:00
if ( Nodes . Count < = 0 )
return false ;
2009-03-04 20:33:12 +00:00
first_child = GetIter ( Nodes [ 0 ] as ITreeNode ) ;
} else {
ITreeNode node = GetNode ( parent ) ;
if ( node . ChildCount < = 0 )
return false ;
2007-03-09 14:22:43 +00:00
2009-03-04 20:33:12 +00:00
first_child = GetIter ( node [ 0 ] ) ;
}
2003-11-04 23:20:15 +00:00
return true ;
}
2009-03-04 20:33:12 +00:00
public bool IterHasChild ( Gtk . TreeIter iter )
{
return IterNChildren ( iter ) > 0 ;
2007-03-09 14:22:43 +00:00
}
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
public int IterNChildren ( Gtk . TreeIter iter )
{
if ( iter . Equals ( TreeIter . Zero ) )
2007-03-09 14:22:43 +00:00
return Nodes . Count ;
2009-03-04 20:33:12 +00:00
else
return GetNode ( iter ) . ChildCount ;
2007-03-09 14:22:43 +00:00
}
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
public bool IterNthChild ( out Gtk . TreeIter child , Gtk . TreeIter parent , int n )
{
child = TreeIter . Zero ;
2007-03-09 14:22:43 +00:00
2009-03-04 20:33:12 +00:00
if ( parent . Equals ( TreeIter . Zero ) ) {
2007-03-09 14:22:43 +00:00
if ( Nodes . Count < = n )
return false ;
2009-03-04 20:33:12 +00:00
child = GetIter ( Nodes [ n ] as ITreeNode ) ;
} else {
ITreeNode parent_node = GetNode ( parent ) ;
if ( parent_node . ChildCount < = n )
return false ;
child = GetIter ( parent_node [ n ] ) ;
2007-03-09 14:22:43 +00:00
}
2003-11-04 23:20:15 +00:00
return true ;
}
2009-03-04 20:33:12 +00:00
public bool IterParent ( out Gtk . TreeIter parent , Gtk . TreeIter child )
{
parent = TreeIter . Zero ;
ITreeNode child_node = GetNode ( child ) ;
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
if ( child_node . Parent = = null )
return false ;
else {
parent = GetIter ( child_node . Parent ) ;
return true ;
}
2007-03-09 14:22:43 +00:00
}
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
private IList Nodes {
get {
return nodes as IList ;
2003-11-04 23:20:15 +00:00
}
}
2004-12-16 20:30:43 +00:00
2009-03-04 20:33:12 +00:00
private void changed_cb ( object o , EventArgs args )
{
ITreeNode node = o as ITreeNode ;
model_adapter . EmitRowChanged ( GetPath ( node ) , GetIter ( node ) ) ;
2003-11-04 23:20:15 +00:00
}
2009-03-04 20:33:12 +00:00
private void EmitRowInserted ( ITreeNode node )
{
model_adapter . EmitRowInserted ( GetPath ( node ) , GetIter ( node ) ) ;
for ( int i = 0 ; i < node . ChildCount ; i + + )
EmitRowInserted ( node [ i ] ) ;
}
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
private void child_added_cb ( object sender , ITreeNode child )
{
AddNodeInternal ( child ) ;
EmitRowInserted ( child ) ;
}
2005-03-14 21:38:27 +00:00
2009-03-04 20:33:12 +00:00
private void child_deleted_cb ( object sender , ITreeNode child , int idx )
{
ITreeNode node = sender as ITreeNode ;
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
TreePath path = GetPath ( node ) ;
TreePath child_path = path . Copy ( ) ;
child_path . AppendIndex ( idx ) ;
2003-11-04 23:20:15 +00:00
2009-03-04 20:33:12 +00:00
model_adapter . EmitRowDeleted ( child_path ) ;
2003-11-18 20:13:39 +00:00
2009-03-04 20:33:12 +00:00
if ( node . ChildCount < = 0 )
model_adapter . EmitRowHasChildToggled ( GetPath ( node ) , GetIter ( node ) ) ;
}
2003-11-18 20:13:39 +00:00
2009-03-04 20:33:12 +00:00
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 ) ;
2003-11-18 20:13:39 +00:00
2009-03-04 20:33:12 +00:00
for ( int i = 0 ; i < node . ChildCount ; i + + )
AddNodeInternal ( node [ i ] ) ;
2003-11-18 20:13:39 +00:00
}
2009-03-04 20:33:12 +00:00
public void AddNode ( ITreeNode node )
{
nodes . Add ( node ) ;
AddNodeInternal ( node ) ;
EmitRowInserted ( node ) ;
}
2003-11-18 20:13:39 +00:00
2009-03-04 20:33:12 +00:00
public void AddNode ( ITreeNode node , int position )
{
nodes . Insert ( position , node ) ;
AddNodeInternal ( node ) ;
EmitRowInserted ( node ) ;
}
2004-08-31 18:19:32 +00:00
2009-03-04 20:33:12 +00:00
public void RemoveNode ( ITreeNode node )
{
int idx = nodes . IndexOf ( node ) ;
if ( idx < 0 )
return ;
nodes . Remove ( node ) ;
2004-12-17 20:32:33 +00:00
2009-03-04 20:33:12 +00:00
TreePath path = new TreePath ( ) ;
path . AppendIndex ( idx ) ;
2004-12-16 20:30:43 +00:00
2009-03-04 20:33:12 +00:00
model_adapter . EmitRowDeleted ( path ) ;
2004-12-16 20:30:43 +00:00
}
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 18:45:21 +00:00
2009-03-04 20:33:12 +00:00
public void Clear ( )
{
while ( nodes . Count > 0 )
RemoveNode ( ( ITreeNode ) nodes [ 0 ] ) ;
2004-08-31 18:19:32 +00:00
}
2005-04-26 13:45:27 +00:00
2009-03-04 20:33:12 +00:00
public IEnumerator GetEnumerator ( )
{
return nodes . GetEnumerator ( ) ;
}
2005-04-26 13:45:27 +00:00
}
2009-03-04 20:33:12 +00:00
}
2003-11-04 23:20:15 +00:00
}