// The SIP library code that implements the main Qt support.
//
// Copyright (c) 2004
// 	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"


#ifdef SIP_QT_SUPPORT

#include <qobject.h>


// This is how Qt "types" signals and slots.
#define	isQtSlot(s)	(*(s) == '1')
#define	isQtSignal(s)	(*(s) == '2')
#define	isQtSigSlot(s)	(isQtSlot(s) || isQtSignal(s))


static sipProxy *proxyList = 0;		// List of proxies.
static void *lastSender = 0;		// The last signal sender.


static int isSameSlot(sipSlot *,PyObject *,const char *);
static int emitQtSig(sipWrapper *,const char *,PyObject *);
static int emitToSlot(sipSlot *slot,PyObject *sigargs);
static int emitToSlotList(sipPySigRx *,PyObject *);
static int addSlotToPySigList(sipWrapper *,const char *,PyObject *,const char *);
static void removeSlotFromPySigList(sipWrapper *,const char *,PyObject *,const char *);
static QObject *findProxy(sipWrapper *,const char *,PyObject *,const char *,const char **);
static void *convertPythonRx(sipProxyFunc,sipWrapper *,const char *,PyObject *,const char *,const char **);
static PyObject *connectToPythonSlot(sipWrapper *,const char *,PyObject *);
static PyObject *disconnectFromPythonSlot(sipWrapper *,const char *,PyObject *);
static PyObject *doDisconnect(sipWrapper *,const char *,const QObject *,const char *);
static PyObject *getWeakRef(PyObject *obj);
static sipPySig *findPySignal(sipWrapper *,const char *);
static char *sipStrdup(const char *);
static int setSlot(sipSlot *,PyObject *,const char *);
static int sameSigSlotName(const char *,const char *);
static void proxyDelete(sipProxy *pp);
static const char *searchProxySlotTable(sipProxy *pp,const char *sigargs);


// Delete a proxy.
static void proxyDelete(sipProxy *pp)
{
	if (pp -> realSlot.name != NULL)
		sip_api_free(pp -> realSlot.name);

	Py_XDECREF(pp -> realSlot.weakSlot);

	if (pp -> txSig != NULL)
		sip_api_free(pp -> txSig);

	// Remove it from the linked list of proxies.
	if (pp -> next != NULL)
		pp -> next -> prev = pp -> prev;

	if (pp -> prev != NULL)
		pp -> prev -> next = pp -> next;
	else
		proxyList = pp -> next;

	delete reinterpret_cast<QObject *>(pp -> qproxy);
}


// Convert receiver and slot objects to the base QObject derived receiver.
static const char *searchProxySlotTable(sipProxy *pp,const char *sigargs)
{
	const char *args;

	// Find the start of the signal arguments.
	if ((args = strchr(sigargs,'(')) != NULL)
	{
		const char **tab = pp -> slotTable;

		while ((pp -> rxSlot = *tab++) != NULL)
			if (sameSigSlotName(strchr(pp -> rxSlot,'('),args))
			{
				// Save the signal and slot names so that they
				// can be used later to disconnect.
				if ((pp -> txSig = sipStrdup(sigargs)) == NULL)
					return NULL;

				return pp -> rxSlot;
			}
	}

	PyErr_Format(PyExc_RuntimeError,"Signal has wrong argument types for slot");

	return NULL;
}


// Compare two signal/slot names and return non-zero if they match.
static int sameSigSlotName(const char *s1,const char *s2)
{
	// moc formats signal names, so we should first convert the supplied
	// string to the same format before comparing them.  Instead we just
	// compare them as they are, but ignoring all spaces - this will have
	// the same result.
	do
	{
		// Skip any spaces.

		while (*s1 == ' ')
			++s1;

		while (*s2 == ' ')
			++s2;

		if (*s1++ != *s2)
			return 0;
	}
	while (*s2++ != '\0');

	return 1;
}


// Search for the proxy connected to a particular Qt signal.
static QObject *findProxy(sipWrapper *txSelf,const char *sig,PyObject *rxObj,
			  const char *slot,const char **memberp)
{
	sipProxy *pp;

	for (pp = proxyList; pp != NULL; pp = pp -> next)
		if (pp -> txSelf == txSelf && sameSigSlotName(pp -> txSig,sig) && isSameSlot(&pp -> realSlot,rxObj,slot))
		{
			*memberp = pp -> rxSlot;
			return reinterpret_cast<QObject *>(pp -> qproxy);
		}

	return NULL;
}


// Emit a Python or Qt signal.
int sip_api_emit_signal(PyObject *self,const char *sig,PyObject *sigargs)
{
	sipPySig *ps;
	QObject *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 = reinterpret_cast<QObject *>(sip_api_get_cpp_ptr(w,sipQObjectClass))) == NULL || tx -> signalsBlocked())
		return 0;

	if (isQtSigSlot(sig))
		return emitQtSig(w,sig,sigargs);

	if ((ps = findPySignal(w,sig)) != NULL)
		return emitToSlotList(ps -> rxlist,sigargs);

	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 (sameSigSlotName(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;
}


// Return the last signal sender.
void *sip_api_get_sender()
{
	return lastSender;
}


// Send a signal to a single slot (Qt or Python) with arguments from the stack.
void sip_api_emit_to_slot(void *sender,sipSlot *slot,char *fmt,...)
{
	PyObject *args;
	va_list va;

#ifdef WITH_THREAD
	PyGILState_STATE state = PyGILState_Ensure();
#endif

	lastSender = sender;

	va_start(va,fmt);

	if ((args = sipBuildObjectTuple(fmt,va)) == NULL || emitToSlot(slot,args) < 0)
		PyErr_Print();

	Py_XDECREF(args);

	va_end(va);

#ifdef WITH_THREAD
	PyGILState_Release(state);
#endif
}


// Send a signal to a single slot (Qt or Python).
static int emitToSlot(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.  (However, programmers
		// may prefer an exception because it usually means a bug where
		// they have forgotten to keep the receiving object alive.)
		Py_DECREF(sref);
		return 0;
	}

	if (slot -> pyobj == NULL)
	{
		if ((sfunc = PyMethod_New(slot -> meth.mfunc,(sref != NULL ? sref : slot -> meth.mself),slot -> meth.mclass)) == NULL)
			return -1;

		// Make sure we garbage collect the new method.
		newmeth = sfunc;
	}
	else if (slot -> name != NULL)
	{
		char *mname = slot -> name + 1;

		if ((sfunc = PyObject_GetAttrString((sref != NULL ? sref : slot -> pyobj),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);
			return -1;
		}

		// Make sure we garbage collect the new method.
		newmeth = sfunc;
	}
	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(sipPySigRx *rxlist,PyObject *sigargs)
{
	int rc;

	// Apply the arguments to each slot method.
	rc = 0;

	while (rxlist != NULL && rc >= 0)
	{
		sipPySigRx *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 = emitToSlot(&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;
	sipPySigRx *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 = (sipPySigRx *)sip_api_malloc(sizeof (sipPySigRx))) == NULL)
		return -1;

	if (setSlot(&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 &&
			sameSigSlotName(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);
}


// 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)
	{
		sipPySigRx **psrxp;

		for (psrxp = &ps -> rxlist; *psrxp != NULL; psrxp = &(*psrxp) -> next)
		{
			sipPySigRx *psrx = *psrxp;

			if (isSameSlot(&psrx -> rx,rxObj,slot))
			{
				*psrxp = psrx -> next;

				if (psrx -> rx.name != NULL)
					sip_api_free(psrx -> rx.name);

				// Remove any weak reference.
				Py_XDECREF(psrx -> rx.weakSlot);

				sip_api_free(psrx);

				break;
			}
		}
	}
}


// A Qt signal is being connected to either a Python signal or a Python slot,
// so create a proxy Qt slot to receive the Qt signal.
static void *convertPythonRx(sipProxyFunc spf,sipWrapper *txSelf,
			     const char *sigargs,PyObject *rxObj,
			     const char *slot,const char **member)
{
	sipProxy *pp = spf();

	// Add it to the head of the linked list of proxies.
	if ((pp -> next = proxyList) != NULL)
		pp -> next -> prev = pp;

	pp -> prev = NULL;
	proxyList = pp;

	if (setSlot(&pp -> realSlot,rxObj,slot) < 0)
		goto delproxy;

	pp -> txSelf = txSelf;

	if ((*member = searchProxySlotTable(pp,sigargs)) != NULL)
		return pp -> qproxy;

delproxy:
	proxyDelete(pp);

	return NULL;
}


// Convert a valid Python signal or slot to an existing proxy Qt slot.
void *sipGetRx(sipWrapper *txSelf,const char *sigargs,PyObject *rxObj,
	       const char *slot,const char **memberp)
{
	QObject *rx;

	if (slot != NULL && isQtSigSlot(slot))
	{
		*memberp = slot;

		return sip_api_get_cpp_ptr((sipWrapper *)rxObj,sipQObjectClass);
	}

	if ((rx = findProxy(txSelf,sigargs,rxObj,slot,memberp)) != NULL)
		return rx;

	PyErr_Format(PyExc_RuntimeError,"Slot hasn't been connected");

	return NULL;
}


// 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 *sipConvertRx(sipProxyFunc spf,sipWrapper *txSelf,const char *sigargs,
		   PyObject *rxObj,const char *slot,const char **memberp)
{
	if (slot == NULL)
		return convertPythonRx(spf,txSelf,sigargs,rxObj,NULL,memberp);

	if (isQtSigSlot(slot))
	{
		*memberp = slot;

		return sip_api_get_cpp_ptr((sipWrapper *)rxObj,sipQObjectClass);
	}

	// The slot is a Python signal so we need a proxy to catch it.
	return convertPythonRx(spf,txSelf,sigargs,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)
{
	sipWrapper *txSelf = (sipWrapper *)txObj;

	// See if the receiver is a Python slot.
	if (slot == NULL)
		return connectToPythonSlot(txSelf,sig,rxObj);

	// Handle Qt signals.
	if (isQtSignal(sig))
	{
		void *txPtr, *rxQObj;
		const char *member;
		bool res;

		if ((txPtr = sip_api_get_cpp_ptr(txSelf,sipQObjectClass)) == NULL)
			return NULL;

		if ((rxQObj = sipConvertRx(((sipWrapperType *)(txSelf -> ob_type)) -> type -> td_proxy,txSelf,sig,rxObj,slot,&member)) == NULL)
			return NULL;

		Py_BEGIN_ALLOW_THREADS
		res = QObject::connect(reinterpret_cast<QObject *>(txPtr),
				       sig,
				       reinterpret_cast<QObject *>(rxQObj),
				       member);
		Py_END_ALLOW_THREADS

		return PyBool_FromLong(res);
	}

	// Handle Python signals.
	if (addSlotToPySigList(txSelf,sig,rxObj,slot) < 0)
		return NULL;

	Py_INCREF(Py_True);
	return Py_True;
}


// Connect either a Qt signal or a Python signal to a Python slot.  This will
// not be called for any other combination.
static PyObject *connectToPythonSlot(sipWrapper *txSelf,const char *sig,
				     PyObject *rxObj)
{
	// Handle Qt signals.
	if (isQtSignal(sig))
	{
		void *txPtr, *rxQObj;
		const char *member;
		bool res;

		if ((txPtr = sip_api_get_cpp_ptr(txSelf,sipQObjectClass)) == NULL)
			return NULL;

		if ((rxQObj = convertPythonRx(((sipWrapperType *)(txSelf -> ob_type)) -> type -> td_proxy,txSelf,sig,rxObj,NULL,&member)) == NULL)
			return NULL;

		Py_BEGIN_ALLOW_THREADS
		res = QObject::connect(reinterpret_cast<QObject *>(txPtr),
				       sig,
				       reinterpret_cast<QObject *>(rxQObj),
				       member);
		Py_END_ALLOW_THREADS

		return PyBool_FromLong(res);
	}

	// Handle Python signals.
	if (addSlotToPySigList(txSelf,sig,rxObj,NULL) < 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;

	if (slot == NULL)
		return disconnectFromPythonSlot(txSelf,sig,rxObj);

	// Handle Qt signals.
	if (isQtSignal(sig))
	{
		const char *member;
		const QObject *rxQObj;

		if ((rxQObj = reinterpret_cast<const QObject *>(sipGetRx(txSelf,sig,rxObj,slot,&member))) == NULL)
			return NULL;

		return doDisconnect(txSelf,sig,rxQObj,member);
	}

	// Handle Python signals.
	removeSlotFromPySigList(txSelf,sig,rxObj,slot);

	Py_INCREF(Py_True);
	return Py_True;
}


// Disconnect a signal from a Python slot.
static PyObject *disconnectFromPythonSlot(sipWrapper *txSelf,const char *sig,
					  PyObject *rxObj)
{
	// Handle Qt signals.
	if (isQtSignal(sig))
	{
		const char *member;
		const QObject *rxQObj;

		if ((rxQObj = reinterpret_cast<const QObject *>(sipGetRx(txSelf,sig,rxObj,NULL,&member))) == NULL)
			return NULL;

		return doDisconnect(txSelf,sig,rxQObj,member);
	}

	// Handle Python signals.
	removeSlotFromPySigList(txSelf,sig,rxObj,NULL);

	Py_INCREF(Py_True);
	return Py_True;
}


// Actually do a QObject disconnect.
static PyObject *doDisconnect(sipWrapper *txSelf,const char *sig,
			      const QObject *rxQObj,const char *slot)
{
	void *txPtr;
	PyObject *res;

	if ((txPtr = sip_api_get_cpp_ptr(txSelf,sipQObjectClass)) == NULL)
		res = NULL;
	else
		res = PyBool_FromLong(QObject::disconnect(reinterpret_cast<QObject *>(txPtr),sig,rxQObj,slot));

	// Delete it if it is a proxy as this will be it's only connection.
	for (sipProxy *pp = proxyList; pp != NULL; pp = pp -> next)
		if (reinterpret_cast<QObject *>(pp -> qproxy) == rxQObj)
		{
			proxyDelete(pp);
			break;
		}

	return res;
}


// 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 setSlot(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 &&
			    sipWrapper_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, SLOT('meth()')"
				// rather than "obj.meth" (see below).

				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
			{
				// 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, 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;
}

#endif


// Set a C++ bool for the main C implementation of the module.
void sipSetBool(void *ptr,int val)
{
	*(bool *)ptr = val;
}
