1// Builder.custom - customizations to Gtk.Builder 2// 3// Authors: Stephane Delcroix <stephane@delcroix.org> 4// The biggest part of this code is adapted from glade#, by 5// Ricardo Fernández Pascual <ric@users.sourceforge.net> 6// Rachel Hestilow <hestilow@ximian.com> 7// 8// Copyright (c) 2008, 2009 Novell, Inc. 9// 10// This program is free software; you can redistribute it and/or 11// modify it under the terms of version 2 of the Lesser GNU General 12// Public License as published by the Free Software Foundation. 13// 14// This program is distributed in the hope that it will be useful, 15// but WITHOUT ANY WARRANTY; without even the implied warranty of 16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17// Lesser General Public License for more details. 18// 19// You should have received a copy of the GNU Lesser General Public 20// License along with this program; if not, write to the 21// Free Software Foundation, Inc., 59 Temple Place - Suite 330, 22// Boston, MA 02111-1307, USA. 23 24[System.Serializable] 25public class HandlerNotFoundException : SystemException 26{ 27 string handler_name; 28 string signal_name; 29 System.Reflection.EventInfo evnt; 30 Type delegate_type; 31 32 public HandlerNotFoundException (string handler_name, string signal_name, 33 System.Reflection.EventInfo evnt, Type delegate_type) 34 : this (handler_name, signal_name, evnt, delegate_type, null) 35 { 36 } 37 38 public HandlerNotFoundException (string handler_name, string signal_name, 39 System.Reflection.EventInfo evnt, Type delegate_type, Exception inner) 40 : base ("No handler " + handler_name + " found for signal " + signal_name, 41 inner) 42 { 43 this.handler_name = handler_name; 44 this.signal_name = signal_name; 45 this.evnt = evnt; 46 this.delegate_type = delegate_type; 47 } 48 49 public HandlerNotFoundException (string message, string handler_name, string signal_name, 50 System.Reflection.EventInfo evnt, Type delegate_type) 51 : base ((message != null) ? message : "No handler " + handler_name + " found for signal " + signal_name, 52 null) 53 { 54 this.handler_name = handler_name; 55 this.signal_name = signal_name; 56 this.evnt = evnt; 57 this.delegate_type = delegate_type; 58 } 59 60 protected HandlerNotFoundException (System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) 61 : base (info, context) 62 { 63 handler_name = info.GetString ("HandlerName"); 64 signal_name = info.GetString ("SignalName"); 65 evnt = info.GetValue ("Event", typeof (System.Reflection.EventInfo)) as System.Reflection.EventInfo; 66 delegate_type = info.GetValue ("DelegateType", typeof (Type)) as Type; 67 } 68 69 public string HandlerName 70 { 71 get { 72 return handler_name; 73 } 74 } 75 76 public string SignalName 77 { 78 get { 79 return signal_name; 80 } 81 } 82 83 public System.Reflection.EventInfo Event 84 { 85 get { 86 return evnt; 87 } 88 } 89 90 public Type DelegateType 91 { 92 get { 93 return delegate_type; 94 } 95 } 96 97 public override void GetObjectData (System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) 98 { 99 base.GetObjectData (info, context); 100 info.AddValue ("HandlerName", handler_name); 101 info.AddValue ("SignalName", signal_name); 102 info.AddValue ("Event", evnt); 103 info.AddValue ("DelegateType", delegate_type); 104 } 105} 106 107 108[AttributeUsage (AttributeTargets.Field)] 109public class ObjectAttribute : Attribute 110{ 111 private string name; 112 private bool specified; 113 114 public ObjectAttribute (string name) 115 { 116 specified = true; 117 this.name = name; 118 } 119 120 public ObjectAttribute () 121 { 122 specified = false; 123 } 124 125 public string Name 126 { 127 get { return name; } 128 } 129 130 public bool Specified 131 { 132 get { return specified; } 133 } 134} 135 136public IntPtr GetRawObject(string name) { 137 IntPtr native_name = GLib.Marshaller.StringToPtrGStrdup (name); 138 IntPtr raw_ret = gtk_builder_get_object(Handle, native_name); 139 GLib.Marshaller.Free (native_name); 140 return raw_ret; 141} 142 143public Builder (System.IO.Stream s) : this (s, null) 144{ 145} 146 147public Builder (System.IO.Stream s, string translation_domain) 148{ 149 if (s == null) 150 throw new ArgumentNullException ("s"); 151 152 int size = (int) s.Length; 153 byte[] buffer = new byte[size]; 154 s.Read (buffer, 0, size); 155 s.Close (); 156 157 AddFromString(System.Text.Encoding.UTF8.GetString (buffer)); 158 159 TranslationDomain = translation_domain; 160} 161 162public Builder (string resource_name) : this (resource_name, null) 163{ 164} 165 166public Builder (string resource_name, string translation_domain) : this (System.Reflection.Assembly.GetEntryAssembly (), resource_name, translation_domain) 167{ 168} 169 170public Builder (System.Reflection.Assembly assembly, string resource_name, string translation_domain) : this () 171{ 172 if (GetType() != typeof (Builder)) 173 throw new InvalidOperationException ("Cannot chain to this constructor from subclasses."); 174 175 if (assembly == null) 176 assembly = System.Reflection.Assembly.GetCallingAssembly (); 177 178 System.IO.Stream s = assembly.GetManifestResourceStream (resource_name); 179 if (s == null) 180 throw new ArgumentException ("Cannot get resource file '" + resource_name + "'", 181 "resource_name"); 182 183 int size = (int) s.Length; 184 byte[] buffer = new byte[size]; 185 s.Read (buffer, 0, size); 186 s.Close (); 187 188 AddFromString(System.Text.Encoding.UTF8.GetString (buffer)); 189 190 TranslationDomain = translation_domain; 191} 192 193public void Autoconnect (object handler) 194{ 195 BindFields (handler); 196 (new SignalConnector (this, handler)).ConnectSignals (); 197} 198 199public void Autoconnect (Type handler_class) 200{ 201 BindFields (handler_class); 202 (new SignalConnector (this, handler_class)).ConnectSignals (); 203} 204 205class SignalConnector 206{ 207 Builder builder; 208 Type handler_type; 209 object handler; 210 211 public SignalConnector (Builder builder, object handler) 212 { 213 this.builder = builder; 214 this.handler = handler; 215 handler_type = handler.GetType (); 216 } 217 218 public SignalConnector (Builder builder, Type handler_type) 219 { 220 this.builder = builder; 221 this.handler = null; 222 this.handler_type = handler_type; 223 } 224 225 [DllImport("libgtk-win32-2.0-0.dll")] 226 static extern void gtk_builder_connect_signals_full(IntPtr raw, GtkBeansSharp.BuilderConnectFuncNative func, IntPtr user_data); 227 228 public void ConnectSignals() { 229 GtkBeansSharp.BuilderConnectFuncWrapper func_wrapper = new GtkBeansSharp.BuilderConnectFuncWrapper (new GtkBeans.BuilderConnectFunc (ConnectFunc)); 230 gtk_builder_connect_signals_full(builder.Handle, func_wrapper.NativeDelegate, IntPtr.Zero); 231 } 232 233 public void ConnectFunc (Builder builder, GLib.Object objekt, string signal_name, string handler_name, GLib.Object connect_object, GLib.ConnectFlags flags) 234 { 235 /* search for the event to connect */ 236 System.Reflection.MemberInfo[] evnts = objekt.GetType (). 237 FindMembers (System.Reflection.MemberTypes.Event, 238 System.Reflection.BindingFlags.Instance 239 | System.Reflection.BindingFlags.Static 240 | System.Reflection.BindingFlags.Public 241 | System.Reflection.BindingFlags.NonPublic, 242 new System.Reflection.MemberFilter (SignalFilter), signal_name); 243 foreach (System.Reflection.EventInfo ei in evnts) { 244 bool connected = false; 245 System.Reflection.MethodInfo add = ei.GetAddMethod (); 246 System.Reflection.ParameterInfo[] addpi = add.GetParameters (); 247 if (addpi.Length == 1) { /* this should be always true, unless there's something broken */ 248 Type delegate_type = addpi[0].ParameterType; 249 250 /* look for an instance method */ 251 if (connect_object != null || handler != null) 252 try { 253 Delegate d = Delegate.CreateDelegate (delegate_type, connect_object != null ? connect_object : handler, handler_name); 254 add.Invoke (objekt, new object[] { d } ); 255 connected = true; 256 } catch (ArgumentException) { /* ignore if there is not such instance method */ 257 } 258 259 /* look for a static method if no instance method has been found */ 260 if (!connected && handler_type != null) 261 try { 262 Delegate d = Delegate.CreateDelegate (delegate_type, handler_type, handler_name); 263 add.Invoke (objekt, new object[] { d } ); 264 connected = true; 265 } catch (ArgumentException) { /* ignore if there is not such static method */ 266 } 267 268 if (!connected) { 269 string msg = ExplainError (ei.Name, delegate_type, handler_type, handler_name); 270 throw new HandlerNotFoundException (msg, handler_name, signal_name, ei, delegate_type); 271 } 272 } 273 } 274 } 275 276 static bool SignalFilter (System.Reflection.MemberInfo m, object filterCriteria) 277 { 278 string signame = (filterCriteria as string); 279 object[] attrs = m.GetCustomAttributes (typeof (GLib.SignalAttribute), false); 280 if (attrs.Length > 0) 281 { 282 foreach (GLib.SignalAttribute a in attrs) 283 { 284 if (signame == a.CName) 285 { 286 return true; 287 } 288 } 289 return false; 290 } 291 else 292 { 293 /* this tries to match the names when no attibutes are present. 294 It is only a fallback. */ 295 signame = signame.ToLower ().Replace ("_", ""); 296 string evname = m.Name.ToLower (); 297 return signame == evname; 298 } 299 } 300 301 static string GetSignature (System.Reflection.MethodInfo method) 302 { 303 if (method == null) 304 return null; 305 306 System.Reflection.ParameterInfo [] parameters = method.GetParameters (); 307 System.Text.StringBuilder sb = new System.Text.StringBuilder (); 308 sb.Append ('('); 309 foreach (System.Reflection.ParameterInfo info in parameters) { 310 sb.Append (info.ParameterType.ToString ()); 311 sb.Append (','); 312 } 313 if (sb.Length != 0) 314 sb.Length--; 315 316 sb.Append (')'); 317 return sb.ToString (); 318 } 319 320 static string GetSignature (Type delegate_type) 321 { 322 System.Reflection.MethodInfo method = delegate_type.GetMethod ("Invoke"); 323 return GetSignature (method); 324 } 325 326 const System.Reflection.BindingFlags flags = System.Reflection.BindingFlags.NonPublic | 327 System.Reflection.BindingFlags.Public | 328 System.Reflection.BindingFlags.Static | 329 System.Reflection.BindingFlags.Instance; 330 static string GetSignature (Type klass, string method_name) 331 { 332 try { 333 System.Reflection.MethodInfo method = klass.GetMethod (method_name, flags); 334 return GetSignature (method); 335 } catch { 336 // May be more than one method with that name and none matches 337 return null; 338 } 339 } 340 341 342 static string ExplainError (string event_name, Type deleg, Type klass, string method) 343 { 344 if (deleg == null || klass == null || method == null) 345 return null; 346 347 System.Text.StringBuilder sb = new System.Text.StringBuilder (); 348 string expected = GetSignature (deleg); 349 string actual = GetSignature (klass, method); 350 if (actual == null) 351 return null; 352 sb.AppendFormat ("The handler for the event {0} should take '{1}', " + 353 "but the signature of the provided handler ('{2}') is '{3}'\n", 354 event_name, expected, method, actual); 355 return sb.ToString (); 356 } 357 358} 359 360 361void BindFields (object target) 362{ 363 BindFields (target, target.GetType ()); 364} 365 366void BindFields (Type type) 367{ 368 BindFields (null, type); 369} 370 371void BindFields (object target, Type type) 372{ 373 System.Reflection.BindingFlags flags = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.DeclaredOnly; 374 if (target != null) 375 flags |= System.Reflection.BindingFlags.Instance; 376 else 377 flags |= System.Reflection.BindingFlags.Static; 378 379 do { 380 System.Reflection.FieldInfo[] fields = type.GetFields (flags); 381 if (fields == null) 382 return; 383 384 foreach (System.Reflection.FieldInfo field in fields) 385 { 386 object[] attrs = field.GetCustomAttributes (typeof (ObjectAttribute), false); 387 if (attrs == null || attrs.Length == 0) 388 continue; 389 // The widget to field binding must be 1:1, so only check 390 // the first attribute. 391 ObjectAttribute attr = (ObjectAttribute) attrs[0]; 392 GLib.Object gobject; 393 if (attr.Specified) 394 gobject = GetObject (attr.Name); 395 else 396 gobject = GetObject (field.Name); 397 398 if (gobject != null) 399 try { 400 field.SetValue (target, gobject, flags, null, null); 401 } catch (Exception e) { 402 Console.WriteLine ("Unable to set value for field " + field.Name); 403 throw e; 404 } 405 } 406 type = type.BaseType; 407 } 408 while (type != typeof(object) && type != null); 409} 410