Revision control

++ // Copyright (C) 2021 The Qt Company Ltd.
++ // Copyright (C) 2016 Intel Corporation.
++ // Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include <QtCore/qtconfigmacros.h>
++
++ #ifdef QT_NO_DISCONNECT_CONST_CONNECTION
++ # undef QT_NO_DISCONNECT_CONST_CONNECTION
++ #endif
++
++ #include "qobject.h"
++ #include "qobject_p.h"
++ #include "qobject_p_p.h"
++ #include "qmetaobject_p.h"
++
++ #include <QtCore/private/qtclasshelper_p.h>
++ #include <QtCore/qspan.h>
++ #include "qabstracteventdispatcher.h"
++ #include "qabstracteventdispatcher_p.h"
++ #include "qcoreapplication.h"
++ #include "qcoreapplication_p.h"
++ #include "qcoreevent_p.h"
++ #include "qloggingcategory.h"
++ #include "qvariant.h"
++ #include "qmetaobject.h"
++ #if QT_CONFIG(regularexpression)
++ # include <qregularexpression.h>
++ #endif
++ #include <qthread.h>
++ #include <private/qthread_p.h>
++ #include <qdebug.h>
++ #include <qvarlengtharray.h>
++ #include <qscopeguard.h>
++ #include <qset.h>
++ #if QT_CONFIG(thread)
++ #include <private/qlatch_p.h>
++ #endif
++
++ #include <private/qorderedmutexlocker_p.h>
++ #include <private/qhooks_p.h>
++ #include <qtcore_tracepoints_p.h>
++
++ #include <new>
++ #include <mutex>
++ #include <memory>
++ #include <optional>
++ #include <iterator>
++
++ #include <ctype.h>
++ #include <limits.h>
++
++ QT_BEGIN_NAMESPACE
++
++ Q_TRACE_POINT(qtcore, QObject_ctor, QObject *object);
++ Q_TRACE_POINT(qtcore, QObject_dtor, QObject *object);
++ Q_TRACE_POINT(qtcore, QMetaObject_activate_entry, QObject *sender, int signalIndex);
++ Q_TRACE_POINT(qtcore, QMetaObject_activate_exit);
++ Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_entry, QObject *receiver, int slotIndex);
++ Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_exit);
++ Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_functor_entry, void *slotObject);
++ Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_functor_exit);
++ Q_TRACE_POINT(qtcore, QMetaObject_activate_declarative_signal_entry, QObject *sender, int signalIndex);
++ Q_TRACE_POINT(qtcore, QMetaObject_activate_declarative_signal_exit);
++
++ static int DIRECT_CONNECTION_ONLY = 0;
++
++ Q_STATIC_LOGGING_CATEGORY(lcConnectSlotsByName, "qt.core.qmetaobject.connectslotsbyname")
++ Q_STATIC_LOGGING_CATEGORY(lcConnect, "qt.core.qobject.connect")
++
++ Q_CORE_EXPORT QBasicAtomicPointer<QSignalSpyCallbackSet> qt_signal_spy_callback_set = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
++
++ void qt_register_signal_spy_callbacks(QSignalSpyCallbackSet *callback_set)
++ {
++ qt_signal_spy_callback_set.storeRelease(callback_set);
++ }
++
++ QDynamicMetaObjectData::~QDynamicMetaObjectData()
++ {
++ }
++
++ QAbstractDynamicMetaObject::~QAbstractDynamicMetaObject()
++ {
++ }
++
++ static int *queuedConnectionTypes(const QMetaMethod &method)
++ {
++ const auto parameterCount = method.parameterCount();
++ int *typeIds = new int[parameterCount + 1];
++ Q_CHECK_PTR(typeIds);
++ for (int i = 0; i < parameterCount; ++i) {
++ const QMetaType metaType = method.parameterMetaType(i);
++ if (metaType.flags() & QMetaType::IsPointer)
++ typeIds[i] = QMetaType::VoidStar;
++ else
++ typeIds[i] = metaType.id();
++ if (!typeIds[i] && method.parameterTypeName(i).endsWith('*'))
++ typeIds[i] = QMetaType::VoidStar;
++ if (!typeIds[i]) {
++ const QByteArray typeName = method.parameterTypeName(i);
++ qCWarning(lcConnect,
++ "QObject::connect: Cannot queue arguments of type '%s'\n"
++ "(Make sure '%s' is registered using qRegisterMetaType().)",
++ typeName.constData(), typeName.constData());
++ delete[] typeIds;
++ return nullptr;
++ }
++ }
++ typeIds[parameterCount] = 0;
++
++ return typeIds;
++ }
++
++ // ### Future work: replace with an array of QMetaType or QtPrivate::QMetaTypeInterface *
++ static int *queuedConnectionTypes(QSpan<const QArgumentType> argumentTypes)
++ {
++ const int argc = int(argumentTypes.size());
++ auto types = std::make_unique<int[]>(argc + 1);
++ for (int i = 0; i < argc; ++i) {
++ const QArgumentType &type = argumentTypes[i];
++ if (type.metaType().isValid())
++ types[i] = type.metaType().id();
++ else if (type.name().endsWith('*'))
++ types[i] = QMetaType::VoidStar;
++ else
++ types[i] = QMetaType::fromName(type.name()).rawId();
++
++ if (!types[i]) {
++ qCWarning(lcConnect,
++ "QObject::connect: Cannot queue arguments of type '%s'\n"
++ "(Make sure '%s' is registered using qRegisterMetaType().)",
++ type.name().constData(), type.name().constData());
++ return nullptr;
++ }
++ }
++ types[argc] = 0;
++
++ return types.release();
++ }
++
++ Q_CONSTINIT static QBasicMutex _q_ObjectMutexPool[131];
++
++ /**
++ * \internal
++ * mutex to be locked when accessing the connection lists or the senders list
++ */
++ static inline QBasicMutex *signalSlotLock(const QObject *o)
++ {
++ return &_q_ObjectMutexPool[quintptr(o) % std::size(_q_ObjectMutexPool)];
++ }
++
++ /**
++ * \internal
++ * Tells \a sender that the connection for \a signalIndex is gone, because its receiver is being
++ * destroyed.
++ *
++ * The caller holds the receiver's signalSlotLock, which \a sender's destructor must also take to
++ * tear down this (still-present) connection. So \a sender cannot get past ~QObject(), and reading
++ * its QObjectPrivate here is safe. That lock does not, however, hold back \a sender's
++ * further-derived destructors: those run before ~QObject() takes any lock, and rewrite the vptr as
++ * they go. Resolving the signal and calling disconnectNotify() are therefore virtual calls that
++ * may only be made from the thread that owns \a sender. Post them to that thread otherwise, unless
++ * it can no longer deliver events (e.g. it has finished), where the event would leak.
++ */
++ static void disconnectNotifySender(QObject *sender, int signalIndex, Qt::HANDLE currentThreadId)
++ {
++ QObjectPrivate *senderPriv = QObjectPrivate::get(sender);
++ QThreadData *senderThread = senderPriv->threadData.loadRelaxed();
++ if (senderThread->threadId.loadRelaxed() == currentThreadId)
++ senderPriv->disconnectNotify(QMetaObjectPrivate::signal(sender->metaObject(), signalIndex));
++ else if (senderThread->hasEventDispatcher())
++ QCoreApplication::postEvent(sender, new QDisconnectNotifyEvent(signalIndex));
++ }
++
++ void (*QAbstractDeclarativeData::destroyed)(QAbstractDeclarativeData *, QObject *) = nullptr;
++ void (*QAbstractDeclarativeData::signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **) = nullptr;
++ int (*QAbstractDeclarativeData::receivers)(QAbstractDeclarativeData *, const QObject *, int) = nullptr;
++ bool (*QAbstractDeclarativeData::isSignalConnected)(QAbstractDeclarativeData *, const QObject *, int) = nullptr;
++ void (*QAbstractDeclarativeData::setWidgetParent)(QObject *, QObject *) = nullptr;
++
++ /*!
++ \fn QObjectData::QObjectData()
++ \internal
++ */
++
++
++ QObjectData::~QObjectData() {}
++
++ const QMetaObject *QObjectData::dynamicMetaObject() const
++ {
++ // ### keep in sync with removed_api.cpp version
++ return metaObject->toDynamicMetaObject(q_ptr);
++ }
++
++ QObjectPrivate::QObjectPrivate(decltype(QObjectPrivateVersion))
++ : threadData(nullptr), currentChildBeingDeleted(nullptr)
++ {
++ // QObjectData initialization
++ q_ptr = nullptr;
++ parent = nullptr; // no parent yet. It is set by setParent()
++ isWidget = false; // assume not a widget object
++ blockSig = false; // not blocking signals
++ wasDeleted = false; // double-delete catcher
++ isDeletingChildren = false; // set by deleteChildren()
++ sendChildEvents = true; // if we should send ChildAdded and ChildRemoved events to parent
++ receiveChildEvents = true;
++ postedEvents.storeRelaxed(0);
++ extraData = nullptr;
++ metaObject = nullptr;
++ isWindow = false;
++ deleteLaterCalled = false;
++ isQuickItem = false;
++ willBeWidget = false;
++ wasWidget = false;
++ receiveParentEvents = false; // If object wants ParentAboutToChange and ParentChange
++ }
++
++ QObjectPrivate::~QObjectPrivate()
++ {
++ auto thisThreadData = threadData.loadRelaxed();
++ if (extraData && !extraData->runningTimers.isEmpty()) {
++ if (thisThreadData->threadId.loadRelaxed() == QThread::currentThreadId()) {
++ // unregister pending timers
++ if (thisThreadData->hasEventDispatcher())
++ thisThreadData->eventDispatcher.loadRelaxed()->unregisterTimers(q_ptr);
++
++ // release the timer ids back to the pool
++ for (auto id : std::as_const(extraData->runningTimers))
++ QAbstractEventDispatcherPrivate::releaseTimerId(id);
++ } else {
++ qWarning("QObject::~QObject: Timers cannot be stopped from another thread");
++ }
++ }
++
++ if (postedEvents.loadRelaxed())
++ QCoreApplication::removePostedEvents(q_ptr, 0);
++
++ thisThreadData->deref();
++
++ if (metaObject)
++ metaObject->objectDestroyed(q_ptr);
++
++ delete extraData;
++ }
++
++ /*!
++ \internal
++ For a given metaobject, compute the signal offset, and the method offset (including signals)
++ */
++ static void computeOffsets(const QMetaObject *metaobject, int *signalOffset, int *methodOffset)
++ {
++ *signalOffset = *methodOffset = 0;
++ const QMetaObject *m = metaobject->d.superdata;
++ while (m) {
++ const QMetaObjectPrivate *d = QMetaObjectPrivate::get(m);
++ *methodOffset += d->methodCount;
++ Q_ASSERT(d->revision >= 4);
++ *signalOffset += d->signalCount;
++ m = m->d.superdata;
++ }
++ }
++
++ // Used by QAccessibleWidget
++ QObjectList QObjectPrivate::receiverList(const char *signal) const
++ {
++ QObjectList returnValue;
++ int signal_index = signalIndex(signal);
++ ConnectionData *cd = connections.loadAcquire();
++ if (signal_index < 0 || !cd)
++ return returnValue;
++ if (signal_index < cd->signalVectorCount()) {
++ const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
++
++ while (c) {
++ QObject *r = c->receiver.loadRelaxed();
++ if (r)
++ returnValue << r;
++ c = c->nextConnectionList.loadRelaxed();
++ }
++ }
++ return returnValue;
++ }
++
++ /*!
++ \internal
++ The signalSlotLock() of the sender must be locked while calling this function
++ */
++ inline void QObjectPrivate::ensureConnectionData()
++ {
++ if (connections.loadRelaxed())
++ return;
++ ConnectionData *cd = new ConnectionData;
++ cd->ref.ref();
++ connections.storeRelease(cd);
++ }
++
++ /*!
++ \internal
++ Add the connection \a c to the list of connections of the sender's object
++ for the specified \a signal
++
++ The signalSlotLock() of the sender and receiver must be locked while calling
++ this function
++
++ Will also add the connection in the sender's list of the receiver.
++ */
++ inline void QObjectPrivate::addConnection(int signal, Connection *c)
++ {
++ Q_ASSERT(c->sender == q_ptr);
++ ensureConnectionData();
++ ConnectionData *cd = connections.loadRelaxed();
++ cd->resizeSignalVector(signal + 1);
++
++ ConnectionList &connectionList = cd->connectionsForSignal(signal);
++ Connection *lastConnection = connectionList.last.loadRelaxed();
++ if (lastConnection) {
++ Q_ASSERT(lastConnection->receiver.loadRelaxed());
++ lastConnection->nextConnectionList.storeRelease(c);
++ } else {
++ connectionList.first.storeRelease(c);
++ }
++ c->id.storeRelease(++cd->currentConnectionId);
++ c->prevConnectionList = lastConnection;
++ connectionList.last.storeRelaxed(c);
++
++ QObjectPrivate *rd = QObjectPrivate::get(c->receiver.loadRelaxed());
++ rd->ensureConnectionData();
++
++ c->prev = &(rd->connections.loadRelaxed()->senders);
++ c->next = *c->prev;
++ *c->prev = c;
++ if (c->next)
++ c->next->prev = &c->next;
++ }
++
++ void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection *c)
++ {
++ Q_ASSERT(c->receiver.loadRelaxed());
++ ConnectionList &connections = signalVector.loadRelaxed()->at(c->signal_index);
++ c->receiver.storeRelaxed(nullptr);
++ QThreadData *td = c->receiverThreadData.loadRelaxed();
++ if (td)
++ td->deref();
++ c->receiverThreadData.storeRelaxed(nullptr);
++
++ #ifndef QT_NO_DEBUG
++ bool found = false;
++ for (Connection *cc = connections.first.loadRelaxed(); cc; cc = cc->nextConnectionList.loadRelaxed()) {
++ if (cc == c) {
++ found = true;
++ break;
++ }
++ }
++ Q_ASSERT(found);
++ #endif
++
++ // remove from the senders linked list
++ *c->prev = c->next;
++ if (c->next)
++ c->next->prev = c->prev;
++ c->prev = nullptr;
++
++ Connection *n = c->nextConnectionList.loadRelaxed();
++ if (connections.first.loadRelaxed() == c)
++ connections.first.storeRelaxed(n);
++ if (connections.last.loadRelaxed() == c)
++ connections.last.storeRelaxed(c->prevConnectionList);
++ Q_ASSERT(signalVector.loadRelaxed()->at(c->signal_index).first.loadRelaxed() != c);
++ Q_ASSERT(signalVector.loadRelaxed()->at(c->signal_index).last.loadRelaxed() != c);
++
++ // keep c->nextConnectionList intact, as it might still get accessed by activate
++ if (n)
++ n->prevConnectionList = c->prevConnectionList;
++ if (c->prevConnectionList)
++ c->prevConnectionList->nextConnectionList.storeRelaxed(n);
++ c->prevConnectionList = nullptr;
++
++ Q_ASSERT(c != static_cast<Connection *>(orphaned.load(std::memory_order_relaxed)));
++ // add c to orphanedConnections
++ TaggedSignalVector o = nullptr;
++ /* No ABA issue here: When adding a node, we only care about the list head, it doesn't
++ * matter if the tail changes.
++ */
++ o = orphaned.load(std::memory_order_acquire);
++ do {
++ c->nextInOrphanList = o;
++ } while (!orphaned.compare_exchange_strong(o, TaggedSignalVector(c), std::memory_order_release));
++
++ #ifndef QT_NO_DEBUG
++ found = false;
++ for (Connection *cc = connections.first.loadRelaxed(); cc; cc = cc->nextConnectionList.loadRelaxed()) {
++ if (cc == c) {
++ found = true;
++ break;
++ }
++ }
++ Q_ASSERT(!found);
++ #endif
++
++ }
++
++ void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy)
++ {
++ QBasicMutex *senderMutex = signalSlotLock(sender);
++ TaggedSignalVector c = nullptr;
++ {
++ std::unique_lock<QBasicMutex> lock(*senderMutex, std::defer_lock_t{});
++ if (lockPolicy == NeedToLock)
++ lock.lock();
++ if (ref.loadAcquire() > 1)
++ return;
++
++ // Since ref == 1, no activate() is in process since we locked the mutex. That implies,
++ // that nothing can reference the orphaned connection objects anymore and they can
++ // be safely deleted
++ c = orphaned.exchange(nullptr, std::memory_order_relaxed);
++ }
++ if (c) {
++ // Deleting c might run arbitrary user code, so we must not hold the lock
++ if (lockPolicy == AlreadyLockedAndTemporarilyReleasingLock) {
++ senderMutex->unlock();
++ deleteOrphaned(c);
++ senderMutex->lock();
++ } else {
++ deleteOrphaned(c);
++ }
++ }
++ }
++
++ inline void QObjectPrivate::ConnectionData::deleteOrphaned(TaggedSignalVector o)
++ {
++ while (o) {
++ TaggedSignalVector next = nullptr;
++ if (SignalVector *v = static_cast<SignalVector *>(o)) {
++ next = v->nextInOrphanList;
++ free(v);
++ } else {
++ QObjectPrivate::Connection *c = static_cast<Connection *>(o);
++ next = c->nextInOrphanList;
++ Q_ASSERT(!c->receiver.loadRelaxed());
++ Q_ASSERT(!c->prev);
++ c->freeSlotObject();
++ c->deref();
++ }
++ o = next;
++ }
++ }
++
++ /*! \internal
++
++ Returns \c true if the signal with index \a signal_index from object \a sender is connected.
++
++ \a signal_index must be the index returned by QObjectPrivate::signalIndex;
++ */
++ bool QObjectPrivate::isSignalConnected(uint signalIndex, bool checkDeclarative) const
++ {
++ if (checkDeclarative && isDeclarativeSignalConnected(signalIndex))
++ return true;
++
++ ConnectionData *cd = connections.loadAcquire();
++ if (!cd)
++ return false;
++ SignalVector *signalVector = cd->signalVector.loadRelaxed();
++ if (!signalVector)
++ return false;
++
++ if (signalVector->at(-1).first.loadRelaxed())
++ return true;
++
++ if (signalIndex < uint(cd->signalVectorCount())) {
++ const QObjectPrivate::Connection *c = signalVector->at(signalIndex).first.loadRelaxed();
++ while (c) {
++ if (c->receiver.loadRelaxed())
++ return true;
++ c = c->nextConnectionList.loadRelaxed();
++ }
++ }
++ return false;
++ }
++
++ bool QObjectPrivate::maybeSignalConnected(uint signalIndex) const
++ {
++ ConnectionData *cd = connections.loadAcquire();
++ if (!cd)
++ return false;
++ SignalVector *signalVector = cd->signalVector.loadAcquire();
++ if (!signalVector)
++ return false;
++
++ if (signalVector->at(-1).first.loadAcquire())
++ return true;
++
++ if (signalIndex < uint(cd->signalVectorCount())) {
++ const QObjectPrivate::Connection *c = signalVector->at(signalIndex).first.loadAcquire();
++ return c != nullptr;
++ }
++ return false;
++ }
++
++ void QObjectPrivate::reinitBindingStorageAfterThreadMove()
++ {
++ bindingStorage.reinitAfterThreadMove();
++ for (int i = 0; i < children.size(); ++i)
++ children[i]->d_func()->reinitBindingStorageAfterThreadMove();
++ }
++
++ /*!
++ \internal
++ */
++ QAbstractMetaCallEvent::~QAbstractMetaCallEvent()
++ {
++ #if QT_CONFIG(thread)
++ if (latch)
++ latch->countDown();
++ #endif
++ }
++
++ /*!
++ \internal
++
++ Used for blocking queued connections, just passes \a args through without
++ allocating any memory.
++ */
++ QMetaCallEvent::QMetaCallEvent(ushort method_offset, ushort method_relative,
++ QObjectPrivate::StaticMetaCallFunction callFunction,
++ const QObject *sender, int signalId,
++ void **args, QLatch *latch)
++ : QAbstractMetaCallEvent(sender, signalId, latch),
++ d{nullptr, args, callFunction, 0, method_offset, method_relative}
++ {
++ }
++
++ /*!
++ \internal
++
++ Used for blocking queued connections, just passes \a args through without
++ allocating any memory.
++ */
++ QMetaCallEvent::QMetaCallEvent(QtPrivate::QSlotObjectBase *slotO,
++ const QObject *sender, int signalId,
++ void **args, QLatch *latch)
++ : QAbstractMetaCallEvent(sender, signalId, latch),
++ d{QtPrivate::SlotObjUniquePtr{slotO}, args, nullptr, 0, 0, ushort(-1)}
++ {
++ if (d.slotObj_)
++ d.slotObj_->ref();
++ }
++
++ /*!
++ \internal
++
++ Used for blocking queued connections, just passes \a args through without
++ allocating any memory.
++ */
++ QMetaCallEvent::QMetaCallEvent(QtPrivate::SlotObjUniquePtr slotO,
++ const QObject *sender, int signalId,
++ void **args, QLatch *latch)
++ : QAbstractMetaCallEvent(sender, signalId, latch),
++ d{std::move(slotO), args, nullptr, 0, 0, ushort(-1)}
++ {
++ }
++
++ /*!
++ \internal
++ */
++ QMetaCallEvent::QMetaCallEvent(const QObject *sender, int signalId, Data &&data)
++ : QAbstractMetaCallEvent(sender, signalId),
++ d(std::move(data))
++ {
++ }
++
++ /*!
++ \internal
++ */
++ void QMetaCallEvent::placeMetaCall(QObject *object)
++ {
++ if (d.slotObj_) {
++ d.slotObj_->call(object, d.args_);
++ } else if (d.callFunction_ && d.method_offset_ <= object->metaObject()->methodOffset()) {
++ d.callFunction_(object, QMetaObject::InvokeMetaMethod, d.method_relative_, d.args_);
++ } else {
++ QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod,
++ d.method_offset_ + d.method_relative_, d.args_);
++ }
++ }
++
++ /*!
++ \internal
++
++ Constructs a QQueuedMetaCallEvent by copying the argument values using their meta-types.
++ */
++ QQueuedMetaCallEvent::QQueuedMetaCallEvent(ushort method_offset, ushort method_relative,
++ QObjectPrivate::StaticMetaCallFunction callFunction,
++ const QObject *sender, int signalId, int argCount,
++ const QtPrivate::QMetaTypeInterface * const *argTypes,
++ const void * const *argValues)
++ : QMetaCallEvent(sender, signalId, {nullptr, nullptr, callFunction, argCount,
++ method_offset, method_relative}),
++ prealloc_()
++ {
++ copyArgValues(argCount, argTypes, argValues);
++ }
++
++ /*!
++ \internal
++
++ Constructs a QQueuedMetaCallEvent by copying the argument values using their meta-types.
++ */
++ QQueuedMetaCallEvent::QQueuedMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj,
++ const QObject *sender, int signalId, int argCount,
++ const QtPrivate::QMetaTypeInterface * const *argTypes,
++ const void * const *argValues)
++ : QMetaCallEvent(sender, signalId, {QtPrivate::SlotObjUniquePtr(slotObj), nullptr, nullptr, argCount,
++ 0, ushort(-1)}),
++ prealloc_()
++ {
++ if (d.slotObj_)
++ d.slotObj_->ref();
++ copyArgValues(argCount, argTypes, argValues);
++ }
++
++ /*!
++ \internal
++
++ Constructs a QQueuedMetaCallEvent by copying the argument values using their meta-types.
++ */
++ QQueuedMetaCallEvent::QQueuedMetaCallEvent(QtPrivate::SlotObjUniquePtr slotObj,
++ const QObject *sender, int signalId, int argCount,
++ const QtPrivate::QMetaTypeInterface * const *argTypes,
++ const void * const *argValues)
++ : QMetaCallEvent(sender, signalId, {std::move(slotObj), nullptr, nullptr, argCount,
++ 0, ushort(-1)}),
++ prealloc_()
++ {
++ copyArgValues(argCount, argTypes, argValues);
++ }
++
++ /*!
++ \internal
++ */
++ QQueuedMetaCallEvent::~QQueuedMetaCallEvent()
++ {
++ const QMetaType *t = reinterpret_cast<QMetaType *>(d.args_ + d.nargs_);
++ int inplaceIndex = 0;
++ for (int i = 0; i < d.nargs_; ++i) {
++ if (t[i].isValid() && d.args_[i]) {
++ if (typeFitsInPlace(t[i]) && inplaceIndex < InplaceValuesCapacity) {
++ // Only destruct
++ void *where = &valuesPrealloc_[inplaceIndex++].storage;
++ t[i].destruct(where);
++ } else {
++ // Destruct and deallocate
++ t[i].destroy(d.args_[i]);
++ }
++ }
++ }
++ if (d.nargs_) {
++ if (static_cast<void *>(d.args_) != prealloc_)
++ QtPrivate::sizedFree(d.args_, d.nargs_, PtrAndTypeSize);
++ }
++ }
++
++ /*!
++ \internal
++ */
++ inline void QQueuedMetaCallEvent::allocArgs()
++ {
++ if (!d.nargs_)
++ return;
++
++ void *const memory = d.nargs_ * PtrAndTypeSize > sizeof(prealloc_) ?
++ calloc(d.nargs_, PtrAndTypeSize) : prealloc_;
++
++ Q_CHECK_PTR(memory);
++ d.args_ = static_cast<void **>(memory);
++ }
++
++ /*!
++ \internal
++ */
++ inline void QQueuedMetaCallEvent::copyArgValues(int argCount, const QtPrivate::QMetaTypeInterface * const *argTypes,
++ const void * const *argValues)
++ {
++ allocArgs();
++ void **args = d.args_;
++ QMetaType *types = reinterpret_cast<QMetaType *>(d.args_ + d.nargs_);
++ int inplaceIndex = 0;
++
++ if (argCount) {
++ types[0] = QMetaType(); // return type
++ args[0] = nullptr; // return value pointer
++ }
++ // no return value
++
++ for (int n = 1; n < argCount; ++n) {
++ types[n] = QMetaType(argTypes[n]);
++ if (typeFitsInPlace(types[n]) && inplaceIndex < InplaceValuesCapacity) {
++ // Copy-construct in place
++ void *where = &valuesPrealloc_[inplaceIndex++].storage;
++ types[n].construct(where, argValues[n]);
++ args[n] = where;
++ } else {
++ // Allocate and copy-construct
++ args[n] = types[n].create(argValues[n]);
++ }
++ }
++ }
++
++ /*!
++ \internal
++ */
++ inline bool QQueuedMetaCallEvent::typeFitsInPlace(const QMetaType type)
++ {
++ return (q20::cmp_less_equal(type.sizeOf(), sizeof(ArgValueStorage)) &&
++ q20::cmp_less_equal(type.alignOf(), alignof(ArgValueStorage)));
++ }
++
++ /*!
++ \class QSignalBlocker
++ \brief Exception-safe wrapper around QObject::blockSignals().
++ \since 5.3
++ \ingroup objectmodel
++ \inmodule QtCore
++
++ \reentrant
++
++ QSignalBlocker can be used wherever you would otherwise use a
++ pair of calls to QObject::blockSignals(). It blocks signals in its
++ constructor and in the destructor it resets the state to what
++ it was before the constructor ran.
++
++ \snippet code/src_corelib_kernel_qobject.cpp 53
++ is thus equivalent to
++ \snippet code/src_corelib_kernel_qobject.cpp 54
++
++ except the code using QSignalBlocker is safe in the face of
++ exceptions.
++
++ \sa QMutexLocker, QEventLoopLocker
++ */
++
++ /*!
++ \fn QSignalBlocker::QSignalBlocker(QObject *object)
++
++ Constructor. Calls \a{object}->blockSignals(true).
++ */
++
++ /*!
++ \fn QSignalBlocker::QSignalBlocker(QObject &object)
++ \overload
++
++ Calls \a{object}.blockSignals(true).
++ */
++
++ /*!
++ \fn QSignalBlocker::QSignalBlocker(QSignalBlocker &&other)
++
++ Move-constructs a signal blocker from \a other. \a other will have
++ a no-op destructor, while responsibility for restoring the
++ QObject::signalsBlocked() state is transferred to the new object.
++ */
++
++ /*!
++ \fn QSignalBlocker &QSignalBlocker::operator=(QSignalBlocker &&other)
++
++ Move-assigns this signal blocker from \a other. \a other will have
++ a no-op destructor, while responsibility for restoring the
++ QObject::signalsBlocked() state is transferred to this object.
++
++ The object's signals this signal blocker was blocking prior to
++ being moved to, if any, are unblocked \e except in the case where
++ both instances block the same object's signals and \c *this is
++ unblocked while \a other is not, at the time of the move.
++ */
++
++ /*!
++ \fn QSignalBlocker::~QSignalBlocker()
++
++ Destructor. Restores the QObject::signalsBlocked() state to what it
++ was before the constructor ran, unless unblock() has been called
++ without a following reblock(), in which case it does nothing.
++ */
++
++ /*!
++ \fn void QSignalBlocker::reblock()
++
++ Re-blocks signals after a previous unblock().
++
++ The numbers of reblock() and unblock() calls are not counted, so
++ every reblock() undoes any number of unblock() calls.
++ */
++
++ /*!
++ \fn void QSignalBlocker::unblock()
++
++ Temporarily restores the QObject::signalsBlocked() state to what
++ it was before this QSignalBlocker's constructor ran. To undo, use
++ reblock().
++
++ The numbers of reblock() and unblock() calls are not counted, so
++ every unblock() undoes any number of reblock() calls.
++ */
++
++ /*!
++ \fn void QSignalBlocker::dismiss()
++ \since 6.7
++ Dismisses the QSignalBlocker. It will no longer access the QObject
++ passed to its constructor. unblock(), reblock(), as well as
++ ~QSignalBlocker() will have no effect.
++ */
++
++ /*!
++ \class QObject
++ \inmodule QtCore
++ \brief The QObject class is the base class of all Qt objects.
++
++ \ingroup objectmodel
++
++ \reentrant
++
++ QObject is the heart of the Qt \l{Object Model}. The central
++ feature in this model is a very powerful mechanism for seamless
++ object communication called \l{signals and slots}. You can
++ connect a signal to a slot with connect() and destroy the
++ connection with disconnect(). To avoid never ending notification
++ loops you can temporarily block signals with blockSignals(). The
++ protected functions connectNotify() and disconnectNotify() make
++ it possible to track connections.
++
++ QObjects organize themselves in \l {Object Trees & Ownership}
++ {object trees}. When you create a QObject with another object as
++ parent, the object will automatically add itself to the parent's
++ children() list. The parent takes ownership of the object; i.e.,
++ it will automatically delete its children in its destructor. You
++ can look for an object by name and optionally type using
++ findChild() or findChildren().
++
++ Every object has an objectName() and its class name can be found
++ via the corresponding metaObject() (see QMetaObject::className()).
++ You can determine whether the object's class inherits another
++ class in the QObject inheritance hierarchy by using the
++ inherits() function.
++
++ When an object is deleted, it emits a destroyed() signal. You can
++ catch this signal to avoid dangling references to QObjects.
++
++ QObjects can receive events through event() and filter the events
++ of other objects. See installEventFilter() and eventFilter() for
++ details. A convenience handler, childEvent(), can be reimplemented
++ to catch child events.
++
++ Last but not least, QObject provides the basic timer support in
++ Qt; see QChronoTimer for high-level support for timers.
++
++ Notice that the Q_OBJECT macro is mandatory for any object that
++ implements signals, slots or properties. You also need to run the
++ \l{moc}{Meta Object Compiler} on the source file. We strongly
++ recommend the use of this macro in all subclasses of QObject
++ regardless of whether or not they actually use signals, slots and
++ properties, since failure to do so may lead certain functions to
++ exhibit strange behavior.
++
++ All Qt widgets inherit QObject. The convenience function
++ isWidgetType() returns whether an object is actually a widget. It
++ is much faster than
++ \l{qobject_cast()}{qobject_cast}<QWidget *>(\e{obj}) or
++ \e{obj}->\l{inherits()}{inherits}("QWidget").
++
++ Some QObject functions, e.g. children(), return a QObjectList.
++ QObjectList is a typedef for QList<QObject *>.
++
++ \section1 Thread Affinity
++
++ A QObject instance is said to have a \e{thread affinity}, or that
++ it \e{lives} in a certain thread. When a QObject receives a
++ \l{Qt::QueuedConnection}{queued signal} or a \l{The Event
++ System#Sending Events}{posted event}, the slot or event handler
++ will run in the thread that the object lives in.
++
++ \note If a QObject has no thread affinity (that is, if thread()
++ returns zero), or if it lives in a thread that has no running event
++ loop, then it cannot receive queued signals or posted events.
++
++ By default, a QObject lives in the thread in which it is created.
++ An object's thread affinity can be queried using thread() and
++ changed using moveToThread().
++
++ All QObjects must live in the same thread as their parent. Consequently:
++
++ \list
++ \li setParent() will fail if the two QObjects involved live in
++ different threads.
++ \li When a QObject is moved to another thread, all its children
++ will be automatically moved too.
++ \li moveToThread() will fail if the QObject has a parent.
++ \li If QObjects are created within QThread::run(), they cannot
++ become children of the QThread object because the QThread does
++ not live in the thread that calls QThread::run().
++ \endlist
++
++ \note A QObject's member variables \e{do not} automatically become
++ its children. The parent-child relationship must be set by either
++ passing a pointer to the child's \l{QObject()}{constructor}, or by
++ calling setParent(). Without this step, the object's member variables
++ will remain in the old thread when moveToThread() is called.
++
++ \target No copy constructor
++ \section1 No Copy Constructor or Assignment Operator
++
++ QObject has neither a copy constructor nor an assignment operator.
++ This is by design. Actually, they are declared, but in a
++ \c{private} section with the macro Q_DISABLE_COPY(). In fact, all
++ Qt classes derived from QObject (direct or indirect) use this
++ macro to declare their copy constructor and assignment operator to
++ be private. The reasoning is found in the discussion on
++ \l{Identity vs Value} {Identity vs Value} on the Qt \l{Object
++ Model} page.
++
++ The main consequence is that you should use pointers to QObject
++ (or to your QObject subclass) where you might otherwise be tempted
++ to use your QObject subclass as a value. For example, without a
++ copy constructor, you can't use a subclass of QObject as the value
++ to be stored in one of the container classes. You must store
++ pointers.
++
++ \section1 Auto-Connection
++
++ Qt's meta-object system provides a mechanism to automatically connect
++ signals and slots between QObject subclasses and their children. As long
++ as objects are defined with suitable object names, and slots follow a
++ simple naming convention, this connection can be performed at run-time
++ by the QMetaObject::connectSlotsByName() function.
++
++ \l uic generates code that invokes this function to enable
++ auto-connection to be performed between widgets on forms created
++ with \e{\QD}. More information about using auto-connection with \e{\QD} is
++ given in the \l{Using a Qt Widgets Designer UI File in Your Application} section of
++ the \l{Qt Widgets Designer Manual}{\QD} manual.
++
++ \section1 Dynamic Properties
++
++ Dynamic properties can be added to and removed from QObject
++ instances at run-time. Dynamic properties do not need to be declared at
++ compile-time, yet they provide the same advantages as static properties
++ and are manipulated using the same API - using property() to read them
++ and setProperty() to write them.
++
++ Dynamic properties are supported by
++ \l{Qt Widgets Designer's Widget Editing Mode#The Property Editor}{\QD},
++ and both standard Qt widgets and user-created forms can be given dynamic
++ properties.
++
++ \section1 Internationalization (I18n)
++
++ All QObject subclasses support Qt's translation features, making it possible
++ to translate an application's user interface into different languages.
++
++ To make user-visible text translatable, it must be wrapped in calls to
++ the tr() function. This is explained in detail in the
++ \l{Writing Source Code for Translation} document.
++
++ \sa QMetaObject, QPointer, QObjectCleanupHandler, Q_DISABLE_COPY()
++ \sa {Object Trees & Ownership}
++ */
++
++ /*****************************************************************************
++ QObject member functions
++ *****************************************************************************/
++
++ // check the constructor's parent thread argument
++ static bool check_parent_thread(QObject *parent,
++ QThreadData *parentThreadData,
++ QThreadData *currentThreadData)
++ {
++ if (parent && parentThreadData != currentThreadData) {
++ QThread *parentThread = parentThreadData->thread.loadAcquire();
++ QThread *currentThread = currentThreadData->thread.loadAcquire();
++ qWarning("QObject: Cannot create children for a parent that is in a different thread.\n"
++ "(Parent is %s(%p), parent's thread is %s(%p), current thread is %s(%p)",
++ parent->metaObject()->className(),
++ parent,
++ parentThread ? parentThread->metaObject()->className() : "QThread",
++ parentThread,
++ currentThread ? currentThread->metaObject()->className() : "QThread",
++ currentThread);
++ return false;
++ }
++ return true;
++ }
++
++ /*!
++ Constructs an object with parent object \a parent.
++
++ The parent of an object may be viewed as the object's owner. For
++ instance, a \l{QDialog}{dialog box} is the parent of the \uicontrol{OK}
++ and \uicontrol{Cancel} buttons it contains.
++
++ The destructor of a parent object destroys all child objects.
++
++ Setting \a parent to \nullptr constructs an object with no parent. If the
++ object is a widget, it will become a top-level window.
++
++ \sa parent(), findChild(), findChildren()
++ */
++
++ QObject::QObject(QObject *parent)
++ : QObject(*new QObjectPrivate, parent)
++ {
++ }
++
++ /*!
++ \internal
++ */
++ QObject::QObject(QObjectPrivate &dd, QObject *parent)
++ : d_ptr(&dd)
++ {
++ Q_ASSERT_X(this != parent, Q_FUNC_INFO, "Cannot parent a QObject to itself");
++
++ Q_D(QObject);
++ d_ptr->q_ptr = this;
++ QThreadData *parentThreadData = parent ? parent->d_func()->threadData.loadRelaxed() : nullptr;
++ QThreadData *threadData;
++ if (parent && !parentThreadData->thread.loadRelaxed()) {
++ threadData = parentThreadData;
++ } else {
++ threadData = QThreadData::current();
++ if (!check_parent_thread(parent, parentThreadData, threadData))
++ parent = nullptr;
++ }
++ threadData->ref();
++ d->threadData.storeRelaxed(threadData);
++
++ if (parent) {
++ // Protect against setParent() throwing (we send an event to the parent).
++ auto scopeDeref = qScopeGuard([threadData] { threadData->deref(); });
++ if (d->willBeWidget) {
++ d->parent = parent;
++ d->parent->d_func()->children.append(this);
++ // no events sent here, this is done at the end of the QWidget constructor
++ } else {
++ setParent(parent);
++ }
++ scopeDeref.dismiss();
++ }
++
++ // Neither the hook nor the trace should throw or otherwise halt this
++ // object's creation. It's a bug in them if they do.
++ if (Q_UNLIKELY(qtHookData[QHooks::AddQObject]))
++ reinterpret_cast<QHooks::AddQObjectCallback>(qtHookData[QHooks::AddQObject])(this);
++ Q_TRACE(QObject_ctor, this);
++ }
++
++ void QObjectPrivate::clearBindingStorage()
++ {
++ bindingStorage.clear();
++ }
++
++ /*!
++ Destroys the object, deleting all its child objects.
++
++ All signals to and from the object are automatically disconnected, and
++ any pending posted events for the object are removed from the event
++ queue. However, it is often safer to use deleteLater() rather than
++ deleting a QObject subclass directly.
++
++ \warning All child objects are deleted. If any of these objects
++ are on the stack or global, sooner or later your program will
++ crash. We do not recommend holding pointers to child objects from
++ outside the parent. If you still do, the destroyed() signal gives
++ you an opportunity to detect when an object is destroyed.
++
++ \warning Deleting a QObject while it is handling an event
++ delivered to it can cause a crash. You must not delete the QObject
++ directly if it exists in a different thread than the one currently
++ executing. Use deleteLater() instead, which will cause the event
++ loop to delete the object after all pending events have been
++ delivered to it.
++
++ \sa deleteLater()
++ */
++
++ QObject::~QObject()
++ {
++ Q_D(QObject);
++ d->wasDeleted = true;
++ d->blockSig = 0; // unblock signals so we always emit destroyed()
++
++ if (!d->bindingStorage.isValid()) {
++ // this might be the case after an incomplete thread-move
++ // remove this object from the pending list in that case
++ if (QThread *ownThread = thread()) {
++ auto *privThread = static_cast<QThreadPrivate *>(
++ QObjectPrivate::get(ownThread));
++ privThread->removeObjectWithPendingBindingStatusChange(this);
++ }
++ }
++
++ // If we reached this point, we need to clear the binding data
++ // as the corresponding properties are no longer useful
++ d->clearBindingStorage();
++
++ QtSharedPointer::ExternalRefCountData *sharedRefcount = d->sharedRefcount.loadRelaxed();
++ if (sharedRefcount) {
++ if (sharedRefcount->strongref.loadRelaxed() > 0) {
++ qWarning("QObject: shared QObject was deleted directly. The program is malformed and may crash.");
++ // but continue deleting, it's too late to stop anyway
++ }
++
++ // indicate to all QWeakPointers that this QObject has now been deleted
++ sharedRefcount->strongref.storeRelaxed(0);
++ if (!sharedRefcount->weakref.deref())
++ delete sharedRefcount;
++ }
++
++ if (!d->wasWidget && d->isSignalConnected(0)) {
++ emit destroyed(this);
++ }
++
++ if (!d->isDeletingChildren && d->declarativeData && QAbstractDeclarativeData::destroyed)
++ QAbstractDeclarativeData::destroyed(d->declarativeData, this);
++
++ QObjectPrivate::ConnectionData *cd = d->connections.loadAcquire();
++ if (cd) {
++ if (cd->currentSender) {
++ cd->currentSender->receiverDeleted();
++ cd->currentSender = nullptr;
++ }
++
++ QBasicMutex *signalSlotMutex = signalSlotLock(this);
++ QMutexLocker locker(signalSlotMutex);
++
++ // disconnect all receivers
++ int receiverCount = cd->signalVectorCount();
++ for (int signal = -1; signal < receiverCount; ++signal) {
++ QObjectPrivate::ConnectionList &connectionList = cd->connectionsForSignal(signal);
++
++ while (QObjectPrivate::Connection *c = connectionList.first.loadRelaxed()) {
++ Q_ASSERT(c->receiver.loadAcquire());
++
++ QBasicMutex *m = signalSlotLock(c->receiver.loadRelaxed());
++ bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
++ if (c == connectionList.first.loadAcquire() && c->receiver.loadAcquire()) {
++ cd->removeConnection(c);
++ Q_ASSERT(connectionList.first.loadRelaxed() != c);
++ }
++ if (needToUnlock)
++ m->unlock();
++ }
++ }
++
++ /* Disconnect all senders:
++ */
++ const Qt::HANDLE currentThreadId = QThread::currentThreadId();
++ while (QObjectPrivate::Connection *node = cd->senders) {
++ Q_ASSERT(node->receiver.loadAcquire());
++ QObject *sender = node->sender;
++ // Notify sender before removing the connection from its list. If sender is being
++ // destroyed too, this makes its destructor block on the receiver's lock (which we
++ // hold) and not finish until we release it, so sender stays alive while
++ // disconnectNotifySender() touches it.
++ disconnectNotifySender(sender, node->signal_index, currentThreadId);
++ QBasicMutex *m = signalSlotLock(sender);
++ bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
++ //the node has maybe been removed while the mutex was unlocked in relock?
++ if (node != cd->senders) {
++ // We hold the wrong mutex
++ Q_ASSERT(needToUnlock);
++ m->unlock();
++ continue;
++ }
++
++ QObjectPrivate::ConnectionData *senderData = sender->d_func()->connections.loadRelaxed();
++ Q_ASSERT(senderData);
++
++ QtPrivate::QSlotObjectBase *slotObj = nullptr;
++ if (node->isSlotObject) {
++ slotObj = node->slotObj;
++ node->isSlotObject = false;
++ }
++
++ senderData->removeConnection(node);
++ /*
++ When we unlock, another thread has the chance to delete/modify sender data.
++ Thus we need to call cleanOrphanedConnections before unlocking. We use the
++ variant of the function which assumes that the lock is already held to avoid
++ a deadlock.
++ We need to hold m, the sender lock. Considering that we might execute arbitrary user
++ code, we should already release the signalSlotMutex here – unless they are the same.
++ */
++ const bool locksAreTheSame = signalSlotMutex == m;
++ if (!locksAreTheSame)
++ locker.unlock();
++ senderData->cleanOrphanedConnections(
++ sender,
++ QObjectPrivate::ConnectionData::AlreadyLockedAndTemporarilyReleasingLock
++ );
++ if (needToUnlock)
++ m->unlock();
++
++ if (locksAreTheSame) // otherwise already unlocked
++ locker.unlock();
++ if (slotObj)
++ slotObj->destroyIfLastRef();
++ locker.relock();
++ }
++
++ // invalidate all connections on the object and make sure
++ // activate() will skip them
++ cd->currentConnectionId.storeRelaxed(0);
++ }
++ if (cd && !cd->ref.deref())
++ delete cd;
++ d->connections.storeRelaxed(nullptr);
++
++ if (!d->children.isEmpty())
++ d->deleteChildren();
++
++ if (Q_UNLIKELY(qtHookData[QHooks::RemoveQObject]))
++ reinterpret_cast<QHooks::RemoveQObjectCallback>(qtHookData[QHooks::RemoveQObject])(this);
++
++ Q_TRACE(QObject_dtor, this);
++
++ if (d->parent) // remove it from parent object
++ d->setParent_helper(nullptr);
++ }
++
++ inline QObjectPrivate::Connection::~Connection()
++ {
++ if (ownArgumentTypes) {
++ const int *v = argumentTypes.loadRelaxed();
++ if (v != &DIRECT_CONNECTION_ONLY)
++ delete[] v;
++ }
++ if (isSlotObject)
++ slotObj->destroyIfLastRef();
++ }
++
++
++ /*!
++ \fn const QMetaObject *QObject::metaObject() const
++
++ Returns a pointer to the meta-object of this object.
++
++ A meta-object contains information about a class that inherits
++ QObject, e.g. class name, superclass name, properties, signals and
++ slots. Every QObject subclass that contains the Q_OBJECT macro will have a
++ meta-object.
++
++ The meta-object information is required by the signal/slot
++ connection mechanism and the property system. The inherits()
++ function also makes use of the meta-object.
++
++ If you have no pointer to an actual object instance but still
++ want to access the meta-object of a class, you can use \l
++ staticMetaObject.
++
++ Example:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 1
++
++ \sa staticMetaObject
++ */
++
++ /*!
++ \variable QObject::staticMetaObject
++
++ This variable stores the meta-object for the class.
++
++ A meta-object contains information about a class that inherits
++ QObject, e.g. class name, superclass name, properties, signals and
++ slots. Every class that contains the Q_OBJECT macro will also have
++ a meta-object.
++
++ The meta-object information is required by the signal/slot
++ connection mechanism and the property system. The inherits()
++ function also makes use of the meta-object.
++
++ If you have a pointer to an object, you can use metaObject() to
++ retrieve the meta-object associated with that object.
++
++ Example:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 2
++
++ \sa metaObject()
++ */
++
++ /*!
++ \fn template <class T> T qobject_cast(QObject *object)
++ \fn template <class T> T qobject_cast(const QObject *object)
++ \relates QObject
++
++ Returns the given \a object cast to type T if the object is of type
++ T (or of a subclass); otherwise returns \nullptr. If \a object is
++ \nullptr then it will also return \nullptr.
++
++ The class T must inherit (directly or indirectly) QObject and be
++ declared with the \l Q_OBJECT macro.
++
++ A class is considered to inherit itself.
++
++ Example:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 3
++
++ The qobject_cast() function behaves similarly to the standard C++
++ \c dynamic_cast(), with the advantages that it doesn't require
++ RTTI support and it works across dynamic library boundaries.
++
++ qobject_cast() can also be used in conjunction with interfaces.
++
++ \warning If T isn't declared with the Q_OBJECT macro, this
++ function's return value is undefined.
++
++ \sa QObject::inherits()
++ */
++
++ /*!
++ \fn bool QObject::inherits(const char *className) const
++
++ Returns \c true if this object is an instance of a class that
++ inherits \a className or a QObject subclass that inherits \a
++ className; otherwise returns \c false.
++
++ A class is considered to inherit itself.
++
++ Example:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 4
++
++ If you need to determine whether an object is an instance of a particular
++ class for the purpose of casting it, consider using qobject_cast<Type *>(object)
++ instead.
++
++ \sa metaObject(), qobject_cast()
++ */
++
++ /*!
++ \property QObject::objectName
++
++ \brief the name of this object
++
++ You can find an object by name (and type) using findChild().
++ You can find a set of objects with findChildren().
++
++ \snippet code/src_corelib_kernel_qobject.cpp 5
++
++ By default, this property contains an empty string.
++
++ \sa metaObject(), QMetaObject::className()
++ */
++
++ QString QObject::objectName() const
++ {
++ Q_D(const QObject);
++ #if QT_CONFIG(thread)
++ if (QThread::currentThreadId() != d->threadData.loadRelaxed()->threadId.loadRelaxed()) // Unsafe code path
++ return d->extraData ? d->extraData->objectName.valueBypassingBindings() : QString();
++ #endif
++ if (!d->extraData && QtPrivate::isAnyBindingEvaluating()) {
++ QObjectPrivate *dd = const_cast<QObjectPrivate *>(d);
++ // extraData is mutable, so this should be safe
++ dd->extraData = new QObjectPrivate::ExtraData(dd);
++ }
++ return d->extraData ? d->extraData->objectName : QString();
++ }
++
++ /*!
++ \internal
++ Only use if you know nothing can be bound yet. Usually used for
++ internal objects that do get names.
++ */
++ void QObjectPrivate::setObjectNameWithoutBindings(const QString &name)
++ {
++ ensureExtraData();
++ extraData->objectName.setValueBypassingBindings(name);
++ }
++
++ /*!
++ \fn void QObject::setObjectName(const QString &name)
++ Sets the object's name to \a name.
++ */
++ void QObject::doSetObjectName(const QString &name)
++ {
++ Q_D(QObject);
++
++ d->ensureExtraData();
++
++ d->extraData->objectName.removeBindingUnlessInWrapper();
++
++ if (d->extraData->objectName.valueBypassingBindings() != name) {
++ d->extraData->objectName.setValueBypassingBindings(name);
++ d->extraData->objectName.notify(); // also emits a signal
++ }
++ }
++
++ /*!
++ \overload
++ \since 6.4
++ */
++ void QObject::setObjectName(QAnyStringView name)
++ {
++ Q_D(QObject);
++
++ d->ensureExtraData();
++
++ d->extraData->objectName.removeBindingUnlessInWrapper();
++
++ if (d->extraData->objectName.valueBypassingBindings() != name) {
++ d->extraData->objectName.setValueBypassingBindings(name.toString());
++ d->extraData->objectName.notify(); // also emits a signal
++ }
++ }
++
++ QBindable<QString> QObject::bindableObjectName()
++ {
++ Q_D(QObject);
++
++ d->ensureExtraData();
++
++ return QBindable<QString>(&d->extraData->objectName);
++ }
++
++ /*! \fn void QObject::objectNameChanged(const QString &objectName)
++
++ This signal is emitted after the object's name has been changed. The new object name is passed as \a objectName.
++
++ \sa QObject::objectName
++ */
++
++ /*!
++ \fn bool QObject::isWidgetType() const
++
++ Returns \c true if the object is a widget; otherwise returns \c false.
++
++ Calling this function is equivalent to calling
++ \c{inherits("QWidget")}, except that it is much faster.
++ */
++
++ /*!
++ \fn bool QObject::isWindowType() const
++
++ Returns \c true if the object is a window; otherwise returns \c false.
++
++ Calling this function is equivalent to calling
++ \c{inherits("QWindow")}, except that it is much faster.
++ */
++
++ /*!
++ \fn bool QObject::isQuickItemType() const
++
++ Returns \c true if the object is a QQuickItem; otherwise returns \c false.
++
++ Calling this function is equivalent to calling
++ \c{inherits("QQuickItem")}, except that it is much faster.
++
++ \since 6.4
++ */
++
++ /*!
++ Returns whether the object has been created by the QML engine or
++ ownership has been explicitly set via QJSEngine::setObjectOwnership().
++ \since 6.11
++ */
++ bool QObject::isQmlExposed() const noexcept
++ {
++ Q_D(const QObject);
++ return !d->isDeletingChildren && d->declarativeData;
++ }
++
++ /*!
++ This virtual function receives events to an object and should
++ return true if the event \a e was recognized and processed.
++
++ The event() function can be reimplemented to customize the
++ behavior of an object.
++
++ Make sure you call the parent event class implementation
++ for all the events you did not handle.
++
++ Example:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 52
++
++ \sa installEventFilter(), timerEvent(), QCoreApplication::sendEvent(),
++ QCoreApplication::postEvent()
++ */
++
++ bool QObject::event(QEvent *e)
++ {
++ switch (e->type()) {
++ case QEvent::Timer:
++ timerEvent((QTimerEvent *)e);
++ break;
++
++ case QEvent::ChildAdded:
++ case QEvent::ChildPolished:
++ case QEvent::ChildRemoved:
++ childEvent((QChildEvent *)e);
++ break;
++
++ case QEvent::DeferredDelete:
++ delete this;
++ break;
++
++ case QEvent::DisconnectNotify: {
++ // Posted by disconnectNotifySender(), now that we are in our own thread
++ const QMetaObject *mo = metaObject();
++ const int signalIndex = static_cast<QDisconnectNotifyEvent *>(e)->signalIndex();
++ disconnectNotify(QMetaObjectPrivate::signal(mo, signalIndex));
++ break;
++ }
++
++ case QEvent::MetaCall:
++ {
++ QAbstractMetaCallEvent *mce = static_cast<QAbstractMetaCallEvent*>(e);
++
++ QObjectPrivate::ConnectionData *connections = d_func()->connections.loadAcquire();
++ if (!connections) {
++ QMutexLocker locker(signalSlotLock(this));
++ d_func()->ensureConnectionData();
++ connections = d_func()->connections.loadRelaxed();
++ }
++ QObjectPrivate::Sender sender(this, const_cast<QObject*>(mce->sender()), mce->signalId(), connections);
++
++ mce->placeMetaCall(this);
++ break;
++ }
++
++ case QEvent::ThreadChange: {
++ Q_D(QObject);
++ QThreadData *threadData = d->threadData.loadRelaxed();
++ QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.loadRelaxed();
++ if (eventDispatcher) {
++ QList<QAbstractEventDispatcher::TimerInfoV2> timers = eventDispatcher->timersForObject(this);
++ if (!timers.isEmpty()) {
++ const bool res = eventDispatcher->unregisterTimers(this);
++ // do not to release our timer ids back to the pool (since the timer ids are moving to a new thread).
++ Q_ASSERT_X(res, Q_FUNC_INFO,
++ "QAbstractEventDispatcher::unregisterTimers() returned false,"
++ " but there are timers associated with this object.");
++ auto reRegisterTimers = [this, timers = std::move(timers)]() {
++ QAbstractEventDispatcher *eventDispatcher =
++ d_func()->threadData.loadRelaxed()->eventDispatcher.loadRelaxed();
++ for (const auto &ti : timers)
++ eventDispatcher->registerTimer(ti.timerId, ti.interval, ti.timerType, this);
++ };
++ QMetaObject::invokeMethod(this, std::move(reRegisterTimers), Qt::QueuedConnection);
++ }
++ }
++ break;
++ }
++
++ default:
++ if (e->type() >= QEvent::User) {
++ customEvent(e);
++ break;
++ }
++ return false;
++ }
++ return true;
++ }
++
++ /*!
++ \fn void QObject::timerEvent(QTimerEvent *event)
++
++ This event handler can be reimplemented in a subclass to receive
++ timer events for the object.
++
++ QChronoTimer provides higher-level interfaces to the timer functionality,
++ and also more general information about timers. The timer event is passed
++ in the \a event parameter.
++
++ \sa startTimer(), killTimer(), event()
++ */
++
++ void QObject::timerEvent(QTimerEvent *)
++ {
++ }
++
++
++ /*!
++ This event handler can be reimplemented in a subclass to receive
++ child events. The event is passed in the \a event parameter.
++
++ QEvent::ChildAdded and QEvent::ChildRemoved events are sent to
++ objects when children are added or removed. In both cases you can
++ only rely on the child being a QObject, or if isWidgetType()
++ returns \c true, a QWidget. (This is because, in the
++ \l{QEvent::ChildAdded}{ChildAdded} case, the child is not yet
++ fully constructed, and in the \l{QEvent::ChildRemoved}{ChildRemoved}
++ case it might have been destructed already).
++
++ QEvent::ChildPolished events are sent to widgets when children
++ are polished, or when polished children are added. If you receive
++ a child polished event, the child's construction is usually
++ completed. However, this is not guaranteed, and multiple polish
++ events may be delivered during the execution of a widget's
++ constructor.
++
++ For every child widget, you receive one
++ \l{QEvent::ChildAdded}{ChildAdded} event, zero or more
++ \l{QEvent::ChildPolished}{ChildPolished} events, and one
++ \l{QEvent::ChildRemoved}{ChildRemoved} event.
++
++ The \l{QEvent::ChildPolished}{ChildPolished} event is omitted if
++ a child is removed immediately after it is added. If a child is
++ polished several times during construction and destruction, you
++ may receive several child polished events for the same child,
++ each time with a different virtual table.
++
++ \sa event()
++ */
++
++ void QObject::childEvent(QChildEvent * /* event */)
++ {
++ }
++
++
++ /*!
++ This event handler can be reimplemented in a subclass to receive
++ custom events. Custom events are user-defined events with a type
++ value at least as large as the QEvent::User item of the
++ QEvent::Type enum, and is typically a QEvent subclass. The event
++ is passed in the \a event parameter.
++
++ \sa event(), QEvent
++ */
++ void QObject::customEvent(QEvent * /* event */)
++ {
++ }
++
++
++
++ /*!
++ Filters events if this object has been installed as an event
++ filter for the \a watched object.
++
++ In your reimplementation of this function, if you want to filter
++ the \a event out, i.e. stop it being handled further, return
++ true; otherwise return false.
++
++ Example:
++ \snippet code/src_corelib_kernel_qobject.cpp 6
++
++ Notice in the example above that unhandled events are passed to
++ the base class's eventFilter() function, since the base class
++ might have reimplemented eventFilter() for its own internal
++ purposes.
++
++ Some events, such as \l QEvent::ShortcutOverride must be explicitly
++ accepted (by calling \l {QEvent::}{accept()} on them) in order to prevent
++ propagation.
++
++ \warning If you delete the receiver object in this function, be
++ sure to return true. Otherwise, Qt will forward the event to the
++ deleted object and the program might crash.
++
++ \sa installEventFilter()
++ */
++
++ bool QObject::eventFilter(QObject * /* watched */, QEvent * /* event */)
++ {
++ return false;
++ }
++
++ /*!
++ \fn bool QObject::signalsBlocked() const
++
++ Returns \c true if signals are blocked; otherwise returns \c false.
++
++ Signals are not blocked by default.
++
++ \sa blockSignals(), QSignalBlocker
++ */
++
++ /*!
++ If \a block is true, signals emitted by this object are blocked
++ (i.e., emitting a signal will not invoke anything connected to it).
++ If \a block is false, no such blocking will occur.
++
++ The return value is the previous value of signalsBlocked().
++
++ Note that the destroyed() signal will be emitted even if the signals
++ for this object have been blocked.
++
++ Signals emitted while being blocked are not buffered.
++
++ \sa signalsBlocked(), QSignalBlocker
++ */
++
++ bool QObject::blockSignals(bool block) noexcept
++ {
++ Q_D(QObject);
++ bool previous = d->blockSig;
++ d->blockSig = block;
++ return previous;
++ }
++
++ /*!
++ Returns the thread in which the object lives.
++
++ \sa moveToThread()
++ */
++ QThread *QObject::thread() const
++ {
++ return d_func()->threadData.loadRelaxed()->thread.loadAcquire();
++ }
++
++ /*!
++ Changes the thread affinity for this object and its children and
++ returns \c true on success. The object cannot be moved if it has a
++ parent. Event processing will continue in the \a targetThread.
++
++ To move an object to the main thread, use QApplication::instance()
++ to retrieve a pointer to the current application, and then use
++ QApplication::thread() to retrieve the thread in which the
++ application lives. For example:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 7
++
++ If \a targetThread is \nullptr, all event processing for this object
++ and its children stops, as they are no longer associated with any
++ thread.
++
++ Note that all active timers for the object will be reset. The
++ timers are first stopped in the current thread and restarted (with
++ the same interval) in the \a targetThread. As a result, constantly
++ moving an object between threads can postpone timer events
++ indefinitely.
++
++ A QEvent::ThreadChange event is sent to this object just before
++ the thread affinity is changed. You can handle this event to
++ perform any special processing. Note that any new events that are
++ posted to this object will be handled in the \a targetThread,
++ provided it is not \nullptr: when it is \nullptr, no event processing
++ for this object or its children can happen, as they are no longer
++ associated with any thread.
++
++ \warning This function is \e not thread-safe; the current thread
++ must be same as the current thread affinity. In other words, this
++ function can only "push" an object from the current thread to
++ another thread, it cannot "pull" an object from any arbitrary
++ thread to the current thread. There is one exception to this rule
++ however: objects with no thread affinity can be "pulled" to the
++ current thread.
++
++ In Qt versions prior to 6.7, this function had no return value (\c void).
++
++ \sa thread()
++ */
++ bool QObject::moveToThread(QThread *targetThread QT6_IMPL_NEW_OVERLOAD_TAIL)
++ {
++ Q_D(QObject);
++
++ if (d->threadData.loadRelaxed()->thread.loadAcquire() == targetThread) {
++ // object is already in this thread
++ return true;
++ }
++
++ if (d->parent != nullptr) {
++ qWarning("QObject::moveToThread: Cannot move objects with a parent");
++ return false;
++ }
++ if (d->isWidget) {
++ qWarning("QObject::moveToThread: Widgets cannot be moved to a new thread");
++ return false;
++ }
++ if (!d->bindingStorage.isEmpty()) {
++ qWarning("QObject::moveToThread: Can not move objects that contain bindings or are used in bindings to a new thread.");
++ return false;
++ }
++
++ QThreadData *currentData = QThreadData::current();
++ QThreadData *targetData = targetThread ? QThreadData::get2(targetThread) : nullptr;
++ QThreadData *thisThreadData = d->threadData.loadAcquire();
++ if (!thisThreadData->thread.loadRelaxed() && currentData == targetData) {
++ // one exception to the rule: we allow moving objects with no thread affinity to the current thread
++ currentData = thisThreadData;
++ } else if (thisThreadData != currentData) {
++ qWarning("QObject::moveToThread: Current thread (%p) is not the object's thread (%p).\n"
++ "Cannot move to target thread (%p)\n",
++ currentData->thread.loadRelaxed(), thisThreadData->thread.loadRelaxed(), targetData ? targetData->thread.loadRelaxed() : nullptr);
++
++ #ifdef Q_OS_DARWIN
++ qWarning("You might be loading two sets of Qt binaries into the same process. "
++ "Check that all plugins are compiled against the right Qt binaries. Export "
++ "DYLD_PRINT_LIBRARIES=1 and check that only one set of binaries are being loaded.");
++ #endif
++
++ return false;
++ }
++
++ // prepare to move
++ d->moveToThread_helper();
++
++ if (!targetData)
++ targetData = new QThreadData(0);
++
++ // make sure nobody adds/removes connections to this object while we're moving it
++ QMutexLocker l(signalSlotLock(this));
++
++ QOrderedMutexLocker locker(&currentData->postEventList.mutex,
++ &targetData->postEventList.mutex);
++
++ // keep currentData alive (since we've got it locked)
++ currentData->ref();
++
++ // move the object
++ auto threadPrivate = targetThread
++ ? static_cast<QThreadPrivate *>(QThreadPrivate::get(targetThread))
++ : nullptr;
++ QBindingStatus *bindingStatus = threadPrivate
++ ? threadPrivate->bindingStatus()
++ : nullptr;
++ if (threadPrivate && !bindingStatus) {
++ bindingStatus = threadPrivate->addObjectWithPendingBindingStatusChange(this);
++ }
++ d_func()->setThreadData_helper(currentData, targetData, bindingStatus);
++
++ locker.unlock();
++
++ // now currentData can commit suicide if it wants to
++ currentData->deref();
++ return true;
++ }
++
++ void QObjectPrivate::moveToThread_helper()
++ {
++ Q_Q(QObject);
++ QEvent e(QEvent::ThreadChange);
++ QCoreApplication::sendEvent(q, &e);
++ bindingStorage.clear();
++ for (int i = 0; i < children.size(); ++i) {
++ QObject *child = children.at(i);
++ child->d_func()->moveToThread_helper();
++ }
++ }
++
++ void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData *targetData, QBindingStatus *status)
++ {
++ Q_Q(QObject);
++
++ if (status) {
++ // the new thread is already running
++ this->bindingStorage.bindingStatus = status;
++ }
++
++ // move posted events
++ qsizetype eventsMoved = 0;
++ for (qsizetype i = 0; i < currentData->postEventList.size(); ++i) {
++ const QPostEvent &pe = currentData->postEventList.at(i);
++ if (!pe.event)
++ continue;
++ if (pe.receiver == q) {
++ // move this post event to the targetList
++ targetData->postEventList.addEvent(pe);
++ const_cast<QPostEvent &>(pe).event = nullptr;
++ ++eventsMoved;
++ }
++ }
++ if (eventsMoved > 0 && targetData->hasEventDispatcher()) {
++ targetData->canWait = false;
++ targetData->eventDispatcher.loadRelaxed()->wakeUp();
++ }
++
++ // the current emitting thread shouldn't restore currentSender after calling moveToThread()
++ ConnectionData *cd = connections.loadAcquire();
++ if (cd) {
++ if (cd->currentSender) {
++ cd->currentSender->receiverDeleted();
++ cd->currentSender = nullptr;
++ }
++
++ // adjust the receiverThreadId values in the Connections
++ if (cd) {
++ auto *c = cd->senders;
++ while (c) {
++ QObject *r = c->receiver.loadRelaxed();
++ if (r) {
++ Q_ASSERT(r == q);
++ targetData->ref();
++ QThreadData *old = c->receiverThreadData.loadRelaxed();
++ if (old)
++ old->deref();
++ c->receiverThreadData.storeRelaxed(targetData);
++ }
++ c = c->next;
++ }
++ }
++
++ }
++
++ // set new thread data
++ targetData->ref();
++ threadData.loadRelaxed()->deref();
++
++ // synchronizes with loadAcquire e.g. in QCoreApplication::postEvent
++ threadData.storeRelease(targetData);
++
++ for (int i = 0; i < children.size(); ++i) {
++ QObject *child = children.at(i);
++ child->d_func()->setThreadData_helper(currentData, targetData, status);
++ }
++ }
++
++ //
++ // The timer flag hasTimer is set when startTimer is called.
++ // It is not reset when killing the timer because more than
++ // one timer might be active.
++ //
++
++ /*!
++ \fn int QObject::startTimer(int interval, Qt::TimerType timerType)
++
++ This is an overloaded function that will start a timer of type
++ \a timerType and a timeout of \a interval milliseconds. This is
++ equivalent to calling:
++ \code
++ startTimer(std::chrono::milliseconds{interval}, timerType);
++ \endcode
++
++ \include timers-common.qdocinc negative-intervals-not-allowed
++
++ \sa timerEvent(), killTimer(), QChronoTimer, QBasicTimer
++ */
++
++ int QObject::startTimer(int interval, Qt::TimerType timerType)
++ {
++ // no overflow can happen here:
++ // 2^31 ms * 1,000,000 always fits a 64-bit signed integer type
++ return startTimer(std::chrono::milliseconds{interval}, timerType);
++ }
++
++ /*!
++ \since 5.9
++ \overload
++
++ Starts a timer and returns a timer identifier, or returns zero if
++ it could not start a timer.
++
++ A timer event will occur every \a interval until killTimer()
++ is called. If \a interval is equal to \c{std::chrono::duration::zero()},
++ then the timer event occurs once every time control returns to the event
++ loop. The ordering between these timer events and other sources of events
++ is unspecified.
++
++ \include timers-common.qdocinc negative-intervals-not-allowed
++
++ The virtual timerEvent() function is called with the QTimerEvent
++ event parameter class when a timer event occurs. Reimplement this
++ function to get timer events.
++
++ If multiple timers are running, the QTimerEvent::id() method can be
++ used to find out which timer was activated.
++
++ Example:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 8
++
++ Note that the accuracy of the timer depends on the underlying operating
++ system and hardware.
++
++ The \a timerType argument allows you to customize the accuracy of
++ the timer. See Qt::TimerType for information on the different timer types.
++ Most platforms support an accuracy of 20 milliseconds; some provide more.
++ If Qt is unable to deliver the requested number of timer events, it will
++ silently discard some.
++
++ The QTimer and QChronoTimer classes provide a high-level programming
++ interface with single-shot timers and timer signals instead of
++ events. There is also a QBasicTimer class that is more lightweight than
++ QChronoTimer but less clumsy than using timer IDs directly.
++
++ \note Starting from Qt 6.8 the type of \a interval
++ is \c std::chrono::nanoseconds, prior to that it was \c
++ std::chrono::milliseconds. This change is backwards compatible with
++ older releases of Qt.
++
++ \note In Qt 6.8, QObject was changed to use Qt::TimerId to represent timer
++ IDs. This method converts the TimerId to int for backwards compatibility
++ reasons, however you can use Qt::TimerId to check the value returned by
++ this method, for example:
++ \snippet code/src_corelib_kernel_qobject.cpp invalid-timer-id
++
++ \sa timerEvent(), killTimer(), QChronoTimer, QBasicTimer
++ */
++ int QObject::startTimer(std::chrono::nanoseconds interval, Qt::TimerType timerType)
++ {
++ Q_D(QObject);
++
++ using namespace std::chrono_literals;
++
++ if (interval < 0ns) {
++ qWarning("QObject::startTimer: negative intervals aren't allowed; the "
++ "interval will be set to 1ms.");
++ interval = 1ms;
++ }
++
++ auto thisThreadData = d->threadData.loadRelaxed();
++ if (Q_UNLIKELY(thisThreadData != QThreadData::current())) {
++ qWarning("QObject::startTimer: Timers cannot be started from another thread");
++ return 0;
++ }
++
++ auto dispatcher = thisThreadData->eventDispatcher.loadRelaxed();
++ if (Q_UNLIKELY(!dispatcher)) {
++ qWarning("QObject::startTimer: current thread's event dispatcher has already been destroyed");
++ return 0;
++ }
++
++ Qt::TimerId timerId = dispatcher->registerTimer(interval, timerType, this);
++ d->ensureExtraData();
++ d->extraData->runningTimers.append(timerId);
++ return int(timerId);
++ }
++
++ /*!
++ Kills the timer with timer identifier, \a id.
++
++ The timer identifier is returned by startTimer() when a timer
++ event is started.
++
++ \sa timerEvent(), startTimer()
++ */
++
++ void QObject::killTimer(int id)
++ {
++ killTimer(Qt::TimerId{id});
++ }
++
++ /*!
++ \since 6.8
++ \overload
++ */
++ void QObject::killTimer(Qt::TimerId id)
++ {
++ Q_D(QObject);
++ auto thisThreadData = d->threadData.loadRelaxed();
++ if (thisThreadData->threadId.loadRelaxed() != QThread::currentThreadId()) {
++ qWarning("QObject::killTimer: Timers cannot be stopped from another thread");
++ return;
++ }
++ if (id > Qt::TimerId::Invalid) {
++ qsizetype at = d->extraData ? d->extraData->runningTimers.indexOf(id) : -1;
++ if (at == -1) {
++ // timer isn't owned by this object
++ qWarning("QObject::killTimer(): Error: timer id %d is not valid for object %p (%s, %ls), timer has not been killed",
++ qToUnderlying(id),
++ this,
++ metaObject()->className(),
++ qUtf16Printable(objectName()));
++ return;
++ }
++
++ if (thisThreadData->hasEventDispatcher())
++ thisThreadData->eventDispatcher.loadRelaxed()->unregisterTimer(id);
++
++ d->extraData->runningTimers.remove(at);
++ QAbstractEventDispatcherPrivate::releaseTimerId(id);
++ }
++ }
++
++ /*!
++ \fn QObject *QObject::parent() const
++
++ Returns a pointer to the parent object.
++
++ \sa children()
++ */
++
++ /*!
++ \fn const QObjectList &QObject::children() const
++
++ Returns a list of child objects.
++ The QObjectList class is defined in the \c{<QObject>} header
++ file as the following:
++
++ \quotefromfile kernel/qobject.h
++ \skipto /typedef .*QObjectList/
++ \printuntil QObjectList
++
++ The first child added is the \l{QList::first()}{first} object in
++ the list and the last child added is the \l{QList::last()}{last}
++ object in the list, i.e. new children are appended at the end.
++
++ Note that the list order changes when QWidget children are
++ \l{QWidget::raise()}{raised} or \l{QWidget::lower()}{lowered}. A
++ widget that is raised becomes the last object in the list, and a
++ widget that is lowered becomes the first object in the list.
++
++ \sa findChild(), findChildren(), parent(), setParent()
++ */
++
++
++ /*!
++ \fn template<typename T> T *QObject::findChild(QAnyStringView name, Qt::FindChildOptions options) const
++
++ Returns the child of this object that can be cast into type T and
++ that is called \a name, or \nullptr if there is no such object.
++ A null \a name argument causes all objects to be matched. An empty,
++ non-null \a name matches only objects whose \l objectName is empty.
++ The search is performed recursively, unless \a options specifies the
++ option FindDirectChildrenOnly.
++
++ If there is more than one child matching the search, the most-direct
++ ancestor is returned. If there are several most-direct ancestors, the
++ first child in children() will be returned. In that case, it's better
++ to use findChildren() to get the complete list of all children.
++
++ This example returns a child \c{QPushButton} of \c{parentWidget}
++ named \c{"button1"}, even if the button isn't a direct child of
++ the parent:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 10
++
++ This example returns a \c{QListWidget} child of \c{parentWidget}:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 11
++
++ This example returns a child \c{QPushButton} of \c{parentWidget}
++ (its direct parent) named \c{"button1"}:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 41
++
++ This example returns a \c{QListWidget} child of \c{parentWidget},
++ its direct parent:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 42
++
++ \note In Qt versions prior to 6.7, this function took \a name as
++ \c{QString}, not \c{QAnyStringView}.
++
++ \sa findChildren()
++ */
++
++ /*!
++ \fn template<typename T> T *QObject::findChild(Qt::FindChildOptions options) const
++ \overload
++ \since 6.7
++
++ Returns the child of this object that can be cast into type T, or
++ \nullptr if there is no such object.
++ The search is performed recursively, unless \a options specifies the
++ option FindDirectChildrenOnly.
++
++ If there is more than one child matching the search, the most-direct ancestor
++ is returned. If there are several most-direct ancestors, the first child in
++ children() will be returned. In that case, it's better to use findChildren()
++ to get the complete list of all children.
++
++ \sa findChildren()
++ */
++
++ /*!
++ \fn template<typename T> QList<T> QObject::findChildren(QAnyStringView name, Qt::FindChildOptions options) const
++
++ Returns all children of this object with the given \a name that can be
++ cast to type T, or an empty list if there are no such objects.
++ A null \a name argument causes all objects to be matched, an empty one
++ only those whose objectName is empty.
++ The search is performed recursively, unless \a options specifies the
++ option FindDirectChildrenOnly.
++
++ The following example shows how to find a list of child \c{QWidget}s of
++ the specified \c{parentWidget} named \c{widgetname}:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 12
++
++ This example returns all \c{QPushButton}s that are children of \c{parentWidget}:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 13
++
++ This example returns all \c{QPushButton}s that are immediate children of \c{parentWidget}:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 43
++
++ \note In Qt versions prior to 6.7, this function took \a name as
++ \c{QString}, not \c{QAnyStringView}.
++
++ \sa findChild()
++ */
++
++ /*!
++ \fn template<typename T> QList<T> QObject::findChildren(Qt::FindChildOptions options) const
++ \overload
++ \since 6.3
++
++ Returns all children of this object that can be cast to type T, or
++ an empty list if there are no such objects.
++ The search is performed recursively, unless \a options specifies the
++ option FindDirectChildrenOnly.
++
++ \sa findChild()
++ */
++
++ /*!
++ \fn template<typename T> QList<T> QObject::findChildren(const QRegularExpression &re, Qt::FindChildOptions options) const
++ \overload findChildren()
++
++ \since 5.0
++
++ Returns the children of this object that can be cast to type T
++ and that have names matching the regular expression \a re,
++ or an empty list if there are no such objects.
++ The search is performed recursively, unless \a options specifies the
++ option FindDirectChildrenOnly.
++ */
++
++ /*!
++ \fn template<typename T> T qFindChild(const QObject *obj, const QString &name)
++ \relates QObject
++ \overload qFindChildren()
++ \deprecated [4.8]
++
++ This function is equivalent to
++ \a{obj}->\l{QObject::findChild()}{findChild}<T>(\a name).
++
++ \note This function was provided as a workaround for MSVC 6
++ which did not support member template functions. It is advised
++ to use the other form in new code.
++
++ \sa QObject::findChild()
++ */
++
++ /*!
++ \fn template<typename T> QList<T> qFindChildren(const QObject *obj, const QString &name)
++ \relates QObject
++ \overload qFindChildren()
++ \deprecated [4.8]
++
++ This function is equivalent to
++ \a{obj}->\l{QObject::findChildren()}{findChildren}<T>(\a name).
++
++ \note This function was provided as a workaround for MSVC 6
++ which did not support member template functions. It is advised
++ to use the other form in new code.
++
++ \sa QObject::findChildren()
++ */
++
++ static bool matches_objectName_non_null(QObject *obj, QAnyStringView name)
++ {
++ if (auto ext = QObjectPrivate::get(obj)->extraData)
++ return ext ->objectName.valueBypassingBindings() == name;
++ return name.isEmpty();
++ }
++
++ /*!
++ \internal
++ */
++ void qt_qFindChildren_helper(const QObject *parent, QAnyStringView name,
++ const QMetaObject &mo, QList<void*> *list, Qt::FindChildOptions options)
++ {
++ Q_ASSERT(parent);
++ Q_ASSERT(list);
++ for (QObject *obj : parent->children()) {
++ if (mo.cast(obj) && (name.isNull() || matches_objectName_non_null(obj, name)))
++ list->append(obj);
++ if (options & Qt::FindChildrenRecursively)
++ qt_qFindChildren_helper(obj, name, mo, list, options);
++ }
++ }
++
++ #if QT_CONFIG(regularexpression)
++ /*!
++ \internal
++ */
++ void qt_qFindChildren_helper(const QObject *parent, const QRegularExpression &re,
++ const QMetaObject &mo, QList<void*> *list, Qt::FindChildOptions options)
++ {
++ Q_ASSERT(parent);
++ Q_ASSERT(list);
++ for (QObject *obj : parent->children()) {
++ if (mo.cast(obj)) {
++ QRegularExpressionMatch m = re.match(obj->objectName());
++ if (m.hasMatch())
++ list->append(obj);
++ }
++ if (options & Qt::FindChildrenRecursively)
++ qt_qFindChildren_helper(obj, re, mo, list, options);
++ }
++ }
++ #endif // QT_CONFIG(regularexpression)
++
++ /*!
++ \internal
++ */
++ QObject *qt_qFindChild_helper(const QObject *parent, QAnyStringView name, const QMetaObject &mo, Qt::FindChildOptions options)
++ {
++ Q_ASSERT(parent);
++ for (QObject *obj : parent->children()) {
++ if (mo.cast(obj) && (name.isNull() || matches_objectName_non_null(obj, name)))
++ return obj;
++ }
++ if (options & Qt::FindChildrenRecursively) {
++ for (QObject *child : parent->children()) {
++ if (QObject *obj = qt_qFindChild_helper(child, name, mo, options))
++ return obj;
++ }
++ }
++ return nullptr;
++ }
++
++ /*!
++ Makes the object a child of \a parent.
++
++ \sa parent(), children()
++ */
++ void QObject::setParent(QObject *parent)
++ {
++ Q_D(QObject);
++ Q_ASSERT(!d->isWidget);
++ d->setParent_helper(parent);
++ }
++
++ void QObjectPrivate::deleteChildren()
++ {
++ Q_ASSERT_X(!isDeletingChildren, "QObjectPrivate::deleteChildren()", "isDeletingChildren already set, did this function recurse?");
++ isDeletingChildren = true;
++ // delete children objects
++ // don't use qDeleteAll as the destructor of the child might
++ // delete siblings
++ for (int i = 0; i < children.size(); ++i) {
++ currentChildBeingDeleted = children.at(i);
++ children[i] = nullptr;
++ delete currentChildBeingDeleted;
++ }
++ children.clear();
++ currentChildBeingDeleted = nullptr;
++ isDeletingChildren = false;
++ }
++
++ void QObjectPrivate::setParent_helper(QObject *o)
++ {
++ Q_Q(QObject);
++ Q_ASSERT_X(q != o, Q_FUNC_INFO, "Cannot parent a QObject to itself");
++ #ifdef QT_DEBUG
++ const auto checkForParentChildLoops = qScopeGuard([&](){
++ int depth = 0;
++ auto p = parent;
++ while (p) {
++ if (++depth == CheckForParentChildLoopsWarnDepth) {
++ qWarning("QObject %p (class: '%s', object name: '%s') may have a loop in its parent-child chain; "
++ "this is undefined behavior",
++ q, q->metaObject()->className(), qPrintable(q->objectName()));
++ }
++ p = p->parent();
++ }
++ });
++ #endif
++
++ if (o == parent)
++ return;
++
++ if (parent) {
++ QObjectPrivate *parentD = parent->d_func();
++ if (parentD->isDeletingChildren && wasDeleted
++ && parentD->currentChildBeingDeleted == q) {
++ // don't do anything since QObjectPrivate::deleteChildren() already
++ // cleared our entry in parentD->children.
++ } else {
++ const qsizetype index = parentD->children.indexOf(q);
++ if (index < 0) {
++ // we're probably recursing into setParent() from a ChildRemoved event, don't do anything
++ } else if (parentD->isDeletingChildren) {
++ parentD->children[index] = nullptr;
++ } else {
++ parentD->children.removeAt(index);
++ if (sendChildEvents && parentD->receiveChildEvents) {
++ QChildEvent e(QEvent::ChildRemoved, q);
++ QCoreApplication::sendEvent(parent, &e);
++ }
++ }
++ }
++ }
++
++ if (receiveParentEvents) {
++ Q_ASSERT(!isWidget); // Handled in QWidget
++ QEvent e(QEvent::ParentAboutToChange);
++ QCoreApplication::sendEvent(q, &e);
++ }
++
++ parent = o;
++
++ if (parent) {
++ // object hierarchies are constrained to a single thread
++ if (threadData.loadRelaxed() != parent->d_func()->threadData.loadRelaxed()) {
++ qWarning("QObject::setParent: Cannot set parent, new parent is in a different thread");
++ parent = nullptr;
++ return;
++ }
++ parent->d_func()->children.append(q);
++ if (sendChildEvents && parent->d_func()->receiveChildEvents) {
++ if (!isWidget) {
++ QChildEvent e(QEvent::ChildAdded, q);
++ QCoreApplication::sendEvent(parent, &e);
++ }
++ }
++ }
++
++ if (receiveParentEvents) {
++ Q_ASSERT(!isWidget); // Handled in QWidget
++ QEvent e(QEvent::ParentChange);
++ QCoreApplication::sendEvent(q, &e);
++ }
++ }
++
++ /*!
++ \fn void QObject::installEventFilter(QObject *filterObj)
++
++ Installs an event filter \a filterObj on this object. For example:
++ \snippet code/src_corelib_kernel_qobject.cpp 14
++
++ An event filter is an object that receives all events that are
++ sent to this object. The filter can either stop the event or
++ forward it to this object. The event filter \a filterObj receives
++ events via its eventFilter() function. The eventFilter() function
++ must return true if the event should be filtered, (i.e. stopped);
++ otherwise it must return false.
++
++ If multiple event filters are installed on a single object, the
++ filter that was installed last is activated first.
++
++ If \a filterObj has already been installed for this object,
++ this function moves it so it acts as if it was installed last.
++
++ Here's a \c KeyPressEater class that eats the key presses of its
++ monitored objects:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 15
++
++ And here's how to install it on two widgets:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 16
++
++ The QShortcut class, for example, uses this technique to intercept
++ shortcut key presses.
++
++ \warning If you delete the receiver object in your eventFilter()
++ function, be sure to return true. If you return false, Qt sends
++ the event to the deleted object and the program will crash.
++
++ Note that the filtering object must be in the same thread as this
++ object. If \a filterObj is in a different thread, this function does
++ nothing. If either \a filterObj or this object are moved to a different
++ thread after calling this function, the event filter will not be
++ called until both objects have the same thread affinity again (it
++ is \e not removed).
++
++ \sa removeEventFilter(), eventFilter(), event()
++ */
++
++ void QObject::installEventFilter(QObject *obj)
++ {
++ Q_D(QObject);
++ if (!obj)
++ return;
++ if (d->threadData.loadRelaxed() != obj->d_func()->threadData.loadRelaxed()) {
++ qWarning("QObject::installEventFilter(): Cannot filter events for objects in a different thread.");
++ return;
++ }
++
++ d->ensureExtraData();
++
++ // clean up unused items in the list along the way:
++ auto isNullOrEquals = [](auto obj) { return [obj](const auto &p) { return !p || p == obj; }; };
++ d->extraData->eventFilters.removeIf(isNullOrEquals(obj));
++ d->extraData->eventFilters.prepend(obj);
++ }
++
++ /*!
++ Removes an event filter object \a obj from this object. The
++ request is ignored if such an event filter has not been installed.
++
++ All event filters for this object are automatically removed when
++ this object is destroyed.
++
++ It is always safe to remove an event filter, even during event
++ filter activation (i.e. from the eventFilter() function).
++
++ \sa installEventFilter(), eventFilter(), event()
++ */
++
++ void QObject::removeEventFilter(QObject *obj)
++ {
++ Q_D(QObject);
++ if (d->extraData) {
++ for (auto &filter : d->extraData->eventFilters) {
++ if (filter == obj) {
++ filter = nullptr;
++ break;
++ }
++ }
++ }
++ }
++
++ /*!
++ \fn void QObject::destroyed(QObject *obj)
++
++ This signal is emitted immediately before the object \a obj is
++ destroyed, after any instances of QPointer have been notified,
++ and cannot be blocked.
++
++ All the objects's children are destroyed immediately after this
++ signal is emitted.
++
++ \sa deleteLater(), QPointer
++ */
++
++ /*!
++ \threadsafe
++
++ Schedules this object for deletion.
++
++ The object will be deleted when control returns to the event
++ loop. If the event loop is not running when this function is
++ called (e.g. deleteLater() is called on an object before
++ QCoreApplication::exec()), the object will be deleted once the
++ event loop is started. If deleteLater() is called after the main event loop
++ has stopped, the object will not be deleted.
++ If deleteLater() is called on an object that lives in a
++ thread with no running event loop, the object will be destroyed when the
++ thread finishes.
++
++ A common pattern when using a worker \c QObject in a \c QThread
++ is to connect the thread's \c finished() signal to the worker's
++ \c deleteLater() slot to ensure it is safely deleted:
++
++ \code
++ connect(thread, &QThread::finished, worker, &QObject::deleteLater);
++ \endcode
++
++ Note that entering and leaving a new event loop (e.g., by opening a modal
++ dialog) will \e not perform the deferred deletion; for the object to be
++ deleted, the control must return to the event loop from which deleteLater()
++ was called. This does not apply to objects deleted while a previous, nested
++ event loop was still running: the Qt event loop will delete those objects
++ as soon as the new nested event loop starts.
++
++ In situations where Qt is not driving the event dispatcher via e.g.
++ QCoreApplication::exec() or QEventLoop::exec(), deferred deletes
++ will not be processed automatically. To ensure deferred deletion in
++ this scenario, the following workaround can be used:
++
++ \code
++ const auto *eventDispatcher = QThread::currentThread()->eventDispatcher();
++ QObject::connect(eventDispatcher, &QAbstractEventDispatcher::aboutToBlock,
++ QThread::currentThread(), []{
++ if (QThread::currentThread()->loopLevel() == 0)
++ QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
++ }
++ );
++ \endcode
++
++ \sa destroyed(), QPointer
++ */
++ void QObject::deleteLater()
++ {
++ #ifdef QT_DEBUG
++ if (qApp == this)
++ qWarning("You are deferring the delete of QCoreApplication, this may not work as expected.");
++ #endif
++
++
++ // De-bounce QDeferredDeleteEvents. Use the post event list mutex
++ // to guard access to deleteLaterCalled, so we don't need a separate
++ // mutex in QObjectData.
++ auto eventListLocker = QCoreApplicationPrivate::lockThreadPostEventList(this);
++ if (!eventListLocker.threadData)
++ return;
++
++ // FIXME: The deleteLaterCalled flag is part of a bit field,
++ // so we likely have data races here, even with the mutex above,
++ // as long as we're not guarding every access to the bit field.
++
++ Q_D(QObject);
++ if (d->deleteLaterCalled)
++ return;
++
++ d->deleteLaterCalled = true;
++
++ int loopLevel = 0;
++ int scopeLevel = 0;
++
++ auto *objectThreadData = eventListLocker.threadData;
++ if (objectThreadData == QThreadData::current()) {
++ // Remember the current running eventloop for deleteLater
++ // calls in the object's own thread.
++
++ // Events sent by non-Qt event handlers (such as glib) may not
++ // have the scopeLevel set correctly. The scope level makes sure that
++ // code like this:
++ // foo->deleteLater();
++ // qApp->processEvents(); // without passing QEvent::DeferredDelete
++ // will not cause "foo" to be deleted before returning to the event loop.
++
++ loopLevel = objectThreadData->loopLevel;
++ scopeLevel = objectThreadData->scopeLevel;
++
++ // If the scope level is 0 while loopLevel != 0, we are called from a
++ // non-conformant code path, and our best guess is that the scope level
++ // should be 1. (Loop level 0 is special: it means that no event loops
++ // are running.)
++ if (scopeLevel == 0 && loopLevel != 0)
++ scopeLevel = 1;
++ }
++
++ eventListLocker.unlock();
++ QCoreApplication::postEvent(this,
++ new QDeferredDeleteEvent(loopLevel, scopeLevel));
++ }
++
++ /*!
++ \internal
++
++ Destroys \a object in the thread it belongs to, via deleteLater(), so that its
++ vtable and members are not touched from the wrong thread. Falls back to deleting
++ it right away when that thread cannot run the deferred delete: it is the current
++ thread, or it has no event dispatcher (never started, or torn down at application
++ exit). This is meant for objects deleted from a thread that is usually not their
++ own, e.g. a worker handed to a thread pool.
++
++ The thread and the dispatcher are read from the object's QThreadData, which stays
++ alive as long as the object does, rather than from QThread, which the owning thread
++ may be destroying concurrently.
++ */
++ void QObjectPrivate::deleteInOwnThread(QObject *object)
++ {
++ QThreadData *objectData = QObjectPrivate::get(object)->threadData.loadRelaxed();
++ if (objectData->hasEventDispatcher()
++ && objectData->threadId.loadRelaxed() != QThread::currentThreadId()) {
++ object->deleteLater();
++ } else {
++ delete object;
++ }
++ }
++
++ /*!
++ \fn QString QObject::tr(const char *sourceText, const char *disambiguation, int n)
++ \reentrant
++
++ Returns a translated version of \a sourceText, optionally based on a
++ \a disambiguation string and value of \a n for strings containing plurals;
++ otherwise returns QString::fromUtf8(\a sourceText) if no appropriate
++ translated string is available.
++
++ Example:
++ \snippet ../widgets/itemviews/spreadsheet/spreadsheet.cpp implicit tr context
++ \dots
++
++ If the same \a sourceText is used in different roles within the
++ same context, an additional identifying string may be passed in
++ \a disambiguation (\nullptr by default).
++
++ Example:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 17
++ \dots
++
++ See \l{Writing Source Code for Translation} for a detailed description of
++ Qt's translation mechanisms in general, and the
++ \l{Writing Source Code for Translation#Disambiguate Identical Text}
++ {Disambiguate Identical Text} section for information on disambiguation.
++
++ \warning This method is reentrant only if all translators are
++ installed \e before calling this method. Installing or removing
++ translators while performing translations is not supported. Doing
++ so will probably result in crashes or other undesirable behavior.
++
++ \sa QCoreApplication::translate(), {Internationalization with Qt}
++ */
++
++ /*****************************************************************************
++ Signals and slots
++ *****************************************************************************/
++
++ namespace {
++ // This class provides (per-thread) storage for qFlagLocation()
++ class FlaggedDebugSignatures
++ {
++ uint idx = 0;
++ std::array<const char *, 2> locations = {}; // one for the SIGNAL, one for the SLOT
++
++ public:
++ void store(const char* method) noexcept
++ { locations[idx++ % locations.size()] = method; }
++
++ bool contains(const char *method) const noexcept
++ { return std::find(locations.begin(), locations.end(), method) != locations.end(); }
++ };
++
++ Q_CONSTINIT static thread_local FlaggedDebugSignatures flaggedSignatures = {};
++ } // unnamed namespace
++
++ const char *qFlagLocation(const char *method)
++ {
++ flaggedSignatures.store(method);
++ return method;
++ }
++
++ static int extract_code(const char *member)
++ {
++ // extract code, ensure QMETHOD_CODE <= code <= QSIGNAL_CODE
++ return (((int)(*member) - '0') & 0x3);
++ }
++
++ static const char *extract_location(const char *member)
++ {
++ if (flaggedSignatures.contains(member)) {
++ // signature includes location information after the first null-terminator
++ const char *location = member + qstrlen(member) + 1;
++ if (*location != '\0')
++ return location;
++ }
++ return nullptr;
++ }
++
++ static bool check_signal_macro(const QObject *sender, const char *signal,
++ const char *func, const char *op)
++ {
++ int sigcode = extract_code(signal);
++ if (sigcode != QSIGNAL_CODE) {
++ if (sigcode == QSLOT_CODE)
++ qCWarning(lcConnect, "QObject::%s: Attempt to %s non-signal %s::%s", func, op,
++ sender->metaObject()->className(), signal + 1);
++ else
++ qCWarning(lcConnect, "QObject::%s: Use the SIGNAL macro to %s %s::%s", func, op,
++ sender->metaObject()->className(), signal);
++ return false;
++ }
++ return true;
++ }
++
++ static bool check_method_code(int code, const QObject *object, const char *method, const char *func)
++ {
++ if (code != QSLOT_CODE && code != QSIGNAL_CODE) {
++ qCWarning(lcConnect,
++ "QObject::%s: Use the SLOT or SIGNAL macro to "
++ "%s %s::%s",
++ func, func, object->metaObject()->className(), method);
++ return false;
++ }
++ return true;
++ }
++
++ #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
++ static void check_and_warn_non_slot(const char *func, const char *method, int membcode,
++ const QMetaObject *rmeta, const QMetaMethod &rmethod)
++ {
++ if (membcode == QSLOT_CODE && rmethod.methodType() != QMetaMethod::Slot) {
++ // In Qt7 QMetaObject::indexOfSlot{,relative} will return -1 if `method`
++ // isn't a slot.
++ qCWarning(lcConnect,
++ "QObject::%s: the SLOT() macro is used with a non-slot function: %s::%s. "
++ "This currently works due to backwards-compatibility reasons. In Qt7 the "
++ "SLOT() macro will work only for methods marked as slots.",
++ func, rmeta->className(), method);
++ }
++ }
++ #endif // QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
++
++ Q_DECL_COLD_FUNCTION
++ static void err_method_notfound(const QObject *object,
++ const char *method, const char *func)
++ {
++ const char *type = "method";
++ switch (extract_code(method)) {
++ case QSLOT_CODE: type = "slot"; break;
++ case QSIGNAL_CODE: type = "signal"; break;
++ }
++ const char *loc = extract_location(method);
++ const char *err;
++ if (strchr(method, ')') == nullptr) // common typing mistake
++ err = "Parentheses expected,";
++ else
++ err = "No such";
++ qCWarning(lcConnect, "QObject::%s: %s %s %s::%s%s%s", func, err, type,
++ object->metaObject()->className(), method + 1, loc ? " in " : "", loc ? loc : "");
++ }
++
++ enum class ConnectionEnd : bool { Sender, Receiver };
++ Q_DECL_COLD_FUNCTION
++ static void err_info_about_object(const char *func, const QObject *o, ConnectionEnd end)
++ {
++ if (!o)
++ return;
++ const QString name = o->objectName();
++ if (name.isEmpty())
++ return;
++ const bool sender = end == ConnectionEnd::Sender;
++ qCWarning(lcConnect, "QObject::%s: (%s name:%*s'%ls')",
++ func,
++ sender ? "sender" : "receiver",
++ sender ? 3 : 1, // ← length of generated whitespace
++ "",
++ qUtf16Printable(name));
++ }
++
++ Q_DECL_COLD_FUNCTION
++ static void err_info_about_objects(const char *func, const QObject *sender, const QObject *receiver)
++ {
++ err_info_about_object(func, sender, ConnectionEnd::Sender);
++ err_info_about_object(func, receiver, ConnectionEnd::Receiver);
++ }
++
++ Q_DECL_COLD_FUNCTION
++ static void connectWarning(const QObject *sender,
++ const QMetaObject *senderMetaObject,
++ const QObject *receiver,
++ const char *message)
++ {
++ const char *senderString = sender ? sender->metaObject()->className()
++ : senderMetaObject ? senderMetaObject->className()
++ : "Unknown";
++ const char *receiverString = receiver ? receiver->metaObject()->className()
++ : "Unknown";
++ qCWarning(lcConnect, "QObject::connect(%s, %s): %s", senderString, receiverString, message);
++ }
++
++ /*!
++ Returns a pointer to the object that sent the signal, if called in
++ a slot activated by a signal; otherwise it returns \nullptr. The pointer
++ is valid only during the execution of the slot that calls this
++ function from this object's thread context.
++
++ The pointer returned by this function becomes invalid if the
++ sender is destroyed, or if the slot is disconnected from the
++ sender's signal.
++
++ \warning This function violates the object-oriented principle of
++ modularity. However, getting access to the sender might be useful
++ when many signals are connected to a single slot.
++
++ \warning As mentioned above, the return value of this function is
++ not valid when the slot is called via a Qt::DirectConnection from
++ a thread different from this object's thread. Do not use this
++ function in this type of scenario.
++
++ \sa senderSignalIndex()
++ */
++
++ QObject *QObject::sender() const
++ {
++ Q_D(const QObject);
++
++ QMutexLocker locker(signalSlotLock(this));
++ QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
++ if (!cd || !cd->currentSender)
++ return nullptr;
++
++ for (QObjectPrivate::Connection *c = cd->senders; c; c = c->next) {
++ if (c->sender == cd->currentSender->sender)
++ return cd->currentSender->sender;
++ }
++
++ return nullptr;
++ }
++
++ /*!
++ \since 4.8
++
++ Returns the meta-method index of the signal that called the currently
++ executing slot, which is a member of the class returned by sender().
++ If called outside of a slot activated by a signal, -1 is returned.
++
++ For signals with default parameters, this function will always return
++ the index with all parameters, regardless of which was used with
++ connect(). For example, the signal \c {destroyed(QObject *obj = \nullptr)}
++ will have two different indexes (with and without the parameter), but
++ this function will always return the index with a parameter. This does
++ not apply when overloading signals with different parameters.
++
++ \warning This function violates the object-oriented principle of
++ modularity. However, getting access to the signal index might be useful
++ when many signals are connected to a single slot.
++
++ \warning The return value of this function is not valid when the slot
++ is called via a Qt::DirectConnection from a thread different from this
++ object's thread. Do not use this function in this type of scenario.
++
++ \sa sender(), QMetaObject::indexOfSignal(), QMetaObject::method()
++ */
++
++ int QObject::senderSignalIndex() const
++ {
++ Q_D(const QObject);
++
++ QMutexLocker locker(signalSlotLock(this));
++ QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
++ if (!cd || !cd->currentSender)
++ return -1;
++
++ for (QObjectPrivate::Connection *c = cd->senders; c; c = c->next) {
++ if (c->sender == cd->currentSender->sender) {
++ // Convert from signal range to method range
++ return QMetaObjectPrivate::signal(c->sender->metaObject(), cd->currentSender->signal).methodIndex();
++ }
++ }
++
++ return -1;
++ }
++
++ /*!
++ Returns the number of receivers connected to the \a signal.
++
++ Since both slots and signals can be used as receivers for signals,
++ and the same connections can be made many times, the number of
++ receivers is the same as the number of connections made from this
++ signal.
++
++ When calling this function, you can use the \c SIGNAL() macro to
++ pass a specific signal:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 21
++
++ As the code snippet above illustrates, you can use this function to avoid
++ expensive operations or emitting a signal that nobody listens to.
++
++ \warning In a multithreaded application, consecutive calls to this
++ function are not guaranteed to yield the same results.
++
++ \warning This function violates the object-oriented principle of
++ modularity. In particular, this function must not be called from an
++ override of connectNotify() or disconnectNotify(), as those might get
++ called from any thread.
++
++ \sa isSignalConnected()
++ */
++
++ int QObject::receivers(const char *signal) const
++ {
++ Q_D(const QObject);
++ int receivers = 0;
++ if (signal) {
++ QByteArray signal_name = QMetaObject::normalizedSignature(signal);
++ signal = signal_name;
++ #ifndef QT_NO_DEBUG
++ if (!check_signal_macro(this, signal, "receivers", "bind"))
++ return 0;
++ #endif
++ signal++; // skip code
++ int signal_index = d->signalIndex(signal);
++ if (signal_index < 0) {
++ #ifndef QT_NO_DEBUG
++ err_method_notfound(this, signal - 1, "receivers");
++ #endif
++ return 0;
++ }
++
++ if (!d->isSignalConnected(signal_index))
++ return receivers;
++
++ if (!d->isDeletingChildren && d->declarativeData && QAbstractDeclarativeData::receivers) {
++ receivers += QAbstractDeclarativeData::receivers(d->declarativeData, this,
++ signal_index);
++ }
++
++ QMutexLocker locker(signalSlotLock(this));
++ QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
++ if (cd && signal_index < cd->signalVectorCount()) {
++ const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
++ while (c) {
++ receivers += c->receiver.loadRelaxed() ? 1 : 0;
++ c = c->nextConnectionList.loadRelaxed();
++ }
++ }
++ }
++ return receivers;
++ }
++
++ /*!
++ \since 5.0
++ Returns \c true if the \a signal is connected to at least one receiver,
++ otherwise returns \c false.
++
++ \a signal must be a signal member of this object, otherwise the behaviour
++ is undefined.
++
++ \snippet code/src_corelib_kernel_qobject.cpp 49
++
++ As the code snippet above illustrates, you can use this function to avoid
++ expensive operations or emitting a signal that nobody listens to.
++
++ \warning In a multithreaded application, consecutive calls to this
++ function are not guaranteed to yield the same results.
++
++ \warning This function violates the object-oriented principle of
++ modularity. In particular, this function must not be called from an
++ override of connectNotify() or disconnectNotify(), as those might get
++ called from any thread.
++
++ \sa receivers()
++ */
++ bool QObject::isSignalConnected(const QMetaMethod &signal) const
++ {
++ Q_D(const QObject);
++ if (!signal.mobj)
++ return false;
++
++ Q_ASSERT_X(signal.mobj->cast(this) && signal.methodType() == QMetaMethod::Signal,
++ "QObject::isSignalConnected" , "the parameter must be a signal member of the object");
++ uint signalIndex = signal.relativeMethodIndex();
++
++ if (signal.data.flags() & MethodCloned)
++ signalIndex = QMetaObjectPrivate::originalClone(signal.mobj, signalIndex);
++
++ signalIndex += QMetaObjectPrivate::signalOffset(signal.mobj);
++
++ QMutexLocker locker(signalSlotLock(this));
++ return d->isSignalConnected(signalIndex, true);
++ }
++
++ /*!
++ \internal
++
++ This helper function calculates signal and method index for the given
++ member in the specified class.
++
++ \list
++ \li If member.mobj is \nullptr then both signalIndex and methodIndex are set to -1.
++
++ \li If specified member is not a member of obj instance class (or one of
++ its parent classes) then both signalIndex and methodIndex are set to -1.
++ \endlist
++
++ This function is used by QObject::connect and QObject::disconnect which
++ are working with QMetaMethod.
++
++ \a signalIndex is set to the signal index of member. If the member
++ specified is not signal this variable is set to -1.
++
++ \a methodIndex is set to the method index of the member. If the
++ member is not a method of the object specified by the \a obj argument this
++ variable is set to -1.
++ */
++ void QMetaObjectPrivate::memberIndexes(const QObject *obj,
++ const QMetaMethod &member,
++ int *signalIndex, int *methodIndex)
++ {
++ *signalIndex = -1;
++ *methodIndex = -1;
++ if (!obj || !member.mobj)
++ return;
++ const QMetaObject *m = obj->metaObject();
++ // Check that member is member of obj class
++ while (m != nullptr && m != member.mobj)
++ m = m->d.superdata;
++ if (!m)
++ return;
++ *signalIndex = *methodIndex = member.relativeMethodIndex();
++
++ int signalOffset;
++ int methodOffset;
++ computeOffsets(m, &signalOffset, &methodOffset);
++
++ *methodIndex += methodOffset;
++ if (member.methodType() == QMetaMethod::Signal) {
++ *signalIndex = originalClone(m, *signalIndex);
++ *signalIndex += signalOffset;
++ } else {
++ *signalIndex = -1;
++ }
++ }
++
++ #ifndef QT_NO_DEBUG
++ static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaMethod &signal,
++ const QMetaObject *receiver, const QMetaMethod &method)
++ {
++ if (signal.attributes() & QMetaMethod::Compatibility) {
++ if (!(method.attributes() & QMetaMethod::Compatibility))
++ qCWarning(lcConnect, "QObject::connect: Connecting from COMPAT signal (%s::%s)",
++ sender->className(), signal.methodSignature().constData());
++ } else if ((method.attributes() & QMetaMethod::Compatibility)
++ && method.methodType() == QMetaMethod::Signal) {
++ qCWarning(lcConnect, "QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)",
++ sender->className(), signal.methodSignature().constData(), receiver->className(),
++ method.methodSignature().constData());
++ }
++ }
++ #endif
++
++ /*!
++ \threadsafe
++
++ 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 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:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 22
++
++ This example ensures that the label always displays the current
++ scroll bar value. Note that the signal and slots parameters must not
++ contain any variable names, only the type. E.g. the following would
++ not work and return false:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 23
++
++ A signal can also be connected to another signal:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 24
++
++ In this example, the \c MyWidget constructor relays a signal from
++ a private member variable, and makes it available under a name
++ that relates to \c MyWidget.
++
++ A signal can be connected to many slots and signals. Many signals
++ can be connected to one slot.
++
++ If a signal is connected to several slots, the slots are activated
++ in the same order in which the connections were made, when the
++ signal is emitted.
++
++ The function returns a QMetaObject::Connection that represents
++ 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 handle 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
++ all of these connections with a single disconnect() call.
++ If you pass the Qt::UniqueConnection \a type, the connection will only
++ be made if it is not a duplicate. If there is already a duplicate
++ (exact same signal to the exact same slot on the same objects),
++ the connection will fail and connect will return an invalid QMetaObject::Connection.
++
++ \note Qt::UniqueConnections do not work for lambdas, non-member functions
++ and functors; they only apply to connecting to member functions.
++
++ The optional \a type parameter describes the type of connection
++ to establish. In particular, it determines whether a particular
++ signal is delivered to a slot immediately or queued for delivery
++ at a later time. If the signal is queued, the parameters must be
++ of types that are known to Qt's meta-object system, because Qt
++ needs to copy the arguments to store them in an event behind the
++ scenes. If you try to use a queued connection and get the error
++ message
++
++ \snippet code/src_corelib_kernel_qobject.cpp 25
++
++ call qRegisterMetaType() to register the data type before you
++ establish the connection.
++
++ \sa disconnect(), sender(), qRegisterMetaType(), Q_DECLARE_METATYPE(),
++ {Differences between String-Based and Functor-Based Connections}
++ */
++ QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,
++ const QObject *receiver, const char *method,
++ Qt::ConnectionType type)
++ {
++ if (sender == nullptr || receiver == nullptr || signal == nullptr || method == nullptr) {
++ qCWarning(lcConnect, "QObject::connect: Cannot connect %s::%s to %s::%s",
++ sender ? sender->metaObject()->className() : "(nullptr)",
++ (signal && *signal) ? signal + 1 : "(nullptr)",
++ receiver ? receiver->metaObject()->className() : "(nullptr)",
++ (method && *method) ? method + 1 : "(nullptr)");
++ return QMetaObject::Connection(nullptr);
++ }
++
++ if (!check_signal_macro(sender, signal, "connect", "bind"))
++ return QMetaObject::Connection(nullptr);
++
++ int membcode = extract_code(method);
++ if (!check_method_code(membcode, receiver, method, "connect"))
++ return QMetaObject::Connection(nullptr);
++
++ QByteArray pinnedSignal;
++ const QMetaObject *smeta = sender->metaObject();
++ const char *signal_arg = signal;
++ ++signal; // skip code
++ QByteArrayView signalView{signal}; // after skipping code
++ QArgumentTypeArray signalTypes;
++ Q_ASSERT(QMetaObjectPrivate::get(smeta)->revision >= 7);
++ QByteArrayView signalName = QMetaObjectPrivate::decodeMethodSignature(signalView, signalTypes);
++ int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signalName, signalTypes);
++ if (signal_index < 0) {
++ // check for normalized signatures
++ pinnedSignal = QMetaObjectPrivate::normalizedSignature(signalView);
++ signalView = pinnedSignal;
++
++ signalTypes.clear();
++ signalName = QMetaObjectPrivate::decodeMethodSignature(signalView, signalTypes);
++ smeta = sender->metaObject();
++ signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signalName, signalTypes);
++ }
++ if (signal_index < 0) {
++ err_method_notfound(sender, signal_arg, "connect");
++ err_info_about_objects("connect", sender, receiver);
++ return QMetaObject::Connection(nullptr);
++ }
++ signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
++ signal_index += QMetaObjectPrivate::signalOffset(smeta);
++
++ QByteArray pinnedMethod;
++ const char *method_arg = method;
++ ++method; // skip code
++ QByteArrayView methodView{method}; // after skipping code
++
++ QArgumentTypeArray methodTypes;
++ QByteArrayView methodName = QMetaObjectPrivate::decodeMethodSignature(methodView, methodTypes);
++ const QMetaObject *rmeta = receiver->metaObject();
++ int method_index_relative = -1;
++ Q_ASSERT(QMetaObjectPrivate::get(rmeta)->revision >= 7);
++ switch (membcode) {
++ case QSLOT_CODE:
++ method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(
++ &rmeta, methodName, methodTypes);
++ break;
++ case QSIGNAL_CODE:
++ method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(
++ &rmeta, methodName, methodTypes);
++ break;
++ }
++
++ if (method_index_relative < 0) {
++ // check for normalized methods
++ pinnedMethod = QMetaObjectPrivate::normalizedSignature(methodView);
++ methodView = pinnedMethod;
++
++ methodTypes.clear();
++ methodName = QMetaObjectPrivate::decodeMethodSignature(methodView, methodTypes);
++ // rmeta may have been modified above
++ rmeta = receiver->metaObject();
++ switch (membcode) {
++ case QSLOT_CODE:
++ method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(
++ &rmeta, methodName, methodTypes);
++ break;
++ case QSIGNAL_CODE:
++ method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(
++ &rmeta, methodName, methodTypes);
++ break;
++ }
++ }
++
++ if (method_index_relative < 0) {
++ err_method_notfound(receiver, method_arg, "connect");
++ err_info_about_objects("connect", sender, receiver);
++ return QMetaObject::Connection(nullptr);
++ }
++
++ if (!QMetaObjectPrivate::checkConnectArgs(signalTypes, methodTypes)) {
++ qCWarning(lcConnect,
++ "QObject::connect: Incompatible sender/receiver arguments"
++ "\n %s::%s --> %s::%s",
++ sender->metaObject()->className(), signalView.constData(),
++ receiver->metaObject()->className(), methodView.constData());
++ return QMetaObject::Connection(nullptr);
++ }
++
++ // ### Future work: attempt get the metatypes from the meta object first
++ // because it's possible they're all registered.
++ int *types = nullptr;
++ if (type == Qt::QueuedConnection && !(types = queuedConnectionTypes(signalTypes))) {
++ return QMetaObject::Connection(nullptr);
++ }
++
++ QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());
++ #ifndef QT_NO_DEBUG
++ QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
++ check_and_warn_compat(smeta, smethod, rmeta, rmethod);
++ #endif
++
++ #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
++ check_and_warn_non_slot("connect", method, membcode, rmeta, rmethod);
++ #endif
++
++ QMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(
++ sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types));
++ return handle;
++ }
++
++ /*!
++ \since 4.8
++
++ 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 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
++ \c {connect(const QObject *sender, const char *signal,
++ const QObject *receiver, const char *method,
++ Qt::ConnectionType type)}
++ but it uses QMetaMethod to specify signal and method.
++
++ \sa connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
++ */
++ QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMethod &signal,
++ const QObject *receiver, const QMetaMethod &method,
++ Qt::ConnectionType type)
++ {
++ if (sender == nullptr
++ || receiver == nullptr
++ || signal.methodType() != QMetaMethod::Signal
++ || method.methodType() == QMetaMethod::Constructor) {
++ qCWarning(lcConnect, "QObject::connect: Cannot connect %s::%s to %s::%s",
++ sender ? sender->metaObject()->className() : "(nullptr)",
++ signal.methodSignature().constData(),
++ receiver ? receiver->metaObject()->className() : "(nullptr)",
++ method.methodSignature().constData());
++ return QMetaObject::Connection(nullptr);
++ }
++
++ int signal_index;
++ int method_index;
++ {
++ int dummy;
++ QMetaObjectPrivate::memberIndexes(sender, signal, &signal_index, &dummy);
++ QMetaObjectPrivate::memberIndexes(receiver, method, &dummy, &method_index);
++ }
++
++ const QMetaObject *smeta = sender->metaObject();
++ const QMetaObject *rmeta = receiver->metaObject();
++ if (signal_index == -1) {
++ qCWarning(lcConnect, "QObject::connect: Can't find signal %s on instance of class %s",
++ signal.methodSignature().constData(), smeta->className());
++ return QMetaObject::Connection(nullptr);
++ }
++ if (method_index == -1) {
++ qCWarning(lcConnect, "QObject::connect: Can't find method %s on instance of class %s",
++ method.methodSignature().constData(), rmeta->className());
++ return QMetaObject::Connection(nullptr);
++ }
++
++ if (!QMetaObject::checkConnectArgs(signal.methodSignature().constData(),
++ method.methodSignature().constData())) {
++ qCWarning(lcConnect,
++ "QObject::connect: Incompatible sender/receiver arguments"
++ "\n %s::%s --> %s::%s",
++ smeta->className(), signal.methodSignature().constData(), rmeta->className(),
++ method.methodSignature().constData());
++ return QMetaObject::Connection(nullptr);
++ }
++
++ int *types = nullptr;
++ if ((type == Qt::QueuedConnection) && !(types = queuedConnectionTypes(signal)))
++ return QMetaObject::Connection(nullptr);
++
++ #ifndef QT_NO_DEBUG
++ check_and_warn_compat(smeta, signal, rmeta, method);
++ #endif
++ QMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(
++ sender, signal_index, signal.enclosingMetaObject(), receiver, method_index, nullptr, type, types));
++ return handle;
++ }
++
++ /*!
++ \fn bool QObject::connect(const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type) const
++ \overload connect()
++ \threadsafe
++
++ Connects \a signal from the \a sender object to this object's \a
++ method.
++
++ Equivalent to connect(\a sender, \a signal, \c this, \a method, \a type).
++
++ Every connection you make emits a signal, so duplicate connections emit
++ two signals. You can break a connection using disconnect().
++
++ \sa disconnect()
++ */
++
++ /*!
++ \threadsafe
++
++ Disconnects \a signal in object \a sender from \a method in object
++ \a receiver. Returns \c true if the connection is successfully broken;
++ otherwise returns \c false.
++
++ A signal-slot connection is removed when either of the objects
++ involved are destroyed.
++
++ disconnect() is typically used in three ways, as the following
++ examples demonstrate.
++ \list 1
++ \li Disconnect everything connected to an object's signals:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 26
++
++ equivalent to the non-static overloaded function
++
++ \snippet code/src_corelib_kernel_qobject.cpp 27
++
++ \li Disconnect everything connected to a specific signal:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 28
++
++ equivalent to the non-static overloaded function
++
++ \snippet code/src_corelib_kernel_qobject.cpp 29
++
++ \li Disconnect a specific receiver:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 30
++
++ equivalent to the non-static overloaded function
++
++ \snippet code/src_corelib_kernel_qobject.cpp 31
++
++ \endlist
++
++ \include includes/qobject.qdocinc disconnect-mismatch
++ \include includes/qobject.qdocinc disconnect-queued
++
++ \nullptr may be used as a wildcard, meaning "any signal", "any receiving
++ object", or "any slot in the receiving object", respectively.
++
++ The \a sender may never be \nullptr. (You cannot disconnect signals
++ from more than one object in a single call.)
++
++ If \a signal is \nullptr, it disconnects \a receiver and \a method from
++ any signal. If not, only the specified signal is disconnected.
++
++ If \a receiver is \nullptr, it disconnects anything connected to \a
++ signal. If not, slots in objects other than \a receiver are not
++ disconnected.
++
++ If \a method is \nullptr, it disconnects anything that is connected to \a
++ receiver. If not, only slots named \a method will be disconnected,
++ and all other slots are left alone. The \a method must be \nullptr
++ if \a receiver is left out, so you cannot disconnect a
++ specifically-named slot on all objects.
++
++ \include includes/qobject.qdocinc disconnect-all
++
++ \sa connect()
++ */
++ bool QObject::disconnect(const QObject *sender, const char *signal,
++ const QObject *receiver, const char *method)
++ {
++ if (sender == nullptr || (receiver == nullptr && method != nullptr)) {
++ qCWarning(lcConnect, "QObject::disconnect: Unexpected nullptr parameter");
++ return false;
++ }
++
++ const char *signal_arg = signal;
++ if (signal) {
++ if (!check_signal_macro(sender, signal, "disconnect", "unbind"))
++ return false;
++ ++signal; // skip code
++ }
++
++ const char *method_arg = method;
++ int membcode = -1;
++ if (method) {
++ membcode = extract_code(method);
++ if (!check_method_code(membcode, receiver, method, "disconnect"))
++ return false;
++ ++method; // skip code
++ }
++
++ QByteArray pinnedSignal;
++ const QMetaObject *smeta = sender->metaObject();
++ Q_ASSERT(QMetaObjectPrivate::get(smeta)->revision >= 7);
++ int signal_index = -1;
++ QByteArrayView signalName;
++ QArgumentTypeArray signalTypes;
++ if (signal) {
++ signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
++ signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signalName, signalTypes);
++ if (signal_index == -1) {
++ pinnedSignal = QMetaObject::normalizedSignature(signal);
++ signal = pinnedSignal.constData();
++ signalTypes.clear();
++ signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
++ signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signalName,
++ signalTypes);
++ }
++ if (signal_index == -1) {
++ err_method_notfound(sender, signal_arg, "disconnect");
++ err_info_about_objects("disconnect", sender, receiver);
++ return false;
++ }
++ }
++
++ auto getMethodIndex = [](int code, const QMetaObject *mo, QByteArrayView name,
++ const QArgumentTypeArray &types) {
++ switch (code) {
++ case QSLOT_CODE:
++ return QMetaObjectPrivate::indexOfSlot(mo, name, types);
++ case QSIGNAL_CODE:
++ return QMetaObjectPrivate::indexOfSignal(mo, name, types);
++ }
++ return -1;
++ };
++
++ QByteArray pinnedMethod;
++ const QMetaObject *rmeta = receiver ? receiver->metaObject() : nullptr;
++ Q_ASSERT(!rmeta || QMetaObjectPrivate::get(rmeta)->revision >= 7);
++ int method_index = -1;
++ QByteArrayView methodName;
++ QArgumentTypeArray methodTypes;
++ if (method) {
++ methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);
++ method_index = getMethodIndex(membcode, rmeta, methodName, methodTypes);
++ if (method_index == -1) {
++ pinnedMethod = QMetaObject::normalizedSignature(method);
++ method = pinnedMethod.constData();
++ methodTypes.clear();
++ methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);
++ method_index = getMethodIndex(membcode, rmeta, methodName, methodTypes);
++ }
++ if (method_index == -1) {
++ err_method_notfound(receiver, method_arg, "disconnect");
++ err_info_about_objects("disconnect", sender, receiver);
++ return false;
++ }
++ }
++
++ /* We now iterate through all the sender's and receiver's meta
++ * objects in order to also disconnect possibly shadowed signals
++ * and slots with the same signature.
++ */
++ bool res = false;
++ do {
++ if (signal) {
++ // Already computed the signal_index for `smeta` above
++ if (smeta != sender->metaObject()) {
++ signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signalName,
++ signalTypes);
++ }
++ if (signal_index < 0)
++ break;
++ signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
++ signal_index += QMetaObjectPrivate::signalOffset(smeta);
++ }
++
++ if (!method) {
++ res |= QMetaObjectPrivate::disconnect(sender, signal_index, smeta, receiver, -1, nullptr);
++ } else {
++ do {
++ // Already computed the method_index for receiver->metaObject() above
++ if (rmeta != receiver->metaObject())
++ method_index = getMethodIndex(membcode, rmeta, methodName, methodTypes);
++ if (method_index >= 0) {
++ #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
++ check_and_warn_non_slot("disconnect", method, membcode, rmeta,
++ rmeta->method(method_index));
++ #endif
++ while (method_index < rmeta->methodOffset())
++ rmeta = rmeta->superClass();
++ }
++ if (method_index < 0)
++ break;
++ res |= QMetaObjectPrivate::disconnect(sender, signal_index, smeta, receiver, method_index, nullptr);
++ } while ((rmeta = rmeta->superClass()));
++ }
++ } while (signal && (smeta = smeta->superClass()));
++
++ if (res) {
++ if (!signal)
++ const_cast<QObject *>(sender)->disconnectNotify(QMetaMethod());
++ }
++ return res;
++ }
++
++ /*!
++ \since 4.8
++
++ Disconnects \a signal in object \a sender from \a method in object
++ \a receiver. Returns \c true if the connection is successfully broken;
++ otherwise returns \c false.
++
++ This function provides the same possibilities like
++ \c {disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method) }
++ but uses QMetaMethod to represent the signal and the method to be disconnected.
++
++ Additionally this function returns false and no signals and slots disconnected
++ if:
++ \list 1
++
++ \li \a signal is not a member of sender class or one of its parent classes.
++
++ \li \a method is not a member of receiver class or one of its parent classes.
++
++ \li \a signal instance represents not a signal.
++
++ \endlist
++
++ \include includes/qobject.qdocinc disconnect-mismatch
++ \include includes/qobject.qdocinc disconnect-queued
++
++ QMetaMethod() may be used as wildcard in the meaning "any signal" or "any slot in receiving object".
++ In the same way \nullptr can be used for \a receiver in the meaning "any receiving object".
++ In this case method should also be QMetaMethod(). \a sender parameter should be never \nullptr.
++
++ \include includes/qobject.qdocinc disconnect-all
++
++ \sa disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
++ */
++ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal,
++ const QObject *receiver, const QMetaMethod &method)
++ {
++ if (sender == nullptr || (receiver == nullptr && method.mobj != nullptr)) {
++ qCWarning(lcConnect, "QObject::disconnect: Unexpected nullptr parameter");
++ return false;
++ }
++ if (signal.mobj) {
++ if (signal.methodType() != QMetaMethod::Signal) {
++ qCWarning(lcConnect, "QObject::%s: Attempt to %s non-signal %s::%s",
++ "disconnect","unbind",
++ sender->metaObject()->className(), signal.methodSignature().constData());
++ return false;
++ }
++ }
++ if (method.mobj) {
++ if (method.methodType() == QMetaMethod::Constructor) {
++ qCWarning(lcConnect, "QObject::disconnect: cannot use constructor as argument %s::%s",
++ receiver->metaObject()->className(), method.methodSignature().constData());
++ return false;
++ }
++ }
++
++ int signal_index;
++ int method_index;
++ {
++ int dummy;
++ QMetaObjectPrivate::memberIndexes(sender, signal, &signal_index, &dummy);
++ QMetaObjectPrivate::memberIndexes(receiver, method, &dummy, &method_index);
++ }
++ // If we are here sender is not nullptr. If signal is not nullptr while signal_index
++ // is -1 then this signal is not a member of sender.
++ if (signal.mobj && signal_index == -1) {
++ qCWarning(lcConnect, "QObject::disconnect: signal %s not found on class %s",
++ signal.methodSignature().constData(), sender->metaObject()->className());
++ return false;
++ }
++ // If this condition is true then method is not a member of receiver.
++ if (receiver && method.mobj && method_index == -1) {
++ qCWarning(lcConnect, "QObject::disconnect: method %s not found on class %s",
++ method.methodSignature().constData(), receiver->metaObject()->className());
++ return false;
++ }
++
++ if (!QMetaObjectPrivate::disconnect(sender, signal_index, signal.mobj, receiver, method_index, nullptr))
++ return false;
++
++ if (!signal.isValid()) {
++ // The signal is a wildcard, meaning all signals were disconnected.
++ // QMetaObjectPrivate::disconnect() doesn't call disconnectNotify()
++ // per connection in this case. Call it once now, with an invalid
++ // QMetaMethod as argument, as documented.
++ const_cast<QObject *>(sender)->disconnectNotify(signal);
++ }
++ return true;
++ }
++
++ /*!
++ \threadsafe
++
++ \fn bool QObject::disconnect(const char *signal, const QObject *receiver, const char *method) const
++ \overload disconnect()
++
++ Disconnects \a signal from \a method of \a receiver.
++
++ \include includes/qobject.qdocinc disconnect-mismatch
++ \include includes/qobject.qdocinc disconnect-queued
++
++ A signal-slot connection is removed when either of the objects
++ involved are destroyed.
++
++ \include includes/qobject.qdocinc disconnect-all
++ */
++
++ /*!
++ \fn bool QObject::disconnect(const QObject *receiver, const char *method) const
++ \overload disconnect()
++
++ Disconnects all signals in this object from \a receiver's \a
++ method.
++
++ \include includes/qobject.qdocinc disconnect-mismatch
++ \include includes/qobject.qdocinc disconnect-queued
++
++ A signal-slot connection is removed when either of the objects
++ involved are destroyed.
++ */
++
++
++ /*!
++ \since 5.0
++
++ This virtual function is called when something has been connected
++ to \a signal in this object.
++
++ If you want to compare \a signal with a specific signal, you can
++ use QMetaMethod::fromSignal() as follows:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 32
++
++ \warning This function violates the object-oriented principle of
++ modularity. However, it might be useful when you need to perform
++ an expensive operation only if something is connected to a signal.
++
++ \warning This function is called from the thread which performs the
++ connection, which may be a different thread from the thread in which
++ this object lives. This function may also be called with a QObject internal
++ mutex locked. It is therefore not allowed to re-enter any QObject
++ functions, including isSignalConnected(), from your reimplementation. If
++ you lock a mutex in your reimplementation, make sure that you don't call
++ QObject functions with that mutex held in other places or it will result in
++ a deadlock.
++
++ \sa connect(), disconnectNotify()
++ */
++
++ void QObject::connectNotify(const QMetaMethod &signal)
++ {
++ Q_UNUSED(signal);
++ }
++
++ /*!
++ \since 5.0
++
++ This virtual function is called when something has been
++ disconnected from \a signal in this object.
++
++ See connectNotify() for an example of how to compare
++ \a signal with a specific signal.
++
++ If all signals were disconnected from this object (e.g., the
++ signal argument to disconnect() was \nullptr), disconnectNotify()
++ is only called once, and the \a signal will be an invalid
++ QMetaMethod (QMetaMethod::isValid() returns \c false).
++
++ When a receiver living in another thread is destroyed, this function is
++ called later, from this object's own thread; if that thread has no running
++ event loop, it is not called at all. In every other case it is called right
++ away, from the thread that is disconnecting the signal.
++
++ \warning This function violates the object-oriented principle of
++ modularity. However, it might be useful for optimizing access to
++ expensive resources.
++
++ \warning This function is called from the thread which performs the
++ disconnection, which may be a different thread from the thread in which
++ this object lives. This function may also be called with a QObject internal
++ mutex locked. It is therefore not allowed to re-enter any QObject
++ functions, including isSignalConnected(), from your reimplementation. If
++ you lock a mutex in your reimplementation, make sure that you don't call
++ QObject functions with that mutex held in other places or it will result in
++ a deadlock.
++
++ \sa disconnect(), connectNotify()
++ */
++
++ void QObject::disconnectNotify(const QMetaMethod &signal)
++ {
++ Q_UNUSED(signal);
++ }
++
++ /*
++ \internal
++ convert a signal index from the method range to the signal range
++ */
++ static int methodIndexToSignalIndex(const QMetaObject **base, int signal_index)
++ {
++ if (signal_index < 0)
++ return signal_index;
++ const QMetaObject *metaObject = *base;
++ while (metaObject && metaObject->methodOffset() > signal_index)
++ metaObject = metaObject->superClass();
++
++ if (metaObject) {
++ int signalOffset, methodOffset;
++ computeOffsets(metaObject, &signalOffset, &methodOffset);
++ if (signal_index < metaObject->methodCount())
++ signal_index = QMetaObjectPrivate::originalClone(metaObject, signal_index - methodOffset) + signalOffset;
++ else
++ signal_index = signal_index - methodOffset + signalOffset;
++ *base = metaObject;
++ }
++ return signal_index;
++ }
++
++ /*!
++ \internal
++ \a types is a 0-terminated vector of meta types for queued
++ connections.
++
++ if \a signal_index is -1, then we effectively connect *all* signals
++ from the sender to the receiver's slot
++ */
++ QMetaObject::Connection QMetaObject::connect(const QObject *sender, int signal_index,
++ const QObject *receiver, int method_index, int type,
++ int *types)
++ {
++ const QMetaObject *smeta = sender->metaObject();
++ signal_index = methodIndexToSignalIndex(&smeta, signal_index);
++ return Connection(QMetaObjectPrivate::connect(sender, signal_index, smeta,
++ receiver, method_index,
++ nullptr, //FIXME, we could speed this connection up by computing the relative index
++ 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 \nullptr, then it is absolute index
++
++ the QObjectPrivate::Connection* has a refcount of 2, so it must be passed to a QMetaObject::Connection
++ */
++ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
++ int signal_index, const QMetaObject *smeta,
++ const QObject *receiver, int method_index,
++ const QMetaObject *rmeta, int type, int *types)
++ {
++ QObject *s = const_cast<QObject *>(sender);
++ QObject *r = const_cast<QObject *>(receiver);
++
++ int method_offset = rmeta ? rmeta->methodOffset() : 0;
++ Q_ASSERT(!rmeta || QMetaObjectPrivate::get(rmeta)->revision >= 6);
++ QObjectPrivate::StaticMetaCallFunction callFunction = rmeta ? rmeta->d.static_metacall : nullptr;
++
++ QOrderedMutexLocker locker(signalSlotLock(sender),
++ signalSlotLock(receiver));
++
++ QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.loadRelaxed();
++ if (type & Qt::UniqueConnection && scd) {
++ if (scd->signalVectorCount() > signal_index) {
++ const QObjectPrivate::Connection *c2 = scd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
++
++ int method_index_absolute = method_index + method_offset;
++
++ while (c2) {
++ if (!c2->isSlotObject && c2->receiver.loadRelaxed() == receiver && c2->method() == method_index_absolute)
++ return nullptr;
++ c2 = c2->nextConnectionList.loadRelaxed();
++ }
++ }
++ }
++ type &= ~Qt::UniqueConnection;
++
++ const bool isSingleShot = type & Qt::SingleShotConnection;
++ type &= ~Qt::SingleShotConnection;
++
++ Q_ASSERT(type >= 0);
++ Q_ASSERT(type <= 3);
++
++ std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection};
++ c->sender = s;
++ c->signal_index = signal_index;
++ c->receiver.storeRelaxed(r);
++ QThreadData *td = r->d_func()->threadData.loadAcquire();
++ td->ref();
++ c->receiverThreadData.storeRelaxed(td);
++ c->method_relative = method_index;
++ c->method_offset = method_offset;
++ c->connectionType = type;
++ c->isSlotObject = false;
++ c->argumentTypes.storeRelaxed(types);
++ c->callFunction = callFunction;
++ c->isSingleShot = isSingleShot;
++
++ QObjectPrivate::get(s)->addConnection(signal_index, c.get());
++
++ locker.unlock();
++ QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
++ if (smethod.isValid())
++ s->connectNotify(smethod);
++
++ return c.release();
++ }
++
++ /*!
++ \internal
++ */
++ bool QMetaObject::disconnect(const QObject *sender, int signal_index,
++ const QObject *receiver, int method_index)
++ {
++ const QMetaObject *smeta = sender->metaObject();
++ signal_index = methodIndexToSignalIndex(&smeta, signal_index);
++ return QMetaObjectPrivate::disconnect(sender, signal_index, smeta,
++ receiver, method_index, nullptr);
++ }
++
++ /*!
++ \internal
++
++ Disconnect a single signal connection. If QMetaObject::connect() has been called
++ multiple times for the same sender, signal_index, receiver and method_index only
++ one of these connections will be removed.
++ */
++ bool QMetaObject::disconnectOne(const QObject *sender, int signal_index,
++ const QObject *receiver, int method_index)
++ {
++ const QMetaObject *smeta = sender->metaObject();
++ signal_index = methodIndexToSignalIndex(&smeta, signal_index);
++ return QMetaObjectPrivate::disconnect(sender, signal_index, smeta,
++ receiver, method_index, nullptr,
++ QMetaObjectPrivate::DisconnectOne);
++ }
++
++ /*!
++ \internal
++ Helper function to remove the connection from the senders list and set the receivers to \nullptr
++ */
++ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::ConnectionData *connections, int signalIndex,
++ const QObject *receiver, int method_index, void **slot,
++ QBasicMutex *senderMutex, DisconnectType disconnectType)
++ {
++ bool success = false;
++
++ auto &connectionList = connections->connectionsForSignal(signalIndex);
++ auto *c = connectionList.first.loadRelaxed();
++ while (c) {
++ QObject *r = c->receiver.loadRelaxed();
++ if (r && (receiver == nullptr || (r == receiver
++ && (method_index < 0 || (!c->isSlotObject && c->method() == method_index))
++ && (slot == nullptr || (c->isSlotObject && c->slotObj->compare(slot)))))) {
++ bool needToUnlock = false;
++ QBasicMutex *receiverMutex = nullptr;
++ if (r) {
++ receiverMutex = signalSlotLock(r);
++ // need to relock this receiver and sender in the correct order
++ needToUnlock = QOrderedMutexLocker::relock(senderMutex, receiverMutex);
++ }
++ if (c->receiver.loadRelaxed())
++ connections->removeConnection(c);
++
++ if (needToUnlock)
++ receiverMutex->unlock();
++
++ success = true;
++
++ if (disconnectType == DisconnectOne)
++ return success;
++ }
++ c = c->nextConnectionList.loadRelaxed();
++ }
++ return success;
++ }
++
++ /*!
++ \internal
++ Same as the QMetaObject::disconnect, but \a signal_index must be the result of QObjectPrivate::signalIndex
++ */
++ bool QMetaObjectPrivate::disconnect(const QObject *sender,
++ int signal_index, const QMetaObject *smeta,
++ const QObject *receiver, int method_index, void **slot,
++ DisconnectType disconnectType)
++ {
++ if (!sender)
++ return false;
++
++ QObject *s = const_cast<QObject *>(sender);
++
++ QBasicMutex *senderMutex = signalSlotLock(sender);
++ QMutexLocker locker(senderMutex);
++
++ QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.loadRelaxed();
++ if (!scd)
++ return false;
++
++ // Capture the message arguments now and emit the warning after unlocking:
++ // qWarning() may re-enter connect/disconnect from the message handler and
++ // deadlock on the lock held here (QTBUG-145216).
++ struct WildcardDestroyedWarning
++ {
++ QByteArray className;
++ QByteArray objectName;
++ };
++ std::optional<WildcardDestroyedWarning> wildcardDestroyedWarning;
++
++ bool success = false;
++ {
++ // prevent incoming connections changing the connections->receivers while unlocked
++ QObjectPrivate::ConnectionDataPointer connections(scd);
++
++ if (signal_index < 0) {
++ // wildcard disconnect - warn if this disconnects destroyed()
++ if (!receiver && method_index < 0 && sender->d_func()->isSignalConnected(0)) {
++ wildcardDestroyedWarning = WildcardDestroyedWarning{
++ sender->metaObject()->className(),
++ sender->objectName().toLocal8Bit()
++ };
++ }
++ // remove from all connection lists
++ for (int sig_index = -1; sig_index < scd->signalVectorCount(); ++sig_index) {
++ if (disconnectHelper(connections.data(), sig_index, receiver, method_index, slot, senderMutex, disconnectType))
++ success = true;
++ }
++ } else if (signal_index < scd->signalVectorCount()) {
++ if (disconnectHelper(connections.data(), signal_index, receiver, method_index, slot, senderMutex, disconnectType))
++ success = true;
++ }
++ }
++
++ locker.unlock();
++
++ if (wildcardDestroyedWarning) {
++ qWarning("QObject::disconnect: wildcard call disconnects from destroyed signal of %s::%s",
++ wildcardDestroyedWarning->className.constData(),
++ wildcardDestroyedWarning->objectName.isEmpty()
++ ? "unnamed"
++ : wildcardDestroyedWarning->objectName.constData());
++ }
++
++ if (success) {
++ scd->cleanOrphanedConnections(s);
++
++ QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
++ if (smethod.isValid())
++ s->disconnectNotify(smethod);
++ }
++
++ return success;
++ }
++
++ // Helpers for formatting the connect statements of connectSlotsByName()'s debug mode
++ static QByteArray formatConnectionSignature(const char *className, const QMetaMethod &method)
++ {
++ const auto signature = method.methodSignature();
++ Q_ASSERT(signature.endsWith(')'));
++ const qsizetype openParen = signature.indexOf('(');
++ const bool hasParameters = openParen > 0 && openParen < signature.size() - 2;
++ QByteArray result;
++ if (hasParameters) {
++ const qsizetype len = signature.size() - openParen - 2;
++ result += "qOverload<" + QByteArrayView{signature}.slice(openParen + 1, len) + ">(";
++ }
++ result += '&';
++ result += className + QByteArrayLiteral("::") + method.name();
++ if (hasParameters)
++ result += ')';
++ return result;
++ }
++
++ static QByteArray msgConnect(const QMetaObject *senderMo, const QByteArray &senderName,
++ const QMetaMethod &signal, const QObject *receiver, int receiverIndex)
++ {
++ const auto receiverMo = receiver->metaObject();
++ const auto slot = receiverMo->method(receiverIndex);
++ QByteArray message = QByteArrayLiteral("QObject::connect(")
++ + senderName + ", " + formatConnectionSignature(senderMo->className(), signal)
++ + ", " + receiver->objectName().toLatin1() + ", "
++ + formatConnectionSignature(receiverMo->className(), slot) + ");";
++ return message;
++ }
++
++ /*!
++ \fn void QMetaObject::connectSlotsByName(QObject *object)
++
++ Searches recursively for all child objects of the given \a object, and connects
++ matching signals from them to slots of \a object that follow the following form:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 33
++
++ Let's assume our object has a child object of type \c{QPushButton} with
++ the \l{QObject::objectName}{object name} \c{button1}. The slot to catch the
++ button's \c{clicked()} signal would be:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 34
++
++ If \a object itself has a properly set object name, its own signals are also
++ connected to its respective slots.
++
++ \sa QObject::setObjectName()
++ */
++ void QMetaObject::connectSlotsByName(QObject *o)
++ {
++ if (!o)
++ return;
++ const QMetaObject *mo = o->metaObject();
++ Q_ASSERT(mo);
++ const QObjectList list = // list of all objects to look for matching signals including...
++ o->findChildren<QObject *>() // all children of 'o'...
++ << o; // and the object 'o' itself
++
++ // for each method/slot of o ...
++ for (int i = 0; i < mo->methodCount(); ++i) {
++ const QByteArray slotSignature = mo->method(i).methodSignature();
++ const char *slot = slotSignature.constData();
++ Q_ASSERT(slot);
++
++ // ...that starts with "on_", ...
++ if (slot[0] != 'o' || slot[1] != 'n' || slot[2] != '_')
++ continue;
++
++ // ...we check each object in our list, ...
++ bool foundIt = false;
++ for (int j = 0; j < list.size(); ++j) {
++ const QObject *co = list.at(j);
++ const QByteArray coName = co->objectName().toLatin1();
++
++ // ...discarding those whose objectName is not fitting the pattern "on_<objectName>_...", ...
++ if (coName.isEmpty() || qstrncmp(slot + 3, coName.constData(), coName.size()) || slot[coName.size()+3] != '_')
++ continue;
++
++ const char *signal = slot + coName.size() + 4; // the 'signal' part of the slot name
++
++ // ...for the presence of a matching signal "on_<objectName>_<signal>".
++ const QMetaObject *smeta;
++ int sigIndex = co->d_func()->signalIndex(signal, &smeta);
++ if (sigIndex < 0) {
++ // if no exactly fitting signal (name + complete parameter type list) could be found
++ // look for just any signal with the correct name and at least the slot's parameter list.
++ // Note: if more than one of those signals exist, the one that gets connected is
++ // chosen 'at random' (order of declaration in source file)
++ QList<QByteArray> compatibleSignals;
++ const QMetaObject *smo = co->metaObject();
++ int sigLen = int(qstrlen(signal)) - 1; // ignore the trailing ')'
++ for (int k = QMetaObjectPrivate::absoluteSignalCount(smo)-1; k >= 0; --k) {
++ const QMetaMethod method = QMetaObjectPrivate::signal(smo, k);
++ if (!qstrncmp(method.methodSignature().constData(), signal, sigLen)) {
++ smeta = method.enclosingMetaObject();
++ sigIndex = k;
++ compatibleSignals.prepend(method.methodSignature());
++ }
++ }
++ if (compatibleSignals.size() > 1)
++ qCWarning(lcConnectSlotsByName) << "QMetaObject::connectSlotsByName: Connecting slot" << slot
++ << "with the first of the following compatible signals:" << compatibleSignals;
++ }
++
++ if (sigIndex < 0)
++ continue;
++
++ // we connect it...
++ if (Connection(QMetaObjectPrivate::connect(co, sigIndex, smeta, o, i))) {
++ foundIt = true;
++ qCDebug(lcConnectSlotsByName, "%s",
++ msgConnect(smeta, coName, QMetaObjectPrivate::signal(smeta, sigIndex), o, i).constData());
++ // ...and stop looking for further objects with the same name.
++ // Note: the Designer will make sure each object name is unique in the above
++ // 'list' but other code may create two child objects with the same name. In
++ // this case one is chosen 'at random'.
++ break;
++ }
++ }
++ if (foundIt) {
++ // we found our slot, now skip all overloads
++ while (mo->method(i + 1).attributes() & QMetaMethod::Cloned)
++ ++i;
++ } else if (!(mo->method(i).attributes() & QMetaMethod::Cloned)) {
++ // check if the slot has the following signature: "on_..._...(..."
++ qsizetype iParen = slotSignature.indexOf('(');
++ qsizetype iLastUnderscore = slotSignature.lastIndexOf('_', iParen - 1);
++ if (iLastUnderscore > 3)
++ qCWarning(lcConnectSlotsByName,
++ "QMetaObject::connectSlotsByName: No matching signal for %s", slot);
++ }
++ }
++ }
++
++ /*!
++ \fn template<typename PointerToMemberFunction> QMetaObject::Connection QMetaObject::connect(
++ const QObject *sender, const QMetaMethod &signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type)
++
++ \threadsafe
++ \overload connect()
++
++ \since 6.10
++
++ 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 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.
++ Pass the returned handle to QObject::disconnect() to disconnect
++ the connection.
++
++ A slot can be connected to a given signal if the signal has at
++ least as many arguments as the slot. There must be an exact match
++ between the corresponding signal and slot arguments, implicit
++ conversions and type checking are not handled by this function.
++ Overloaded slots need to be explicitly be resolved with
++ help of \l qOverload.
++ \a signal needs to be the meta-method of a signal, otherwise an
++ invalid connection will be returned.
++
++ \sa QObject::connect(), QObject::disconnect()
++ */
++
++ /*!
++ \fn template<typename Functor> QMetaObject::Connection QMetaObject::connect(
++ const QObject *sender, const QMetaMethod &signal, const QObject *context, Functor functor, Qt::ConnectionType type)
++
++ \threadsafe
++ \overload connect()
++
++ \since 6.10
++
++ Creates a connection of a given \a type from \a signal in
++ \a sender object to \a functor to be placed in a specific event
++ loop of \a context.
++ Returns a handle to the connection that can be used to disconnect
++ it later.
++ This can be useful for connecting a signal retrieved from
++ meta-object introspection to a lambda capturing local variables.
++
++ \note Qt::UniqueConnections do not work for lambdas, non-member
++ functions and functors; they only apply to member functions.
++
++ The slot function can be any function or functor with with equal
++ or fewer arguments than the signal. There must be an exact match
++ between the corresponding signal and slot arguments, implicit
++ conversions and type checking are not handled by this function.
++ Overloaded functors need to be explicitly be resolved with
++ help of \l qOverload.
++ \a signal needs to be the meta-method of a signal, otherwise an
++ invalid connection will be returned.
++
++ The connection will automatically disconnect if the sender or
++ the context is destroyed.
++ However, you should take care that any objects used within
++ the functor are still alive when the signal is emitted.
++
++ \sa QObject::connect(), QObject::disconnect()
++ */
++ QMetaObject::Connection QMetaObject::connectImpl(const QObject *sender, const QMetaMethod &signal,
++ const QObject *receiver, void **slot,
++ QtPrivate::QSlotObjectBase *slotObjRaw, Qt::ConnectionType type)
++ {
++ QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw);
++ Q_ASSERT_X(slotObjRaw, "QMetaObject::connect", "Internal error, caller must not pass null slotObj");
++
++ const QMetaObject *senderMetaObject = sender->metaObject();
++ if (!signal.isValid() || signal.methodType() != QMetaMethod::Signal) {
++ connectWarning(sender, senderMetaObject, receiver, "invalid signal parameter");
++ return QMetaObject::Connection();
++ }
++
++ int signal_index;
++ {
++ int dummy;
++ QMetaObjectPrivate::memberIndexes(sender, signal, &signal_index, &dummy);
++ }
++
++ if (signal_index == -1) {
++ qCWarning(lcConnect, "QObject::connect: Can't find signal %s on instance of class %s",
++ signal.methodSignature().constData(), senderMetaObject->className());
++ return QMetaObject::Connection();
++ }
++
++ return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj.release(), type, nullptr, senderMetaObject);
++ }
++
++ /*!
++ \internal
++ A small RAII helper for QSlotObjectBase.
++ Calls ref on construction and destroyLastRef in its dtor.
++ Allows construction from a nullptr in which case it does nothing.
++ */
++ struct SlotObjectGuard {
++ SlotObjectGuard() = default;
++ // move would be fine, but we do not need it currently
++ Q_DISABLE_COPY_MOVE(SlotObjectGuard)
++ Q_NODISCARD_CTOR explicit SlotObjectGuard(QtPrivate::QSlotObjectBase *slotObject)
++ : m_slotObject(slotObject)
++ {
++ if (m_slotObject)
++ m_slotObject->ref();
++ }
++
++ QtPrivate::QSlotObjectBase const *operator->() const
++ { return m_slotObject.get(); }
++
++ QtPrivate::QSlotObjectBase *operator->()
++ { return m_slotObject.get(); }
++
++ ~SlotObjectGuard() = default;
++ private:
++ QtPrivate::SlotObjUniquePtr m_slotObject;
++ };
++
++ /*!
++ \internal
++
++ \a signal must be in the signal index range (see QObjectPrivate::signalIndex()).
++ */
++ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv)
++ {
++ const int *argumentTypes = c->argumentTypes.loadRelaxed();
++ if (!argumentTypes) {
++ QMetaMethod m = QMetaObjectPrivate::signal(sender->metaObject(), signal);
++ argumentTypes = queuedConnectionTypes(m);
++ if (!argumentTypes) // cannot queue arguments
++ argumentTypes = &DIRECT_CONNECTION_ONLY;
++ if (!c->argumentTypes.testAndSetOrdered(nullptr, argumentTypes)) {
++ if (argumentTypes != &DIRECT_CONNECTION_ONLY)
++ delete[] argumentTypes;
++ argumentTypes = c->argumentTypes.loadRelaxed();
++ }
++ }
++ if (argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate
++ return;
++ int nargs = 1; // include return type
++ while (argumentTypes[nargs - 1])
++ ++nargs;
++
++ QMutexLocker locker(signalSlotLock(c->receiver.loadRelaxed()));
++ QObject *receiver = c->receiver.loadRelaxed();
++ if (!receiver) {
++ // the connection has been disconnected before we got the lock
++ return;
++ }
++
++ SlotObjectGuard slotObjectGuard { c->isSlotObject ? c->slotObj : nullptr };
++ locker.unlock();
++
++ QVarLengthArray<const QtPrivate::QMetaTypeInterface *, 16> argTypes;
++ argTypes.reserve(nargs);
++ argTypes.emplace_back(nullptr); // return type
++ for (int n = 1; n < nargs; ++n) {
++ argTypes.emplace_back(QMetaType(argumentTypes[n - 1]).iface()); // convert type ids to QMetaTypeInterfaces
++ }
++
++ auto ev = c->isSlotObject ?
++ std::make_unique<QQueuedMetaCallEvent>(c->slotObj,
++ sender, signal, nargs, argTypes.data(), argv) :
++ std::make_unique<QQueuedMetaCallEvent>(c->method_offset, c->method_relative, c->callFunction,
++ sender, signal, nargs, argTypes.data(), argv);
++
++ if (c->isSingleShot && !QObjectPrivate::removeConnection(c)) {
++ return;
++ }
++
++ locker.relock();
++ if (!c->isSingleShot && !c->receiver.loadRelaxed()) {
++ // the connection has been disconnected while we were unlocked
++ locker.unlock();
++ return;
++ }
++
++ QCoreApplication::postEvent(receiver, ev.release());
++ }
++
++ template <bool callbacks_enabled>
++ void doActivate(QObject *sender, int signal_index, void **argv)
++ {
++ QObjectPrivate *sp = QObjectPrivate::get(sender);
++
++ if (sp->blockSig)
++ return;
++
++ Q_TRACE_SCOPE(QMetaObject_activate, sender, signal_index);
++
++ if (sp->isDeclarativeSignalConnected(signal_index)
++ && QAbstractDeclarativeData::signalEmitted) {
++ Q_TRACE_SCOPE(QMetaObject_activate_declarative_signal, sender, signal_index);
++ QAbstractDeclarativeData::signalEmitted(sp->declarativeData, sender,
++ signal_index, argv);
++ }
++
++ const QSignalSpyCallbackSet *signal_spy_set = callbacks_enabled ? qt_signal_spy_callback_set.loadAcquire() : nullptr;
++
++ void *empty_argv[] = { nullptr };
++ if (!argv)
++ argv = empty_argv;
++
++ bool senderDeleted = false;
++ {
++ QObjectPrivate::ConnectionDataPointer connections(sp->connections.loadAcquire());
++ if (!connections || !sp->maybeSignalConnected(signal_index)) {
++ // The possible declarative connection is done, and nothing else is connected
++ if (callbacks_enabled && signal_spy_set->signal_begin_callback != nullptr)
++ signal_spy_set->signal_begin_callback(sender, signal_index, argv);
++ if (callbacks_enabled && signal_spy_set->signal_end_callback != nullptr)
++ signal_spy_set->signal_end_callback(sender, signal_index);
++ return;
++ }
++
++ if (callbacks_enabled && signal_spy_set->signal_begin_callback != nullptr)
++ signal_spy_set->signal_begin_callback(sender, signal_index, argv);
++
++ // loadAcquire pairs with the storeRelease in resizeSignalVector(), ensuring
++ // that all writes to the new SignalVector's contents are visible here.
++ QObjectPrivate::SignalVector *signalVector = connections->signalVector.loadAcquire();
++
++ const QObjectPrivate::ConnectionList *list;
++ if (signal_index < signalVector->count())
++ list = &signalVector->at(signal_index);
++ else
++ list = &signalVector->at(-1);
++
++ Qt::HANDLE currentThreadId = QThread::currentThreadId();
++ bool inSenderThread = currentThreadId == QObjectPrivate::get(sender)->threadData.loadRelaxed()->threadId.loadRelaxed();
++
++ // We need to check against the highest connection id to ensure that signals added
++ // during the signal emission are not emitted in this emission.
++ uint highestConnectionId = connections->currentConnectionId.loadRelaxed();
++ do {
++ QObjectPrivate::Connection *c = list->first.loadAcquire();
++ if (!c)
++ continue;
++
++ do {
++ QObject * const receiver = c->receiver.loadRelaxed();
++ if (!receiver)
++ continue;
++
++ QThreadData *td = c->receiverThreadData.loadRelaxed();
++ if (!td)
++ continue;
++
++ bool receiverInSameThread;
++ if (inSenderThread) {
++ receiverInSameThread = currentThreadId == td->threadId.loadRelaxed();
++ } else {
++ // need to lock before reading the threadId, because moveToThread() could interfere
++ QMutexLocker lock(signalSlotLock(receiver));
++ receiverInSameThread = currentThreadId == td->threadId.loadRelaxed();
++ }
++
++
++ // determine if this connection should be sent immediately or
++ // put into the event queue
++ if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
++ || (c->connectionType == Qt::QueuedConnection)) {
++ queued_activate(sender, signal_index, c, argv);
++ continue;
++ #if QT_CONFIG(thread)
++ } else if (c->connectionType == Qt::BlockingQueuedConnection) {
++ if (receiverInSameThread) {
++ qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: "
++ "Sender is %s(%p), receiver is %s(%p)",
++ sender->metaObject()->className(), sender,
++ receiver->metaObject()->className(), receiver);
++ }
++
++ if (c->isSingleShot && !QObjectPrivate::removeConnection(c))
++ continue;
++
++ QLatch latch(1);
++ {
++ QMutexLocker locker(signalSlotLock(receiver));
++ if (!c->isSingleShot && !c->receiver.loadAcquire())
++ continue;
++ QMetaCallEvent *ev = c->isSlotObject ?
++ new QMetaCallEvent(c->slotObj, sender, signal_index, argv, &latch) :
++ new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction,
++ sender, signal_index, argv, &latch);
++ QCoreApplication::postEvent(receiver, ev);
++ }
++ latch.wait();
++ continue;
++ #endif
++ }
++
++ if (c->isSingleShot && !QObjectPrivate::removeConnection(c))
++ continue;
++
++ QObjectPrivate::Sender senderData(
++ receiverInSameThread ? receiver : nullptr, sender, signal_index,
++ receiverInSameThread ? QObjectPrivate::get(receiver)->connections.loadAcquire() : nullptr);
++
++ if (c->isSlotObject) {
++ SlotObjectGuard obj{c->slotObj};
++
++ {
++ Q_TRACE_SCOPE(QMetaObject_activate_slot_functor, c->slotObj);
++ obj->call(receiver, argv);
++ }
++ } else if (c->callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {
++ //we compare the vtable to make sure we are not in the destructor of the object.
++ const int method_relative = c->method_relative;
++ const auto callFunction = c->callFunction;
++ const int methodIndex = (Q_HAS_TRACEPOINTS || callbacks_enabled) ? c->method() : 0;
++ if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr)
++ signal_spy_set->slot_begin_callback(receiver, methodIndex, argv);
++
++ {
++ Q_TRACE_SCOPE(QMetaObject_activate_slot, receiver, methodIndex);
++ callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv);
++ }
++
++ if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr)
++ signal_spy_set->slot_end_callback(receiver, methodIndex);
++ } else {
++ const int method = c->method_relative + c->method_offset;
++
++ if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr) {
++ signal_spy_set->slot_begin_callback(receiver, method, argv);
++ }
++
++ {
++ Q_TRACE_SCOPE(QMetaObject_activate_slot, receiver, method);
++ QMetaObject::metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv);
++ }
++
++ if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr)
++ signal_spy_set->slot_end_callback(receiver, method);
++ }
++ } while ((c = c->nextConnectionList.loadAcquire()) != nullptr && c->id.loadAcquire() <= highestConnectionId);
++
++ } while (list != &signalVector->at(-1) &&
++ //start over for all signals;
++ ((list = &signalVector->at(-1)), true));
++
++ if (connections->currentConnectionId.loadRelaxed() == 0)
++ senderDeleted = true;
++ }
++
++ if (!senderDeleted) {
++ sp->connections.loadAcquire()->cleanOrphanedConnections(sender);
++
++ if (callbacks_enabled && signal_spy_set->signal_end_callback != nullptr)
++ signal_spy_set->signal_end_callback(sender, signal_index);
++ }
++ }
++
++ /*!
++ \internal
++ */
++ void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
++ void **argv)
++ {
++ int signal_index = local_signal_index + QMetaObjectPrivate::signalOffset(m);
++
++ if (Q_UNLIKELY(qt_signal_spy_callback_set.loadRelaxed()))
++ doActivate<true>(sender, signal_index, argv);
++ else
++ doActivate<false>(sender, signal_index, argv);
++ }
++
++ /*!
++ \internal
++ */
++ void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv)
++ {
++ int signal_index = signalOffset + local_signal_index;
++
++ if (Q_UNLIKELY(qt_signal_spy_callback_set.loadRelaxed()))
++ doActivate<true>(sender, signal_index, argv);
++ else
++ doActivate<false>(sender, signal_index, argv);
++ }
++
++ /*!
++ \internal
++ signal_index comes from indexOfMethod()
++ */
++ void QMetaObject::activate(QObject *sender, int signal_index, void **argv)
++ {
++ const QMetaObject *mo = sender->metaObject();
++ while (mo->methodOffset() > signal_index)
++ mo = mo->superClass();
++ activate(sender, mo, signal_index - mo->methodOffset(), argv);
++ }
++
++ /*!
++ \internal
++ Returns the signal index used in the internal connections->receivers vector.
++
++ It is different from QMetaObject::indexOfSignal(): indexOfSignal is the same as indexOfMethod
++ while QObjectPrivate::signalIndex is smaller because it doesn't give index to slots.
++
++ If \a meta is not \nullptr, it is set to the meta-object where the signal was found.
++ */
++ int QObjectPrivate::signalIndex(const char *signalName,
++ const QMetaObject **meta) const
++ {
++ Q_Q(const QObject);
++ const QMetaObject *base = q->metaObject();
++ Q_ASSERT(QMetaObjectPrivate::get(base)->revision >= 7);
++ QArgumentTypeArray types;
++ QByteArrayView name = QMetaObjectPrivate::decodeMethodSignature(signalName, types);
++ int relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, name, types);
++ if (relative_index < 0)
++ return relative_index;
++ relative_index = QMetaObjectPrivate::originalClone(base, relative_index);
++ if (meta)
++ *meta = base;
++ return relative_index + QMetaObjectPrivate::signalOffset(base);
++ }
++
++ /*****************************************************************************
++ Properties
++ *****************************************************************************/
++
++ /*!
++ \fn bool QObject::setProperty(const char *name, const QVariant &value)
++
++ Sets the value of the object's \a name property to \a value.
++
++ If the property is defined in the class using Q_PROPERTY then
++ true is returned on success and false otherwise. If the property
++ is not defined using Q_PROPERTY, and therefore not listed in the
++ meta-object, it is added as a dynamic property and false is returned.
++
++ Information about all available properties is provided through the
++ metaObject() and dynamicPropertyNames().
++
++ Dynamic properties can be queried again using property() and can be
++ removed by setting the property value to an invalid QVariant.
++ Changing the value of a dynamic property causes a QDynamicPropertyChangeEvent
++ to be sent to the object.
++
++ \b{Note:} Dynamic properties starting with "_q_" are reserved for internal
++ purposes.
++
++ \sa property(), metaObject(), dynamicPropertyNames(), QMetaProperty::write()
++ */
++
++ /*!
++ \fn bool QObject::setProperty(const char *name, QVariant &&value)
++ \since 6.6
++ \overload setProperty
++ */
++
++ bool QObject::doSetProperty(const char *name, const QVariant &value, QVariant *rvalue)
++ {
++ Q_D(QObject);
++ const QMetaObject *meta = metaObject();
++ if (!name || !meta)
++ return false;
++
++ int id = meta->indexOfProperty(name);
++ if (id < 0) {
++ d->ensureExtraData();
++
++ const qsizetype idx = d->extraData->propertyNames.indexOf(name);
++
++ if (!value.isValid()) {
++ if (idx == -1)
++ return false;
++ d->extraData->propertyNames.removeAt(idx);
++ d->extraData->propertyValues.removeAt(idx);
++ } else {
++ if (idx == -1) {
++ d->extraData->propertyNames.append(name);
++ q_choose_append(d->extraData->propertyValues, value, rvalue);
++ } else {
++ if (value.userType() == d->extraData->propertyValues.at(idx).userType()
++ && value == d->extraData->propertyValues.at(idx))
++ return false;
++ q_choose_assign(d->extraData->propertyValues[idx], value, rvalue);
++ }
++ }
++
++ QDynamicPropertyChangeEvent ev(name);
++ QCoreApplication::sendEvent(this, &ev);
++
++ return false;
++ }
++ QMetaProperty p = meta->property(id);
++ #ifndef QT_NO_DEBUG
++ if (!p.isWritable())
++ qWarning("%s::setProperty: Property \"%s\" invalid,"
++ " read-only or does not exist", metaObject()->className(), name);
++ #endif
++ return rvalue ? p.write(this, std::move(*rvalue)) : p.write(this, value);
++ }
++
++ /*!
++ Returns the value of the object's \a name property.
++
++ If no such property exists, the returned variant is invalid.
++
++ Information about all available properties is provided through the
++ metaObject() and dynamicPropertyNames().
++
++ \sa setProperty(), QVariant::isValid(), metaObject(), dynamicPropertyNames()
++ */
++ QVariant QObject::property(const char *name) const
++ {
++ Q_D(const QObject);
++ const QMetaObject *meta = metaObject();
++ if (!name || !meta)
++ return QVariant();
++
++ int id = meta->indexOfProperty(name);
++ if (id < 0) {
++ if (!d->extraData)
++ return QVariant();
++ const qsizetype i = d->extraData->propertyNames.indexOf(name);
++ return d->extraData->propertyValues.value(i);
++ }
++ QMetaProperty p = meta->property(id);
++ #ifndef QT_NO_DEBUG
++ if (!p.isReadable())
++ qWarning("%s::property: Property \"%s\" invalid or does not exist",
++ metaObject()->className(), name);
++ #endif
++ return p.read(this);
++ }
++
++ /*!
++ \since 4.2
++
++ Returns the names of all properties that were dynamically added to
++ the object using setProperty().
++ */
++ QList<QByteArray> QObject::dynamicPropertyNames() const
++ {
++ Q_D(const QObject);
++ if (d->extraData)
++ return d->extraData->propertyNames;
++ return QList<QByteArray>();
++ }
++
++ /*****************************************************************************
++ QObject debugging output routines.
++ *****************************************************************************/
++
++ std::string QObjectPrivate::flagsForDumping() const
++ {
++ return {};
++ }
++
++ static void dumpRecursive(int level, const QObject *object)
++ {
++ if (object) {
++ const int indent = level * 4;
++ qDebug("%*s%s::%ls %s", indent, "", object->metaObject()->className(),
++ qUtf16Printable(object->objectName()),
++ QObjectPrivate::get(object)->flagsForDumping().c_str());
++ for (auto child : object->children())
++ dumpRecursive(level + 1, child);
++ }
++ }
++
++
++ /*!
++ Dumps a tree of children to the debug output.
++
++ \note Before Qt 5.9, this function was not const.
++
++ \sa dumpObjectInfo()
++ */
++
++ void QObject::dumpObjectTree() const
++ {
++ dumpRecursive(0, this);
++ }
++
++ /*!
++ Dumps information about signal connections, etc. for this object
++ to the debug output.
++
++ \note Before Qt 5.9, this function was not const.
++
++ \sa dumpObjectTree()
++ */
++
++ void QObject::dumpObjectInfo() const
++ {
++ qDebug("OBJECT %s::%s", metaObject()->className(),
++ objectName().isEmpty() ? "unnamed" : objectName().toLocal8Bit().data());
++
++ Q_D(const QObject);
++ QMutexLocker locker(signalSlotLock(this));
++
++ // first, look for connections where this object is the sender
++ qDebug(" SIGNALS OUT");
++
++ QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
++ if (cd && cd->signalVectorCount() > 0) {
++ QObjectPrivate::SignalVector *signalVector = cd->signalVector.loadRelaxed();
++ for (int signal_index = 0; signal_index < signalVector->count(); ++signal_index) {
++ const QObjectPrivate::Connection *c = signalVector->at(signal_index).first.loadRelaxed();
++ if (!c)
++ continue;
++ const QMetaMethod signal = QMetaObjectPrivate::signal(metaObject(), signal_index);
++ qDebug(" signal: %s", signal.methodSignature().constData());
++
++ // receivers
++ while (c) {
++ if (!c->receiver.loadRelaxed()) {
++ qDebug(" <Disconnected receiver>");
++ c = c->nextConnectionList.loadRelaxed();
++ continue;
++ }
++ if (c->isSlotObject) {
++ qDebug(" <functor or function pointer>");
++ c = c->nextConnectionList.loadRelaxed();
++ continue;
++ }
++ const QMetaObject *receiverMetaObject = c->receiver.loadRelaxed()->metaObject();
++ const QMetaMethod method = receiverMetaObject->method(c->method());
++ qDebug(" --> %s::%s %s",
++ receiverMetaObject->className(),
++ c->receiver.loadRelaxed()->objectName().isEmpty() ? "unnamed" : qPrintable(c->receiver.loadRelaxed()->objectName()),
++ method.methodSignature().constData());
++ c = c->nextConnectionList.loadRelaxed();
++ }
++ }
++ } else {
++ qDebug( " <None>" );
++ }
++
++ // now look for connections where this object is the receiver
++ qDebug(" SIGNALS IN");
++
++ if (cd && cd->senders) {
++ for (QObjectPrivate::Connection *s = cd->senders; s; s = s->next) {
++ QByteArray slotName = QByteArrayLiteral("<unknown>");
++ if (!s->isSlotObject) {
++ const QMetaMethod slot = metaObject()->method(s->method());
++ slotName = slot.methodSignature();
++ }
++ qDebug(" <-- %s::%s %s",
++ s->sender->metaObject()->className(),
++ s->sender->objectName().isEmpty() ? "unnamed" : qPrintable(s->sender->objectName()),
++ slotName.constData());
++ }
++ } else {
++ qDebug(" <None>");
++ }
++ }
++
++
++ #ifndef QT_NO_DEBUG_STREAM
++ void QObjectPrivate::writeToDebugStream(QDebug &dbg) const
++ {
++ Q_Q(const QObject);
++ dbg.nospace() << q->metaObject()->className() << '(' << (const void *)q;
++ if (!q->objectName().isEmpty())
++ dbg << ", name = " << q->objectName();
++ dbg << ')';
++ }
++
++ QDebug operator<<(QDebug dbg, const QObject *o)
++ {
++ QDebugStateSaver saver(dbg);
++ if (!o)
++ return dbg << "QObject(0x0)";
++
++ const QObjectPrivate *d = QObjectPrivate::get(o);
++ d->writeToDebugStream(dbg);
++ return dbg;
++ }
++ #endif
++
++ /*!
++ \macro Q_CLASSINFO(Name, Value)
++ \relates QObject
++
++ This macro associates extra information to the class, which is available
++ using QObject::metaObject(). The extra information takes the form of a
++ \a Name string and a \a Value literal string.
++
++ Example:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 35
++
++ Qt makes use of the macro in \l{Qt D-Bus} and \l{Qt Qml} modules.
++ For instance, when defining \l{QML Object Types} in C++, you can
++ designate a property as the \e default one:
++
++ \snippet code/doc_src_properties.cpp 7
++
++ \sa QMetaObject::classInfo()
++ \sa {Using Qt D-Bus Adaptors}
++ \sa {Defining QML Types from C++}
++ */
++
++ /*!
++ \macro Q_INTERFACES(...)
++ \relates QObject
++
++ This macro tells Qt which interfaces the class implements. This
++ is used when implementing plugins.
++
++ \sa Q_DECLARE_INTERFACE(), Q_PLUGIN_METADATA(), {How to Create Qt Plugins}
++ */
++
++ /*!
++ \macro Q_PROPERTY(...)
++ \relates QObject
++
++ This macro is used for declaring properties in classes that
++ inherit QObject. Properties behave like class data members, but
++ they have additional features accessible through the \l
++ {Meta-Object System}.
++
++ \snippet code/doc_src_properties.cpp 0
++
++ The property name and type and the \c READ function are required.
++ The type can be any type supported by QVariant, or it can be a
++ user-defined type. The other items are optional, but a \c WRITE
++ function is common. The attributes default to true except \c USER,
++ which defaults to false.
++
++ For example:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 37
++
++ For more details about how to use this macro, and a more detailed
++ example of its use, see the discussion on \l {Qt's Property System}.
++
++ \sa {Qt's Property System}
++ */
++
++ /*!
++ \macro Q_ENUMS(...)
++ \relates QObject
++ \deprecated [5.5]
++
++ In new code, you should prefer the use of the Q_ENUM() macro, which makes the
++ type available also to the meta type system.
++ For instance, QMetaEnum::fromType() will not work with types declared with Q_ENUMS().
++
++ This macro registers one or several enum types to the meta-object
++ system.
++
++ If you want to register an enum that is declared in another class,
++ the enum must be fully qualified with the name of the class
++ defining it. In addition, the class \e defining the enum has to
++ inherit QObject as well as declare the enum using Q_ENUMS().
++
++ \sa {Qt's Property System}
++ */
++
++ /*!
++ \macro Q_FLAGS(...)
++ \relates QObject
++ \deprecated [5.5]
++
++ This macro registers one or several \l{QFlags}{flags types} with the
++ meta-object system. It is typically used in a class definition to declare
++ that values of a given enum can be used as flags and combined using the
++ bitwise OR operator.
++
++ \note This macro takes care of registering individual flag values
++ with the meta-object system, so it is unnecessary to use Q_ENUMS()
++ in addition to this macro.
++
++ In new code, you should prefer the use of the Q_FLAG() macro, which makes the
++ type available also to the meta type system.
++
++ \sa {Qt's Property System}
++ */
++
++ /*!
++ \macro Q_ENUM(...)
++ \relates QObject
++ \since 5.5
++
++ This macro registers an enum type with the meta-object system.
++ It must be placed after the enum declaration in a class that has the Q_OBJECT,
++ Q_GADGET or Q_GADGET_EXPORT macro. For namespaces use \l Q_ENUM_NS() instead.
++
++ For example:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 38
++
++ Enumerations that are declared with Q_ENUM have their QMetaEnum registered in the
++ enclosing QMetaObject. You can also use QMetaEnum::fromType() to get the QMetaEnum.
++
++ Registered enumerations are automatically registered also to the Qt meta
++ type system, making them known to QMetaType without the need to use
++ Q_DECLARE_METATYPE(). This will enable useful features; for example, if used
++ in a QVariant, you can convert them to strings. Likewise, passing them to
++ QDebug will print out their names.
++
++ \sa {Qt's Property System}
++ */
++
++
++ /*!
++ \macro Q_FLAG(...)
++ \relates QObject
++ \since 5.5
++
++ This macro registers a single \l{QFlags}{flags type} with the
++ meta-object system. It is typically used in a class definition to declare
++ that values of a given enum can be used as flags and combined using the
++ bitwise OR operator. For namespaces use \l Q_FLAG_NS() instead.
++
++ The macro must be placed after the enum declaration. The declaration of
++ the flags type is done using the \l Q_DECLARE_FLAGS() macro.
++
++ For example, in QItemSelectionModel, the
++ \l{QItemSelectionModel::SelectionFlags}{SelectionFlags} flag is
++ declared in the following way:
++
++ \quotefromfile itemmodels/qitemselectionmodel.h
++
++ \skipto class Q_CORE_EXPORT QItemSelectionModel
++ \printuntil Q_OBJECT
++
++ \dots
++
++ \skipto public:
++ \printuntil Q_FLAG(SelectionFlags)
++
++ \skipuntil Q_DISABLE_COPY
++ \printto Q_DECLARE_OPERATORS_FOR_FLAGS
++
++ \note The Q_FLAG macro takes care of registering individual flag values
++ with the meta-object system, so it is unnecessary to use Q_ENUM()
++ in addition to this macro.
++
++ \sa {Qt's Property System}
++ */
++
++ /*!
++ \macro Q_ENUM_NS(...)
++ \relates QObject
++ \since 5.8
++
++ This macro registers an enum type with the meta-object system.
++ It must be placed after the enum declaration in a namespace that
++ has the Q_NAMESPACE macro. It is the same as \l Q_ENUM but in a
++ namespace.
++
++ Enumerations that are declared with Q_ENUM_NS have their QMetaEnum
++ registered in the enclosing QMetaObject. You can also use
++ QMetaEnum::fromType() to get the QMetaEnum.
++
++ Registered enumerations are automatically registered also to the Qt meta
++ type system, making them known to QMetaType without the need to use
++ Q_DECLARE_METATYPE(). This will enable useful features; for example, if
++ used in a QVariant, you can convert them to strings. Likewise, passing them
++ to QDebug will print out their names.
++
++ \sa {Qt's Property System}
++ */
++
++
++ /*!
++ \macro Q_FLAG_NS(...)
++ \relates QObject
++ \since 5.8
++
++ This macro registers a single \l{QFlags}{flags type} with the
++ meta-object system. It is used in a namespace that has the
++ Q_NAMESPACE macro, to declare that values of a given enum can be
++ used as flags and combined using the bitwise OR operator.
++ It is the same as \l Q_FLAG but in a namespace.
++
++ The macro must be placed after the enum declaration.
++
++ \note The Q_FLAG_NS macro takes care of registering individual flag
++ values with the meta-object system, so it is unnecessary to use
++ Q_ENUM_NS() in addition to this macro.
++
++ \sa {Qt's Property System}
++ */
++
++ /*!
++ \macro Q_OBJECT
++ \relates QObject
++
++ The Q_OBJECT macro is used to enable meta-object features, such as dynamic
++ properties, signals, and slots.
++
++ You can add the Q_OBJECT macro to any section of a class definition that
++ declares its own signals and slots or that uses other services provided by
++ Qt's meta-object system.
++
++ //! [qobject-macros-private-access-specifier]
++ \note This macro expansion ends with a \c private: access specifier. If you
++ declare members immediately after this macro, those members will also be
++ private. To add public (or protected) members right after the macro, use a
++ \c {public:} (or \c {protected:}) access specifier.
++ //! [qobject-macros-private-access-specifier]
++
++ Example:
++
++ \snippet signalsandslots/signalsandslots.h 1
++ \codeline
++ \snippet signalsandslots/signalsandslots.h 2
++ \snippet signalsandslots/signalsandslots.h 3
++
++ \note This macro requires the class to be a subclass of QObject. Use
++ Q_GADGET or Q_GADGET_EXPORT instead of Q_OBJECT to enable the meta object
++ system's support for enums in a class that is not a QObject subclass.
++
++ \sa {Meta-Object System}, {Signals and Slots}, {Qt's Property System}
++ */
++
++ /*!
++ \macro Q_GADGET
++ \relates QObject
++
++ The Q_GADGET macro is a lighter version of the Q_OBJECT macro for classes
++ that do not inherit from QObject but still want to use some of the
++ reflection capabilities offered by QMetaObject.
++
++ \include qobject.cpp qobject-macros-private-access-specifier
++
++ Q_GADGETs can have Q_ENUM, Q_PROPERTY and Q_INVOKABLE, but they cannot have
++ signals or slots.
++
++ Q_GADGET makes a class member, \c{staticMetaObject}, available.
++ \c{staticMetaObject} is of type QMetaObject and provides access to the
++ enums declared with Q_ENUM.
++
++ \sa Q_GADGET_EXPORT
++ */
++
++ /*!
++ \macro Q_GADGET_EXPORT(EXPORT_MACRO)
++ \relates QObject
++ \since 6.3
++
++ The Q_GADGET_EXPORT macro works exactly like the Q_GADGET macro.
++ However, the \c{staticMetaObject} variable that is made available (see
++ Q_GADGET) is declared with the supplied \a EXPORT_MACRO qualifier. This is
++ useful if the object needs to be exported from a dynamic library, but the
++ enclosing class as a whole should not be (e.g. because it consists of mostly
++ inline functions).
++
++ \include qobject.cpp qobject-macros-private-access-specifier
++
++ For example:
++
++ \code
++ class Point {
++ Q_GADGET_EXPORT(EXPORT_MACRO)
++ Q_PROPERTY(int x MEMBER x)
++ Q_PROPERTY(int y MEMBER y)
++ ~~~
++ \endcode
++
++ \sa Q_GADGET, {Creating Shared Libraries}
++ */
++
++ /*!
++ \macro Q_NAMESPACE
++ \relates QObject
++ \since 5.8
++
++ The Q_NAMESPACE macro can be used to add QMetaObject capabilities
++ to a namespace.
++
++ Q_NAMESPACEs can have Q_CLASSINFO, Q_ENUM_NS, Q_FLAG_NS, but they
++ cannot have Q_ENUM, Q_FLAG, Q_PROPERTY, Q_INVOKABLE, signals nor slots.
++
++ Q_NAMESPACE makes an external variable, \c{staticMetaObject}, available.
++ \c{staticMetaObject} is of type QMetaObject and provides access to the
++ enums declared with Q_ENUM_NS/Q_FLAG_NS.
++
++ For example:
++
++ \code
++ namespace test {
++ Q_NAMESPACE
++ ...
++ \endcode
++
++ \sa Q_NAMESPACE_EXPORT
++ */
++
++ /*!
++ \macro Q_NAMESPACE_EXPORT(EXPORT_MACRO)
++ \relates QObject
++ \since 5.14
++
++ The Q_NAMESPACE_EXPORT macro can be used to add QMetaObject capabilities
++ to a namespace.
++
++ It works exactly like the Q_NAMESPACE macro. However, the external
++ \c{staticMetaObject} variable that gets defined in the namespace
++ is declared with the supplied \a EXPORT_MACRO qualifier. This is
++ useful if the object needs to be exported from a dynamic library.
++
++ For example:
++
++ \code
++ namespace test {
++ Q_NAMESPACE_EXPORT(EXPORT_MACRO)
++ ...
++ \endcode
++
++ \sa Q_NAMESPACE, {Creating Shared Libraries}
++ */
++
++ /*!
++ \macro Q_MOC_INCLUDE
++ \relates QObject
++ \since 6.0
++
++ The Q_MOC_INCLUDE macro can be used within or outside a class, and tell the
++ \l{moc}{Meta Object Compiler} to add an include.
++
++ \code
++ // Put this in your code and the generated code will include this header.
++ Q_MOC_INCLUDE("myheader.h")
++ \endcode
++
++ This is useful if the types you use as properties or signal/slots arguments
++ are forward declared.
++ */
++
++ /*!
++ \macro Q_SIGNALS
++ \relates QObject
++
++ Use this macro to replace the \c signals keyword in class
++ declarations, when you want to use Qt Signals and Slots with a
++ \l{3rd Party Signals and Slots} {3rd party signal/slot mechanism}.
++
++ The macro is normally used when \c no_keywords is specified with
++ the \c CONFIG variable in the \c .pro file, but it can be used
++ even when \c no_keywords is \e not specified.
++ */
++
++ /*!
++ \macro Q_SIGNAL
++ \relates QObject
++
++ This is an additional macro that allows you to mark a single
++ function as a signal. It can be quite useful, especially when you
++ use a 3rd-party source code parser which doesn't understand a \c
++ signals or \c Q_SIGNALS groups.
++
++ Use this macro to replace the \c signals keyword in class
++ declarations, when you want to use Qt Signals and Slots with a
++ \l{3rd Party Signals and Slots} {3rd party signal/slot mechanism}.
++
++ The macro is normally used when \c no_keywords is specified with
++ the \c CONFIG variable in the \c .pro file, but it can be used
++ even when \c no_keywords is \e not specified.
++ */
++
++ /*!
++ \macro Q_SLOTS
++ \relates QObject
++
++ Use this macro to replace the \c slots keyword in class
++ declarations, when you want to use Qt Signals and Slots with a
++ \l{3rd Party Signals and Slots} {3rd party signal/slot mechanism}.
++
++ The macro is normally used when \c no_keywords is specified with
++ the \c CONFIG variable in the \c .pro file, but it can be used
++ even when \c no_keywords is \e not specified.
++ */
++
++ /*!
++ \macro Q_SLOT
++ \relates QObject
++
++ This is an additional macro that allows you to mark a single
++ function as a slot. It can be quite useful, especially when you
++ use a 3rd-party source code parser which doesn't understand a \c
++ slots or \c Q_SLOTS groups.
++
++ Use this macro to replace the \c slots keyword in class
++ declarations, when you want to use Qt Signals and Slots with a
++ \l{3rd Party Signals and Slots} {3rd party signal/slot mechanism}.
++
++ The macro is normally used when \c no_keywords is specified with
++ the \c CONFIG variable in the \c .pro file, but it can be used
++ even when \c no_keywords is \e not specified.
++ */
++
++ /*!
++ \macro Q_EMIT
++ \relates QObject
++
++ Use this macro to replace the \c emit keyword for emitting
++ signals, when you want to use Qt Signals and Slots with a
++ \l{3rd Party Signals and Slots} {3rd party signal/slot mechanism}.
++
++ The macro is normally used when \c no_keywords is specified with
++ the \c CONFIG variable in the \c .pro file, but it can be used
++ even when \c no_keywords is \e not specified.
++ */
++
++ /*!
++ \macro Q_INVOKABLE
++ \relates QObject
++
++ Apply this macro to declarations of member functions to allow them to
++ be invoked via the meta-object system. The macro is written before
++ the return type, as shown in the following example:
++
++ \snippet qmetaobject-invokable/window.h Window class with invokable method
++
++ The \c invokableMethod() function is marked up using Q_INVOKABLE, causing
++ it to be registered with the meta-object system and enabling it to be
++ invoked using QMetaObject::invokeMethod().
++ Since \c normalMethod() function is not registered in this way, it cannot
++ be invoked using QMetaObject::invokeMethod().
++
++ If an invokable member function returns a pointer to a QObject or a
++ subclass of QObject and it is invoked from QML, special ownership rules
++ apply. See \l{qtqml-cppintegration-data.html}{Data Type Conversion Between QML and C++}
++ for more information.
++ */
++
++ /*!
++ \macro Q_REVISION
++ \relates QObject
++
++ Apply this macro to declarations of member functions to tag them with a
++ revision number in the meta-object system. The macro is written before
++ the return type, as shown in the following example:
++
++ \snippet qmetaobject-revision/window.h Window class with revision
++
++ This is useful when using the meta-object system to dynamically expose
++ objects to another API, as you can match the version expected by multiple
++ versions of the other API. Consider the following simplified example:
++
++ \snippet qmetaobject-revision/main.cpp Window class using revision
++
++ Using the same Window class as the previous example, the newProperty and
++ newMethod would only be exposed in this code when the expected version is
++ \c{2.1} or greater.
++
++ Since all methods are considered to be in revision \c{0} if untagged, a tag
++ of \c{Q_REVISION(0)} or \c{Q_REVISION(0, 0)} is invalid and ignored.
++
++ You can pass one or two integer parameters to \c{Q_REVISION}. If you pass
++ one parameter, it denotes the minor version only. This means that the major
++ version is unspecified. If you pass two, the first parameter is the major
++ version and the second parameter is the minor version.
++
++ This tag is not used by the meta-object system itself. Currently this is only
++ used by the QtQml module.
++
++ For a more generic string tag, see \l QMetaMethod::tag()
++
++ \sa QMetaMethod::revision()
++ */
++
++ /*!
++ \macro Q_SET_OBJECT_NAME(Object)
++ \relates QObject
++ \since 5.0
++
++ This macro assigns \a Object the objectName "Object".
++
++ It doesn't matter whether \a Object is a pointer or not, the
++ macro figures that out by itself.
++
++ \sa QObject::objectName()
++ */
++
++ /*!
++ \macro QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
++ \relates QObject
++ \since 5.8
++
++ Defining this macro will disable narrowing and floating-point-to-integral
++ conversions between the arguments carried by a signal and the arguments
++ accepted by a slot, when the signal and the slot are connected using the
++ PMF-based syntax.
++
++ \sa QObject::connect
++ */
++
++ /*!
++ \macro QT_NO_CONTEXTLESS_CONNECT
++ \relates QObject
++ \since 6.7
++
++ Defining this macro will disable the overload of QObject::connect() that
++ connects a signal to a functor, without also specifying a QObject
++ as a receiver/context object (that is, the 3-arguments overload
++ of QObject::connect()).
++
++ Using the context-less overload is error prone, because it is easy
++ to connect to functors that depend on some local state of the
++ receiving end. If such local state gets destroyed, the connection
++ does not get automatically disconnected.
++
++ Moreover, such connections are always direct connections, which may
++ cause issues in multithreaded scenarios (for instance, if the
++ signal is emitted from another thread).
++
++ \sa QObject::connect, Qt::ConnectionType
++ */
++
++ /*!
++ \since 6.12
++ \macro QT_NO_DISCONNECT_CONST_CONNECTION
++ \relates QObject
++
++ Disables the \c{const &} overload of
++ \l{QObject::disconnect(QMetaObject::Connection&)}{QObject::disconnect()} to
++ force callers to pass non-const objects.
++
++ Passing non-const objects is preferred, because they can be reset
++ immediately, releasing resources sooner. Different versions of Qt handle
++ the const overload differently. In older Qt versions, calling the const
++ overload with a Connection originally declared \c{const} may invoke
++ undefined behavior. Current Qt versions are safe in this regard, but future
++ Qt versions may remove the const overload, or have it delay releasing
++ resources until the Connection object is reassigned or destroyed.
++
++ Passing non-const objects avoids all of the issues above.
++
++ Code that compiles with this macro set also compiles (and doesn't invoke
++ undefined behavior) without the macro enabled, and in all Qt versions since
++ 5.0.
++ */
++
++ /*!
++ \typedef QObjectList
++ \relates QObject
++
++ Synonym for QList<QObject *>.
++ */
++
++ /*!
++ \fn template<typename PointerToMemberFunction> QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type)
++ \overload connect()
++ \threadsafe
++
++ 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 a handle to the connection that can be used to disconnect
++ it later.
++
++ The signal must be a function declared as a signal in the header.
++ The slot function can be any member function that can be connected
++ to the signal.
++ A slot can be connected to a given signal if the signal has at
++ least as many arguments as the slot, and there is an implicit
++ conversion between the types of the corresponding arguments in the
++ signal and the slot.
++
++ Example:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 44
++
++ This example ensures that the label always displays the current
++ line edit text.
++
++ A signal can be connected to many slots and signals. Many signals
++ can be connected to one slot.
++
++ If a signal is connected to several slots, the slots are activated
++ in the same order as the order the connection was made, when the
++ signal is emitted
++
++ The function returns an 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 \a signal (if it was not declared as a signal)
++ 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
++ all of these connections with a single disconnect() call.
++ If you pass the Qt::UniqueConnection \a type, the connection will only
++ be made if it is not a duplicate. If there is already a duplicate
++ (exact same signal to the exact same slot on the same objects),
++ the connection will fail and connect will return an invalid QMetaObject::Connection.
++
++ The optional \a type parameter describes the type of connection
++ to establish. In particular, it determines whether a particular
++ signal is delivered to a slot immediately or queued for delivery
++ at a later time. If the signal is queued, the parameters must be
++ of types that are known to Qt's meta-object system, because Qt
++ needs to copy the arguments to store them in an event behind the
++ scenes. If you try to use a queued connection and get the error
++ message
++
++ \snippet code/src_corelib_kernel_qobject.cpp 25
++
++ make sure to declare the argument type with Q_DECLARE_METATYPE
++
++ Overloaded functions can be resolved with help of \l qOverload.
++
++ \sa {Differences between String-Based and Functor-Based Connections}
++ */
++
++ /*!
++ \fn template<typename PointerToMemberFunction, typename Functor> QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
++
++ \threadsafe
++ \overload connect()
++
++ Creates a connection from \a signal in
++ \a sender object to \a functor, and returns a handle to the connection
++
++ The signal must be a function declared as a signal in the header.
++ The slot function can be any function or functor that can be connected
++ to the signal.
++ A slot function can be connected to a given signal if the signal has at
++ least as many arguments as the slot function. There must exist implicit
++ conversion between the types of the corresponding arguments in the
++ signal and the slot.
++
++ Example:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 45
++
++ Lambda expressions can also be used:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 46
++
++ The connection will automatically disconnect if the sender is destroyed.
++ However, you should take care that any objects used within the functor
++ are still alive when the signal is emitted.
++
++ For this reason, it is recommended to use the overload of connect()
++ that also takes a QObject as a receiver/context. It is possible
++ to disable the usage of the context-less overload by defining the
++ \c{QT_NO_CONTEXTLESS_CONNECT} macro.
++
++ Overloaded functions can be resolved with help of \l qOverload.
++
++ */
++
++ /*!
++ \fn template<typename PointerToMemberFunction, typename Functor> QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type)
++
++ \threadsafe
++ \overload connect()
++
++ \since 5.2
++
++ Creates a connection of a given \a type from \a signal in
++ \a sender object to \a functor to be placed in a specific event
++ loop of \a context, and returns a handle to the connection.
++
++ \note Qt::UniqueConnections do not work for lambdas, non-member functions
++ and functors; they only apply to connecting to member functions.
++
++ The signal must be a function declared as a signal in the header.
++ The slot function can be any function or functor that can be connected
++ to the signal.
++ A slot function can be connected to a given signal if the signal has at
++ least as many arguments as the slot function. There must exist implicit
++ conversion between the types of the corresponding arguments in the
++ signal and the slot.
++
++ Example:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 50_someFunction
++ \snippet code/src_corelib_kernel_qobject.cpp 50
++
++ Lambda expressions can also be used:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 51
++
++ The connection will automatically disconnect if the sender or the context
++ is destroyed.
++ However, you should take care that any objects used within the functor
++ are still alive when the signal is emitted.
++
++ Overloaded functions can be resolved with help of \l qOverload.
++ */
++
++ /*!
++ \internal
++
++ Implementation of the template version of connect
++
++ \a sender is the sender object
++ \a signal is a pointer to a pointer to a member signal of the sender
++ \a receiver is the receiver object, may not be \nullptr, will be equal to sender when
++ connecting to a static function or a functor
++ \a slot a pointer only used when using Qt::UniqueConnection
++ \a type the Qt::ConnectionType passed as argument to connect
++ \a types an array of integer with the metatype id of the parameter of the signal
++ to be used with queued connection
++ must stay valid at least for the whole time of the connection, this function
++ do not take ownership. typically static data.
++ If \nullptr, then the types will be computed when the signal is emit in a queued
++ connection from the types from the signature.
++ \a senderMetaObject is the metaobject used to lookup the signal, the signal must be in
++ this metaobject
++ */
++ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signal,
++ const QObject *receiver, void **slot,
++ QtPrivate::QSlotObjectBase *slotObjRaw, Qt::ConnectionType type,
++ const int *types, const QMetaObject *senderMetaObject)
++ {
++ QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw);
++ Q_ASSERT_X(slotObjRaw, "QObject::connect", "Internal error, caller must not pass null slotObj");
++ if (!signal) {
++ connectWarning(sender, senderMetaObject, receiver, "invalid nullptr parameter");
++ return QMetaObject::Connection();
++ }
++
++ int signal_index = -1;
++ void *args[] = { &signal_index, signal };
++ for (; senderMetaObject && signal_index < 0; senderMetaObject = senderMetaObject->superClass()) {
++ senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args);
++ if (signal_index >= 0 && signal_index < QMetaObjectPrivate::get(senderMetaObject)->signalCount)
++ break;
++ }
++ if (!senderMetaObject) {
++ connectWarning(sender, senderMetaObject, receiver, "signal not found");
++ return QMetaObject::Connection(nullptr);
++ }
++ signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject);
++ return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj.release(), type, types, senderMetaObject);
++ }
++
++ /*!
++ \internal
++
++ Internal version of connect used by the template version of QObject::connect (called via connectImpl) and
++ also used by the QObjectPrivate::connect version used by QML. The signal_index is expected to be relative
++ to the number of signals.
++ */
++ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int signal_index,
++ const QObject *receiver, void **slot,
++ QtPrivate::QSlotObjectBase *slotObjRaw, int type,
++ const int *types, const QMetaObject *senderMetaObject)
++ {
++ QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw);
++ Q_ASSERT(senderMetaObject);
++ Q_ASSERT(slotObj);
++
++ if (!sender || !receiver) {
++ connectWarning(sender, senderMetaObject, receiver, "invalid nullptr parameter");
++ return QMetaObject::Connection();
++ }
++
++ if (type & Qt::UniqueConnection && !slot) {
++ connectWarning(sender, senderMetaObject, receiver, "unique connections require a pointer to member function of a QObject subclass");
++ return QMetaObject::Connection();
++ }
++
++ QObject *s = const_cast<QObject *>(sender);
++ QObject *r = const_cast<QObject *>(receiver);
++
++ QOrderedMutexLocker locker(signalSlotLock(sender),
++ signalSlotLock(receiver));
++
++ if (type & Qt::UniqueConnection) {
++ QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.loadRelaxed();
++ if (connections && connections->signalVectorCount() > signal_index) {
++ const QObjectPrivate::Connection *c2 = connections->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
++
++ while (c2) {
++ if (c2->receiver.loadRelaxed() == receiver && c2->isSlotObject && c2->slotObj->compare(slot))
++ return QMetaObject::Connection();
++ c2 = c2->nextConnectionList.loadRelaxed();
++ }
++ }
++ }
++ type &= ~Qt::UniqueConnection;
++
++ const bool isSingleShot = type & Qt::SingleShotConnection;
++ type &= ~Qt::SingleShotConnection;
++
++ Q_ASSERT(type >= 0);
++ Q_ASSERT(type <= 3);
++
++ std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection};
++ c->sender = s;
++ c->signal_index = signal_index;
++ QThreadData *td = r->d_func()->threadData.loadAcquire();
++ td->ref();
++ c->receiverThreadData.storeRelaxed(td);
++ c->receiver.storeRelaxed(r);
++ c->connectionType = type;
++ c->isSlotObject = true;
++ c->slotObj = slotObj.release();
++ if (types) {
++ c->argumentTypes.storeRelaxed(types);
++ c->ownArgumentTypes = false;
++ }
++ c->isSingleShot = isSingleShot;
++
++ QObjectPrivate::get(s)->addConnection(signal_index, c.get());
++ QMetaObject::Connection ret(c.release());
++ locker.unlock();
++
++ QMetaMethod method = QMetaObjectPrivate::signal(senderMetaObject, signal_index);
++ Q_ASSERT(method.isValid());
++ s->connectNotify(method);
++
++ return ret;
++ }
++
++ #ifndef QT_NO_DISCONNECT_CONST_CONNECTION
++ /*!
++ Disconnects \a connection and resets it to
++ \l{QMetaObject::Connection::operator bool()}{invalid}.
++
++ If \a connection is invalid or has already been disconnected, do nothing
++ and return false.
++
++ \note Future versions of Qt may only accept non-const objects here.
++
++ \sa QT_NO_DISCONNECT_CONST_CONNECTION
++ \sa connect()
++ */
++ bool QObject::disconnect(const QMetaObject::Connection &connection)
++ {
++ // keep in sync with non-const overload
++ QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(connection.d_ptr);
++ if (!c)
++ return false;
++ const bool disconnected = QObjectPrivate::removeConnection(c);
++ connection.d_ptr = nullptr;
++ c->deref(); // has been removed from the QMetaObject::Connection object
++ return disconnected;
++ }
++ #endif // QT_NO_DISCONNECT_CONST_CONNECTION
++
++ /*!
++ \since 6.12
++ \fn bool QObject::disconnect(QMetaObject::Connection &connection)
++ \fn bool QObject::disconnect(QMetaObject::Connection &&connection)
++
++ Disconnect a connection.
++
++ If \a connection is
++ \l{QMetaObject::Connection::operator bool()}{invalid}
++ or has already been disconnected, do nothing and return false.
++
++ \note In Qt versions prior to 6.12, this function took only by \c{const-&}.
++
++ \sa connect()
++ */
++ bool QObject::disconnect(QMetaObject::Connection &connection)
++ {
++ // keep in sync with the overload above
++ QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(connection.d_ptr);
++ if (!c)
++ return false;
++ const bool disconnected = QObjectPrivate::removeConnection(c);
++ connection.d_ptr = nullptr;
++ c->deref(); // has been removed from the QMetaObject::Connection object
++ return disconnected;
++ }
++
++ /*! \fn template<typename PointerToMemberFunction> bool QObject::disconnect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method)
++ \overload disconnect()
++ \threadsafe
++
++ Disconnects \a signal in object \a sender from \a method in object
++ \a receiver. Returns \c true if the connection is successfully broken;
++ otherwise returns \c false.
++
++ A signal-slot connection is removed when either of the objects
++ involved are destroyed.
++
++ disconnect() is typically used in three ways, as the following
++ examples demonstrate.
++ \list 1
++ \li Disconnect everything connected to an object's signals:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 26
++
++ \li Disconnect everything connected to a specific signal:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 47
++
++ \li Disconnect a specific receiver:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 30
++
++ \li Disconnect a connection from one specific signal to a specific slot:
++
++ \snippet code/src_corelib_kernel_qobject.cpp 48
++
++
++ \endlist
++
++ \nullptr may be used as a wildcard, meaning "any signal", "any receiving
++ object", or "any slot in the receiving object", respectively.
++
++ The \a sender may never be \nullptr. (You cannot disconnect signals
++ from more than one object in a single call.)
++
++ If \a signal is \nullptr, it disconnects \a receiver and \a method from
++ any signal. If not, only the specified signal is disconnected.
++
++ If \a receiver is \nullptr, it disconnects anything connected to \a
++ signal. If not, only slots in the specified receiver are disconnected.
++ disconnect() with a non-null \a receiver also disconnects slot functions
++ that were connected with \a receiver as their context object.
++
++ If \a method is \nullptr, it disconnects anything that is connected to \a
++ receiver. If not, only slots named \a method will be disconnected,
++ and all other slots are left alone. The \a method must be \nullptr
++ if \a receiver is left out, so you cannot disconnect a
++ specifically-named slot on all objects.
++
++ \note It is not possible to use this overload to disconnect signals
++ connected to functors or lambda expressions. That is because it is not
++ possible to compare them. Instead, use the overload that takes a
++ QMetaObject::Connection.
++
++ \note Unless \a method is \nullptr, this function will also not break
++ connections that were made using the string-based version of connect(). To
++ break such connections, use the corresponding string-based overload of
++ disconnect().
++
++ \sa connect()
++ */
++
++ bool QObject::disconnectImpl(const QObject *sender, void **signal, const QObject *receiver, void **slot, const QMetaObject *senderMetaObject)
++ {
++ if (sender == nullptr || (receiver == nullptr && slot != nullptr)) {
++ qCWarning(lcConnect, "QObject::disconnect: Unexpected nullptr parameter");
++ return false;
++ }
++
++ int signal_index = -1;
++ if (signal) {
++ void *args[] = { &signal_index, signal };
++ for (; senderMetaObject && signal_index < 0; senderMetaObject = senderMetaObject->superClass()) {
++ senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args);
++ if (signal_index >= 0 && signal_index < QMetaObjectPrivate::get(senderMetaObject)->signalCount)
++ break;
++ }
++ if (!senderMetaObject) {
++ qCWarning(lcConnect, "QObject::disconnect: signal not found in %s", sender->metaObject()->className());
++ return false;
++ }
++ signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject);
++ }
++
++ return QMetaObjectPrivate::disconnect(sender, signal_index, senderMetaObject, receiver, -1, slot);
++ }
++
++ /*!
++ \internal
++ Used by QML to connect a signal by index to a slot implemented in JavaScript
++ (wrapped in a custom QSlotObjectBase subclass).
++
++ This version of connect assumes that sender and receiver are the same object.
++
++ The signal_index is an index relative to the number of methods.
++ */
++ QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signal_index, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type)
++ {
++ return QObjectPrivate::connect(sender, signal_index, sender, slotObj, type);
++ }
++
++ /*!
++ \internal
++ Used by QML to connect a signal by index to a slot implemented in JavaScript
++ (wrapped in a custom QSlotObjectBase subclass).
++
++ This is an overload that should be used when \a sender and \a receiver are
++ different objects.
++
++ The signal_index is an index relative to the number of methods.
++ */
++ QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signal_index,
++ const QObject *receiver,
++ QtPrivate::QSlotObjectBase *slotObjRaw,
++ Qt::ConnectionType type)
++ {
++ QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw);
++ Q_ASSERT_X(slotObjRaw, "QObjectPrivate::connect", "Internal error, caller must not pass null slotObj");
++ if (!sender) {
++ connectWarning(sender, nullptr, receiver, "invalid nullptr parameter");
++ return QMetaObject::Connection();
++ }
++ const QMetaObject *senderMetaObject = sender->metaObject();
++ signal_index = methodIndexToSignalIndex(&senderMetaObject, signal_index);
++
++ return connectImpl(sender, signal_index, receiver, /*slot*/ nullptr, slotObj.release(),
++ type, /*types*/ nullptr, senderMetaObject);
++ }
++
++ /*!
++ \internal
++ Used by QML to disconnect a signal by index that's connected to a slot implemented in JavaScript (wrapped in a custom QSlotObjectBase subclass)
++ In the QML case the slot is not a pointer to a pointer to the function to disconnect, but instead it is a pointer to an array of internal values
++ required for the disconnect.
++
++ This version of disconnect assumes that sender and receiver are the same object.
++ */
++ bool QObjectPrivate::disconnect(const QObject *sender, int signal_index, void **slot)
++ {
++ return QObjectPrivate::disconnect(sender, signal_index, sender, slot);
++ }
++
++ /*!
++ \internal
++
++ Used by QML to disconnect a signal by index that's connected to a slot
++ implemented in JavaScript (wrapped in a custom QSlotObjectBase subclass) In the
++ QML case the slot is not a pointer to a pointer to the function to disconnect,
++ but instead it is a pointer to an array of internal values required for the
++ disconnect.
++
++ This is an overload that should be used when \a sender and \a receiver are
++ different objects.
++ */
++ bool QObjectPrivate::disconnect(const QObject *sender, int signal_index, const QObject *receiver,
++ void **slot)
++ {
++ const QMetaObject *senderMetaObject = sender->metaObject();
++ signal_index = methodIndexToSignalIndex(&senderMetaObject, signal_index);
++
++ return QMetaObjectPrivate::disconnect(sender, signal_index, senderMetaObject, receiver, -1,
++ slot);
++ }
++
++ /*!
++ \internal
++ \threadsafe
++ */
++ inline bool QObjectPrivate::removeConnection(QObjectPrivate::Connection *c)
++ {
++ if (!c)
++ return false;
++ QObject *receiver = c->receiver.loadRelaxed();
++ if (!receiver)
++ return false;
++
++ QBasicMutex *senderMutex = signalSlotLock(c->sender);
++ QBasicMutex *receiverMutex = signalSlotLock(receiver);
++
++ QObjectPrivate::ConnectionData *connections;
++ {
++ QOrderedMutexLocker locker(senderMutex, receiverMutex);
++
++ // load receiver once again and recheck to ensure nobody else has removed the connection in the meantime
++ receiver = c->receiver.loadRelaxed();
++ if (!receiver)
++ return false;
++
++ connections = QObjectPrivate::get(c->sender)->connections.loadRelaxed();
++ Q_ASSERT(connections);
++ connections->removeConnection(c);
++
++ c->sender->disconnectNotify(QMetaObjectPrivate::signal(c->sender->metaObject(), c->signal_index));
++ // We must not hold the receiver mutex, else we risk dead-locking; we also only need the sender mutex
++ // It is however vital to hold the senderMutex before calling cleanOrphanedConnections, as otherwise
++ // another thread might modify/delete the connection
++ if (receiverMutex != senderMutex) {
++ receiverMutex->unlock();
++ }
++ connections->cleanOrphanedConnections(c->sender, ConnectionData::AlreadyLockedAndTemporarilyReleasingLock);
++ senderMutex->unlock(); // now both sender and receiver mutex have been manually unlocked
++ locker.dismiss(); // so we dismiss the QOrderedMutexLocker
++ }
++
++ return true;
++ }
++
++ /*!
++ \internal
++
++ Used by QPropertyAdaptorSlotObject to get an existing instance for a property, if available
++ */
++ QtPrivate::QPropertyAdaptorSlotObject *
++ QObjectPrivate::getPropertyAdaptorSlotObject(const QMetaProperty &property)
++ {
++ if (auto conns = connections.loadAcquire()) {
++ Q_Q(QObject);
++ const QMetaObject *metaObject = q->metaObject();
++ int signal_index = methodIndexToSignalIndex(&metaObject, property.notifySignalIndex());
++ if (signal_index >= conns->signalVectorCount())
++ return nullptr;
++ const auto &connectionList = conns->connectionsForSignal(signal_index);
++ for (auto c = connectionList.first.loadRelaxed(); c;
++ c = c->nextConnectionList.loadRelaxed()) {
++ if (c->isSlotObject) {
++ if (auto p = QtPrivate::QPropertyAdaptorSlotObject::cast(c->slotObj,
++ property.propertyIndex()))
++ return p;
++ }
++ }
++ }
++ return nullptr;
++ }
++
++ /*! \class QMetaObject::Connection
++ \inmodule QtCore
++ Represents a handle to a signal-slot (or signal-functor) connection.
++
++ It can be used to check if the connection is valid and to disconnect it using
++ QObject::disconnect(). For a signal-functor connection without a context object,
++ it is the only way to selectively disconnect that connection.
++
++ As Connection is just a handle, the underlying signal-slot connection is unaffected
++ when Connection is destroyed or reassigned.
++ */
++
++ /*!
++ Create a copy of the handle to the \a other connection
++ */
++ QMetaObject::Connection::Connection(const QMetaObject::Connection &other) : d_ptr(other.d_ptr)
++ {
++ if (d_ptr)
++ static_cast<QObjectPrivate::Connection *>(d_ptr)->ref();
++ }
++
++ /*!
++ Assigns \a other to this connection and returns a reference to this connection.
++ */
++ 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;
++ }
++
++ /*!
++ \fn QMetaObject::Connection::Connection();
++
++ Creates a Connection instance.
++ */
++
++ /*!
++ Destructor for QMetaObject::Connection.
++ */
++ QMetaObject::Connection::~Connection()
++ {
++ if (d_ptr)
++ static_cast<QObjectPrivate::Connection *>(d_ptr)->deref();
++ }
++
++ /*! \internal Returns true if the object is still connected */
++ bool QMetaObject::Connection::isConnected_helper() const
++ {
++ Q_ASSERT(d_ptr); // we're only called from operator RestrictedBool() const
++ QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(d_ptr);
++
++ return c->receiver.loadRelaxed();
++ }
++
++
++ /*!
++ \fn QMetaObject::Connection::operator bool() const
++
++ Returns \c 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
++
++ #include "moc_qobject.cpp"