diff options
author | Olivier Goffart <ogoffart@woboq.com> | 2011-11-11 17:01:06 +0100 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2011-11-25 01:12:14 +0100 |
commit | 79f675a1e0f628bbc25345ebc1eb1f5809166c6b (patch) | |
tree | 998a846e047fcadb588d9922f6aa8aaa6bd562e0 /src/corelib/kernel/qobject.cpp | |
parent | e759f9580ef0a76131c7540015a87968742fce9f (diff) |
Change the return value of QObject::connect
From a bool to a handle to to connection.
Also added a new overload of disconnect that disconnect a handle
This is required because with the new syntax taking lambda or functors,
it is the only way to disconnect a connection (as it is impossible to
compare functors)
The new return value is QMetaObject::Connection, it is a wrapper around
the internal QObjectPrivate::Connection.
QObjectPrivate::Connection is now reference counted.
tst_qglobal.cpp:
This test set up an internal callback, and the callback do not set any
proper connection handle (and tbh, it would be hard for it to do so).
So the returned QMetaObject::Connection is invalid, and ok is false
(Internal callbacks are only used for jambi and should probably be removed)
Change-Id: I111626fb4f47efc4db5e2ea5bff9da15f08fea7b
Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/corelib/kernel/qobject.cpp')
-rw-r--r-- | src/corelib/kernel/qobject.cpp | 190 |
1 files changed, 138 insertions, 52 deletions
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 6bca22b6df..59282d3464 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -373,7 +373,7 @@ void QObjectPrivate::cleanConnectionLists() } else { QObjectPrivate::Connection *next = c->nextConnectionList; *prev = next; - delete c; + c->deref(); c = next; } } @@ -854,7 +854,7 @@ QObject::~QObject() while (QObjectPrivate::Connection *c = connectionList.first) { if (!c->receiver) { connectionList.first = c->nextConnectionList; - delete c; + c->deref(); continue; } @@ -865,11 +865,12 @@ QObject::~QObject() *c->prev = c->next; if (c->next) c->next->prev = c->prev; } + c->receiver = 0; if (needToUnlock) m->unlock(); connectionList.first = c->nextConnectionList; - delete c; + c->deref(); } } @@ -2280,7 +2281,8 @@ static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaM Creates a connection of the given \a type from the \a signal in the \a sender object to the \a method in the \a receiver object. - Returns true if the connection succeeds; otherwise returns false. + Returns a handle to the connection that can be used to disconnect + it later. You must use the \c SIGNAL() and \c SLOT() macros when specifying the \a signal and the \a method, for example: @@ -2309,11 +2311,12 @@ static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaM in the same order as the order the connection was made, when the signal is emitted. - The function returns true if it successfully connects the signal - to the slot. It will return false if it cannot create the - connection, for example, if QObject is unable to verify the - existence of either \a signal or \a method, or if their signatures - aren't compatible. + The function returns a handle to a connection if it successfully + connects the signal to the slot. The Connection handle will be invalid + if it cannot create the connection, for example, if QObject is unable + to verify the existence of either \a signal or \a method, or if their + signatures aren't compatible. + You can check if the QMetaObject::Connection is valid by casting it to a bool. By default, a signal is emitted for every connection you make; two signals are emitted for duplicate connections. You can break @@ -2339,15 +2342,15 @@ static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaM \sa disconnect(), sender(), qRegisterMetaType() */ - -bool QObject::connect(const QObject *sender, const char *signal, - const QObject *receiver, const char *method, - Qt::ConnectionType type) +QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, + const QObject *receiver, const char *method, + Qt::ConnectionType type) { { - const void *cbdata[] = { sender, signal, receiver, method, &type }; + void *result = 0; + const void *cbdata[] = { sender, signal, receiver, method, &type , &result}; if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) - return true; + return QMetaObject::Connection(result); } #ifndef QT_NO_DEBUG @@ -2366,12 +2369,12 @@ bool QObject::connect(const QObject *sender, const char *signal, (signal && *signal) ? signal+1 : "(null)", receiver ? receiver->metaObject()->className() : "(null)", (method && *method) ? method+1 : "(null)"); - return false; + return QMetaObject::Connection(0); } QByteArray tmp_signal_name; if (!check_signal_macro(sender, signal, "connect", "bind")) - return false; + return QMetaObject::Connection(0); const QMetaObject *smeta = sender->metaObject(); const char *signal_arg = signal; ++signal; //skip code @@ -2393,7 +2396,7 @@ bool QObject::connect(const QObject *sender, const char *signal, if (signal_index < 0) { err_method_notfound(sender, signal_arg, "connect"); err_info_about_objects("connect", sender, receiver); - return false; + return QMetaObject::Connection(0); } signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index); int signalOffset, methodOffset; @@ -2405,7 +2408,7 @@ bool QObject::connect(const QObject *sender, const char *signal, int membcode = extract_code(method); if (!check_method_code(membcode, receiver, method, "connect")) - return false; + return QMetaObject::Connection(0); const char *method_arg = method; ++method; // skip code @@ -2444,7 +2447,7 @@ bool QObject::connect(const QObject *sender, const char *signal, if (method_index_relative < 0) { err_method_notfound(receiver, method_arg, "connect"); err_info_about_objects("connect", sender, receiver); - return false; + return QMetaObject::Connection(0); } if (!QMetaObject::checkConnectArgs(signal, method)) { @@ -2452,13 +2455,13 @@ bool QObject::connect(const QObject *sender, const char *signal, "\n %s::%s --> %s::%s", sender->metaObject()->className(), signal, receiver->metaObject()->className(), method); - return false; + return QMetaObject::Connection(0); } int *types = 0; if ((type == Qt::QueuedConnection) && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes()))) - return false; + return QMetaObject::Connection(0); #ifndef QT_NO_DEBUG if (warnCompat) { @@ -2467,10 +2470,11 @@ bool QObject::connect(const QObject *sender, const char *signal, check_and_warn_compat(smeta, smethod, rmeta, rmethod); } #endif - if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index_relative, rmeta ,type, types)) - return false; - const_cast<QObject*>(sender)->connectNotify(signal - 1); - return true; + QMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect( + sender, signal_index, receiver, method_index_relative, rmeta ,type, types)); + if (handle) + const_cast<QObject*>(sender)->connectNotify(signal - 1); + return handle; } /*! @@ -2478,7 +2482,12 @@ bool QObject::connect(const QObject *sender, const char *signal, Creates a connection of the given \a type from the \a signal in the \a sender object to the \a method in the \a receiver object. - Returns true if the connection succeeds; otherwise returns false. + Returns a handle to the connection that can be used to disconnect + it later. + + The Connection handle will be invalid if it cannot create the + connection, for example, the parameters were invalid. + You can check if the QMetaObject::Connection is valid by casting it to a bool. This function works in the same way as connect(const QObject *sender, const char *signal, @@ -2490,9 +2499,9 @@ bool QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type) */ -bool QObject::connect(const QObject *sender, const QMetaMethod &signal, - const QObject *receiver, const QMetaMethod &method, - Qt::ConnectionType type) +QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMethod &signal, + const QObject *receiver, const QMetaMethod &method, + Qt::ConnectionType type) { #ifndef QT_NO_DEBUG bool warnCompat = true; @@ -2513,7 +2522,7 @@ bool QObject::connect(const QObject *sender, const QMetaMethod &signal, signal.signature(), receiver ? receiver->metaObject()->className() : "(null)", method.signature() ); - return false; + return QMetaObject::Connection(0); } // Reconstructing SIGNAL() macro result for signal.signature() string @@ -2528,12 +2537,12 @@ bool QObject::connect(const QObject *sender, const QMetaMethod &signal, methodSignature.append((char)(method.methodType() == QMetaMethod::Slot ? QSLOT_CODE : method.methodType() == QMetaMethod::Signal ? QSIGNAL_CODE : 0 + '0')); methodSignature.append(method.signature()); - const void *cbdata[] = { sender, signalSignature.constData(), receiver, methodSignature.constData(), &type }; + void *result = 0; + const void *cbdata[] = { sender, signalSignature.constData(), receiver, methodSignature.constData(), &type, &result }; if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) - return true; + return QMetaObject::Connection(result); } - int signal_index; int method_index; { @@ -2547,36 +2556,36 @@ bool QObject::connect(const QObject *sender, const QMetaMethod &signal, if (signal_index == -1) { qWarning("QObject::connect: Can't find signal %s on instance of class %s", signal.signature(), smeta->className()); - return false; + return QMetaObject::Connection(0); } if (method_index == -1) { qWarning("QObject::connect: Can't find method %s on instance of class %s", method.signature(), rmeta->className()); - return false; + return QMetaObject::Connection(0); } - + if (!QMetaObject::checkConnectArgs(signal.signature(), method.signature())) { qWarning("QObject::connect: Incompatible sender/receiver arguments" "\n %s::%s --> %s::%s", smeta->className(), signal.signature(), rmeta->className(), method.signature()); - return false; + return QMetaObject::Connection(0); } int *types = 0; if ((type == Qt::QueuedConnection) && !(types = queuedConnectionTypes(signal.parameterTypes()))) - return false; + return QMetaObject::Connection(0); #ifndef QT_NO_DEBUG if (warnCompat) check_and_warn_compat(smeta, signal, rmeta, method); #endif - if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index, 0, type, types)) - return false; - - const_cast<QObject*>(sender)->connectNotify(signalSignature.constData()); - return true; + QMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect( + sender, signal_index, receiver, method_index, 0, type, types)); + if (handle) + const_cast<QObject*>(sender)->connectNotify(signalSignature.constData()); + return handle; } /*! @@ -2958,22 +2967,24 @@ static int methodIndexToSignalIndex(const QMetaObject *metaObject, int signal_in if \a signal_index is -1, then we effectively connect *all* signals from the sender to the receiver's slot */ -bool QMetaObject::connect(const QObject *sender, int signal_index, - const QObject *receiver, int method_index, int type, int *types) +QMetaObject::Connection QMetaObject::connect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index, int type, int *types) { signal_index = methodIndexToSignalIndex(sender->metaObject(), signal_index); - return QMetaObjectPrivate::connect(sender, signal_index, + return Connection(QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index, 0, //FIXME, we could speed this connection up by computing the relative index - type, types); + type, types)); } /*! \internal Same as the QMetaObject::connect, but \a signal_index must be the result of QObjectPrivate::signalIndex method_index is relative to the rmeta metaobject, if rmeta is null, then it is absolute index + + the QObjectPrivate::Connection* has a refcount of 2, so it must be passed to a QMetaObject::Connection */ -bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index, +QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender, int signal_index, const QObject *receiver, int method_index, const QMetaObject *rmeta, int type, int *types) { @@ -2998,7 +3009,7 @@ bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index, while (c2) { if (c2->receiver == receiver && c2->method() == method_index_absolute) - return false; + return 0; c2 = c2->nextConnectionList; } } @@ -3030,8 +3041,7 @@ bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index, sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f)); } - c.take(); // stop tracking - return true; + return c.take(); } /*!\internal @@ -4032,6 +4042,82 @@ void qDeleteInEventHandler(QObject *o) delete o; } +/*! + Disconnect a connection. + + If the \a connection is invalid or has already been disconnected, do nothing + and return false. + + \sa connect() + */ +bool QObject::disconnect(const QMetaObject::Connection &connection) +{ + QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(connection.d_ptr); + + if (!c || !c->receiver) + return false; + + QMutex *senderMutex = signalSlotLock(c->sender); + QMutex *receiverMutex = signalSlotLock(c->receiver); + QOrderedMutexLocker locker(senderMutex, receiverMutex); + + QObjectConnectionListVector *connectionLists = QObjectPrivate::get(c->sender)->connectionLists; + Q_ASSERT(connectionLists); + connectionLists->dirty = true; + + *c->prev = c->next; + if (c->next) + c->next->prev = c->prev; + c->receiver = 0; + return true; +} + +/*! \class QMetaObject::Connection + Represents a handle to a signal-slot connection. + It can be used to disconnect that connection, or check if + the connection was successful + + \sa QObject::disconnect + */ + +/*! + Create a copy of the handle to the connection + */ +QMetaObject::Connection::Connection(const QMetaObject::Connection &other) : d_ptr(other.d_ptr) +{ + if (d_ptr) + static_cast<QObjectPrivate::Connection *>(d_ptr)->ref(); +} + +QMetaObject::Connection& QMetaObject::Connection::operator=(const QMetaObject::Connection& other) +{ + if (other.d_ptr != d_ptr) { + if (d_ptr) + static_cast<QObjectPrivate::Connection *>(d_ptr)->deref(); + d_ptr = other.d_ptr; + if (other.d_ptr) + static_cast<QObjectPrivate::Connection *>(other.d_ptr)->ref(); + } + return *this; +} + +QMetaObject::Connection::Connection() : d_ptr(0) {} + +QMetaObject::Connection::~Connection() +{ + if (d_ptr) + static_cast<QObjectPrivate::Connection *>(d_ptr)->deref(); +} + +/*! + \fn bool QMetaObject::Connection::operator bool() + + Returns true if the connection is valid. + + The connection is valid if the call to QObject::connect succeeded. + The connection is invalid if QObject::connect was not able to find + the signal or the slot, or if the arguments do not match. + */ QT_END_NAMESPACE |