[Gtk-sharp-list] Autoconnection of signals in Glade# (updated)

Ricardo Fernández Pascual rfp1@ono.com
07 Sep 2002 21:33:26 +0200


--=-iS6WyCAUhuX5SGJ3Q/D+
Content-Type: text/plain; charset=ISO-8859-15
Content-Transfer-Encoding: quoted-printable


   I have updated the patch. I think it is ready now. Let me know of any
problem.

(About the bug in the runtime: I can't create a testcase that crashes
mono. All I get is the expected NullReferenceException. It only crashes
with the sample program, and not always).

--=20
Ricardo Fern=E1ndez Pascual
ric@users.sourceforge.net
Murcia. Espa=F1a.

--=-iS6WyCAUhuX5SGJ3Q/D+
Content-Description: sample/GladeTest.cs
Content-Disposition: attachment; filename=GladeTest.cs
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset=ISO-8859-15

// GladeViewer.cs - Tests for LibGlade in C#
//
// Author: Ricardo Fern=E1ndez Pascual <ric@users.sourceforge.net>
//
// (c) 2002 Ricardo Fern=E1ndez Pascual

namespace GladeSamples {
	using System;
=09
	using Gtk;
	using Gnome;
	using Glade;
	using GtkSharp;

	public class GladeTest : Program
	{
		public static void Main (string[] args)
		{
			new GladeTest (args).Run ();
		}
		public GladeTest (string[] args, params object[] props)=20
			: base ("GladeTest", "0.1", Modules.UI, args, props)
		{
			Glade.XML gxml =3D new Glade.XML ("test.glade", "main_window", null);
			gxml.Autoconnect (this);
		}

		public void OnWindowDeleteEvent (object o, DeleteEventArgs args)=20
		{
			Quit ();
			args.RetVal =3D true;
		}
	=09
		public void OnButton1Clicked (Object b, EventArgs e)=20
		{
			Console.WriteLine ("Button 1 clicked");
		}

		public static void OnButton2Clicked (Object b, EventArgs e)=20
		{
			Console.WriteLine ("Button 2 clicked");
		}
	=09
		public void OnButton2Entered (Object b, EventArgs e)=20
		{
			Console.WriteLine ("Button 2 entered");
		}
	}
}


--=-iS6WyCAUhuX5SGJ3Q/D+
Content-Description: sample/test.glade
Content-Type: application/x-glade
Content-Disposition: attachment; filename=test.glade
Content-Transfer-Encoding: base64

PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PiA8IS0tKi0gbW9kZTogeG1sIC0q
LS0+CjwhRE9DVFlQRSBnbGFkZS1pbnRlcmZhY2UgU1lTVEVNICJodHRwOi8vZ2xhZGUuZ25vbWUu
b3JnL2dsYWRlLTIuMC5kdGQiPgoKPGdsYWRlLWludGVyZmFjZT4KCjx3aWRnZXQgY2xhc3M9Ikd0
a1dpbmRvdyIgaWQ9Im1haW5fd2luZG93Ij4KICA8cHJvcGVydHkgbmFtZT0idmlzaWJsZSI+VHJ1
ZTwvcHJvcGVydHk+CiAgPHByb3BlcnR5IG5hbWU9InRpdGxlIiB0cmFuc2xhdGFibGU9InllcyI+
R2xhZGUjIHRlc3Q8L3Byb3BlcnR5PgogIDxwcm9wZXJ0eSBuYW1lPSJ0eXBlIj5HVEtfV0lORE9X
X1RPUExFVkVMPC9wcm9wZXJ0eT4KICA8cHJvcGVydHkgbmFtZT0id2luZG93X3Bvc2l0aW9uIj5H
VEtfV0lOX1BPU19OT05FPC9wcm9wZXJ0eT4KICA8cHJvcGVydHkgbmFtZT0ibW9kYWwiPkZhbHNl
PC9wcm9wZXJ0eT4KICA8cHJvcGVydHkgbmFtZT0icmVzaXphYmxlIj5UcnVlPC9wcm9wZXJ0eT4K
ICA8cHJvcGVydHkgbmFtZT0iZGVzdHJveV93aXRoX3BhcmVudCI+RmFsc2U8L3Byb3BlcnR5Pgog
IDxzaWduYWwgbmFtZT0iZGVsZXRlX2V2ZW50IiBoYW5kbGVyPSJPbldpbmRvd0RlbGV0ZUV2ZW50
IiBsYXN0X21vZGlmaWNhdGlvbl90aW1lPSJUdWUsIDAzIFNlcCAyMDAyIDE0OjQ3OjU3IEdNVCIv
PgoKICA8Y2hpbGQ+CiAgICA8d2lkZ2V0IGNsYXNzPSJHdGtWQm94IiBpZD0idmJveDEiPgogICAg
ICA8cHJvcGVydHkgbmFtZT0idmlzaWJsZSI+VHJ1ZTwvcHJvcGVydHk+CiAgICAgIDxwcm9wZXJ0
eSBuYW1lPSJob21vZ2VuZW91cyI+RmFsc2U8L3Byb3BlcnR5PgogICAgICA8cHJvcGVydHkgbmFt
ZT0ic3BhY2luZyI+MDwvcHJvcGVydHk+CgogICAgICA8Y2hpbGQ+Cgk8d2lkZ2V0IGNsYXNzPSJH
dGtCdXR0b24iIGlkPSJidXR0b24xIj4KCSAgPHByb3BlcnR5IG5hbWU9InZpc2libGUiPlRydWU8
L3Byb3BlcnR5PgoJICA8cHJvcGVydHkgbmFtZT0iY2FuX2ZvY3VzIj5UcnVlPC9wcm9wZXJ0eT4K
CSAgPHByb3BlcnR5IG5hbWU9ImxhYmVsIiB0cmFuc2xhdGFibGU9InllcyI+Q2xpY2sgaGVyZTwv
cHJvcGVydHk+CgkgIDxwcm9wZXJ0eSBuYW1lPSJ1c2VfdW5kZXJsaW5lIj5UcnVlPC9wcm9wZXJ0
eT4KCSAgPHByb3BlcnR5IG5hbWU9InJlbGllZiI+R1RLX1JFTElFRl9OT1JNQUw8L3Byb3BlcnR5
PgoJICA8c2lnbmFsIG5hbWU9ImNsaWNrZWQiIGhhbmRsZXI9Ik9uQnV0dG9uMUNsaWNrZWQiIGxh
c3RfbW9kaWZpY2F0aW9uX3RpbWU9IlR1ZSwgMTMgQXVnIDIwMDIgMTU6MjQ6MzUgR01UIi8+Cgk8
L3dpZGdldD4KCTxwYWNraW5nPgoJICA8cHJvcGVydHkgbmFtZT0icGFkZGluZyI+MDwvcHJvcGVy
dHk+CgkgIDxwcm9wZXJ0eSBuYW1lPSJleHBhbmQiPkZhbHNlPC9wcm9wZXJ0eT4KCSAgPHByb3Bl
cnR5IG5hbWU9ImZpbGwiPkZhbHNlPC9wcm9wZXJ0eT4KCTwvcGFja2luZz4KICAgICAgPC9jaGls
ZD4KCiAgICAgIDxjaGlsZD4KCTx3aWRnZXQgY2xhc3M9Ikd0a0J1dHRvbiIgaWQ9ImJ1dHRvbjIi
PgoJICA8cHJvcGVydHkgbmFtZT0idmlzaWJsZSI+VHJ1ZTwvcHJvcGVydHk+CgkgIDxwcm9wZXJ0
eSBuYW1lPSJjYW5fZm9jdXMiPlRydWU8L3Byb3BlcnR5PgoJICA8cHJvcGVydHkgbmFtZT0ibGFi
ZWwiIHRyYW5zbGF0YWJsZT0ieWVzIj5Eb24ndCBjbGljayBoZXJlPC9wcm9wZXJ0eT4KCSAgPHBy
b3BlcnR5IG5hbWU9InVzZV91bmRlcmxpbmUiPlRydWU8L3Byb3BlcnR5PgoJICA8cHJvcGVydHkg
bmFtZT0icmVsaWVmIj5HVEtfUkVMSUVGX05PUk1BTDwvcHJvcGVydHk+CgkgIDxzaWduYWwgbmFt
ZT0iY2xpY2tlZCIgaGFuZGxlcj0iT25CdXR0b24yQ2xpY2tlZCIgbGFzdF9tb2RpZmljYXRpb25f
dGltZT0iVHVlLCAxMyBBdWcgMjAwMiAxNToyNDozNSBHTVQiLz4KCSAgPHNpZ25hbCBuYW1lPSJl
bnRlciIgaGFuZGxlcj0iT25CdXR0b24yRW50ZXJlZCIgbGFzdF9tb2RpZmljYXRpb25fdGltZT0i
V2VkLCAwNCBTZXAgMjAwMiAxNDowNDo0MCBHTVQiLz4KCTwvd2lkZ2V0PgoJPHBhY2tpbmc+Cgkg
IDxwcm9wZXJ0eSBuYW1lPSJwYWRkaW5nIj4wPC9wcm9wZXJ0eT4KCSAgPHByb3BlcnR5IG5hbWU9
ImV4cGFuZCI+RmFsc2U8L3Byb3BlcnR5PgoJICA8cHJvcGVydHkgbmFtZT0iZmlsbCI+RmFsc2U8
L3Byb3BlcnR5PgoJPC9wYWNraW5nPgogICAgICA8L2NoaWxkPgogICAgPC93aWRnZXQ+CiAgPC9j
aGlsZD4KPC93aWRnZXQ+Cgo8L2dsYWRlLWludGVyZmFjZT4K

--=-iS6WyCAUhuX5SGJ3Q/D+
Content-Description: 
Content-Disposition: inline; filename=ac.diff
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset=ISO-8859-15

Index: generator/Signal.cs
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /mono/gtk-sharp/generator/Signal.cs,v
retrieving revision 1.7
diff -u -r1.7 Signal.cs
--- generator/Signal.cs	20 Aug 2002 19:56:14 -0000	1.7
+++ generator/Signal.cs	7 Sep 2002 19:23:57 -0000
@@ -152,6 +152,7 @@
 			string argsname;=0D
 			string handler =3D GenHandler (out argsname);=0D
=20=0D
+			sw.WriteLine("\t\t[GLib.Signal("+ cname + ")]");=0D
 			sw.Write("\t\tpublic ");=0D
 			if (elem.HasAttribute("new_flag") || (container_type !=3D null && conta=
iner_type.GetSignalRecursively (Name) !=3D null) || (implementor !=3D null =
&& implementor.GetSignalRecursively (Name) !=3D null))=0D
 				sw.Write("new ");=0D
Index: glade/Makefile.in
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /mono/gtk-sharp/glade/Makefile.in,v
retrieving revision 1.2
diff -u -r1.2 Makefile.in
--- glade/Makefile.in	23 Aug 2002 21:22:15 -0000	1.2
+++ glade/Makefile.in	7 Sep 2002 19:23:57 -0000
@@ -9,7 +9,7 @@
=20
 linux: glade-sharp.dll
=20
-glade-sharp.dll: generated/*.cs
+glade-sharp.dll: *.cs generated/*.cs
 	$(MCS) --unsafe --target library -r System.Drawing -L ../glib -L ../pango=
 -L ../atk -L ../gdk -L ../gtk -r glib-sharp.dll -r pango-sharp.dll -r atk-=
sharp.dll -r gdk-sharp.dll -r gtk-sharp.dll -o glade-sharp.dll --recurse '*=
.cs'
=20
 clean:
Index: glade/XML.custom
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /mono/gtk-sharp/glade/XML.custom,v
retrieving revision 1.1
diff -u -r1.1 XML.custom
--- glade/XML.custom	12 Aug 2002 19:14:43 -0000	1.1
+++ glade/XML.custom	7 Sep 2002 19:23:58 -0000
@@ -45,4 +45,153 @@
 			return ret;
 		}
=20
+		/* signal autoconnection using reflection */
+
+		/// <summary>Automatically connect signals</summary>
+		/// <remarks>Connects the signals defined in the glade file with handler=
 methods
+		///     provided by the given object.</remarks>
+		public void Autoconnect (object handler)
+		{
+			SignalConnector sc =3D new SignalConnector (this, handler);
+			sc.Autoconnect ();
+		}
 	=09
+		/// <summary>Automatically connect signals</summary>
+		/// <remarks>Connects the signals defined in the glade file with static =
handler=20
+		///     methods provided by the given type.</remarks>
+		public void Autoconnect (Type handler_class)
+		{
+			SignalConnector sc =3D new SignalConnector (this, handler_class);
+			sc.Autoconnect ();
+		}
+
+		class SignalConnector=20
+		{
+			/* the Glade.XML object whose signals we want to connect */
+			XML gxml;
+=09
+			/* the object to look for handlers */
+			object handler_object;
+=09
+			/* the type to look for handlers if no object has been specified */
+			Type handler_type;
+=09
+			public SignalConnector (XML gxml, object handler)=20
+			{
+				this.gxml =3D gxml;
+				this.handler_object =3D handler;
+				this.handler_type =3D handler.GetType ();
+			}
+=09
+			public SignalConnector (XML gxml, Type type)
+			{
+				this.gxml =3D gxml;
+				this.handler_object =3D null;
+				this.handler_type =3D type;
+			}
+		=09
+			delegate void RawXMLConnectFunc (string handler_name, IntPtr objekt,=20
+						         string signal_name, string signal_data,=20
+						         IntPtr connect_object, int after, IntPtr user_data);
+		=09
+			[DllImport("glade-2.0")]
+			static extern void glade_xml_signal_autoconnect_full (IntPtr raw, RawXM=
LConnectFunc func,
+									      IntPtr user_data);
+		=09
+			public void Autoconnect () {
+				RawXMLConnectFunc cf =3D new RawXMLConnectFunc (ConnectFunc);
+				glade_xml_signal_autoconnect_full (gxml.Handle, cf, IntPtr.Zero);
+			}
+		=09
+			void ConnectFunc (string handler_name, IntPtr objekt_ptr,=20
+					  string signal_name, string signal_data,=20
+					  IntPtr connect_object_ptr, int after, IntPtr user_data) {
+			=09
+				GLib.Object objekt =3D GLib.Object.GetObject (objekt_ptr);
+=09
+				/* if an connect_object_ptr is provided, use that as handler */
+				object connect_object =3D=20
+					connect_object_ptr =3D=3D IntPtr.Zero=20
+						? handler_object
+						: GLib.Object.GetObject (connect_object_ptr);
+			=09
+				/* search for the event to connect */
+				System.Reflection.MemberInfo[] evnts =3D objekt.GetType ().
+					FindMembers (System.Reflection.MemberTypes.Event,=20
+					     System.Reflection.BindingFlags.Instance=20
+					     | System.Reflection.BindingFlags.Static
+					     | System.Reflection.BindingFlags.Public=20
+					     | System.Reflection.BindingFlags.NonPublic,=20
+					     signalFilter, signal_name);
+				foreach (System.Reflection.EventInfo ei in evnts)=20
+				{
+					bool connected =3D false;
+					System.Reflection.MethodInfo add =3D ei.GetAddMethod ();
+					System.Reflection.ParameterInfo[] addpi =3D add.GetParameters ();
+					if (addpi.Length =3D=3D 1)=20
+					{ /* this should be always true, unless there's something broken */
+						Type delegate_type =3D addpi[0].ParameterType;
+=09
+						/* look for an instance method */
+						if (connect_object !=3D null) try=20
+						{
+							Delegate d =3D Delegate.CreateDelegate=20
+								(delegate_type, connect_object, handler_name);
+							add.Invoke (objekt, new object[] { d } );
+							connected =3D true;
+						}=20
+						catch (ArgumentException)=20
+						{
+							/* ignore if there is not such instance method */
+						}
+					=09
+						/* look for a static method if no instance method has been found */
+						if (!connected && handler_type !=3D null) try=20
+						{
+							Delegate d =3D Delegate.CreateDelegate=20
+								(delegate_type, handler_type, handler_name);
+							add.Invoke (objekt, new object[] { d } );
+							connected =3D true;
+						}=20
+						catch (ArgumentException)=20
+						{
+							/* ignore if there is not such static method */
+						}
+=09
+						if (!connected)=20
+						{
+							throw new HandlerNotFoundException (handler_name, signal_name, ei, =
delegate_type);
+						}
+					}
+				}
+=09
+			}
+=09
+			System.Reflection.MemberFilter signalFilter =3D new System.Reflection.M=
emberFilter (SignalFilter);
+		=09
+			/* matches events to GLib signal names */
+			static bool SignalFilter (System.Reflection.MemberInfo m, object filter=
Criteria)=20
+			{
+				string signame =3D (filterCriteria as string);
+				object[] attrs =3D m.GetCustomAttributes (typeof (GLib.SignalAttribute=
), true);
+				if (attrs.Length > 0)
+				{
+					foreach (GLib.SignalAttribute a in attrs)
+					{
+						if (signame =3D=3D a.CName)
+						{
+							return true;
+						}
+					}
+					return false;
+				}
+				else
+				{
+					/* this tries to match the names when no attibutes are present.
+					   It is only a fallback. */
+					signame =3D signame.ToLower ().Replace ("_", "");
+					string evname =3D m.Name.ToLower ();
+					return signame =3D=3D evname;
+				}
+			}
+		}
Index: sample/Makefile.in
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /mono/gtk-sharp/sample/Makefile.in,v
retrieving revision 1.16
diff -u -r1.16 Makefile.in
--- sample/Makefile.in	16 Aug 2002 05:00:31 -0000	1.16
+++ sample/Makefile.in	7 Sep 2002 19:23:59 -0000
@@ -6,7 +6,7 @@
=20
 @ENABLE_GLADE_TRUE@ GLADE_PATH=3D-L ../glade
 @ENABLE_GLADE_TRUE@ GLADE_ASSEMBLY=3D-r glade-sharp.dll
-@ENABLE_GLADE_TRUE@ GLADE_TARGETS=3Dglade-viewer.exe
+@ENABLE_GLADE_TRUE@ GLADE_TARGETS=3Dglade-viewer.exe glade-test.exe
=20
 local_paths=3D-L ../glib -L ../pango -L ../atk -L ../gdk -L ../gtk $(GNOME=
_PATH) $(GLADE_PATH)=20
 all_assemblies=3D-r glib-sharp.dll -r pango-sharp.dll -r atk-sharp.dll -r =
gdk-sharp.dll -r gtk-sharp.dll $(GNOME_ASSEMBLY) $(GLADE_ASSEMBLY) -r Syste=
m.Drawing
@@ -49,6 +49,9 @@
=20
 glade-viewer.exe: GladeViewer.cs
 	$(MCS) --unsafe -o glade-viewer.exe $(local_paths) $(all_assemblies) Glad=
eViewer.cs
+
+glade-test.exe: GladeTest.cs
+	$(MCS) --unsafe -o glade-test.exe $(local_paths) $(all_assemblies) GladeT=
est.cs
=20
 clean:
 	rm -f *.exe

--=-iS6WyCAUhuX5SGJ3Q/D+
Content-Description: glib/SignalAttribute.cs
Content-Disposition: inline; filename=SignalAttribute.cs
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset=ISO-8859-15

//
// SignalAttribute.cs
//
// Author:
//   Ricardo Fern=E1ndez Pascual <ric@users.sourceforge.net>
//
// (C) Ricardo Fern=E1ndez Pascual <ric@users.sourceforge.net>
//

namespace GLib {

	using System;

	/// <summary>
	///   Marks events genrated from glib signals
	/// </summary>
	///
	/// <remarks>
	///   This attribute indentifies events generated from glib signals=20
	///   and allows obtaining its original name.
	/// </remarks>
	[Serializable]
	public class SignalAttribute : Attribute=20
	{
		private string cname;

		public SignalAttribute (string cname)
		{
			this.cname =3D cname;
		}

		private SignalAttribute () {}

		public string CName=20
		{
			get {
				return cname;
			}
		}
	}
}

--=-iS6WyCAUhuX5SGJ3Q/D+
Content-Description: glade/HandlerNotFoundExeception.cs
Content-Disposition: inline; filename=HandlerNotFoundExeception.cs
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset=ISO-8859-15

// HandlerNotFoundException.cs=20
//
// Author: Ricardo Fern=E1ndez Pascual <ric@users.sourceforge.net>
//
// (c) 2002 Ricardo Fern=E1ndez Pascual

namespace Glade {

	using System;
	using System.Reflection;
	using System.Runtime.Serialization;

	/// <summary>
	///	Exception thrown when signal autoconnection fails.
	/// </summary>
	[Serializable]
	public class HandlerNotFoundException : Exception=20
	{
		string handler_name;
		string signal_name;
		EventInfo evnt;
		Type delegate_type;

		public HandlerNotFoundException (string handler_name, string signal_name,=
=20
						 EventInfo evnt, Type delegate_type)
		{
			this.handler_name =3D handler_name;
			this.signal_name =3D signal_name;
			this.evnt =3D evnt;
			this.delegate_type =3D delegate_type;
		}				=20

		protected HandlerNotFoundException (SerializationInfo info, StreamingCont=
ext context)
			: base (info, context)
		{
			handler_name =3D info.GetString ("HandlerName");
			signal_name =3D info.GetString ("SignalName");
			evnt =3D info.GetValue ("Event", typeof (EventInfo)) as EventInfo;
			delegate_type =3D info.GetValue ("DelegateType", typeof (Type)) as Type;
		}

		public override string Message
		{
			get {=20
				return "No handler " + handler_name + " found for signal " + signal_nam=
e;
			}
		}

		public string HandlerName
		{
			get {=20
				return handler_name;
			}
		}

		public string SignalName
		{
			get {
				return signal_name;
			}
		}

		public EventInfo Event
		{
			get {
				return evnt;
			}
		}

		public Type DelegateType=20
		{
			get {
				return delegate_type;
			}
		}

		public override void GetObjectData (SerializationInfo info, StreamingCont=
ext context)
		{
			base.GetObjectData (info, context);
			info.AddValue ("HandlerName", handler_name);
			info.AddValue ("SignalName", signal_name);
			info.AddValue ("Event", evnt);
			info.AddValue ("DelegateType", delegate_type);
		}
	}

}


--=-iS6WyCAUhuX5SGJ3Q/D+--