com gtk/php-gtk: * Re-implement GladeXML::signal_autoconn ect() (too many changes) * Add GladeXML: :signal_autoconnect_instance() * Adjust GladeXM L::signal_connect() to use 'replace' type connection when appropriate: ext/libglade/libg lade.overrides
[email protected] (David Soria Parra) Sat, 05 Nov 2005 08:45:07 +0000
| Newsgroups | php.gtk.cvs |
|---|---|
| Message-ID | <[email protected]> |
Commit: d89e0cfadc7c788f7b316bb9383e70a8663dce23 Author: Andrei Zmievski <[email protected]> Sat, 5 Nov 2005 08:45:07 +0000 Parents: fb4b283e6c200a1da24c6410ba84f319a866de60 Branches: master Link: http://git.php.net/?p=gtk/php-gtk.git;a=commitdiff;h=d89e0cfadc7c788f7b316bb9383e70a8663dce23 Log: * Re-implement GladeXML::signal_autoconnect() (too many changes) * Add GladeXML::signal_autoconnect_instance() * Adjust GladeXML::signal_connect() to use 'replace' type connection when appropriate Changed paths: M ext/libglade/libglade.overrides
diff_d89e0cfadc7c788f7b316bb9383e70a8663dce23.txt
(text/plain, 12 KB)
d89e0cfadc7c788f7b316bb9383e70a8663dce23
diff --git a/ext/libglade/libglade.overrides b/ext/libglade/libglade.overrides
index 2c4df0c..420a5f3 100644
--- a/ext/libglade/libglade.overrides
+++ b/ext/libglade/libglade.overrides
@@ -1,9 +1,18 @@
-/* vim: set ft=cpp: */
+/* vim: set ft=cpp fdm=marker et: */
%%
headers
#include <glade/glade.h>
#include "php_gtk_api.h"
+typedef struct {
+ zval *instance;
+ zval *extra;
+} phpg_autoc_t;
+
+static void phpg_glade_signal_connect_auto(const gchar *handler_name, GObject *object,
+ const gchar *signal_name, const gchar *signal_data,
+ GObject *connect_object, gboolean after, phpg_autoc_t *data);
+
%% {{{ ignores [confirmed]
%%
ignore-glob
@@ -99,35 +108,24 @@ PHP_METHOD
%%
override glade_xml_signal_connect
-static void glade_signal_marshal(const gchar *handler_name, GObject *object,
- const gchar *signal_name, const gchar *signal_data,
- GObject *connect_object, gboolean after,
- gpointer *data)
+static void phpg_glade_signal_connect_one(const gchar *handler_name, GObject *object,
+ const gchar *signal_name, const gchar *signal_data,
+ GObject *connect_object, gboolean after,
+ gpointer *data)
{
phpg_cb_data_t *cbd = (phpg_cb_data_t *)data;
char *callback_name;
- int handler_id, n_args = 0;
+ int handler_id;
GClosure *closure = NULL;
zval *php_object = NULL, *php_connect_object = NULL;
- zval *hash = NULL, ***args = NULL;
TSRMLS_FETCH();
- if (!zend_is_callable(cbd->callback, 0, &callback_name)) {
- php_error(E_WARNING, "unable to call GladeXML callback '%s' specified in %s on line %ld\n",
- callback_name, cbd->src_filename, cbd->src_lineno);
- efree(callback_name);
- return;
- }
-
if (connect_object) {
phpg_gobject_new(&php_connect_object, (GObject*)connect_object TSRMLS_CC);
- args = php_gtk_hash_as_array_offset(cbd->user_args, 1, &n_args);
- args[0] = &php_connect_object;
- hash = php_gtk_array_as_hash(args, n_args, 0, n_args);
- closure = phpg_closure_new(cbd->callback, hash, PHPG_CONNECT_OBJECT TSRMLS_CC);
- efree(args);
+ closure = phpg_closure_new(cbd->callback, cbd->user_args, PHPG_CONNECT_REPLACE, php_connect_object TSRMLS_CC);
+ zval_ptr_dtor(&php_connect_object);
} else {
- closure = phpg_closure_new(cbd->callback, cbd->user_args, PHPG_CONNECT_NORMAL TSRMLS_CC);
+ closure = phpg_closure_new(cbd->callback, cbd->user_args, PHPG_CONNECT_NORMAL, NULL TSRMLS_CC);
}
phpg_gobject_new(&php_object, (GObject*)object TSRMLS_CC);
@@ -135,10 +133,10 @@ static void glade_signal_marshal(const gchar *handler_name, GObject *object,
handler_id = g_signal_connect_closure(object, signal_name, closure, after);
efree(callback_name);
- efree(php_object);
+ zval_ptr_dtor(&php_object);
}
-PHP_METHOD(glade_xml_signal_connect)
+PHP_METHOD
{
char *handlername = NULL;
zval *callback = NULL, *extra = NULL;
@@ -154,7 +152,7 @@ PHP_METHOD(glade_xml_signal_connect)
data = phpg_cb_data_new(callback, extra TSRMLS_CC);
glade_xml_signal_connect_full(GLADE_XML(PHPG_GOBJECT(this_ptr)), handlername,
- (GladeXMLConnectFunc)glade_signal_marshal, data);
+ (GladeXMLConnectFunc)phpg_glade_signal_connect_one, data);
if (free_handlername) g_free(handlername);
phpg_cb_data_destroy(data);
@@ -164,74 +162,168 @@ PHP_METHOD(glade_xml_signal_connect)
%%
override glade_xml_signal_autoconnect
-static void glade_auto_signal_marshal(const gchar *handler_name, GObject *object,
- const gchar *signal_name, const gchar *signal_data,
- GObject *connect_object, gboolean after,
- zval *data)
+static void phpg_glade_signal_connect_auto(const gchar *handler_name, GObject *object,
+ const gchar *signal_name, const gchar *signal_data,
+ GObject *connect_object, gboolean after, phpg_autoc_t *data)
{
- zval *callback = NULL, *hash = NULL, ***args = NULL;
- zval *php_object = NULL, *php_connect_object = NULL;
+ zval *callback = NULL, *php_object = NULL, *user_args = NULL;
+ zval **callback_data = NULL, **callback_ptr = NULL;
GClosure *closure = NULL;
- int handler_id, n_args = 0, len = 0, gtk_flag = 0;
+ int handler_id, len = 0, connect_normal = 1;
char *php_class = NULL, *php_method = NULL;
TSRMLS_FETCH();
- MAKE_STD_ZVAL(callback);
-
+ /*
+ * First, check if the handler represents a static method. This can be used to invoke
+ * Gtk/Gdk functions, such as Gtk::main_quit(). It may refer to a user class, of
+ * course, but those connections are much less useful.
+ */
if (strstr(handler_name, "::")) {
+ /* If the handler is prefixed by '-', use simple signal connection. */
+ if (handler_name[0] == '-') {
+ connect_normal = 0;
+ handler_name++;
+ }
len = strcspn(handler_name, ":");
php_class = safe_estrndup(handler_name, len);
php_method = strstr(handler_name, "::") + 2;
+ MAKE_STD_ZVAL(callback);
array_init(callback);
add_next_index_string(callback, php_class, 1);
add_next_index_string(callback, php_method, 1);
-
- if (strcmp(php_class, "gtk") == 0 || strcmp(php_class, "gdk") == 0) {
- gtk_flag = 1;
- }
-
efree(php_class);
} else {
- ZVAL_STRING(callback, (char *)handler_name, 0);
+ /*
+ * Secondly, check if we are supposed to connect handlers to methods of the
+ * specified object - signal_autoconnect_instance().
+ */
+ if (data->instance) {
+ MAKE_STD_ZVAL(callback);
+ array_init(callback);
+ zval_add_ref(&data->instance);
+ add_next_index_zval(callback, data->instance);
+ add_next_index_string(callback, (char *)handler_name, 1);
+ user_args = data->extra;
+
+ /*
+ * We verify callability right away, because classes cannot acquire new
+ * methods at runtime (unless you decide to stab yourself in the foot with
+ * runkit).
+ */
+ if (!zend_is_callable(callback, 0, NULL)) {
+ php_error(E_NOTICE, "Unable to auto-connect handler '%s': no such method in the specified object of class '%s'", handler_name, Z_OBJCE_P(data->instance)->name);
+ return;
+ }
+ } else {
+ /*
+ * Thirdly, if we have no object, we must have a map representing non-default
+ * handler mappings of the form:
+ *
+ * array('handler' => <callback>)
+ * array('handler' => array(<callback> [, $arg1, $arg2, ...]))
+ * array('handler' => array(null [, $arg1, $arg2, ...]))
+ *
+ * where <callback> is the standard PHP callback form. If null is specified
+ * instead of callback, the connection will be made to the function of the
+ * same name as the handler.
+ */
+ if (data->extra && zend_hash_find(Z_ARRVAL_P(data->extra), (char *)handler_name, strlen(handler_name) + 1, (void **)&callback_data) == SUCCESS) {
+ /* Check if the value is callable. */
+ if (zend_is_callable(*callback_data, 0, NULL)) {
+ zval_add_ref(callback_data);
+ callback = *callback_data;
+ } else {
+ /*
+ * If not, check that it is an array with at least one entry, which is
+ * assumed to be the callback reference.
+ */
+ if (Z_TYPE_PP(callback_data) != IS_ARRAY ||
+ zend_hash_index_find(Z_ARRVAL_PP(callback_data), 0, (void **)&callback_ptr) == FAILURE) {
+ php_error(E_NOTICE, "Unable to auto-connect handler '%s': invalid callback data structure", handler_name);
+ return;
+ }
+ if (Z_TYPE_PP(callback_ptr) != IS_NULL) {
+ zval_add_ref(callback_ptr);
+ callback = *callback_ptr;
+ }
+
+ /* Shift callback data - the rest are extra user arguments. */
+ zend_hash_index_del(Z_ARRVAL_PP(callback_data), 0);
+ user_args = *callback_data;
+ }
+ }
+ }
}
- if (!zend_is_callable(callback, 0, NULL)) {
- php_error(E_WARNING, "%s() is unable to autoconnect callback for handler '%s'",
- get_active_function_name(TSRMLS_C), handler_name);
- return;
+ /*
+ * Make a simple function callback if everything else failed.
+ */
+ if (callback == NULL) {
+ MAKE_STD_ZVAL(callback);
+ ZVAL_STRING(callback, (char *)handler_name, 1);
}
- if (connect_object || gtk_flag) {
- if (connect_object) {
- phpg_gobject_new(&php_connect_object, (GObject*)connect_object TSRMLS_CC);
- args = php_gtk_hash_as_array_offset(data, 1, &n_args);
- args[0] = &php_connect_object;
- hash = php_gtk_array_as_hash(args, n_args, 0, n_args);
- efree(args);
- }
- closure = phpg_closure_new(callback, hash, PHPG_CONNECT_OBJECT TSRMLS_CC);
+ /*
+ * If we have a swapped connection object, make a PHP wrapper for it and use it with
+ * 'replace' type of connection.
+ */
+ if (connect_object) {
+ zval *php_connect_object = NULL;
+ phpg_gobject_new(&php_connect_object, (GObject*)connect_object TSRMLS_CC);
+ closure = phpg_closure_new(callback, user_args, PHPG_CONNECT_REPLACE, php_connect_object TSRMLS_CC);
+ /* it's okay to decrement the only reference to the wrapper here - the GObject will save it! */
+ zval_ptr_dtor(&php_connect_object);
+ } else if (!connect_normal) {
+ closure = phpg_closure_new(callback, user_args, PHPG_CONNECT_SIMPLE, NULL TSRMLS_CC);
} else {
- closure = phpg_closure_new(callback, data, PHPG_CONNECT_NORMAL TSRMLS_CC);
+ closure = phpg_closure_new(callback, user_args, PHPG_CONNECT_NORMAL, NULL TSRMLS_CC);
}
phpg_gobject_new(&php_object, (GObject*)object TSRMLS_CC);
phpg_gobject_watch_closure(php_object, closure TSRMLS_CC);
handler_id = g_signal_connect_closure(object, signal_name, closure, after);
- efree(php_object);
+ /* same as above - okay to decrement the reference here */
+ zval_ptr_dtor(&php_object);
+ zval_ptr_dtor(&callback);
+ if (user_args) {
+ zval_ptr_dtor(&user_args);
+ }
}
-PHP_METHOD(glade_xml_signal_autoconnect)
+PHP_METHOD
+{
+ zval *map = NULL;
+ phpg_autoc_t data;
+
+ NOT_STATIC_METHOD();
+
+ if (!php_gtk_parse_args(ZEND_NUM_ARGS(), "|a", &map))
+ return;
+
+ data.instance = NULL;
+ data.extra = map;
+ glade_xml_signal_autoconnect_full(GLADE_XML(PHPG_GOBJECT(this_ptr)),
+ (GladeXMLConnectFunc)phpg_glade_signal_connect_auto, &data);
+}
+
+%%
+add GladeXML signal_autoconnect_instance
+PHP_METHOD
{
+ zval *instance = NULL;
zval *extra = NULL;
+ phpg_autoc_t data;
NOT_STATIC_METHOD();
- if (!php_gtk_parse_varargs(ZEND_NUM_ARGS(), 0, &extra, ""))
+ if (!php_gtk_parse_varargs(ZEND_NUM_ARGS(), 1, &extra, "o", &instance))
return;
+ data.instance = instance;
+ data.extra = extra;
glade_xml_signal_autoconnect_full(GLADE_XML(PHPG_GOBJECT(this_ptr)),
- (GladeXMLConnectFunc)glade_auto_signal_marshal, extra);
+ (GladeXMLConnectFunc)phpg_glade_signal_connect_auto, &data);
}
%% }}}