Hi,I have added a new feature to Gtk# which makes it possible to register managed properties within the GObject type system.
A scenario where this feature proves really useful is when developing custom cell renderers and you want to set an attribute mapping to make it display the data of a tree model. So after having the attached patch applied(with "patch -p0 > patchfile" from the gtk-sharp svn directory) it is possible to write
MyTreeView.AppendColumn("European article number", EANRenderer, "ean", 0);
or
MyColumn.AddAttribute(EANRenderer, "ean", 0);
And in the cell renderer you could write:
[GLib.RegisterPropertyAttribute("ean")]
public System.Int64 EAN {
get {
// ....
}
set {
// ....
}
}
The patch was really a lot of work(although it is not as much new code
as I have expected) and I would be great if you commit it to SVN or give
me any feedback on the patch. I have also included a demo application
with MonoDevelop which is attached, too.
Christian Hoff
CellRendererAttributeTest.tar.gz
Description: application/gzip
diff -Naur ../gtk-sharp-modified/glib/glue/object.c ./glib/glue/object.c
--- ../gtk-sharp-modified/glib/glue/object.c 2008-05-25 12:09:44.000000000 +0200
+++ ./glib/glue/object.c 2008-05-25 19:31:00.000000000 +0200
@@ -18,12 +18,12 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
-
#include <glib-object.h>
-
/* Forward declarations */
int gtksharp_object_get_ref_count (GObject *obj);
GObject *gtksharp_object_newv (GType type, gint cnt, gchar **names, GValue *vals);
+void gtksharp_override_property_handlers(GType type, gpointer get_property_cb, gpointer set_property_cb);
+GParamSpec *gtksharp_register_property(GType type, const gchar *name, guint property_id, GType property_type, gboolean CanRead, gboolean CanWrite);
/* */
int
@@ -53,3 +53,105 @@
return result;
}
+// Override the default get_property and set_property accessors of the GObject class
+void
+gtksharp_override_property_handlers(GType type, gpointer get_property_cb, gpointer set_property_cb) {
+ // Peek the object's class structure
+ GObjectClass *type_class = g_type_class_peek (type);
+ if(!type_class)
+ type_class = g_type_class_ref (type);
+
+ type_class->get_property = get_property_cb;
+ type_class->set_property = set_property_cb;
+}
+
+GParamSpec *
+gtksharp_register_property(GType type, const gchar *name, guint property_id, GType property_type, gboolean CanRead, gboolean CanWrite) {
+ // Peek the object's class structure
+ GObjectClass *type_class = g_type_class_peek (type);
+ if(!type_class)
+ type_class = g_type_class_ref (type);
+ // Create the param flags of the property
+ // Specifying G_PARAM_CONSTRUCT is neccessary; otherwise gobject would set the property to a default value
+ GParamFlags property_flags = 0;
+ if(CanRead)
+ property_flags |= G_PARAM_READABLE;
+ if(CanWrite)
+ property_flags |= G_PARAM_WRITABLE;
+
+
+ gchar* property_nick = g_strconcat ("Access ", name, NULL);
+ gchar* property_blurb = g_strconcat ("To cut it short: I don't know anything about ", name, NULL);
+
+ /* Create the ParamSpec for the property
+ * These are used to hold default values and to validate values
+ * Both is not needed since the check for invalid values takes place in the managed set accessor of the property and properties do not
+ * contain default values. Therefore the ParamSpecs must allow every value that can be assigned to the property type.
+ * Furthermore the default value that is specified in the constructors will never be used and assigned to the property;
+ * they are not relevant, but have to be passed
+ */
+ GParamSpec *param_spec;
+
+ switch(property_type) {
+ case G_TYPE_CHAR:
+ param_spec = g_param_spec_char (name, property_nick, property_blurb, G_MININT8, G_MAXINT8, 0, property_flags);
+ break;
+ case G_TYPE_UCHAR:
+ param_spec = g_param_spec_uchar (name, property_nick, property_blurb, 0, G_MAXUINT8, 0, property_flags);
+ break;
+ case G_TYPE_BOOLEAN:
+ param_spec = g_param_spec_boolean (name, property_nick, property_blurb, FALSE, property_flags);
+ break;
+ case G_TYPE_INT:
+ param_spec = g_param_spec_int (name, property_nick, property_blurb, G_MININT, G_MAXINT, 0, property_flags);
+ break;
+ case G_TYPE_UINT:
+ param_spec = g_param_spec_uint (name, property_nick, property_blurb, 0, G_MAXUINT, 0, property_flags);
+ break;
+ case G_TYPE_LONG:
+ param_spec = g_param_spec_long (name, property_nick, property_blurb, G_MINLONG, G_MAXLONG, 0, property_flags);
+ break;
+ case G_TYPE_ULONG:
+ param_spec = g_param_spec_ulong (name, property_nick, property_blurb, 0, G_MAXULONG, 0, property_flags);
+ break;
+ case G_TYPE_INT64:
+ param_spec = g_param_spec_int64 (name, property_nick, property_blurb, G_MININT64, G_MAXINT64, 0, property_flags);
+ break;
+ case G_TYPE_UINT64:
+ param_spec = g_param_spec_uint64 (name, property_nick, property_blurb, 0, G_MAXUINT64, 0, property_flags);
+ break;
+ /* case G_TYPE_ENUM:
+ * case G_TYPE_FLAGS:
+ * TODO: Implement both G_TYPE_ENUM and G_TYPE_FLAGS
+ * My problem: Both g_param_spec_enum and g_param_spec_flags expect default property values and the members of the enum seemingly cannot be enumerated
+ */
+ case G_TYPE_FLOAT:
+ param_spec = g_param_spec_float (name, property_nick, property_blurb, G_MINFLOAT, G_MAXFLOAT, 0, property_flags);
+ break;
+ case G_TYPE_DOUBLE:
+ param_spec = g_param_spec_double (name, property_nick, property_blurb, G_MINDOUBLE, G_MAXDOUBLE, 0, property_flags);
+ break;
+ case G_TYPE_STRING:
+ param_spec = g_param_spec_string (name, property_nick, property_blurb, NULL, property_flags);
+ break;
+ case G_TYPE_POINTER:
+ param_spec = g_param_spec_pointer (name, property_nick, property_blurb, property_flags);
+ break;
+ case G_TYPE_BOXED:
+ param_spec = g_param_spec_boxed (name, property_nick, property_blurb, property_type, property_flags);
+ break;
+ case G_TYPE_PARAM:
+ break;
+ case G_TYPE_OBJECT:
+ param_spec = g_param_spec_object (name, property_nick, property_blurb, property_type, property_flags);
+ break;
+ default:
+ // The type not supported
+ return NULL;
+ }
+ g_free (property_nick);
+ g_free (property_blurb);
+
+ g_object_class_install_property (type_class, property_id, param_spec);
+ return param_spec;
+}
diff -Naur ../gtk-sharp-modified/glib/Makefile.am ./glib/Makefile.am
--- ../gtk-sharp-modified/glib/Makefile.am 2008-05-25 12:09:45.000000000 +0200
+++ ./glib/Makefile.am 2008-05-25 19:17:42.000000000 +0200
@@ -55,6 +55,7 @@
ObjectManager.cs \
Opaque.cs \
PropertyAttribute.cs \
+ RegisterPropertyAttribute.cs \
Signal.cs \
SignalArgs.cs \
SignalAttribute.cs \
diff -Naur ../gtk-sharp-modified/glib/Object.cs ./glib/Object.cs
--- ../gtk-sharp-modified/glib/Object.cs 2008-05-25 12:09:45.000000000 +0200
+++ ./glib/Object.cs 2008-05-25 19:21:40.000000000 +0200
@@ -173,6 +173,70 @@
minfo.Invoke (null, parms);
}
}
+
+ /* This hashtable holds all the properties that have been registered in the native vtable
+ *
+ * Key: The pointer to the ParamSpec of the property
+ * Value: The corresponding PropertyInfo object
+ */
+ static Hashtable VTableProperties = new Hashtable();
+
+ [GLib.CDeclCallback]
+ delegate void GetPropertyDelegate (IntPtr GObject, uint property_id, ref GLib.Value value, IntPtr pspec);
+
+ [GLib.CDeclCallback]
+ delegate void SetPropertyDelegate (IntPtr GObject, uint property_id, ref GLib.Value value, IntPtr pspec);
+
+ [DllImport ("glibsharpglue-2")]
+ static extern void gtksharp_override_property_handlers(IntPtr type, GetPropertyDelegate get_property_cb, SetPropertyDelegate set_property_cb);
+
+ [DllImport ("glibsharpglue-2")]
+ static extern IntPtr gtksharp_register_property(IntPtr type, IntPtr name, uint property_id, IntPtr property_type, bool CanRead, bool CanWrite);
+
+ private static void RegisterVTableProperties(GType gtype, System.Type t)
+ {
+ if(!typeof(GLib.Object).IsAssignableFrom(t))
+ // Class does not derive from GLib.Object -> No properties can be registered
+ return;
+
+ // Range of allowed property IDs in Gtk starts with 1, not with zero!
+ uint property_index = 1;
+
+ bool property_handlers_overridden = false;
+ foreach (PropertyInfo pinfo in t.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly)) {
+ foreach (object attr in pinfo.GetCustomAttributes (typeof (RegisterPropertyAttribute), false)) {
+ RegisterPropertyAttribute vtable_attr = attr as RegisterPropertyAttribute;
+ if(pinfo.GetIndexParameters().Length > 0)
+ throw(new InvalidOperationException(String.Format("GLib.RegisterPropertyAttribute cannot be applied to property {0} of type {1} because the property expects one or more indexed parameters", pinfo.Name, t.FullName)));
+
+ if(!property_handlers_overridden) {
+ gtksharp_override_property_handlers(gtype.Val, GetPropertyCallback, SetPropertyCallback);
+ property_handlers_overridden = true;
+ }
+
+ IntPtr VTableNameNative = GLib.Marshaller.StringToPtrGStrdup(vtable_attr.VTableName);
+ IntPtr ParamSpec = gtksharp_register_property(gtype.Val, VTableNameNative, property_index, ((GType) pinfo.PropertyType).Val, pinfo.CanRead, pinfo.CanWrite);
+ GLib.Marshaller.Free(VTableNameNative);
+ if(ParamSpec == IntPtr.Zero)
+ // The GType of the property is not supported
+ throw(new InvalidOperationException(String.Format("GLib.RegisterPropertyAttribute cannot be applied to property {0} of type {1} because the return type of the property is not supported", pinfo.Name, t.FullName)));
+
+ VTableProperties.Add(ParamSpec, pinfo);
+ property_index++;
+ break;
+ }
+ }
+ }
+
+ private static void GetPropertyCallback(IntPtr GObject, uint property_id, ref GLib.Value value, IntPtr ParamSpec) {
+ GLib.Object obj = GLib.Object.GetObject(GObject, false);
+ value.Val = (VTableProperties[ParamSpec] as PropertyInfo).GetValue(obj, new System.Object[0]);
+ }
+
+ private static void SetPropertyCallback(IntPtr GObject, uint property_id, ref GLib.Value value, IntPtr ParamSpec) {
+ GLib.Object obj = GLib.Object.GetObject(GObject, false);
+ (VTableProperties[ParamSpec] as PropertyInfo).SetValue(obj, value.Val, new System.Object[0]);
+ }
[DllImport("libgobject-2.0-0.dll")]
static extern void g_type_add_interface_static (IntPtr gtype, IntPtr iface_type, ref GInterfaceInfo info);
@@ -227,6 +291,7 @@
GType gtype = new GType (gtksharp_register_type (native_name, parent_gtype.Val));
GLib.Marshaller.Free (native_name);
GLib.GType.Register (gtype, t);
+ RegisterVTableProperties(gtype, t);
ConnectDefaultHandlers (gtype, t);
InvokeClassInitializers (gtype, t);
AddInterfaces (gtype, t);
diff -Naur ../gtk-sharp-modified/glib/RegisterPropertyAttribute.cs ./glib/RegisterPropertyAttribute.cs
--- ../gtk-sharp-modified/glib/RegisterPropertyAttribute.cs 1970-01-01 01:00:00.000000000 +0100
+++ ./glib/RegisterPropertyAttribute.cs 2008-05-21 14:59:44.000000000 +0200
@@ -0,0 +1,42 @@
+// RegisterPropertyAttribute.cs - Attribute which can be applied to properties that are to be registered in the gtype's vtable
+//
+// Copyright (c) 2008 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 GLib {
+
+ using System;
+
+ [AttributeUsage (AttributeTargets.Property)]
+ public sealed class RegisterPropertyAttribute : Attribute {
+ string vtable_name;
+
+ public RegisterPropertyAttribute (string vtable_name)
+ {
+ this.VTableName = vtable_name;
+ }
+
+ public string VTableName {
+ get {
+ return vtable_name;
+ }
+ set {
+ vtable_name = value;
+ }
+ }
+ }
+}
_______________________________________________ Gtk-sharp-list maillist - [email protected] http://lists.ximian.com/mailman/listinfo/gtk-sharp-list
