[Gtk-sharp-list] Gtk.TreeModelFilterModifyFunc NOT working -> Patch
Christian Hoff
christian_hoff at gmx.net
Tue Jul 31 12:45:40 EDT 2007
Hi,
I have discovered a really serious bug in Gtk# appointing
Gtk.TreeModelFilterModifyFunc.
As you probably know it is possible to set a modify function to a
Gtk.TreeModelFilter to modify the display of the underlying
Gtk.TreeModel. In fact that means nothing different than creating a
different model based on the old one, but with different columns.
This can be very useful if you have a Gtk.TreeModel containing objects
of a custom datatype. If you want to expose this model on a Gtk.TreeView
you can either use a CellDataFunc to render the content of the TreeView
or you can create a new TreeModel based on the old one with some columns
containing data in a format that can be rendered by setting rendering
attributes via AddAttribute(for example of type System.String). The
second option can be done very easily and handy by using a ModifyFunc
that is called for each data access/cell in the new model.
A code example:
void xyz()
{
GLib.GType[] ColTypes = new GLib.GType[5];
ColTypes[0] = GLib.GType.Int;
ColTypes[1] = GLib.GType.String;
ColTypes[2] = GLib.GType.String;
ColTypes[3] = GLib.GType.String;
ColTypes[4] = GLib.GType.String;
treeview.SetModifyFunc(5, ColTypes, this.ModifyFunc);
}
private void ModifyFunc(Gtk.TreeModel model, Gtk.TreeIter iter,
GLib.Value val, System.Int32 ColID)
{
// Get corresponding iter(row) in the old model
Gtk.TreeIter MainIter;
TreeModel.GetIterFromString(out MainIter,
model.GetPath(iter).ToString());
// Fill the cell with the book's properties
switch(ColID)
{
case 0:
val.Val = (TreeModel.GetValue(MainIter, 0) as
Bestandsverwaltung.Database.DataStruct.Book).ID; break;
case 1:
val.Val = (TreeModel.GetValue(MainIter, 0) as
Bestandsverwaltung.Database.DataStruct.Book).BookType.Title;
break;
case 2:
val.Val = (TreeModel.GetValue(MainIter, 0) as
Bestandsverwaltung.Database.DataStruct.Book).BookType.Publisher;
break;
case 3:
val.Val = (TreeModel.GetValue(MainIter, 0) as
Bestandsverwaltung.Database.DataStruct.Book).BookType.Faculty.Name;
System.Console.WriteLine((System.String) val.Val);
break;
case 4:
val.Val = (TreeModel.GetValue(MainIter, 0) as
Bestandsverwaltung.Database.DataStruct.Book).DamageDescr;
System.Console.WriteLine((System.String) val.Val);
break;
}
}
As you see it is necessary to modify the parameter named val as it
contains the new content of the cell. *Therefore it needs to be passed
by reference and NOT by value* because otherwise the parameter(e.g. the
new content of this cell) cannot be modified. That means that the above
code snippet won't work and the content of the cell will always stay
uninitialized(or, to be correct, stay initialized with the default
value, which is 0 for a numeric datatype, "" for GLib.GType.String and
so on).
That's the reason why the ModifyFunc is not working. The delegate has to
be changed so that the val argument is passed by reference.
To do so, I have downloaded the sources of Gtk version 2.10 and also
made it to find and fix the error(and even a second one). It's hidden in
GtkSharp.TreeModelFilterModifyFuncNative.cs and
TreeModelFilterModifyFunc.cs in the /gtk/generated directory, which are
autogenerated by GAPI. However, I cannot commit a patch for this error
because I simply didn't have the time to get familiar enough with GAPI
to find out how the files are generated. But I modified them, recompiled
the assembly, installed it into the GAC via make install and finally
really got it working!
These are the new versions of the files:
*
GtkSharp.TreeModelFilterModifyFuncNative.cs:*
/// This file was generated by the Gtk# code generator.
// Any changes made will be lost if regenerated.
namespace GtkSharp {
using System;
#region Autogenerated code
[GLib.CDeclCallback]
internal delegate void TreeModelFilterModifyFuncNative(IntPtr model,
ref Gtk.TreeIter iter, ref GLib.Value value, int column, IntPtr data);
internal class TreeModelFilterModifyFuncWrapper {
public void NativeCallback (IntPtr model, ref Gtk.TreeIter iter,
ref GLib.Value value, int column, IntPtr data)
{
Gtk.TreeModel _arg0 = GLib.Object.GetObject(model) as
Gtk.TreeModel;
Gtk.TreeIter _arg1 = iter;
int _arg3 = column;
managed ( _arg0, _arg1, ref value, _arg3);
}
internal TreeModelFilterModifyFuncNative NativeDelegate;
Gtk.TreeModelFilterModifyFunc managed;
public TreeModelFilterModifyFuncWrapper
(Gtk.TreeModelFilterModifyFunc managed)
{
this.managed = managed;
if (managed != null)
NativeDelegate = new TreeModelFilterModifyFuncNative
(NativeCallback);
}
public static Gtk.TreeModelFilterModifyFunc GetManagedDelegate
(TreeModelFilterModifyFuncNative native)
{
if (native == null)
return null;
TreeModelFilterModifyFuncWrapper wrapper =
(TreeModelFilterModifyFuncWrapper) native.Target;
if (wrapper == null)
return null;
return wrapper.managed;
}
}
#endregion
}
*
*/*TreeModelFilterModifyFunc.cs:*
/// This file was generated by the Gtk# code generator.
// Any changes made will be lost if regenerated.
namespace Gtk {
using System;
public delegate void TreeModelFilterModifyFunc(Gtk.TreeModel model,
Gtk.TreeIter iter, ref GLib.Value value, int column);
}
/Everything I did in the TreeModelFilterModyFunc.cs file was to change
the delegate so that value is passed by reference. Therefore I also
needed to change GtkSharp.TreeModelFilterModifyFuncNative.cs. But my
first change wasn't enough because there is still another error hidden
in the second file:
/public void NativeCallback (IntPtr model, ref Gtk.TreeIter iter, ref
GLib.Value value, int column, IntPtr data)
{
Gtk.TreeModel _arg0 = GLib.Object.GetObject(model) as Gtk.TreeModel;
Gtk.TreeIter _arg1 = iter;
GLib.Value _arg2 = value;
int _arg3 = column;
managed ( _arg0, _arg1, ref _arg2, _arg3);
}/
When you assign _arg2 to value(/GLib.Value _arg2 = value;/), normally
both should be equal e.g. contain a reference to the same place in
memory as they are no value datatypes like System.String. But to my own
surprise this code didn't work. It is really necessary to pass the
variable value directly to the delegate functionas in the complete
listing further above.
After doing so, I have recompiled the assembly, modified my program and
suddenly the ModifyFunc worked! The assembly in which the error has been
fixed can be found here:
Hi,
I have discovered a really serious bug in Gtk# appointing
Gtk.TreeModelFilterModifyFunc.
As you probably know it is possible to set a modify function to a
Gtk.TreeModelFilter to modify the display of the underlying
Gtk.TreeModel. In fact that means nothing different than creating a
different model based on the old one, but with different columns.
This can be very useful if you have a Gtk.TreeModel containing objects
of a custom datatype. If you want to expose this model on a Gtk.TreeView
you can either use a CellDataFunc to render the content of the TreeView
or you can create a new TreeModel based on the old one with some columns
containing data in a format that can be rendered by setting rendering
attributes via AddAttribute(for example of type System.String). The
second option can be done very easily and handy by using a ModifyFunc
that is called for each data access/cell in the new model.
A code example:
void xyz()
{
GLib.GType[] ColTypes = new GLib.GType[5];
ColTypes[0] = GLib.GType.Int;
ColTypes[1] = GLib.GType.String;
ColTypes[2] = GLib.GType.String;
ColTypes[3] = GLib.GType.String;
ColTypes[4] = GLib.GType.String;
treeview.SetModifyFunc(5, ColTypes, this.ModifyFunc);
}
private void ModifyFunc(Gtk.TreeModel model, Gtk.TreeIter iter,
GLib.Value val, System.Int32 ColID)
{
// Get corresponding iter(row) in the old model
Gtk.TreeIter MainIter;
TreeModel.GetIterFromString(out MainIter,
model.GetPath(iter).ToString());
// Fill the cell with the book's properties
switch(ColID)
{
case 0:
val.Val = (TreeModel.GetValue(MainIter, 0) as
Bestandsverwaltung.Database.DataStruct.Book).ID; break;
case 1:
val.Val = (TreeModel.GetValue(MainIter, 0) as
Bestandsverwaltung.Database.DataStruct.Book).BookType.Title;
break;
case 2:
val.Val = (TreeModel.GetValue(MainIter, 0) as
Bestandsverwaltung.Database.DataStruct.Book).BookType.Publisher;
break;
case 3:
val.Val = (TreeModel.GetValue(MainIter, 0) as
Bestandsverwaltung.Database.DataStruct.Book).BookType.Faculty.Name;
System.Console.WriteLine((System.String) val.Val);
break;
case 4:
val.Val = (TreeModel.GetValue(MainIter, 0) as
Bestandsverwaltung.Database.DataStruct.Book).DamageDescr;
System.Console.WriteLine((System.String) val.Val);
break;
}
}
As you see it is necessary to modify the parameter named val as it
contains the new content of the cell. *Therefore it needs to be passed
by reference and NOT by value* because otherwise the parameter(e.g. the
new content of this cell) cannot be modified. That means that the above
code snippet won't work and the content of the cell will always stay
uninitialized(or, to be correct, stay initialized with the default
value, which is 0 for a numeric datatype, "" for GLib.GType.String and
so on).
That's the reason why the ModifyFunc is not working. The delegate has to
be changed so that the val argument is passed by reference.
To do so, I have downloaded the sources of Gtk version 2.10 and also
made it to find and fix the error(and even a second one). It's hidden in
GtkSharp.TreeModelFilterModifyFuncNative.cs and
TreeModelFilterModifyFunc.cs in the /gtk/generated directory, which are
autogenerated by GAPI. However, I cannot commit a patch for this error
because I simply didn't have the time to get familiar enough with GAPI
to find out how the files are generated. But I modified them, recompiled
the assembly, installed it into the GAC via make install and finally
really got it working!
These are the new versions of the files:
*
GtkSharp.TreeModelFilterModifyFuncNative.cs:*
/// This file was generated by the Gtk# code generator.
// Any changes made will be lost if regenerated.
namespace GtkSharp {
using System;
#region Autogenerated code
[GLib.CDeclCallback]
internal delegate void TreeModelFilterModifyFuncNative(IntPtr model,
ref Gtk.TreeIter iter, ref GLib.Value value, int column, IntPtr data);
internal class TreeModelFilterModifyFuncWrapper {
public void NativeCallback (IntPtr model, ref Gtk.TreeIter iter,
ref GLib.Value value, int column, IntPtr data)
{
Gtk.TreeModel _arg0 = GLib.Object.GetObject(model) as
Gtk.TreeModel;
Gtk.TreeIter _arg1 = iter;
int _arg3 = column;
managed ( _arg0, _arg1, ref value, _arg3);
}
internal TreeModelFilterModifyFuncNative NativeDelegate;
Gtk.TreeModelFilterModifyFunc managed;
public TreeModelFilterModifyFuncWrapper
(Gtk.TreeModelFilterModifyFunc managed)
{
this.managed = managed;
if (managed != null)
NativeDelegate = new TreeModelFilterModifyFuncNative
(NativeCallback);
}
public static Gtk.TreeModelFilterModifyFunc GetManagedDelegate
(TreeModelFilterModifyFuncNative native)
{
if (native == null)
return null;
TreeModelFilterModifyFuncWrapper wrapper =
(TreeModelFilterModifyFuncWrapper) native.Target;
if (wrapper == null)
return null;
return wrapper.managed;
}
}
#endregion
}
*
*/*TreeModelFilterModifyFunc.cs:*
/// This file was generated by the Gtk# code generator.
// Any changes made will be lost if regenerated.
namespace Gtk {
using System;
public delegate void TreeModelFilterModifyFunc(Gtk.TreeModel model,
Gtk.TreeIter iter, ref GLib.Value value, int column);
}
/Everything I did in the TreeModelFilterModyFunc.cs file was to change
the delegate so that value is passed by reference. Therefore I also
needed to change GtkSharp.TreeModelFilterModifyFuncNative.cs. But my
first change wasn't enough because there is still another error hidden
in the second file:
/public void NativeCallback (IntPtr model, ref Gtk.TreeIter iter, ref
GLib.Value value, int column, IntPtr data)
{
Gtk.TreeModel _arg0 = GLib.Object.GetObject(model) as Gtk.TreeModel;
Gtk.TreeIter _arg1 = iter;
GLib.Value _arg2 = value;
int _arg3 = column;
managed ( _arg0, _arg1, ref _arg2, _arg3);
}/
When you assign _arg2 to value(/GLib.Value _arg2 = value;/), normally
both should be equal e.g. contain a reference to the same place in
memory as they are no value datatypes like System.String. But to my own
surprise this code didn't work. It is really necessary to pass the
variable value directly to the delegate functionas in the complete
listing further above.
After doing so, I have recompiled the assembly, modified my program and
suddenly the ModifyFunc worked! I have attached the gtk-sharp.dll
assembly based on Gtk 2.10 where these errors are corrected can be found
here: http://www.gmx.de/mc/CEtA2LYY4TxOZNeZS9qDwVtFhPCp1E(after opening
the link in your browser you have to click on "GMX Media Center starten"
to see and download the file; I'm sorry for this, but the list doesn't
seem to accept such large attachments). Of course, fixing this error
with the modify func will break support for old programs using it. But I
don't think there are any; there is nearly no documentation to be found
on this function and when I searched for it I didn't find any example
for Mono(quiet logical ^^). That's why I really ask you to get this
fixed; If you want, I can also update the Mono documentation and post an
example for the correct use of this function.
However, who added this non-working code to the repository didn't test
it; otherwise the error would have been noticed because it occurs
everytime when using this function. Quiet a worse indicator for the code
quality in Gtk#.
Christian Hoff
Of course, fixing this error with the modify func will break support for
old programs using it. But I don't think there are any; there is nearly
no documentation to be found on this function and when I searched for it
I didn't find any example for Mono(quiet logical ^^). That's why I
really ask you to get this fixed; If you want, I can also update the
Mono documentation and post an example for the correct use of this
function.
However, who added this non-working code to the repository didn't test
it; otherwise the error would have been noticed because it occurs
everytime when using this function. Quiet a worse indicator for the code
quality in Gtk#.
Christian Hoff
More information about the Gtk-sharp-list
mailing list