[Gtk-sharp-list] The mail again(without formatting errors)

Christian Hoff christian_hoff at gmx.net
Tue Jul 31 12:53:27 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! 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

_______________________________________________
Gtk-sharp-list maillist  -  Gtk-sharp-list at lists.ximian.com
http://lists.ximian.com/mailman/listinfo/gtk-sharp-list



More information about the Gtk-sharp-list mailing list