
# HG changeset patch
# User Phil Thompson <phil@riverbankcomputing.com>
# Date 1392749782 0
# Node ID 4fc63f9adb4402f38d012b0c18c85b74dc0f7f2d
# Parent  b96a5e69adb6f2243f8079b5381b4a6a28f86468
Implemented the PyQt5 signal emitters.

diff -r b96a5e69adb6 -r 4fc63f9adb44 sipgen/gencode.c
--- a/sipgen/gencode.c	Tue Feb 18 13:54:02 2014 +0000
+++ b/sipgen/gencode.c	Tue Feb 18 18:56:22 2014 +0000
@@ -124,8 +124,10 @@
 static void generateNamedBaseType(ifaceFileDef *, argDef *, const char *, int,
         FILE *);
 static void generateTupleBuilder(moduleDef *, signatureDef *, FILE *);
-static void generateEmitters(moduleDef *mod, classDef *cd, FILE *fp);
-static void generateEmitter(moduleDef *, classDef *, visibleList *, FILE *);
+static void generatePyQt3Emitters(moduleDef *mod, classDef *cd, FILE *fp);
+static void generatePyQt3Emitter(moduleDef *, classDef *, visibleList *,
+        FILE *);
+static void generatePyQt5Emitters(moduleDef *mod, classDef *cd, FILE *fp);
 static void generateVirtualHandler(moduleDef *mod, virtHandlerDef *vhd,
         FILE *fp);
 static void generateDefaultInstanceReturn(argDef *res, const char *indent,
@@ -256,7 +258,7 @@
 static const char *resultOwner(overDef *od);
 static void prCachedName(FILE *fp, nameDef *nd, const char *prefix);
 static void generateSignalTableEntry(sipSpec *pt, classDef *cd, overDef *sig,
-        memberDef *md, int membernr, FILE *fp);
+        memberDef *md, int membernr, int optional_args, FILE *fp);
 static void generateTypesTable(sipSpec *pt, moduleDef *mod, FILE *fp);
 static int py2OnlySlot(slotType st);
 static int py2_5LaterSlot(slotType st);
@@ -279,6 +281,7 @@
 static void generatePreprocLine(int linenr, const char *fname, FILE *fp);
 static virtErrorHandler *getVirtErrorHandler(sipSpec *pt, overDef *od,
         classDef *cd, moduleDef *mod);
+static int hasOptionalArgs(overDef *od);
 
 
 /*
@@ -6668,14 +6671,14 @@
 
     /* Generate the emitters if needed. */
     if (pluginPyQt3(pt))
-        generateEmitters(mod, cd, fp);
-}
-
-
-/*
- * Generate the emitter functions.
- */
-static void generateEmitters(moduleDef *mod, classDef *cd, FILE *fp)
+        generatePyQt3Emitters(mod, cd, fp);
+}
+
+
+/*
+ * Generate the PyQt3 emitter functions.
+ */
+static void generatePyQt3Emitters(moduleDef *mod, classDef *cd, FILE *fp)
 {
     int noIntro;
     visibleList *vl;
@@ -6687,7 +6690,7 @@
         for (od = vl->cd->overs; od != NULL; od = od->next)
             if (od->common == vl->m && isSignal(od))
             {
-                generateEmitter(mod, cd, vl, fp);
+                generatePyQt3Emitter(mod, cd, vl, fp);
                 break;
             }
     }
@@ -7346,9 +7349,9 @@
 
 
 /*
- * Generate the emitter function for a signal.
- */
-static void generateEmitter(moduleDef *mod, classDef *cd, visibleList *vl,
+ * Generate the PyQt3 emitter function for a signal.
+ */
+static void generatePyQt3Emitter(moduleDef *mod, classDef *cd, visibleList *vl,
         FILE *fp)
 {
     const char *pname = vl->m->pyname->text;
@@ -9632,7 +9635,7 @@
     nr_methods = generateClassMethodTable(pt, cd, fp);
     nr_enums = generateEnumMemberTable(pt, mod, cd, NULL, fp);
 
-    /* Generate the PyQt4 signals table. */
+    /* Generate the PyQt4/5 signals table. */
     is_signals = FALSE;
 
     if ((pluginPyQt4(pt) || pluginPyQt5(pt)) && isQObjectSubClass(cd))
@@ -9647,9 +9650,6 @@
 
             for (od = cd->overs; od != NULL; od = od->next)
             {
-                int a, nr_args;
-                signatureDef *cppsig;
-
                 if (od->common != md || !isSignal(od))
                     continue;
 
@@ -9671,6 +9671,9 @@
                 {
                     is_signals = TRUE;
 
+                    if (pluginPyQt5(pt))
+                        generatePyQt5Emitters(mod, cd, fp);
+
                     prcode(fp,
 "\n"
 "\n"
@@ -9680,26 +9683,37 @@
                 }
 
                 /*
-                 * Default arguments are handled as multiple signals.  We make
-                 * sure the largest is first and the smallest last which is
-                 * what Qt does.
+                 * For PyQt4 optional arguments are handled as multiple
+                 * signals.  We make sure the largest is first and the smallest
+                 * last which is what Qt does.  For PyQt5 we only include the
+                 * version with all arguments and provide an emitter function
+                 * which handles the optional arguments.
                  */
-                cppsig = od->cppsig;
-                nr_args = cppsig->nrArgs;
-
-                generateSignalTableEntry(pt, cd, od, md, membernr, fp);
+                generateSignalTableEntry(pt, cd, od, md, membernr,
+                        hasOptionalArgs(od), fp);
+
                 membernr = -1;
 
-                for (a = nr_args - 1; a >= 0; --a)
+                if (pluginPyQt4(pt))
                 {
-                    if (cppsig->args[a].defval == NULL)
-                        break;
-
-                    cppsig->nrArgs = a;
-                    generateSignalTableEntry(pt, cd, od, md, -1, fp);
+                    int a, nr_args;
+                    signatureDef *cppsig;
+
+                    cppsig = od->cppsig;
+                    nr_args = cppsig->nrArgs;
+
+                    for (a = nr_args - 1; a >= 0; --a)
+                    {
+                        if (cppsig->args[a].defval == NULL)
+                            break;
+
+                        cppsig->nrArgs = a;
+                        generateSignalTableEntry(pt, cd, od, md, -1, FALSE,
+                                fp);
+                    }
+
+                    cppsig->nrArgs = nr_args;
                 }
-
-                cppsig->nrArgs = nr_args;
             }
         }
 
@@ -10354,12 +10368,110 @@
 
 
 /*
- * Generate an entry in the PyQt4 signal table.
+ * See if an overload has optional arguments.
+ */
+static int hasOptionalArgs(overDef *od)
+{
+    return (od->cppsig->nrArgs > 0 && od->cppsig->args[od->cppsig->nrArgs - 1].defval != NULL);
+}
+
+
+/*
+ * Generate the PyQt5 emitters for a class.
+ */
+static void generatePyQt5Emitters(moduleDef *mod, classDef *cd, FILE *fp)
+{
+    memberDef *md;
+
+    for (md = cd->members; md != NULL; md = md->next)
+    {
+        int in_emitter = FALSE;
+        overDef *od;
+
+        for (od = cd->overs; od != NULL; od = od->next)
+        {
+            if (od->common != md || !isSignal(od) || !hasOptionalArgs(od))
+                continue;
+
+            if (!in_emitter)
+            {
+                in_emitter = TRUE;
+
+                prcode(fp,
+"\n"
+"\n"
+                    );
+
+                if (!generating_c)
+                    prcode(fp,
+"extern \"C\" {static int emit_%L_%s(void *, PyObject *);}\n"
+"\n"
+                        , cd->iff, od->cppname);
+
+                prcode(fp,
+"static int emit_%L_%s(void *sipCppV, PyObject *sipArgs)\n"
+"{\n"
+"    PyObject *sipParseErr = NULL;\n"
+"    %C *sipCpp = reinterpret_cast<%C *>(sipCppV);\n"
+                    , cd->iff, od->cppname
+                    , classFQCName(cd), classFQCName(cd));
+            }
+
+            /*
+             * Generate the code that parses the args and emits the appropriate
+             * overloaded signal.
+             */
+            prcode(fp,
+"\n"
+"    {\n"
+                );
+
+            generateArgParser(mod, &od->pysig, cd, NULL, NULL, NULL, FALSE, fp);
+
+            prcode(fp,
+"        {\n"
+"            Py_BEGIN_ALLOW_THREADS\n"
+"            sipCpp->%s("
+                , od->cppname);
+
+            generateCallArgs(mod, od->cppsig, &od->pysig, fp);
+
+            prcode(fp, ");\n"
+"            Py_END_ALLOW_THREADS\n"
+"\n"
+                );
+
+            deleteTemps(mod, &od->pysig, fp);
+
+            prcode(fp,
+"\n"
+"            return 0;\n"
+"        }\n"
+"    }\n"
+            );
+        }
+
+        if (in_emitter)
+        {
+            prcode(fp,
+"\n"
+"    sipNoMethod(sipParseErr, %N, %N, NULL);\n"
+"\n"
+"    return -1;\n"
+"}\n"
+                , cd->pyname, md->pyname);
+        }
+    }
+}
+
+
+/*
+ * Generate an entry in the PyQt4 or PyQt5 signal table.
  */
 static void generateSignalTableEntry(sipSpec *pt, classDef *cd, overDef *sig,
-        memberDef *md, int membernr, FILE *fp)
-{
-    int a;
+        memberDef *md, int membernr, int optional_args, FILE *fp)
+{
+    int a, pyqt5 = pluginPyQt5(pt);
 
     prcode(fp,
 "    {\"%s(", sig->cppname);
@@ -10411,8 +10523,11 @@
     else
         prcode(fp, "0");
 
-    if (pluginPyQt5(pt))
-        prcode(fp, ", 0");
+    if (pyqt5)
+        if (optional_args)
+            prcode(fp, ", emit_%L_%s", cd->iff, sig->cppname);
+        else
+            prcode(fp, ", 0");
 
     prcode(fp,"},\n"
         );
diff -r b96a5e69adb6 -r 4fc63f9adb44 siplib/sip.h.in.in
--- a/siplib/sip.h.in	Tue Feb 18 13:54:02 2014 +0000
+++ b/siplib/sip.h.in	Tue Feb 18 18:56:22 2014 +0000
@@ -1757,13 +1757,10 @@
 /*
  * The description of a Qt signal for PyQt5.
  */
-typedef PyObject *(*pyqt5EmitFunc)(sipSimpleWrapper *, PyObject *);
+typedef int (*pyqt5EmitFunc)(void *, PyObject *);
 
 typedef struct _pyqt5QtSignal {
-    /*
-     * The normalised C++ name and signature of the signal that would be
-     * returned by SIGNAL().
-     */
+    /* The normalised C++ name and signature of the signal. */
     const char *signature;
 
     /* The optional docstring. */

