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

Attachment: 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  -  Gtk-sharp-list@lists.ximian.com
http://lists.ximian.com/mailman/listinfo/gtk-sharp-list

Reply via email to