diff options
Diffstat (limited to 'python/sip/siplib/qtlib.c')
-rw-r--r-- | python/sip/siplib/qtlib.c | 1254 |
1 files changed, 0 insertions, 1254 deletions
diff --git a/python/sip/siplib/qtlib.c b/python/sip/siplib/qtlib.c deleted file mode 100644 index f3ba4301..00000000 --- a/python/sip/siplib/qtlib.c +++ /dev/null @@ -1,1254 +0,0 @@ -/* - * The SIP library code that implements the interface to the optional module - * supplied Qt support. - * - * Copyright (c) 2007 - * Riverbank Computing Limited <info@riverbankcomputing.co.uk> - * - * This file is part of SIP. - * - * This copy of SIP is licensed for use under the terms of the SIP License - * Agreement. See the file LICENSE for more details. - * - * SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - */ - - -#include <Python.h> -#include <string.h> - -#include "sip.h" -#include "sipint.h" - - -/* This is how Qt "types" signals and slots. */ -#define isQtSlot(s) (*(s) == '1') -#define isQtSignal(s) (*(s) == '2') - - -static PyObject *py_sender = NULL; /* The last Python signal sender. */ - - -static int isSameSlot(sipSlot *,PyObject *,const char *); -static int emitQtSig(sipWrapper *,const char *,PyObject *); -static int emitToSlotList(sipSlotList *rxlist, PyObject *sigargs); -static int addSlotToPySigList(sipWrapper *,const char *,PyObject *,const char *); -static void removeSlotFromPySigList(sipWrapper *,const char *,PyObject *,const char *); -static PyObject *getWeakRef(PyObject *obj); -static sipPySig *findPySignal(sipWrapper *,const char *); -static char *sipStrdup(const char *); -static int saveSlot(sipSlot *sp, PyObject *rxObj, const char *slot); -static void *createUniversalSlot(sipWrapper *txSelf, const char *sig, PyObject *rxObj, const char *slot, const char **member); -static void *findSignal(void *txrx, const char **sig); -static void *newSignal(void *txrx, const char **sig); -static void freeSlot(sipSlot *slot); - - -/* - * Return the most recent signal sender. - */ -PyObject *sip_api_get_sender() -{ - PyObject *sender; - const void *qt_sender; - - /* - * If there is a Qt sender then it is more recent than the last Python - * sender, so use it instead. - */ - if ((qt_sender = sipQtSupport->qt_get_sender()) != NULL) - sender = sip_api_convert_from_instance((void *)qt_sender, sipQObjectClass, NULL); - else - { - if ((sender = py_sender) == NULL) - sender = Py_None; - - Py_INCREF(sender); - } - - return sender; -} - - -/* - * Release the resources held by a connection. - */ -void sip_api_free_connection(sipSlotConnection *conn) -{ - freeSlot(&conn->sc_slot); -} - - -/* - * Compare two connections and return TRUE if they are the same. - */ -int sip_api_same_connection(sipSlotConnection *conn, void *tx, const char *sig, - PyObject *rxObj, const char *slot) -{ - return (conn->sc_transmitter == tx && - sipQtSupport->qt_same_name(conn->sc_signature->sg_signature, sig) && - isSameSlot(&conn->sc_slot, rxObj, slot)); -} - - -/* - * Parse the signal arguments for a connection. - */ -sipSignature *sip_api_parse_signature(const char *sig) -{ - static sipSignature *psig_list = NULL; - sipSignature *psig; - const char *sp, *ep; - - /* - * First see if it has already been parsed. Note that both sides of a - * connection will probably be parsed twice because the function names will - * be different even though the signatures will probably be the same. We - * could be more clever, the most saving is when repeatedly emitting a - * signal for which this is sufficient. - */ - for (psig = psig_list; psig != NULL; psig = psig->sg_next) - if (sipQtSupport->qt_same_name(psig->sg_signature, sig)) - return psig; - - /* Create a new one including space for the copy of the signature. */ - if ((psig = (sipSignature *)sip_api_malloc(sizeof (sipSignature) + strlen(sig) + 1)) == NULL) - return NULL; - - psig->sg_signature = (char *)&psig[1]; - psig->sg_nrargs = 0; - psig->sg_args = 0; - - /* Find the start and end of the arguments. */ - sp = strchr(sig, '('); - ep = strrchr(sig, ')'); - - /* If the signal isn't well formed we assume Qt will pick it up. */ - if (sp && ep && sp < ep) - { - /* - * Copy the signature arguments while counting them and - * removing non-significant spaces. Each argument is left as a - * '\0' terminated string. - */ - char *dp = psig->sg_signature; - int depth = 0, nrcommas = 0, argstart = TRUE; - - for (;;) - { - char ch = *++sp; - - if (strchr(",*&)<>", ch)) - { - /* Backup over any previous trailing space. */ - if (dp > psig->sg_signature && dp[-1] == ' ') - --dp; - - if (sp == ep) - { - *dp = '\0'; - break; - } - - if (ch == ',' && depth == 0) - { - *dp++ = '\0'; - ++nrcommas; - argstart = TRUE; - } - else - { - *dp++ = ch; - - /* - * Make sure commas in template - * arguments are ignored. - */ - if (ch == '<') - ++depth; - else if (ch == '>') - --depth; - } - } - else if (ch == ' ') - { - /* Ignore leading and multiple spaces. */ - if (!argstart && dp[-1] != ' ') - *dp++ = ch; - } - else - { - *dp++ = ch; - argstart = FALSE; - } - } - - /* Handle the arguments now they are in a normal form. */ - if (*psig->sg_signature) - { - char *arg = psig->sg_signature; - int a; - - /* Allocate the space. */ - psig->sg_nrargs = nrcommas + 1; - - if ((psig->sg_args = (sipSigArg *)sip_api_malloc(sizeof (sipSigArg) * psig->sg_nrargs)) == NULL) - { - sip_api_free(psig); - return NULL; - } - - for (a = 0; a < psig->sg_nrargs; ++a) - { - size_t btlen = 0; - int unsup, isref = FALSE, indir = 0; - sipSigArgType sat = unknown_sat; - - /* Find the start of the significant part of the type. */ - dp = arg; - - if (strncmp(dp, "const ", 6) == 0) - dp += 6; - - /* - * Find the length of the base type, the number of indirections - * and if it is a reference. - */ - for (ep = dp; *ep; ++ep) - if (*ep == '&') - isref = TRUE; - else if (*ep == '*') - ++indir; - else - ++btlen; - - /* - * Assume that anything other than a base type is unsupported. - */ - unsup = (isref || indir); - - /* Parse the base type. */ - switch (btlen) - { - case 3: - if (strncmp(dp, "int", 3) == 0) - sat = int_sat; - break; - - case 4: - if (strncmp(dp, "bool", 4) == 0) - sat = bool_sat; - else if (strncmp(dp, "long", 4) == 0) - sat = long_sat; - else if (strncmp(dp, "char", 4) == 0) - { - sat = (indir ? string_sat : char_sat); - unsup = (isref || indir > 1); - } - else if (strncmp(dp, "void", 4) == 0) - { - sat = void_sat; - unsup = (isref || indir != 1); - } - break; - - case 5: - if (strncmp(dp, "float", 5) == 0) - sat = float_sat; - else if (strncmp(dp, "short", 5) == 0) - sat = short_sat; - break; - - case 6: - if (strncmp(dp, "double", 6) == 0) - sat = double_sat; - break; - - case 7: - if (strncmp(dp, "__int64", 7) == 0) - sat = longlong_sat; - else if (strncmp(dp, "wchar_t", 7) == 0) - { - sat = (indir ? wstring_sat : wchar_sat); - unsup = (isref || indir > 1); - } - break; - - case 8: - if (strncmp(dp, "unsigned", 8) == 0) - sat = uint_sat; - else if (strncmp(dp, "TQVariant", 8) == 0) - { - if (indir == 0) - { - sat = qvariant_sat; - unsup = FALSE; - } - else if (indir == 1) - { - sat = qvariantp_sat; - unsup = FALSE; - } - } - break; - - case 9: - if (strncmp(dp, "long long", 9) == 0) - sat = longlong_sat; - break; - - case 11: - if (strncmp(dp, "signed char", 11) == 0) - { - sat = (indir ? sstring_sat : schar_sat); - unsup = (isref || indir > 1); - } - break; - - case 12: - if (strncmp(dp, "unsigned int", 12) == 0) - sat = uint_sat; - break; - - case 13: - if (strncmp(dp, "unsigned long", 13) == 0) - sat = ulong_sat; - else if (strncmp(dp, "unsigned char", 13) == 0) - { - sat = (indir ? ustring_sat : uchar_sat); - unsup = (isref || indir > 1); - } - else if (strncmp(dp, "PyQt_PyObject", 13) == 0 && indir == 0) - { - sat = pyobject_sat; - unsup = FALSE; - } - break; - - case 14: - if (strncmp(dp, "unsigned short", 14) == 0) - sat = ushort_sat; - break; - - case 16: - if (strncmp(dp, "unsigned __int64", 16) == 0) - sat = ulonglong_sat; - break; - - case 18: - if (strncmp(dp, "unsigned long long", 18) == 0) - sat = ulonglong_sat; - break; - } - - if (sat == unknown_sat) - sipFindSigArgType(dp, btlen, &psig->sg_args[a], indir); - else - { - if (unsup) - sat = unknown_sat; - - psig->sg_args[a].atype = sat; - } - - /* Move to the start of the next argument. */ - arg += strlen(arg) + 1; - } - } - } - - /* Make a deep copy of the signal. */ - strcpy(psig->sg_signature, sig); - - /* Add it to the list so it can be re-used. */ - psig->sg_next = psig_list; - psig_list = psig; - - return psig; -} - - -/* - * Find an existing signal. - */ -static void *findSignal(void *txrx, const char **sig) -{ - sipSignature *psig; - - /* - * Handle the trivial case where the Qt implementation doesn't support - * universal signals. - */ - if (sipQtSupport->qt_is_qt_signal == NULL) - return txrx; - - /* See if this a shortcircuited Python signal. */ - if (strchr(*sig, '(') == NULL) - return sipQtSupport->qt_find_universal_signal_shortcut(txrx, *sig, sig); - - /* See if the existing object can be used itself. */ - if (sipQtSupport->qt_is_qt_signal(txrx, *sig)) - return txrx; - - if ((psig = sip_api_parse_signature(*sig)) == NULL) - return NULL; - - /* Find an ordinary universal signal. */ - return sipQtSupport->qt_find_universal_signal(txrx, psig); -} - - -/* - * Return a usable signal, creating a new universal signal if needed. - */ -static void *newSignal(void *txrx, const char **sig) -{ - sipSignature *psig; - - /* - * Handle the trivial case where the Qt implementation doesn't support - * universal signals. - */ - if (sipQtSupport->qt_is_qt_signal == NULL) - return txrx; - - /* See if this a shortcircuited Python signal. */ - if (strchr(*sig, '(') == NULL) - return sipQtSupport->qt_create_universal_signal_shortcut(txrx, *sig, sig); - - /* See if the existing object can be used itself. */ - if (sipQtSupport->qt_is_qt_signal(txrx, *sig)) - return txrx; - - if ((psig = sip_api_parse_signature(*sig)) == NULL) - return NULL; - - /* Create an ordinary universal signal. */ - return sipQtSupport->qt_create_universal_signal(txrx, psig); -} - - -/* - * Create a universal slot. Returns a pointer to it or 0 if there was an - * error. - */ -static void *createUniversalSlot(sipWrapper *txSelf, const char *sig, - PyObject *rxObj, const char *slot, - const char **member) -{ - sipSlotConnection conn; - void *us; - - /* Initialise the connection. */ - conn.sc_transmitter = (txSelf ? sipGetAddress(txSelf) : 0); - - /* Save the real slot. */ - if (saveSlot(&conn.sc_slot, rxObj, slot) < 0) - return 0; - - /* Parse the signature and create the universal slot. */ - if ((conn.sc_signature = sip_api_parse_signature(sig)) == NULL || (us = sipQtSupport->qt_create_universal_slot(txSelf, &conn, member)) == NULL) - { - sip_api_free_connection(&conn); - return 0; - } - - return us; -} - - -/* - * Emit a Python or Qt signal. - */ -int sip_api_emit_signal(PyObject *self,const char *sig,PyObject *sigargs) -{ - sipPySig *ps; - void *tx; - sipWrapper *w = (sipWrapper *)self; - - /* - * Don't do anything if signals are blocked. Qt signals would be blocked - * anyway, but this blocks Python signals as well. - */ - if ((tx = sip_api_get_cpp_ptr(w, sipQObjectClass)) == NULL || sipQtSupport->qt_signals_blocked(tx)) - return 0; - - if (isQtSignal(sig)) - { - sipSignature *psig; - - /* Handle Qt implementations that emit using generated code. */ - if (!sipQtSupport->qt_emit_signal) - return emitQtSig(w, sig, sigargs); - - /* See if the signal is a shortcut. */ - if (strchr(sig, '(') == NULL) - return sipQtSupport->qt_emit_signal_shortcut(tx, sig, sigargs); - - if ((psig = sip_api_parse_signature(sig)) == NULL) - return -1; - - if (psig->sg_nrargs != PyTuple_GET_SIZE(sigargs)) - PyErr_Format(PyExc_TypeError, "Signal has %d arguments, but %d given", psig->sg_nrargs, PyTuple_GET_SIZE(sigargs)); - - return sipQtSupport->qt_emit_signal(tx, psig, sigargs); - } - - if ((ps = findPySignal(w,sig)) != NULL) - { - int rc; - - /* Forget the last Qt sender and remember this one. */ - sipQtSupport->qt_forget_sender(); - py_sender = self; - - rc = emitToSlotList(ps -> rxlist,sigargs); - - /* Forget this as a sender. */ - py_sender = NULL; - - return rc; - } - - return 0; -} - - -/* - * Search the Python signal list for a signal. - */ -static sipPySig *findPySignal(sipWrapper *w,const char *sig) -{ - sipPySig *ps; - - for (ps = w -> pySigList; ps != NULL; ps = ps -> next) - if (sipQtSupport->qt_same_name(ps -> name,sig)) - return ps; - - return NULL; -} - - -/* - * Search a signal table for a signal. If found, call the emitter function - * with the signal arguments. Return 0 if the signal was emitted or <0 if - * there was an error. - */ -static int emitQtSig(sipWrapper *w,const char *sig,PyObject *sigargs) -{ - sipQtSignal *tab; - - /* Search the table. */ - for (tab = ((sipWrapperType *)(w -> ob_type)) -> type -> td_emit; tab -> st_name != NULL; ++tab) - { - const char *sp, *tp; - int found; - - /* Compare only the base name. */ - sp = &sig[1]; - tp = tab -> st_name; - - found = TRUE; - - while (*sp != '\0' && *sp != '(' && *tp != '\0') - if (*sp++ != *tp++) - { - found = FALSE; - break; - } - - if (found) - return (*tab -> st_emitfunc)(w,sigargs); - } - - /* It wasn't found if we got this far. */ - PyErr_Format(PyExc_NameError,"Invalid signal %s",&sig[1]); - - return -1; -} - - -/* - * Send a signal to a single slot (Qt or Python). - */ -int sip_api_emit_to_slot(sipSlot *slot, PyObject *sigargs) -{ - PyObject *sa, *oxtype, *oxvalue, *oxtb, *sfunc, *newmeth, *sref; - - /* Keep some compilers quiet. */ - oxtype = oxvalue = oxtb = NULL; - - /* Fan out Qt signals. */ - if (slot -> name != NULL && slot -> name[0] != '\0') - return sip_api_emit_signal(slot -> pyobj,slot -> name,sigargs); - - /* Get the object to call, resolving any weak references. */ - if (slot -> weakSlot == NULL) - sref = NULL; - else if ((sref = PyWeakref_GetObject(slot -> weakSlot)) == NULL) - return -1; - else - Py_INCREF(sref); - - if (sref == Py_None) - { - /* - * If the real object has gone then we pretend everything is Ok. This - * mimics the Qt behaviour of not caring if a receiving object has been - * deleted. - */ - Py_DECREF(sref); - return 0; - } - - if (slot -> pyobj == NULL) - { - PyObject *self = (sref != NULL ? sref : slot->meth.mself); - - /* See if any underlying C++ instance has gone. */ - if (self != NULL && sip_api_wrapper_check(self) && ((sipWrapper *)self)->u.cppPtr == NULL) - { - Py_XDECREF(sref); - return 0; - } - - if ((sfunc = PyMethod_New(slot->meth.mfunc, self, slot->meth.mclass)) == NULL) - { - Py_XDECREF(sref); - return -1; - } - - /* Make sure we garbage collect the new method. */ - newmeth = sfunc; - } - else if (slot -> name != NULL) - { - char *mname = slot -> name + 1; - PyObject *self = (sref != NULL ? sref : slot->pyobj); - - /* See if any underlying C++ instance has gone. */ - if (self != NULL && sip_api_wrapper_check(self) && ((sipWrapper *)self)->u.cppPtr == NULL) - { - Py_XDECREF(sref); - return 0; - } - - if ((sfunc = PyObject_GetAttrString(self, mname)) == NULL || !PyCFunction_Check(sfunc)) - { - /* - * Note that in earlier versions of SIP this error would be - * detected when the slot was connected. - */ - PyErr_Format(PyExc_NameError,"Invalid slot %s",mname); - - Py_XDECREF(sref); - return -1; - } - - /* Make sure we garbage collect the new method. */ - newmeth = sfunc; - } - else if (slot->pyobj == Py_None) - { - /* - * This was a lambda function that has been freed by the cyclic garbage - * collector so ignore it. - */ - Py_XDECREF(sref); - return 0; - } - else - { - sfunc = slot -> pyobj; - newmeth = NULL; - } - - /* - * We make repeated attempts to call a slot. If we work out that it failed - * because of an immediate type error we try again with one less argument. - * We keep going until we run out of arguments to drop. This emulates the - * Qt ability of the slot to accept fewer arguments than a signal provides. - */ - sa = sigargs; - Py_INCREF(sa); - - for (;;) - { - PyObject *nsa, *xtype, *xvalue, *xtb, *resobj; - - if ((resobj = PyEval_CallObject(sfunc,sa)) != NULL) - { - Py_DECREF(resobj); - - Py_XDECREF(newmeth); - Py_XDECREF(sref); - - /* Remove any previous exception. */ - - if (sa != sigargs) - { - Py_XDECREF(oxtype); - Py_XDECREF(oxvalue); - Py_XDECREF(oxtb); - PyErr_Clear(); - } - - Py_DECREF(sa); - - return 0; - } - - /* Get the exception. */ - PyErr_Fetch(&xtype,&xvalue,&xtb); - - /* - * See if it is unacceptable. An acceptable failure is a type error - * with no traceback - so long as we can still reduce the number of - * arguments and try again. - */ - if (!PyErr_GivenExceptionMatches(xtype,PyExc_TypeError) || - xtb != NULL || - PyTuple_GET_SIZE(sa) == 0) - { - /* - * If there is a traceback then we must have called the slot and - * the exception was later on - so report the exception as is. - */ - if (xtb != NULL) - { - if (sa != sigargs) - { - Py_XDECREF(oxtype); - Py_XDECREF(oxvalue); - Py_XDECREF(oxtb); - } - - PyErr_Restore(xtype,xvalue,xtb); - } - else if (sa == sigargs) - PyErr_Restore(xtype,xvalue,xtb); - else - { - /* - * Discard the latest exception and restore the original one. - */ - Py_XDECREF(xtype); - Py_XDECREF(xvalue); - Py_XDECREF(xtb); - - PyErr_Restore(oxtype,oxvalue,oxtb); - } - - break; - } - - /* If this is the first attempt, save the exception. */ - if (sa == sigargs) - { - oxtype = xtype; - oxvalue = xvalue; - oxtb = xtb; - } - else - { - Py_XDECREF(xtype); - Py_XDECREF(xvalue); - Py_XDECREF(xtb); - } - - /* Create the new argument tuple. */ - if ((nsa = PyTuple_GetSlice(sa,0,PyTuple_GET_SIZE(sa) - 1)) == NULL) - { - /* Tidy up. */ - Py_XDECREF(oxtype); - Py_XDECREF(oxvalue); - Py_XDECREF(oxtb); - - break; - } - - Py_DECREF(sa); - sa = nsa; - } - - Py_XDECREF(newmeth); - Py_XDECREF(sref); - - Py_DECREF(sa); - - return -1; -} - - -/* - * Send a signal to the slots (Qt or Python) in a Python list. - */ -static int emitToSlotList(sipSlotList *rxlist,PyObject *sigargs) -{ - int rc; - - /* Apply the arguments to each slot method. */ - rc = 0; - - while (rxlist != NULL && rc >= 0) - { - sipSlotList *next; - - /* - * We get the next in the list before calling the slot in case the list - * gets changed by the slot - usually because the slot disconnects - * itself. - */ - next = rxlist -> next; - rc = sip_api_emit_to_slot(&rxlist -> rx, sigargs); - rxlist = next; - } - - return rc; -} - - -/* - * Add a slot to a transmitter's Python signal list. The signal is a Python - * signal, the slot may be either a Qt signal, a Qt slot, a Python signal or a - * Python slot. - */ -static int addSlotToPySigList(sipWrapper *txSelf,const char *sig, - PyObject *rxObj,const char *slot) -{ - sipPySig *ps; - sipSlotList *psrx; - - /* Create a new one if necessary. */ - if ((ps = findPySignal(txSelf,sig)) == NULL) - { - if ((ps = (sipPySig *)sip_api_malloc(sizeof (sipPySig))) == NULL) - return -1; - - if ((ps -> name = sipStrdup(sig)) == NULL) - { - sip_api_free(ps); - return -1; - } - - ps -> rxlist = NULL; - ps -> next = txSelf -> pySigList; - - txSelf -> pySigList = ps; - } - - /* Create the new receiver. */ - if ((psrx = (sipSlotList *)sip_api_malloc(sizeof (sipSlotList))) == NULL) - return -1; - - if (saveSlot(&psrx->rx, rxObj, slot) < 0) - { - sip_api_free(psrx); - return -1; - } - - psrx -> next = ps -> rxlist; - ps -> rxlist = psrx; - - return 0; -} - - -/* - * Compare two slots to see if they are the same. - */ -static int isSameSlot(sipSlot *slot1,PyObject *rxobj2,const char *slot2) -{ - /* See if they are signals or Qt slots, ie. they have a name. */ - if (slot1 -> name != NULL) - return (slot2 != NULL && - sipQtSupport->qt_same_name(slot1 -> name,slot2) && - slot1 -> pyobj == rxobj2); - - /* Both must be Python slots. */ - if (slot2 != NULL) - return 0; - - /* See if they are Python methods. */ - if (slot1 -> pyobj == NULL) - return (PyMethod_Check(rxobj2) && - slot1 -> meth.mfunc == PyMethod_GET_FUNCTION(rxobj2) && - slot1 -> meth.mself == PyMethod_GET_SELF(rxobj2) && - slot1 -> meth.mclass == PyMethod_GET_CLASS(rxobj2)); - - if (PyMethod_Check(rxobj2)) - return 0; - - /* The objects must be the same. */ - return (slot1 -> pyobj == rxobj2); -} - - -/* - * Convert a valid Python signal or slot to an existing universal slot. - */ -void *sipGetRx(sipWrapper *txSelf,const char *sigargs,PyObject *rxObj, - const char *slot,const char **memberp) -{ - if (slot != NULL) - if (isQtSlot(slot) || isQtSignal(slot)) - { - void *rx; - - *memberp = slot; - - if ((rx = sip_api_get_cpp_ptr((sipWrapper *)rxObj, sipQObjectClass)) == NULL) - return NULL; - - if (isQtSignal(slot)) - rx = findSignal(rx, memberp); - - return rx; - } - - /* - * The slot was either a Python callable or PyQt3 Python signal so there - * should be a universal slot. - */ - return sipQtSupport->qt_find_slot(sipGetAddress(txSelf), sigargs, rxObj, slot, memberp); -} - - -/* - * Convert a Python receiver (either a Python signal or slot or a Qt signal or - * slot) to a Qt receiver. It is only ever called when the signal is a Qt - * signal. Return NULL is there was an error. - */ -void *sip_api_convert_rx(sipWrapper *txSelf,const char *sig,PyObject *rxObj, - const char *slot,const char **memberp) -{ - if (slot == NULL) - return createUniversalSlot(txSelf, sig, rxObj, NULL, memberp); - - if (isQtSlot(slot) || isQtSignal(slot)) - { - void *rx; - - *memberp = slot; - - if ((rx = sip_api_get_cpp_ptr((sipWrapper *)rxObj, sipQObjectClass)) == NULL) - return NULL; - - if (isQtSignal(slot)) - rx = newSignal(rx, memberp); - - return rx; - } - - /* The slot is a Python signal so we need a universal slot to catch it. */ - return createUniversalSlot(txSelf, sig, rxObj, slot, memberp); -} - - -/* - * Connect a Qt signal or a Python signal to a Qt slot, a Qt signal, a Python - * slot or a Python signal. This is all possible combinations. - */ -PyObject *sip_api_connect_rx(PyObject *txObj,const char *sig,PyObject *rxObj, - const char *slot, int type) -{ - sipWrapper *txSelf = (sipWrapper *)txObj; - - /* Handle Qt signals. */ - if (isQtSignal(sig)) - { - void *tx, *rx; - const char *member, *real_sig; - int res; - - if ((tx = sip_api_get_cpp_ptr(txSelf, sipQObjectClass)) == NULL) - return NULL; - - real_sig = sig; - - if ((tx = newSignal(tx, &real_sig)) == NULL) - return NULL; - - if ((rx = sip_api_convert_rx(txSelf, sig, rxObj, slot, &member)) == NULL) - return NULL; - - res = sipQtSupport->qt_connect(tx, real_sig, rx, member, type); - - return PyBool_FromLong(res); - } - - /* Handle Python signals. */ - if (addSlotToPySigList(txSelf, sig, rxObj, slot) < 0) - return NULL; - - Py_INCREF(Py_True); - return Py_True; -} - - -/* - * Disconnect a signal to a signal or a Qt slot. - */ -PyObject *sip_api_disconnect_rx(PyObject *txObj,const char *sig, - PyObject *rxObj,const char *slot) -{ - sipWrapper *txSelf = (sipWrapper *)txObj; - - /* Handle Qt signals. */ - if (isQtSignal(sig)) - { - void *tx, *rx; - const char *member; - int res; - - if ((tx = sip_api_get_cpp_ptr(txSelf, sipQObjectClass)) == NULL) - return NULL; - - if ((rx = sipGetRx(txSelf, sig, rxObj, slot, &member)) == NULL) - { - Py_INCREF(Py_False); - return Py_False; - } - - /* Handle Python signals. */ - tx = findSignal(tx, &sig); - - res = sipQtSupport->qt_disconnect(tx, sig, rx, member); - - /* - * Delete it if it is a universal slot as this will be it's only - * connection. If the slot is actually a universal signal then it - * should leave it in place. - */ - sipQtSupport->qt_destroy_universal_slot(rx); - - return PyBool_FromLong(res); - } - - /* Handle Python signals. */ - removeSlotFromPySigList(txSelf,sig,rxObj,slot); - - Py_INCREF(Py_True); - return Py_True; -} - - -/* - * Remove a slot from a transmitter's Python signal list. - */ -static void removeSlotFromPySigList(sipWrapper *txSelf,const char *sig, - PyObject *rxObj,const char *slot) -{ - sipPySig *ps; - - if ((ps = findPySignal(txSelf,sig)) != NULL) - { - sipSlotList **psrxp; - - for (psrxp = &ps -> rxlist; *psrxp != NULL; psrxp = &(*psrxp) -> next) - { - sipSlotList *psrx = *psrxp; - - if (isSameSlot(&psrx -> rx,rxObj,slot)) - { - *psrxp = psrx -> next; - sipFreeSlotList(psrx); - break; - } - } - } -} - - -/* - * Free a sipSlot structure. - */ -static void freeSlot(sipSlot *slot) -{ - if (slot->name != NULL) - sip_api_free(slot->name); - else - { - PyObject *lam = slot->pyobj; - - if (lam != NULL && (lam == Py_None || sipLambdaSlot(lam))) - Py_DECREF(lam); - } - - /* Remove any weak reference. */ - Py_XDECREF(slot->weakSlot); -} - - -/* - * Free a sipSlotList structure on the heap. - */ -void sipFreeSlotList(sipSlotList *rx) -{ - freeSlot(&rx->rx); - sip_api_free(rx); -} - - -/* - * Implement strdup() using sip_api_malloc(). - */ -static char *sipStrdup(const char *s) -{ - char *d; - - if ((d = (char *)sip_api_malloc(strlen(s) + 1)) != NULL) - strcpy(d,s); - - return d; -} - - -/* - * Initialise a slot, returning 0 if there was no error. If the signal was a - * Qt signal, then the slot may be a Python signal or a Python slot. If the - * signal was a Python signal, then the slot may be anything. - */ -static int saveSlot(sipSlot *sp, PyObject *rxObj, const char *slot) -{ - sp -> weakSlot = NULL; - - if (slot == NULL) - { - sp -> name = NULL; - - if (PyMethod_Check(rxObj)) - { - /* - * Python creates methods on the fly. We could increment the - * reference count to keep it alive, but that would keep "self" - * alive as well and would probably be a circular reference. - * Instead we remember the component parts and hope they are still - * valid when we re-create the method when we need it. - */ - sipSaveMethod(&sp -> meth,rxObj); - - /* Notice if the class instance disappears. */ - sp -> weakSlot = getWeakRef(sp -> meth.mself); - - /* This acts a flag to say that the slot is a method. */ - sp -> pyobj = NULL; - } - else - { - PyObject *self; - - /* - * We know that it is another type of callable, ie. a - * function/builtin. - */ - - if (PyCFunction_Check(rxObj) && - (self = PyCFunction_GET_SELF(rxObj)) != NULL && - sip_api_wrapper_check(self)) - { - /* - * It is a wrapped C++ class method. We can't keep a copy - * because they are generated on the fly and we can't take a - * reference as that may keep the instance (ie. self) alive. - * We therefore treat it as if the user had specified the slot - * at "obj, TQT_SLOT('meth()')" rather than "obj.meth" (see below). - */ - - const char *meth; - - /* Get the method name. */ - meth = ((PyCFunctionObject *)rxObj) -> m_ml -> ml_name; - - if ((sp -> name = (char *)sip_api_malloc(strlen(meth) + 2)) == NULL) - return -1; - - /* - * Copy the name and set the marker that it needs converting to - * a built-in method. - */ - sp -> name[0] = '\0'; - strcpy(&sp -> name[1],meth); - - sp -> pyobj = self; - sp -> weakSlot = getWeakRef(self); - } - else - { - /* - * A bit of a hack to allow lamba functions to be used as - * slots. - */ - if (sipLambdaSlot(rxObj)) - Py_INCREF(rxObj); - - /* - * It's unlikely that we will succeed in getting a weak - * reference to the slot, but there is no harm in trying (and - * future versions of Python may support references to more - * object types). - */ - sp -> pyobj = rxObj; - sp -> weakSlot = getWeakRef(rxObj); - } - } - } - else if ((sp -> name = sipStrdup(slot)) == NULL) - return -1; - else if (isQtSlot(slot)) - { - /* - * The user has decided to connect a Python signal to a Qt slot and - * specified the slot as "obj, TQT_SLOT('meth()')" rather than "obj.meth". - */ - - char *tail; - - /* Remove any arguments. */ - if ((tail = strchr(sp -> name,'(')) != NULL) - *tail = '\0'; - - /* - * A bit of a hack to indicate that this needs converting to a built-in - * method. - */ - sp -> name[0] = '\0'; - - /* Notice if the class instance disappears. */ - sp -> weakSlot = getWeakRef(rxObj); - - sp -> pyobj = rxObj; - } - else - /* It's a Qt signal. */ - sp -> pyobj = rxObj; - - return 0; -} - - -/* - * Return a weak reference to the given object. - */ -static PyObject *getWeakRef(PyObject *obj) -{ - PyObject *wr; - - if ((wr = PyWeakref_NewRef(obj,NULL)) == NULL) - PyErr_Clear(); - - return wr; -} - - -/* - * See if an object is a lambda function. - */ -int sipLambdaSlot(PyObject *slotObj) -{ - if (!PyFunction_Check(slotObj)) - return FALSE; - - return (strcmp(PyString_AsString(((PyFunctionObject *)slotObj)->func_name), "<lambda>") == 0); -} |