From edfc0f89a93c07d7aa9a99548c0d83b04f566629 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Wed, 7 Mar 2012 18:13:39 +0100 Subject: [PATCH] Implement QVariant conversions for QObject derived pointer metatypes. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit canConvert() and convert() use the metaobject to convert such types. Change-Id: Ic05e74c5c2423b4b9682b88adc856a16dcba4cff Reviewed-by: Jędrzej Nowacki --- src/corelib/kernel/qvariant.cpp | 65 +++++++++++++++++++++- .../auto/corelib/kernel/qvariant/tst_qvariant.cpp | 13 +++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index b9ccb24..8a0e911 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -2407,6 +2407,40 @@ static const quint32 qCanConvertMatrix[QVariant::LastCoreType + 1] = /*QUuid*/ 1 << QVariant::String }; +#ifndef QT_BOOTSTRAPPED +/*! + Returns true if from inherits to. +*/ +static bool canConvertMetaObject(const QMetaObject *from, const QMetaObject *to) +{ + if (from && to == &QObject::staticMetaObject) + return true; + + while (from) { + if (from == to) + return true; + from = from->superClass(); + } + + return false; +} +#endif + +static bool canConvertMetaObject(int fromId, int toId, QObject *fromObject) +{ +#ifndef QT_BOOTSTRAPPED + QMetaType toType(toId); + if ((QMetaType::typeFlags(fromId) & QMetaType::PointerToQObject) && (toType.flags() & QMetaType::PointerToQObject)) + return canConvertMetaObject(fromObject->metaObject(), toType.metaObject()); +#else + Q_UNUSED(fromId); + Q_UNUSED(toId); + Q_UNUSED(fromObject); +#endif + return false; +} + + /*! Returns true if the variant's type can be cast to the requested type, \a targetTypeId. Such casting is done automatically when calling the @@ -2439,6 +2473,10 @@ static const quint32 qCanConvertMatrix[QVariant::LastCoreType + 1] = \row \li \l ULongLong \li \l Bool, \l Char, \l Double, \l Int, \l LongLong, \l String, \l UInt \endtable + A QVariant containing a pointer to a type derived from QObject will also return true for this + function if a qobject_cast to the type described by \a targetTypeId would succeed. Note that + this only works for QObject subclasses which use the Q_OBJECT macro. + \sa convert() */ bool QVariant::canConvert(int targetTypeId) const @@ -2455,8 +2493,10 @@ bool QVariant::canConvert(int targetTypeId) const if (currentType == uint(targetTypeId)) return true; - if (targetTypeId < 0 || targetTypeId >= QMetaType::User) + if (targetTypeId < 0) return false; + if (targetTypeId >= QMetaType::User) + return canConvertMetaObject(currentType, targetTypeId, d.data.o); // FIXME It should be LastCoreType intead of Uuid if (currentType > int(QMetaType::QUuid) || targetTypeId > int(QMetaType::QUuid)) { @@ -2502,6 +2542,9 @@ bool QVariant::canConvert(int targetTypeId) const case QMetaType::Short: case QMetaType::UShort: return qCanConvertMatrix[QVariant::Int] & (1 << currentType) || currentType == QVariant::Int; + case QMetaType::QWidgetStar: + case QMetaType::QObjectStar: + return canConvertMetaObject(currentType, targetTypeId, d.data.o); default: return false; } @@ -2517,6 +2560,11 @@ bool QVariant::canConvert(int targetTypeId) const done, the variant is cleared. Returns true if the current type of the variant was successfully cast; otherwise returns false. + A QVariant containing a pointer to a type derived from QObject will also convert + and return true for this function if a qobject_cast to the type described + by \a targetTypeId would succeed. Note that this only works for QObject subclasses + which use the Q_OBJECT macro. + \warning For historical reasons, converting a null QVariant results in a null value of the desired type (e.g., an empty string for QString) and a result of false. @@ -2539,6 +2587,11 @@ bool QVariant::convert(int targetTypeId) if (oldValue.isNull()) return false; + if ((QMetaType::typeFlags(d.type) & QMetaType::PointerToQObject) && (QMetaType::typeFlags(targetTypeId) & QMetaType::PointerToQObject)) { + create(targetTypeId, &oldValue.d.data.o); + return true; + } + bool isOk = true; if (!handlerManager[d.type]->convert(&oldValue.d, targetTypeId, data(), &isOk)) isOk = false; @@ -2710,6 +2763,12 @@ QDebug operator<<(QDebug dbg, const QVariant::Type p) \snippet code/src_corelib_kernel_qvariant.cpp 5 + If the QVariant contains a pointer to a type derived from QObject then + \c{T} may be any QObject type. If the pointer stored in the QVariant can be + qobject_cast to T, then that result is returned. Otherwise a null pointer is + returned. Note that this only works for QObject subclasses which use the + Q_OBJECT macro. + \sa setValue(), fromValue(), canConvert() */ @@ -2722,6 +2781,10 @@ QDebug operator<<(QDebug dbg, const QVariant::Type p) \snippet code/src_corelib_kernel_qvariant.cpp 6 + A QVariant containing a pointer to a type derived from QObject will also return true for this + function if a qobject_cast to the template type \c{T} would succeed. Note that this only works + for QObject subclasses which use the Q_OBJECT macro. + \sa convert() */ diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index 57cbdc9..e1542e0 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -2640,6 +2640,19 @@ void tst_QVariant::qvariant_cast_QObject() QCOMPARE(o != 0, success); if (success) { QCOMPARE(o->objectName(), QString::fromLatin1("Hello")); + QVERIFY(data.canConvert()); + QVERIFY(data.canConvert(QMetaType::QObjectStar)); + QVERIFY(data.canConvert(::qMetaTypeId())); + QVERIFY(data.value()); + QVERIFY(data.convert(QMetaType::QObjectStar)); + QCOMPARE(data.userType(), int(QMetaType::QObjectStar)); + } else { + QVERIFY(!data.canConvert()); + QVERIFY(!data.canConvert(QMetaType::QObjectStar)); + QVERIFY(!data.canConvert(::qMetaTypeId())); + QVERIFY(!data.value()); + QVERIFY(!data.convert(QMetaType::QObjectStar)); + QVERIFY(data.userType() != QMetaType::QObjectStar); } } -- 2.7.4