diff --git a/ChangeLog b/ChangeLog index e915f8655..ed97c2aa2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2002-09-13 Ricardo Fernández Pascual + + * glade/HandlerNotFoundExeception.cs: Added. + * glade/Makefile.in + * glade/XML.custom: Support for autoconnecting signals using + reflection. + * glib/SignalAttribute.cs: Added. + * generator/Signal.cs: Mark events generated from glib signals + with the "Signal" attribute. + * sample/GladeTest.cs + * sample/Makefile.in + * sample/test.glade: Test of signal autoconnection. + 2002-09-12 Rachel Hestilow * sources/Gtk.metadata: Set null_ok on the callback argument diff --git a/generator/Signal.cs b/generator/Signal.cs index 08c5080d0..8ca54d45e 100644 --- a/generator/Signal.cs +++ b/generator/Signal.cs @@ -152,6 +152,7 @@ namespace GtkSharp.Generation { string argsname; string handler = GenHandler (out argsname); + sw.WriteLine("\t\t[GLib.Signal("+ cname + ")]"); sw.Write("\t\tpublic "); if (elem.HasAttribute("new_flag") || (container_type != null && container_type.GetSignalRecursively (Name) != null) || (implementor != null && implementor.GetSignalRecursively (Name) != null)) sw.Write("new "); diff --git a/glade/HandlerNotFoundExeception.cs b/glade/HandlerNotFoundExeception.cs new file mode 100644 index 000000000..8702cebb6 --- /dev/null +++ b/glade/HandlerNotFoundExeception.cs @@ -0,0 +1,88 @@ +// HandlerNotFoundException.cs +// +// Author: Ricardo Fernández Pascual +// +// (c) 2002 Ricardo Fernández Pascual + +namespace Glade { + + using System; + using System.Reflection; + using System.Runtime.Serialization; + + /// + /// Exception thrown when signal autoconnection fails. + /// + [Serializable] + public class HandlerNotFoundException : Exception + { + string handler_name; + string signal_name; + EventInfo evnt; + Type delegate_type; + + public HandlerNotFoundException (string handler_name, string signal_name, + EventInfo evnt, Type delegate_type) + { + this.handler_name = handler_name; + this.signal_name = signal_name; + this.evnt = evnt; + this.delegate_type = delegate_type; + } + + protected HandlerNotFoundException (SerializationInfo info, StreamingContext context) + : base (info, context) + { + handler_name = info.GetString ("HandlerName"); + signal_name = info.GetString ("SignalName"); + evnt = info.GetValue ("Event", typeof (EventInfo)) as EventInfo; + delegate_type = info.GetValue ("DelegateType", typeof (Type)) as Type; + } + + public override string Message + { + get { + return "No handler " + handler_name + " found for signal " + signal_name; + } + } + + public string HandlerName + { + get { + return handler_name; + } + } + + public string SignalName + { + get { + return signal_name; + } + } + + public EventInfo Event + { + get { + return evnt; + } + } + + public Type DelegateType + { + get { + return delegate_type; + } + } + + public override void GetObjectData (SerializationInfo info, StreamingContext context) + { + base.GetObjectData (info, context); + info.AddValue ("HandlerName", handler_name); + info.AddValue ("SignalName", signal_name); + info.AddValue ("Event", evnt); + info.AddValue ("DelegateType", delegate_type); + } + } + +} + diff --git a/glade/Makefile.in b/glade/Makefile.in index 73e87d6bd..0a4d60fa8 100755 --- a/glade/Makefile.in +++ b/glade/Makefile.in @@ -9,7 +9,7 @@ windows: linux: glade-sharp.dll -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' clean: diff --git a/glade/XML.custom b/glade/XML.custom index 671a1e947..ada4536d8 100644 --- a/glade/XML.custom +++ b/glade/XML.custom @@ -45,4 +45,153 @@ return ret; } + /* signal autoconnection using reflection */ + + /// Automatically connect signals + /// Connects the signals defined in the glade file with handler methods + /// provided by the given object. + public void Autoconnect (object handler) + { + SignalConnector sc = new SignalConnector (this, handler); + sc.Autoconnect (); + } + /// Automatically connect signals + /// Connects the signals defined in the glade file with static handler + /// methods provided by the given type. + public void Autoconnect (Type handler_class) + { + SignalConnector sc = new SignalConnector (this, handler_class); + sc.Autoconnect (); + } + + class SignalConnector + { + /* the Glade.XML object whose signals we want to connect */ + XML gxml; + + /* the object to look for handlers */ + object handler_object; + + /* the type to look for handlers if no object has been specified */ + Type handler_type; + + public SignalConnector (XML gxml, object handler) + { + this.gxml = gxml; + this.handler_object = handler; + this.handler_type = handler.GetType (); + } + + public SignalConnector (XML gxml, Type type) + { + this.gxml = gxml; + this.handler_object = null; + this.handler_type = type; + } + + delegate void RawXMLConnectFunc (string handler_name, IntPtr objekt, + string signal_name, string signal_data, + IntPtr connect_object, int after, IntPtr user_data); + + [DllImport("glade-2.0")] + static extern void glade_xml_signal_autoconnect_full (IntPtr raw, RawXMLConnectFunc func, + IntPtr user_data); + + public void Autoconnect () { + RawXMLConnectFunc cf = new RawXMLConnectFunc (ConnectFunc); + glade_xml_signal_autoconnect_full (gxml.Handle, cf, IntPtr.Zero); + } + + void ConnectFunc (string handler_name, IntPtr objekt_ptr, + string signal_name, string signal_data, + IntPtr connect_object_ptr, int after, IntPtr user_data) { + + GLib.Object objekt = GLib.Object.GetObject (objekt_ptr); + + /* if an connect_object_ptr is provided, use that as handler */ + object connect_object = + connect_object_ptr == IntPtr.Zero + ? handler_object + : GLib.Object.GetObject (connect_object_ptr); + + /* search for the event to connect */ + System.Reflection.MemberInfo[] evnts = objekt.GetType (). + FindMembers (System.Reflection.MemberTypes.Event, + System.Reflection.BindingFlags.Instance + | System.Reflection.BindingFlags.Static + | System.Reflection.BindingFlags.Public + | System.Reflection.BindingFlags.NonPublic, + signalFilter, signal_name); + foreach (System.Reflection.EventInfo ei in evnts) + { + bool connected = false; + System.Reflection.MethodInfo add = ei.GetAddMethod (); + System.Reflection.ParameterInfo[] addpi = add.GetParameters (); + if (addpi.Length == 1) + { /* this should be always true, unless there's something broken */ + Type delegate_type = addpi[0].ParameterType; + + /* look for an instance method */ + if (connect_object != null) try + { + Delegate d = Delegate.CreateDelegate + (delegate_type, connect_object, handler_name); + add.Invoke (objekt, new object[] { d } ); + connected = true; + } + catch (ArgumentException) + { + /* ignore if there is not such instance method */ + } + + /* look for a static method if no instance method has been found */ + if (!connected && handler_type != null) try + { + Delegate d = Delegate.CreateDelegate + (delegate_type, handler_type, handler_name); + add.Invoke (objekt, new object[] { d } ); + connected = true; + } + catch (ArgumentException) + { + /* ignore if there is not such static method */ + } + + if (!connected) + { + throw new HandlerNotFoundException (handler_name, signal_name, ei, delegate_type); + } + } + } + + } + + System.Reflection.MemberFilter signalFilter = new System.Reflection.MemberFilter (SignalFilter); + + /* matches events to GLib signal names */ + static bool SignalFilter (System.Reflection.MemberInfo m, object filterCriteria) + { + string signame = (filterCriteria as string); + object[] attrs = m.GetCustomAttributes (typeof (GLib.SignalAttribute), true); + if (attrs.Length > 0) + { + foreach (GLib.SignalAttribute a in attrs) + { + if (signame == a.CName) + { + return true; + } + } + return false; + } + else + { + /* this tries to match the names when no attibutes are present. + It is only a fallback. */ + signame = signame.ToLower ().Replace ("_", ""); + string evname = m.Name.ToLower (); + return signame == evname; + } + } + } diff --git a/glib/SignalAttribute.cs b/glib/SignalAttribute.cs new file mode 100644 index 000000000..d69fcb641 --- /dev/null +++ b/glib/SignalAttribute.cs @@ -0,0 +1,41 @@ +// +// SignalAttribute.cs +// +// Author: +// Ricardo Fernández Pascual +// +// (C) Ricardo Fernández Pascual +// + +namespace GLib { + + using System; + + /// + /// Marks events genrated from glib signals + /// + /// + /// + /// This attribute indentifies events generated from glib signals + /// and allows obtaining its original name. + /// + [Serializable] + public class SignalAttribute : Attribute + { + private string cname; + + public SignalAttribute (string cname) + { + this.cname = cname; + } + + private SignalAttribute () {} + + public string CName + { + get { + return cname; + } + } + } +} diff --git a/sample/GladeTest.cs b/sample/GladeTest.cs new file mode 100644 index 000000000..19c78eae5 --- /dev/null +++ b/sample/GladeTest.cs @@ -0,0 +1,50 @@ +// GladeViewer.cs - Tests for LibGlade in C# +// +// Author: Ricardo Fernández Pascual +// +// (c) 2002 Ricardo Fernández Pascual + +namespace GladeSamples { + using System; + + 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) + : base ("GladeTest", "0.1", Modules.UI, args, props) + { + Glade.XML gxml = new Glade.XML ("test.glade", "main_window", null); + gxml.Autoconnect (this); + } + + public void OnWindowDeleteEvent (object o, DeleteEventArgs args) + { + Quit (); + args.RetVal = true; + } + + public void OnButton1Clicked (Object b, EventArgs e) + { + Console.WriteLine ("Button 1 clicked"); + } + + public static void OnButton2Clicked (Object b, EventArgs e) + { + Console.WriteLine ("Button 2 clicked"); + } + + public void OnButton2Entered (Object b, EventArgs e) + { + Console.WriteLine ("Button 2 entered"); + } + } +} + diff --git a/sample/Makefile.in b/sample/Makefile.in index e68ba1b96..a020543ea 100755 --- a/sample/Makefile.in +++ b/sample/Makefile.in @@ -6,7 +6,7 @@ MCS=mcs @ENABLE_GLADE_TRUE@ GLADE_PATH=-L ../glade @ENABLE_GLADE_TRUE@ GLADE_ASSEMBLY=-r glade-sharp.dll -@ENABLE_GLADE_TRUE@ GLADE_TARGETS=glade-viewer.exe +@ENABLE_GLADE_TRUE@ GLADE_TARGETS=glade-viewer.exe glade-test.exe local_paths=-L ../glib -L ../pango -L ../atk -L ../gdk -L ../gtk $(GNOME_PATH) $(GLADE_PATH) all_assemblies=-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 System.Drawing @@ -50,6 +50,9 @@ treeviewdemo.exe: TreeViewDemo.cs glade-viewer.exe: GladeViewer.cs $(MCS) --unsafe -o glade-viewer.exe $(local_paths) $(all_assemblies) GladeViewer.cs +glade-test.exe: GladeTest.cs + $(MCS) --unsafe -o glade-test.exe $(local_paths) $(all_assemblies) GladeTest.cs + clean: rm -f *.exe diff --git a/sample/test.glade b/sample/test.glade new file mode 100644 index 000000000..3e203527e --- /dev/null +++ b/sample/test.glade @@ -0,0 +1,58 @@ + + + + + + + True + Glade# test + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + + + + + True + False + 0 + + + + True + True + Click here + True + GTK_RELIEF_NORMAL + + + + 0 + False + False + + + + + + True + True + Don't click here + True + GTK_RELIEF_NORMAL + + + + + 0 + False + False + + + + + + +