// The SIP library code that implements the main Qt support.
//
// Copyright (c) 2005
// 	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>
#include <qvariant.h>
#include <qmetaobject.h>
#include <private/qucom_p.h>
#include <private/qucomextra_p.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 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 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 *);


// This class is used as a slot on behalf of connections to a Python callable.
// It is derived from QObject but is not run through moc.  Instead the normal
// moc-generated methods are handwritten in order to implement a universal
// slot.  This requires some knowledge of the internal implementation of
// signals and slots but it is likely that they will only change between major
// Qt versions.
class UniversalSlot : public QObject
{
public:
	virtual QMetaObject *metaObject() const
	{
		return staticMetaObject();
	}

	virtual bool qt_invoke(int, QUObject *args);
	static QMetaObject *staticMetaObject();

	static UniversalSlot *create(sipWrapper *txSelf, const char *sig,
				     PyObject *rxObj, const char *slot,
				     const char **member);
	static void dispose(const QObject *rxQObj);
	static UniversalSlot *find(sipWrapper *txSelf, const char *sig,
				   PyObject *rxObj, const char *slot,
				   const char **member);

	static const QObject *lastSender;

private:
	UniversalSlot(sipWrapper *txSelf, const char *sig, PyObject *rxObj,
		      const char *slot, const char **member);
	~UniversalSlot();

	bool parseSignature(const char *sig);

	static QMetaObject *metaObj;
	static UniversalSlot *unislots;

	bool invalid;
	sipSigArg *args;
	int nrargs;
	sipWrapper *xmitter;
	char *xmittersig;
	sipSlot pyslot;
	UniversalSlot *nextus, *prevus;
};


// Create a universal slot.
UniversalSlot::UniversalSlot(sipWrapper *txSelf, const char *sig,
			     PyObject *rxObj, const char *slot,
			     const char **member)
{
	// Assume the initialisation will succeed.
	invalid = FALSE;

	args = 0;
	nrargs = 0;
	xmitter = txSelf;
	xmittersig = 0;

	// Return the slot to connect to.
	*member = SLOT(unislot());

	// Add this one to the global list.
	nextus = unislots;

	if (nextus)
		nextus -> prevus = this;

	prevus = 0;
	unislots = this;

	if (setSlot(&pyslot, rxObj, slot) < 0 || !parseSignature(sig))
		invalid = TRUE;
}


// Destroy a universal slot.
UniversalSlot::~UniversalSlot()
{
	if (args)
		sip_api_free(args);

	if (xmittersig)
		sip_api_free(xmittersig);

	if (pyslot.name)
		sip_api_free(pyslot.name);

	Py_XDECREF(pyslot.weakSlot);

	// Remove this one from the global list.
	if (nextus)
		nextus -> prevus = prevus;

	if (prevus)
		prevus -> nextus = nextus;
	else
		unislots = nextus;
}


QMetaObject *UniversalSlot::metaObj = 0;

#if QT_VERSION >= 0x030100
static QMetaObjectCleanUp cleanUp_UniversalSlot("UniversalSlot", &UniversalSlot::staticMetaObject);
#else
static QMetaObjectCleanUp cleanUp_UniversalSlot;
#endif

const QObject *UniversalSlot::lastSender = 0;
UniversalSlot *UniversalSlot::unislots = 0;


QMetaObject *UniversalSlot::staticMetaObject()
{
	if (metaObj)
		return metaObj;

	// Define a single slot that takes no arguments and so will accept any
	// connection.
	static const QUMethod slot = {"unislot", 0, 0};
	static const QMetaData slot_tbl[] = {
		{"unislot()", &slot, QMetaData::Public}
	};

	metaObj = QMetaObject::new_metaobject(
			"UniversalSlot", QObject::staticMetaObject(),
			slot_tbl, 1,
			0, 0,
#ifndef QT_NO_PROPERTIES
			0, 0,
			0, 0,
#endif
			0, 0);

	cleanUp_UniversalSlot.setMetaObject(metaObj);

	return metaObj;
}


bool UniversalSlot::qt_invoke(int, QUObject *qargs)
{
	bool ok = TRUE;
	QVariant *qv;

	lastSender = sender();

	// If the sender was a QSignal then the single argument will be wrapped
	// in a QVariant instance.  At the moment we handle int argument as
	// that is all that is needed by PyQt (and PyKDE).
	if (lastSender -> inherits("QSignal"))
		qv = &static_QUType_QVariant.get(qargs + 1);
	else
		qv = 0;

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

	PyObject *argtup = PyTuple_New(nrargs);

	if (!argtup)
		ok = FALSE;
	else
	{
		for (int a = 0; a < nrargs; ++a)
		{
			PyObject *arg;

			++qargs;

			switch (args[a].atype)
			{
			case char_sat:
			case uchar_sat:
				arg = PyString_FromStringAndSize((char *)static_QUType_ptr.get(qargs), 1);
				break;

			case string_sat:
				arg = PyString_FromString(static_QUType_charstar.get(qargs));
				break;

			case ustring_sat:
				arg = PyString_FromString((char *)static_QUType_ptr.get(qargs));
				break;

			case short_sat:
				arg = PyInt_FromLong(*(short *)static_QUType_ptr.get(qargs));
				break;

			case ushort_sat:
				arg = PyInt_FromLong(*(unsigned short *)static_QUType_ptr.get(qargs));
				break;

			case int_sat:
				if (qv)
					arg = PyInt_FromLong(qv -> asInt());
				else
					arg = PyInt_FromLong(static_QUType_int.get(qargs));
				break;

			case uint_sat:
				arg = PyInt_FromLong(*(unsigned *)static_QUType_ptr.get(qargs));
				break;

			case long_sat:
				arg = PyInt_FromLong(*(long *)static_QUType_ptr.get(qargs));
				break;

			case ulong_sat:
				arg = PyInt_FromLong(*(unsigned long *)static_QUType_ptr.get(qargs));
				break;

			case float_sat:
				arg = PyFloat_FromDouble(*(float *)static_QUType_ptr.get(qargs));
				break;

			case double_sat:
				arg = PyFloat_FromDouble(static_QUType_double.get(qargs));
				break;

			case enum_sat:
				arg = sip_api_convert_from_named_enum(*(int *)static_QUType_ptr.get(qargs), args[a].u.et);
				break;

			case bool_sat:
				arg = PyInt_FromLong(static_QUType_bool.get(qargs));
				break;

			case void_sat:
				arg = sip_api_convert_from_void_ptr((void *)static_QUType_ptr.get(qargs));
				break;

			case class_sat:
				arg = sip_api_map_cpp_to_self_sub_class((void *)static_QUType_ptr.get(qargs), args[a].u.wt);
				break;

			case mtype_sat:
				arg = args[a].u.mt -> mt_cfrom((void *)static_QUType_ptr.get(qargs));
				break;

			case qvariant_sat:
				arg = sipWrapQVariant((void *)&static_QUType_QVariant.get(qargs));
				break;

			default:
				arg = Py_NotImplemented;
				Py_INCREF(Py_NotImplemented);
			}

			PyTuple_SET_ITEM(argtup, a, arg);
		}

		if (ok && emitToSlot(&pyslot, argtup) < 0)
			ok = FALSE;

		Py_DECREF(argtup);
	}

	if (!ok)
		PyErr_Print();

#ifdef WITH_THREAD
	PyGILState_Release(state);
#endif

	return ok;
}


// Parse the signal arguments.
bool UniversalSlot::parseSignature(const char *sig)
{
	// Allocate space for the deep copy of the signal, but we use it as
	// temporary workspace for the moment.
	if ((xmittersig = (char *)sip_api_malloc(strlen(sig) + 1)) == NULL)
		return FALSE;

	// Find the start and end of the arguments.
	const char *sp, *ep;

	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 = xmittersig;
		int depth = 0, nrcommas = 0;
		bool argstart = TRUE;

		for (;;)
		{
			char ch = *++sp;

			if (strchr(",*&)<>", ch))
			{
				// Backup over any previous trailing space.
				if (dp > xmittersig && 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 (*xmittersig)
		{
			// Allocate the space.
			nrargs = nrcommas + 1;

			if ((args = (sipSigArg *)sip_api_malloc(sizeof (sipSigArg) * nrargs)) == NULL)
				return FALSE;

			char *arg = xmittersig;

			for (int a = 0; a < nrargs; ++a)
			{
				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.
				size_t btlen = 0;
				bool unsup, isref = FALSE;
				int indir = 0;

				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 8:
					if (strncmp(dp, "unsigned", 8) == 0)
						sat = uint_sat;
					else if (strncmp(dp, "QVariant", 8) == 0 && indir == 0)
					{
						sat = qvariant_sat;
						unsup = FALSE;
					}
					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);
					}
					break;

				case 14:
					if (strncmp(dp, "unsigned short", 14) == 0)
						sat = ushort_sat;
					break;
				}

				if (sat == unknown_sat)
				{
					sipFindSigArgType(dp, btlen, &args[a]);

					sat = args[a].atype;

					if (sat == class_sat || sat == mtype_sat || sat == enum_sat)
						unsup = (indir > 1);
				}

				if (unsup)
					sat = unknown_sat;

				args[a].atype = sat;

				// Move to the start of the next argument.
				arg += strlen(arg) + 1;
			}
		}
	}

	// Make a deep copy of the signal.
	strcpy(xmittersig, sig);

	return TRUE;
}


// Factory function to create a universal slot instance.  Returns a pointer to
// the instance or 0 if there was an error.
UniversalSlot *UniversalSlot::create(sipWrapper *txSelf, const char *sig,
				     PyObject *rxObj, const char *slot,
				     const char **member)
{
	UniversalSlot *us = new UniversalSlot(txSelf, sig, rxObj, slot, member);

	if (us -> invalid)
	{
		delete us;
		return 0;
	}

	return us;
}


// Dispose of a receiver that might be a universal slot.
void UniversalSlot::dispose(const QObject *rxQObj)
{
	for (UniversalSlot *us = unislots; us; us = us -> nextus)
		if (us == rxQObj)
		{
			delete us;
			break;
		}
}


// Search for the universal slot connected to a particular Qt signal.
UniversalSlot *UniversalSlot::find(sipWrapper *txSelf, const char *sig,
				   PyObject *rxObj, const char *slot,
				   const char **member)
{
	for (UniversalSlot *us = unislots; us; us = us -> nextus)
		if (us -> xmitter == txSelf && sameSigSlotName(us -> xmittersig, sig) && isSameSlot(&us -> pyslot, rxObj, slot))
		{
			*member = SLOT(unislot());
			return us;
		}

	return 0;
}


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


// 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 const_cast<QObject *>(UniversalSlot::lastSender);
}


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


// 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 && isQtSigSlot(slot))
	{
		*memberp = slot;

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

	UniversalSlot *rx = UniversalSlot::find(txSelf,sigargs,rxObj,slot,memberp);

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

	return rx;
}


// 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(sipWrapper *txSelf,const char *sig,PyObject *rxObj,
		   const char *slot,const char **memberp)
{
	if (slot == NULL)
		return UniversalSlot::create(txSelf,sig,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 universal slot to catch it.
	return UniversalSlot::create(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)
{
	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(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;
		UniversalSlot *rx;
		const char *member;
		bool res;

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

		if ((rx = UniversalSlot::create(txSelf,sig,rxObj,NULL,&member)) == NULL)
			return NULL;

		Py_BEGIN_ALLOW_THREADS
		res = QObject::connect(reinterpret_cast<QObject *>(txPtr), sig,
				       rx, 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 universal slot as this will be it's only
	// connection.
	UniversalSlot::dispose(rxQObj);

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