[vala] Introduce new signal connect/disconnect syntax



commit b08ab373e57021de2623b827afbf42ad5498aaa3
Author: Jürg Billeter <j bitron ch>
Date:   Wed Apr 15 00:15:29 2009 +0200

    Introduce new signal connect/disconnect syntax
    
    foo.clicked.connect (handler);
    
    Fixes bug 566847.
---
 gobject/valagsignalmodule.vala |  156 ++++++++++++++++++++++++++++++++++++++++
 tests/objects/signals.test     |   20 +++---
 vala/valamemberaccess.vala     |    2 +-
 vala/valasignal.vala           |    2 +
 vala/valasignaltype.vala       |   45 ++++++++++++
 5 files changed, 214 insertions(+), 11 deletions(-)

diff --git a/gobject/valagsignalmodule.vala b/gobject/valagsignalmodule.vala
index c073a24..12a4d0c 100644
--- a/gobject/valagsignalmodule.vala
+++ b/gobject/valagsignalmodule.vala
@@ -628,5 +628,161 @@ internal class Vala.GSignalModule : GObjectModule {
 			base.visit_member_access (expr);
 		}
 	}
+
+	public override void visit_method_call (MethodCall expr) {
+		var method_type = expr.call.value_type as MethodType;
+
+		if (method_type == null || !(method_type.method_symbol.parent_symbol is Signal)) {
+			// no signal connect/disconnect call
+			base.visit_method_call (expr);
+			return;
+		}
+
+		var sig = (Signal) method_type.method_symbol.parent_symbol;
+		var signal_access = ((MemberAccess) expr.call).inner;
+		var handler = expr.get_argument_list ().get (0);
+
+		signal_access.accept (codegen);
+		handler.accept (codegen);
+
+		var m = (Method) handler.symbol_reference;
+		var target_type_symbol = m.parent_symbol as TypeSymbol;
+
+		string connect_func;
+		bool disconnect = (method_type.method_symbol.name == "disconnect");
+
+		if (!disconnect) {
+			// connect
+			if (sig is DynamicSignal) {
+				connect_func = head.get_dynamic_signal_connect_wrapper_name ((DynamicSignal) sig);
+			} else {
+				if (m.binding == MemberBinding.INSTANCE) {
+					if (target_type_symbol != null && target_type_symbol.is_subtype_of (gobject_type)) {
+						connect_func = "g_signal_connect_object";
+					} else {
+						connect_func = "g_signal_connect";
+					}
+				} else {
+					connect_func = "g_signal_connect";
+				}
+			}
+		} else {
+			// disconnect
+			if (sig is DynamicSignal) {
+				connect_func = head.get_dynamic_signal_disconnect_wrapper_name ((DynamicSignal) sig);
+			} else {
+				connect_func = "g_signal_handlers_disconnect_matched";
+			}
+		}
+
+		var ccall = new CCodeFunctionCall (new CCodeIdentifier (connect_func));
+
+		string signal_detail = null;
+
+		// first argument: instance of sender
+		MemberAccess ma;
+		if (signal_access is ElementAccess) {
+			var ea = (ElementAccess) signal_access;
+			ma = (MemberAccess) ea.container;
+			var detail_expr = ea.get_indices ().get (0) as StringLiteral;
+			if (detail_expr == null) {
+				expr.error = true;
+				Report.error (detail_expr.source_reference, "internal error: only literal string details supported");
+				return;
+			}
+			signal_detail = detail_expr.eval ();
+		} else {
+			ma = (MemberAccess) signal_access;
+		}
+		if (ma.inner != null) {
+			ccall.add_argument ((CCodeExpression) get_ccodenode (ma.inner));
+		} else {
+			ccall.add_argument (new CCodeIdentifier ("self"));
+		}
+
+		if (sig is DynamicSignal) {
+			// dynamic_signal_connect or dynamic_signal_disconnect
+
+			// second argument: signal name
+			ccall.add_argument (new CCodeConstant ("\"%s\"".printf (sig.name)));
+		} else if (!disconnect) {
+			// g_signal_connect_object or g_signal_connect
+
+			// second argument: signal name
+			ccall.add_argument (sig.get_canonical_cconstant (signal_detail));
+		} else {
+			// g_signal_handlers_disconnect_matched
+
+			// second argument: mask
+			if (signal_detail == null) {
+				ccall.add_argument (new CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
+			} else {
+				ccall.add_argument (new CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
+			}
+
+			// get signal id
+			var ccomma = new CCodeCommaExpression ();
+			var temp_decl = get_temp_variable (uint_type);
+			temp_vars.insert (0, temp_decl);
+			var parse_call = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_parse_name"));
+			parse_call.add_argument (sig.get_canonical_cconstant (signal_detail));
+			var decl_type = (TypeSymbol) sig.parent_symbol;
+			parse_call.add_argument (new CCodeIdentifier (decl_type.get_type_id ()));
+			parse_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (temp_decl.name)));
+			if (signal_detail == null) {
+				parse_call.add_argument (new CCodeConstant ("NULL"));
+			} else {
+				var detail_temp_decl = get_temp_variable (gquark_type);
+				temp_vars.insert (0, detail_temp_decl);
+				parse_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (detail_temp_decl.name)));
+			}
+			parse_call.add_argument (new CCodeConstant ("FALSE"));
+			ccomma.append_expression (parse_call);
+			ccomma.append_expression (new CCodeIdentifier (temp_decl.name));
+
+			// third argument: signal_id
+			ccall.add_argument (ccomma);
+
+			// fourth argument: detail
+			ccall.add_argument (new CCodeConstant ("0"));
+			// fifth argument: closure
+			ccall.add_argument (new CCodeConstant ("NULL"));
+		}
+
+		// third resp. sixth argument: handler
+		ccall.add_argument (new CCodeCastExpression ((CCodeExpression) handler.ccodenode, "GCallback"));
+
+		if (m.binding == MemberBinding.INSTANCE) {
+			// g_signal_connect_object or g_signal_handlers_disconnect_matched
+			// or dynamic_signal_connect or dynamic_signal_disconnect
+
+			// fourth resp. seventh argument: object/user_data
+			if (handler is MemberAccess) {
+				var right_ma = (MemberAccess) handler;
+				if (right_ma.inner != null) {
+					ccall.add_argument ((CCodeExpression) right_ma.inner.ccodenode);
+				} else {
+					ccall.add_argument (new CCodeIdentifier ("self"));
+				}
+			} else if (handler is LambdaExpression) {
+				ccall.add_argument (new CCodeIdentifier ("self"));
+			}
+			if (!disconnect && !(sig is DynamicSignal)
+			    && target_type_symbol != null && target_type_symbol.is_subtype_of (gobject_type)) {
+				// g_signal_connect_object
+
+				// fifth argument: connect_flags
+				ccall.add_argument (new CCodeConstant ("0"));
+			}
+		} else {
+			// g_signal_connect or g_signal_handlers_disconnect_matched
+			// or dynamic_signal_connect or dynamic_signal_disconnect
+
+			// fourth resp. seventh argument: user_data
+			ccall.add_argument (new CCodeConstant ("NULL"));
+		}
+
+		expr.ccodenode = ccall;
+	}
 }
 
diff --git a/tests/objects/signals.test b/tests/objects/signals.test
index ced2f3a..69aa19b 100644
--- a/tests/objects/signals.test
+++ b/tests/objects/signals.test
@@ -25,15 +25,15 @@ class Maman.Bar : Object {
 		
 		var foo = new Foo ();
 		
-		foo.activated += (foo, b) => {
+		foo.activated.connect ((foo, b) => {
 			if (b) {
 				stdout.printf (" 8");
 			} else {
 				stdout.printf (" 4");
 			}
-		};
+		});
 
-		foo.activated += activated;
+		foo.activated.connect (activated);
 
 		stdout.printf (" 3");
 		
@@ -41,7 +41,7 @@ class Maman.Bar : Object {
 
 		stdout.printf (" 6");
 		
-		foo.activated -= activated;
+		foo.activated.disconnect (activated);
 
 		stdout.printf (" 7");
 
@@ -90,9 +90,9 @@ class Maman.UserBar : Object {
 		
 		var foo = new UserFoo ();
 		
-		foo.activated += (foo, i1, i2) => {
+		foo.activated.connect ((foo, i1, i2) => {
 			stdout.printf (" %d", i1 + i2);
-		};
+		});
 
 		stdout.printf (" 3");
 		
@@ -113,15 +113,15 @@ class Maman.ReturnBar : Object {
 
 		var foo = new ReturnFoo ();
 
-		foo.int_activated += (foo, arg) => {
+		foo.int_activated.connect ((foo, arg) => {
 			stdout.printf (" %d", arg);
 			return arg + 1;
-		};
+		});
 
-		foo.string_activated += (foo, arg) => {
+		foo.string_activated.connect ((foo, arg) => {
 			stdout.printf (arg);
 			return " 6";
-		};
+		});
 
 		stdout.printf (" %d", foo.int_activated (3));
 
diff --git a/vala/valamemberaccess.vala b/vala/valamemberaccess.vala
index 0469026..320ff50 100644
--- a/vala/valamemberaccess.vala
+++ b/vala/valamemberaccess.vala
@@ -564,7 +564,7 @@ public class Vala.MemberAccess : Expression {
 					base_method = m;
 				}
 
-				if (instance && base_method.parent_symbol != null) {
+				if (instance && base_method.parent_symbol is TypeSymbol) {
 					inner.target_type = analyzer.get_data_type_for_symbol ((TypeSymbol) base_method.parent_symbol);
 				}
 			} else if (symbol_reference is Property) {
diff --git a/vala/valasignal.vala b/vala/valasignal.vala
index 1059cb9..58f54ef 100644
--- a/vala/valasignal.vala
+++ b/vala/valasignal.vala
@@ -99,6 +99,8 @@ public class Vala.Signal : Member, Lockable {
 
 		var generated_delegate = new Delegate (null, actual_return_type);
 		generated_delegate.has_target = true;
+		generated_delegate.access = SymbolAccessibility.PUBLIC;
+		generated_delegate.owner = scope;
 
 		// sender parameter is never null and doesn't own its value
 		var sender_param_type = sender_type.copy ();
diff --git a/vala/valasignaltype.vala b/vala/valasignaltype.vala
index e934403..19430a8 100644
--- a/vala/valasignaltype.vala
+++ b/vala/valasignaltype.vala
@@ -29,6 +29,9 @@ using Gee;
 public class Vala.SignalType : DataType {
 	public Signal signal_symbol { get; set; }
 
+	Method? connect_method;
+	Method? disconnect_method;
+
 	public SignalType (Signal signal_symbol) {
 		this.signal_symbol = signal_symbol;
 	}
@@ -56,4 +59,46 @@ public class Vala.SignalType : DataType {
 	public override string to_qualified_string (Scope? scope) {
 		return signal_symbol.get_full_name ();
 	}
+
+	DelegateType get_handler_type () {
+		var sender_type = new ObjectType ((ObjectTypeSymbol) signal_symbol.parent_symbol);
+		return new DelegateType (signal_symbol.get_delegate (sender_type, this));
+	}
+
+	Method get_connect_method () {
+		if (connect_method == null) {
+			connect_method = new Method ("connect", new VoidType ());
+			connect_method.access = SymbolAccessibility.PUBLIC;
+			connect_method.external = true;
+			connect_method.owner = signal_symbol.scope;
+			connect_method.add_parameter (new FormalParameter ("handler", get_handler_type ()));
+		}
+		return connect_method;
+	}
+
+	Method get_disconnect_method () {
+		if (disconnect_method == null) {
+			disconnect_method = new Method ("disconnect", new VoidType ());
+			disconnect_method.access = SymbolAccessibility.PUBLIC;
+			disconnect_method.external = true;
+			disconnect_method.owner = signal_symbol.scope;
+			disconnect_method.add_parameter (new FormalParameter ("handler", get_handler_type ()));
+		}
+		return disconnect_method;
+	}
+
+	public override Symbol? get_member (string member_name) {
+		if (member_name == "connect") {
+			return get_connect_method ();
+		} else if (member_name == "disconnect") {
+			return get_disconnect_method ();
+		}
+		return null;
+	}
+
+	public override Gee.List<Symbol> get_symbols () {
+		var symbols = new ArrayList<Symbol> ();
+		symbols.add (signal_symbol);
+		return symbols;
+	}
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]