From fb23b6893d5228d3716ed0899ef089aeeafec3bf Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Fri, 18 Jun 2010 14:30:37 +0200 Subject: Fix compiler warning --- src/corelib/tools/qscopedpointer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corelib/tools/qscopedpointer.h b/src/corelib/tools/qscopedpointer.h index e972d71..40d3851 100644 --- a/src/corelib/tools/qscopedpointer.h +++ b/src/corelib/tools/qscopedpointer.h @@ -213,7 +213,7 @@ public: } private: - explicit inline QScopedArrayPointer(void *p) { + explicit inline QScopedArrayPointer(void *) { // Enforce the same type. // If you get a compile error here, make sure you declare -- cgit v0.12 From 4a2ba043ed09375e4d9423def32084ab97ea560a Mon Sep 17 00:00:00 2001 From: Sergey Vidyuk Date: Fri, 18 Jun 2010 14:44:07 +0200 Subject: Added QObject::connect overload taking QMetaMethod as parameter Added QObject::connect() and QObject::disconnect() functions implementations which use QMetaMethod to specify signals and slots to be connected or disconnected. Merge-request: 636 Reviewed-by: Olivier Goffart Task-number: QTBUG-10637 --- src/corelib/kernel/qmetaobject.h | 2 + src/corelib/kernel/qmetaobject_p.h | 2 + src/corelib/kernel/qobject.cpp | 256 +++++++++++++++++++++++++++++++++++-- src/corelib/kernel/qobject.h | 17 +++ tests/auto/qobject/tst_qobject.cpp | 219 +++++++++++++++++++++++++++++++ 5 files changed, 487 insertions(+), 9 deletions(-) diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h index b700351..8124487 100644 --- a/src/corelib/kernel/qmetaobject.h +++ b/src/corelib/kernel/qmetaobject.h @@ -138,6 +138,8 @@ private: const QMetaObject *mobj; uint handle; friend struct QMetaObject; + friend struct QMetaObjectPrivate; + friend class QObject; }; Q_DECLARE_TYPEINFO(QMetaMethod, Q_MOVABLE_TYPE); diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index b538787..23e08c8 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -131,6 +131,8 @@ struct QMetaObjectPrivate #ifndef QT_NO_QOBJECT //defined in qobject.cpp enum DisconnectType { DisconnectAll, DisconnectOne }; + static void memberIndexes(const QObject *obj, const QMetaMethod &member, + int *signalIndex, int *methodIndex); static bool connect(const QObject *sender, int signal_index, const QObject *receiver, int method_index, int type = 0, int *types = 0); diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 6187633..dff282e 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -2364,6 +2364,71 @@ int QObject::receivers(const char *signal) const } /*! + \internal + + This helper function calculates signal and method index for the given + member in the specified class. + + \li If member.mobj is 0 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. + + This function is used by QObject::connect and QObject::disconnect which + are working with QMetaMethod. + + \param[out] signalIndex is set to the signal index of member. If the member + specified is not signal this variable is set to -1. + + \param[out] methodIndex is set to the method index of the member. If the + member is not a method of the object specified by obj param 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 != 0 && m != member.mobj) + m = m->d.superdata; + if (!m) + return; + *signalIndex = *methodIndex = (member.handle - get(member.mobj)->methodData)/5; + + int signalOffset; + int methodOffset; + computeOffsets(m, &signalOffset, &methodOffset); + + *methodIndex += methodOffset; + if (member.methodType() == QMetaMethod::Signal) { + *signalIndex = originalClone(m, *signalIndex); + *signalIndex += signalOffset; + } else { + *signalIndex = -1; + } +} + +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)) + qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)", + sender->className(), signal.signature()); + } else if ((method.attributes() & QMetaMethod::Compatibility) && + method.methodType() == QMetaMethod::Signal) { + qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)", + sender->className(), signal.signature(), + receiver->className(), method.signature()); + } +} + +/*! \threadsafe Creates a connection of the given \a type from the \a signal in @@ -2548,15 +2613,8 @@ bool QObject::connect(const QObject *sender, const char *signal, { QMetaMethod smethod = smeta->method(signal_absolute_index); QMetaMethod rmethod = rmeta->method(method_index); - if (warnCompat) { - if(smethod.attributes() & QMetaMethod::Compatibility) { - if (!(rmethod.attributes() & QMetaMethod::Compatibility)) - qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)", smeta->className(), signal); - } else if(rmethod.attributes() & QMetaMethod::Compatibility && membcode != QSIGNAL_CODE) { - qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)", - smeta->className(), signal, rmeta->className(), method); - } - } + if (warnCompat) + check_and_warn_compat(smeta, smethod, rmeta, rmethod); } #endif if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index, type, types)) @@ -2565,6 +2623,99 @@ bool QObject::connect(const QObject *sender, const char *signal, return true; } +/*! + \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 true if the connection succeeds; otherwise returns false. + + This function works in the same way as + 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. + + \see connect(const QObject *sender, const char *signal, + const QObject *receiver, const char *method, + Qt::ConnectionType type) + */ +bool QObject::connect(const QObject *sender, const QMetaMethod &signal, + const QObject *receiver, const QMetaMethod &method, + Qt::ConnectionType type) +{ + +#ifndef QT_NO_DEBUG + bool warnCompat = true; +#endif + if (type == Qt::AutoCompatConnection) { + type = Qt::AutoConnection; +#ifndef QT_NO_DEBUG + warnCompat = false; +#endif + } + + if (sender == 0 + || receiver == 0 + || signal.methodType() != QMetaMethod::Signal + || method.methodType() == QMetaMethod::Constructor) { + qWarning("QObject::connect: Cannot connect %s::%s to %s::%s", + sender ? sender->metaObject()->className() : "(null)", + signal.signature(), + receiver ? receiver->metaObject()->className() : "(null)", + method.signature() ); + return false; + } + + 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) { + qWarning("QObject::connect: Can't find signal %s on instance of class %s", + signal.signature(), smeta->className()); + return false; + } + if (method_index == -1) { + qWarning("QObject::connect: Can't find method %s on instance of class %s", + method.signature(), rmeta->className()); + return false; + } + + if (!QMetaObject::checkConnectArgs(signal.signature(), method.signature())) { + qWarning("QObject::connect: Incompatible sender/receiver arguments" + "\n %s::%s --> %s::%s", + smeta->className(), signal.signature(), + rmeta->className(), method.signature()); + return false; + } + + int *types = 0; + if ((type == Qt::QueuedConnection) + && !(types = queuedConnectionTypes(signal.parameterTypes()))) + return false; + +#ifndef QT_NO_DEBUG + if (warnCompat) + check_and_warn_compat(smeta, signal, rmeta, method); +#endif + if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index, type, types)) + return false; + // Reconstructing SIGNAL() macro result for signal.signature() string + QByteArray signalSignature; + signalSignature.reserve(qstrlen(signal.signature())+1); + signalSignature.append((char)(QSIGNAL_CODE + '0')); + signalSignature.append(signal.signature()); + + const_cast(sender)->connectNotify(signalSignature.constData()); + return true; +} /*! \fn bool QObject::connect(const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type) const @@ -2744,6 +2895,93 @@ bool QObject::disconnect(const QObject *sender, const char *signal, return res; } +/*! + \since 4.8 + + Disconnects \a signal in object \a sender from \a method in object + \a receiver. Returns true if the connection is successfully broken; + otherwise returns false. + + This function provides the same posibilities like + 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 returnsfalse and no signals and slots disconnected + if: + \list 1 + + \i \a signal is not a member of sender class or one of its parent classes. + + \i \a method is not a member of receiver class or one of its parent classes. + + \i \a signal instance represents not a signal. + + \endlist + + QMetaMethod() may be used as wildcard in the meaning "any signal" or "any slot in receiving object". + In the same way 0 can be used for \a receiver in the meaning "any receiving object". In this case + method shoud also be QMetaMethod(). \a sender parameter should be never 0. + + \see 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 == 0 || (receiver == 0 && method.mobj != 0)) { + qWarning("Object::disconnect: Unexpected null parameter"); + return false; + } + if (signal.mobj) { + if(signal.methodType() != QMetaMethod::Signal) { + qWarning("Object::%s: Attempt to %s non-signal %s::%s", + "disconnect","unbind", + sender->metaObject()->className(), signal.signature()); + return false; + } + } + if (method.mobj) { + if(method.methodType() == QMetaMethod::Constructor) { + qWarning("QObject::disconect: cannot use constructor as argument %s::%s", + receiver->metaObject()->className(), method.signature()); + 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 null. If signal is not null while signal_index + // is -1 then this signal is not a member of sender. + if (signal.mobj && signal_index == -1) { + qWarning("QObject::disconect: signal %s not found on class %s", + signal.signature(), sender->metaObject()->className()); + return false; + } + // If this condition is true then method is not a member of receeiver. + if (receiver && method.mobj && method_index == -1) { + qWarning("QObject::disconect: method %s not found on class %s", + method.signature(), receiver->metaObject()->className()); + return false; + } + + if (!QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index)) + return false; + if (!signal.mobj) { + const_cast(sender)->disconnectNotify(0); + } else { + // Reconstructing SIGNAL() macro result for signal.signature() string + QByteArray signalSignature; + signalSignature.reserve(qstrlen(signal.signature())+1); + signalSignature.append((char)(QSIGNAL_CODE + '0')); + signalSignature.append(signal.signature()); + const_cast(sender)->disconnectNotify(signalSignature.constData()); + } + return true; +} /*! \threadsafe diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 44e7ce5..d26f078 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -207,6 +207,21 @@ public: #endif #endif ); + + static bool connect(const QObject *sender, const QMetaMethod &signal, + const QObject *receiver, const QMetaMethod &method, + Qt::ConnectionType type = +#ifdef qdoc + Qt::AutoConnection +#else +#ifdef QT3_SUPPORT + Qt::AutoCompatConnection +#else + Qt::AutoConnection +#endif +#endif + ); + inline bool connect(const QObject *sender, const char *signal, const char *member, Qt::ConnectionType type = #ifdef qdoc @@ -222,6 +237,8 @@ public: static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member); + static bool disconnect(const QObject *sender, const QMetaMethod &signal, + const QObject *receiver, const QMetaMethod &member); inline bool disconnect(const char *signal = 0, const QObject *receiver = 0, const char *member = 0) { return disconnect(this, signal, receiver, member); } diff --git a/tests/auto/qobject/tst_qobject.cpp b/tests/auto/qobject/tst_qobject.cpp index 08b7c19..5caac15 100644 --- a/tests/auto/qobject/tst_qobject.cpp +++ b/tests/auto/qobject/tst_qobject.cpp @@ -129,6 +129,12 @@ private slots: void qMetaObjectConnect(); void qMetaObjectDisconnectOne(); void sameName(); + void connectByMetaMethods(); + void connectByMetaMethodSlotInsteadOfSignal(); + void connectConstructorByMetaMethod(); + void disconnectByMetaMethod(); + void disconnectNotSignalMetaMethod(); + protected: }; @@ -727,6 +733,24 @@ void tst_QObject::connectDisconnectNotify() // Test disconnectNotify for a complete disconnect ((SenderObject*)s)->disconnect((ReceiverObject*)r); + // Obtaining meta methods + int signalIndx = ((SenderObject*)s)->metaObject()->indexOfSignal( + QMetaObject::normalizedSignature(a_signal.toLatin1().constData()+1).constData()); + int methodIndx = ((ReceiverObject*)r)->metaObject()->indexOfMethod( + QMetaObject::normalizedSignature(a_slot.toLatin1().constData()+1).constData()); + QMetaMethod signal = ((SenderObject*)s)->metaObject()->method(signalIndx); + QMetaMethod method = ((ReceiverObject*)r)->metaObject()->method(methodIndx); + + // Test connectNotify when connecting by QMetaMethod + connect( (SenderObject*)s, signal, (ReceiverObject*)r, method ); + QCOMPARE( s->org_signal, s->nw_signal ); + QCOMPARE( s->org_signal.toLatin1(), QMetaObject::normalizedSignature(a_signal.toLatin1().constData()) ); + + // Test disconnectNotify when disconnecting by QMetaMethod + QObject::disconnect( (SenderObject*)s, signal, (ReceiverObject*)r, method ); + QCOMPARE( s->org_signal, s->nw_signal ); + QCOMPARE( s->org_signal.toLatin1(), QMetaObject::normalizedSignature(a_signal.toLatin1().constData()) ); + delete s; delete r; } @@ -3610,5 +3634,200 @@ void tst_QObject::sameName() QCOMPARE(c1.s, 4); } +void tst_QObject::connectByMetaMethods() +{ + SenderObject s; + ReceiverObject r; + const QMetaObject *smeta = s.metaObject(); + const QMetaObject *rmeta = r.metaObject(); + int sigIndx = smeta->indexOfSignal(QMetaObject::normalizedSignature("signal1()")); + int slotIndx = rmeta->indexOfSlot(QMetaObject::normalizedSignature("slot1()")); + QVERIFY( sigIndx != -1 ); + QVERIFY( slotIndx != -1 ); + QMetaMethod signal = smeta->method(sigIndx); + QMetaMethod slot = rmeta->method(slotIndx); + + QVERIFY(connect(&s,signal, &r,slot)); + + QVERIFY(!r.called(1)); + s.emitSignal1(); + QVERIFY(r.called(1)); +} + +void tst_QObject::connectByMetaMethodSlotInsteadOfSignal() +{ + SenderObject s; + ReceiverObject r; + const QMetaObject *smeta = s.metaObject(); + const QMetaObject *rmeta = r.metaObject(); + int badIndx = smeta->indexOfSlot(QMetaObject::normalizedSignature("aPublicSlot()")); + int slotIndx = rmeta->indexOfSlot(QMetaObject::normalizedSignature("slot1()")); + QVERIFY( badIndx != -1 ); + QVERIFY( slotIndx != -1 ); + QMetaMethod badMethod = smeta->method(badIndx); + QMetaMethod slot = rmeta->method(slotIndx); + + QTest::ignoreMessage(QtWarningMsg,"QObject::connect: Cannot connect SenderObject::aPublicSlot() to ReceiverObject::slot1()"); + QVERIFY(!connect(&s,badMethod, &r,slot)); +} + +class Constructable: public QObject +{ + Q_OBJECT + +public: + Q_INVOKABLE Constructable(){} + +}; + +void tst_QObject::connectConstructorByMetaMethod() +{ + Constructable sc; + Constructable rc; + SenderObject s; + ReceiverObject r; + + const QMetaObject cmeta = Constructable::staticMetaObject; + const QMetaObject *smeta = s.metaObject(); + const QMetaObject *rmeta = r.metaObject(); + int constructorIndx = cmeta.indexOfConstructor(QMetaObject::normalizedSignature("Constructable()")); + int sigIndx = smeta->indexOfSignal(QMetaObject::normalizedSignature("signal1()")); + int slotIndx = rmeta->indexOfSlot(QMetaObject::normalizedSignature("slot1()")); + QVERIFY( constructorIndx != -1 ); + QVERIFY( sigIndx != -1 ); + QVERIFY( slotIndx != -1 ); + + QMetaMethod constructor = cmeta.constructor(constructorIndx); + QMetaMethod signal = smeta->method(sigIndx); + QMetaMethod slot = rmeta->method(slotIndx); + + QTest::ignoreMessage(QtWarningMsg,"QObject::connect: Cannot connect Constructable::Constructable() to ReceiverObject::slot1()"); + QVERIFY(!connect(&sc,constructor, &r,slot)); + QTest::ignoreMessage(QtWarningMsg,"QObject::connect: Cannot connect SenderObject::signal1() to Constructable::Constructable()"); + QVERIFY(!connect(&s,signal, &rc,constructor)); + QTest::ignoreMessage(QtWarningMsg,"QObject::connect: Cannot connect Constructable::Constructable() to Constructable::Constructable()"); + QVERIFY(!connect(&sc,constructor, &rc,constructor)); +} + +void tst_QObject::disconnectByMetaMethod() +{ + SenderObject *s = new SenderObject; + ReceiverObject *r1 = new ReceiverObject; + ReceiverObject *r2 = new ReceiverObject; + + QMetaMethod signal1 = s->metaObject()->method( + s->metaObject()->indexOfMethod("signal1()")); + QMetaMethod signal2 = s->metaObject()->method( + s->metaObject()->indexOfMethod("signal2()")); + QMetaMethod signal3 = s->metaObject()->method( + s->metaObject()->indexOfMethod("signal3()")); + QMetaMethod signal4 = s->metaObject()->method( + s->metaObject()->indexOfMethod("signal4()")); + + QMetaMethod slot1 = r1->metaObject()->method( + r1->metaObject()->indexOfMethod("slot1()")); + QMetaMethod slot2 = r1->metaObject()->method( + r1->metaObject()->indexOfMethod("slot2()")); + QMetaMethod slot3 = r1->metaObject()->method( + r1->metaObject()->indexOfMethod("slot3()")); + QMetaMethod slot4 = r1->metaObject()->method( + r1->metaObject()->indexOfMethod("slot4()")); + + connect(s, signal1, r1, slot1); + + s->emitSignal1(); + + QVERIFY(r1->called(1)); + r1->reset(); + + // usual disconnect with all parameters given + bool ret = QObject::disconnect(s, signal1, r1, slot1); + + s->emitSignal1(); + + QVERIFY(!r1->called(1)); + r1->reset(); + + QVERIFY(ret); + ret = QObject::disconnect(s, signal1, r1, slot1); + QVERIFY(!ret); + + r1->reset(); + + connect( s, signal1, r1, slot1 ); + connect( s, signal1, r1, slot2 ); + connect( s, signal1, r1, slot3 ); + connect( s, signal2, r1, slot4 ); + + // disconnect s's signal1() from all slots of r1 + QObject::disconnect(s, signal1, r1, QMetaMethod()); + + s->emitSignal1(); + s->emitSignal2(); + + QVERIFY(!r1->called(1)); + QVERIFY(!r1->called(2)); + QVERIFY(!r1->called(3)); + QVERIFY(r1->called(4)); + r1->reset(); + // make sure all is disconnected again + QObject::disconnect(s, 0, r1, 0); + + connect(s, signal1, r1, slot1); + connect(s, signal1, r2, slot1); + connect(s, signal2, r1, slot2); + connect(s, signal2, r2, slot2); + connect(s, signal3, r1, slot3); + connect(s, signal3, r2, slot3); + + // disconnect signal1() from all receivers + QObject::disconnect(s, signal1, 0, QMetaMethod()); + s->emitSignal1(); + s->emitSignal2(); + s->emitSignal3(); + + QVERIFY(!r1->called(1)); + QVERIFY(!r2->called(1)); + QVERIFY(r1->called(2)); + QVERIFY(r2->called(2)); + QVERIFY(r1->called(2)); + QVERIFY(r2->called(2)); + + r1->reset(); + r2->reset(); + + // disconnect all signals of s from all receivers + QObject::disconnect( s, 0, 0, 0 ); + + connect( s, signal1, r1, slot1 ); + connect( s, signal1, r2, slot1 ); + + // disconnect all signals from slot1 of r1 + QObject::disconnect(s, QMetaMethod(), r1, slot1); + + s->emitSignal1(); + + QVERIFY(!r1->called(1)); + QVERIFY(r2->called(1)); + + delete r2; + delete r1; + delete s; +} + +void tst_QObject::disconnectNotSignalMetaMethod() +{ + SenderObject s; + ReceiverObject r; + + connect(&s, SIGNAL(signal1()), &r, SLOT(slot1())); + + QMetaMethod slot = s.metaObject()->method( + s.metaObject()->indexOfMethod("aPublicSlot()")); + + QTest::ignoreMessage(QtWarningMsg,"Object::disconnect: Attempt to unbind non-signal SenderObject::aPublicSlot()"); + QVERIFY(!QObject::disconnect(&s, slot, &r, QMetaMethod())); +} + QTEST_MAIN(tst_QObject) #include "tst_qobject.moc" -- cgit v0.12 From 49831f30f58d08a360d5947ba300aa3a3575d1fa Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Fri, 18 Jun 2010 14:53:23 +0200 Subject: Make QObject::connect with QMetaMethod works with Jambi We need to call QInternal::activateCallbacks to the recently added overload of QObject::connect --- src/corelib/kernel/qobject.cpp | 58 ++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index dff282e..ad689ca 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -2644,7 +2644,6 @@ bool QObject::connect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method, Qt::ConnectionType type) { - #ifndef QT_NO_DEBUG bool warnCompat = true; #endif @@ -2667,6 +2666,24 @@ bool QObject::connect(const QObject *sender, const QMetaMethod &signal, return false; } + // Reconstructing SIGNAL() macro result for signal.signature() string + QByteArray signalSignature; + signalSignature.reserve(qstrlen(signal.signature())+1); + signalSignature.append((char)(QSIGNAL_CODE + '0')); + signalSignature.append(signal.signature()); + + { + QByteArray methodSignature; + methodSignature.reserve(qstrlen(method.signature())+1); + methodSignature.append((char)(method.methodType() == QMetaMethod::Slot ? QSLOT_CODE + : method.methodType() == QMetaMethod::Signal ? QSIGNAL_CODE : 0 + '0')); + methodSignature.append(method.signature()); + const void *cbdata[] = { sender, signalSignature.constData(), receiver, methodSignature.constData(), &type }; + if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) + return true; + } + + int signal_index; int method_index; { @@ -2707,11 +2724,6 @@ bool QObject::connect(const QObject *sender, const QMetaMethod &signal, #endif if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index, type, types)) return false; - // Reconstructing SIGNAL() macro result for signal.signature() string - QByteArray signalSignature; - signalSignature.reserve(qstrlen(signal.signature())+1); - signalSignature.append((char)(QSIGNAL_CODE + '0')); - signalSignature.append(signal.signature()); const_cast(sender)->connectNotify(signalSignature.constData()); return true; @@ -2947,6 +2959,28 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal, } } + // Reconstructing SIGNAL() macro result for signal.signature() string + QByteArray signalSignature; + if (signal.mobj) { + signalSignature.reserve(qstrlen(signal.signature())+1); + signalSignature.append((char)(QSIGNAL_CODE + '0')); + signalSignature.append(signal.signature()); + } + + { + QByteArray methodSignature; + if (method.mobj) { + methodSignature.reserve(qstrlen(method.signature())+1); + methodSignature.append((char)(method.methodType() == QMetaMethod::Slot ? QSLOT_CODE + : method.methodType() == QMetaMethod::Signal ? QSIGNAL_CODE : 0 + '0')); + methodSignature.append(method.signature()); + } + const void *cbdata[] = { sender, signal.mobj ? signalSignature.constData() : 0, + receiver, method.mobj ? methodSignature.constData() : 0 }; + if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) + return true; + } + int signal_index; int method_index; { @@ -2970,16 +3004,8 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal, if (!QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index)) return false; - if (!signal.mobj) { - const_cast(sender)->disconnectNotify(0); - } else { - // Reconstructing SIGNAL() macro result for signal.signature() string - QByteArray signalSignature; - signalSignature.reserve(qstrlen(signal.signature())+1); - signalSignature.append((char)(QSIGNAL_CODE + '0')); - signalSignature.append(signal.signature()); - const_cast(sender)->disconnectNotify(signalSignature.constData()); - } + + const_cast(sender)->disconnectNotify(method.mobj ? signalSignature.constData() : 0); return true; } -- cgit v0.12 From e5d437d6899904de2596b0ece4914274cff37561 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Thu, 17 Jun 2010 14:28:15 +0200 Subject: Refactor QTextureGlyphCache Separate getting the glyph coordinates from actually making and populating the cache. Calling populate() and fillInPendingGlyphs() immediately after should be equivalent to the old code, except it might have fewer reallocations of the texture since you will only create it one per set of glyphs. This mainly enables populating the GL glyph cache outside a GL context and then actually drawing into it later, when the context has actually been established. Reviewed-by: Kim --- src/gui/painting/qpaintengine_raster.cpp | 1 + src/gui/painting/qtextureglyphcache.cpp | 47 ++++++++++++---- src/gui/painting/qtextureglyphcache_p.h | 12 ++++- src/gui/text/qfont_p.h | 5 ++ .../gl2paintengineex/qpaintengineex_opengl2.cpp | 1 + .../gl2paintengineex/qtextureglyphcache_gl.cpp | 62 +++++++++++++++++----- .../gl2paintengineex/qtextureglyphcache_gl_p.h | 13 ++++- 7 files changed, 114 insertions(+), 27 deletions(-) diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 48974e8..06effd4 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -3009,6 +3009,7 @@ void QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, } cache->populate(fontEngine, numGlyphs, glyphs, positions); + cache->fillInPendingGlyphs(); const QImage &image = cache->image(); int bpl = image.bytesPerLine(); diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index 631a9cf..29cd82b 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -126,8 +126,9 @@ void QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const return; rowHeight += margin * 2 + paddingDoubled; - if (isNull()) - createCache(QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH, qt_next_power_of_two(rowHeight)); + + if (m_w == 0) + m_w = QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH; // now actually use the coords and paint the wanted glyps into cache. QHash::iterator iter = listItemCoordinates.begin(); @@ -142,26 +143,50 @@ void QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const m_cy += m_currentRowHeight + paddingDoubled; m_currentRowHeight = 0; // New row } - if (m_cy + c.h > m_h) { - int new_height = m_h*2; - while (new_height < m_cy + c.h) - new_height *= 2; - // if no room in the current texture - realloc a larger texture - resizeTextureData(m_w, new_height); - m_h = new_height; - } c.x = m_cx; c.y = m_cy; - fillTexture(c, iter.key()); coords.insert(iter.key(), c); + m_pendingGlyphs.insert(iter.key(), c); m_cx += c.w + paddingDoubled; ++iter; } +} + +void QTextureGlyphCache::fillInPendingGlyphs() +{ + if (m_pendingGlyphs.isEmpty()) + return; + + int requiredHeight = 0; + { + QHash::iterator iter = m_pendingGlyphs.begin(); + while (iter != m_pendingGlyphs.end()) { + Coord c = iter.value(); + requiredHeight = qMax(requiredHeight, c.y + c.h); + ++iter; + } + } + + if (requiredHeight > m_h) { + if (isNull()) + createCache(m_w, qt_next_power_of_two(requiredHeight)); + else + resizeCache(m_w, qt_next_power_of_two(requiredHeight)); + } + { + QHash::iterator iter = m_pendingGlyphs.begin(); + while (iter != m_pendingGlyphs.end()) { + fillTexture(iter.value(), iter.key()); + + ++iter; + } + } + m_pendingGlyphs.clear(); } QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g) const diff --git a/src/gui/painting/qtextureglyphcache_p.h b/src/gui/painting/qtextureglyphcache_p.h index a818978..0770ed4 100644 --- a/src/gui/painting/qtextureglyphcache_p.h +++ b/src/gui/painting/qtextureglyphcache_p.h @@ -94,6 +94,7 @@ public: void populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions); + void fillInPendingGlyphs(); virtual void createTextureData(int width, int height) = 0; virtual void resizeTextureData(int width, int height) = 0; @@ -108,7 +109,14 @@ public: createTextureData(width, height); } - inline bool isNull() const { return m_w <= 0 || m_h <= 0; } + inline void resizeCache(int width, int height) + { + resizeTextureData(width, height); + m_w = width; + m_h = height; + } + + inline bool isNull() const { return m_h == 0; } QHash coords; @@ -117,6 +125,8 @@ public: protected: QFontEngine *m_current_fontengine; + QHash m_pendingGlyphs; + int m_w; // image width int m_h; // image height int m_cx; // current x diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h index 470c109..646a8b8 100644 --- a/src/gui/text/qfont_p.h +++ b/src/gui/text/qfont_p.h @@ -192,6 +192,11 @@ public: QFont smallCapsFont() const { return QFont(smallCapsFontPrivate()); } QFontPrivate *smallCapsFontPrivate() const; + static QFontPrivate *get(const QFont &font) + { + return font.d.data(); + } + void resolve(uint mask, const QFontPrivate *other); private: QFontPrivate &operator=(const QFontPrivate &) { return *this; } diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index ee49a3d..634b315 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -1463,6 +1463,7 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyp cache->setPaintEnginePrivate(this); cache->populate(staticTextItem->fontEngine, staticTextItem->numGlyphs, staticTextItem->glyphs, staticTextItem->glyphPositions); + cache->fillInPendingGlyphs(); if (cache->width() == 0 || cache->height() == 0) return; diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp index 5371c5e..e2ec8a1 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp @@ -52,39 +52,65 @@ QT_BEGIN_NAMESPACE extern Q_GUI_EXPORT bool qt_cleartype_enabled; #endif -QGLTextureGlyphCache::QGLTextureGlyphCache(QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix) +QGLTextureGlyphCache::QGLTextureGlyphCache(const QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix) : QImageTextureGlyphCache(type, matrix) - , ctx(context) + , ctx(0) , m_width(0) , m_height(0) { - // broken FBO readback is a bug in the SGX 1.3 and 1.4 drivers for the N900 where - // copying between FBO's is broken if the texture is either GL_ALPHA or POT. The - // workaround is to use a system-memory copy of the glyph cache for this device. - // Switching to NPOT and GL_RGBA would both cost a lot more graphics memory and - // be slower, so that is not desireable. - if (!ctx->d_ptr->workaround_brokenFBOReadBack) - glGenFramebuffers(1, &m_fbo); - - connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext*)), - SLOT(contextDestroyed(const QGLContext*))); + if (context != 0) + setContext(context); } QGLTextureGlyphCache::~QGLTextureGlyphCache() { + cleanUpContext(); +} + +void QGLTextureGlyphCache::cleanUpContext() +{ if (ctx) { QGLShareContextScope scope(ctx); if (!ctx->d_ptr->workaround_brokenFBOReadBack) glDeleteFramebuffers(1, &m_fbo); - if (m_width || m_height) + if (m_width || m_height) { glDeleteTextures(1, &m_texture); + m_width = 0; + m_height = 0; + m_h = 0; + } + + ctx = 0; } } +void QGLTextureGlyphCache::setContext(const QGLContext *context) +{ + cleanUpContext(); + + ctx = context; + + // broken FBO readback is a bug in the SGX 1.3 and 1.4 drivers for the N900 where + // copying between FBO's is broken if the texture is either GL_ALPHA or POT. The + // workaround is to use a system-memory copy of the glyph cache for this device. + // Switching to NPOT and GL_RGBA would both cost a lot more graphics memory and + // be slower, so that is not desireable. + if (!ctx->d_ptr->workaround_brokenFBOReadBack) + glGenFramebuffers(1, &m_fbo); + + connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext*)), + SLOT(contextDestroyed(const QGLContext*))); +} + void QGLTextureGlyphCache::createTextureData(int width, int height) { + if (ctx == 0) { + qWarning("QGLTextureGlyphCache::createTextureData: Called with no context"); + return; + } + // create in QImageTextureGlyphCache baseclass is meant to be called // only to create the initial image and does not preserve the content, // so we don't call when this function is called from resize. @@ -118,6 +144,11 @@ void QGLTextureGlyphCache::createTextureData(int width, int height) void QGLTextureGlyphCache::resizeTextureData(int width, int height) { + if (ctx == 0) { + qWarning("QGLTextureGlyphCache::resizeTextureData: Called with no context"); + return; + } + int oldWidth = m_width; int oldHeight = m_height; @@ -213,6 +244,11 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height) void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph) { + if (ctx == 0) { + qWarning("QGLTextureGlyphCache::fillTexture: Called with no context"); + return; + } + if (ctx->d_ptr->workaround_brokenFBOReadBack) { QImageTextureGlyphCache::fillTexture(c, glyph); diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h index 84e9021..d291ac3 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h @@ -66,7 +66,7 @@ class Q_OPENGL_EXPORT QGLTextureGlyphCache : public QObject, public QImageTextur { Q_OBJECT public: - QGLTextureGlyphCache(QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix); + QGLTextureGlyphCache(const QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix); ~QGLTextureGlyphCache(); virtual void createTextureData(int width, int height); @@ -82,6 +82,13 @@ public: inline void setPaintEnginePrivate(QGL2PaintEngineExPrivate *p) { pex = p; } + void setContext(const QGLContext *context); + inline const QGLContext *context() const + { + return ctx; + } + + public Q_SLOTS: void contextDestroyed(const QGLContext *context) { @@ -105,7 +112,9 @@ public Q_SLOTS: } private: - QGLContext *ctx; + void cleanUpContext(); + + const QGLContext *ctx; QGL2PaintEngineExPrivate *pex; -- cgit v0.12