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);
 }
 
 %% }}}