From f0ee2077e61fc8014d74bc94afd1907efc9f3272 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Fri, 18 Dec 2009 10:50:39 +0100 Subject: Add Q_OBJECT to QBalloonTip so it can be intreospected by styles. Reviewed-by: jbache --- src/gui/util/qsystemtrayicon_p.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/util/qsystemtrayicon_p.h b/src/gui/util/qsystemtrayicon_p.h index 86de366..029a259 100644 --- a/src/gui/util/qsystemtrayicon_p.h +++ b/src/gui/util/qsystemtrayicon_p.h @@ -94,6 +94,7 @@ public: class QBalloonTip : public QWidget { + Q_OBJECT public: static void showBalloon(QSystemTrayIcon::MessageIcon icon, const QString& title, const QString& msg, QSystemTrayIcon *trayIcon, -- cgit v0.12 From 37e845d6710ea81aacc33c8ef21308e2cc0cab0d Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Thu, 17 Dec 2009 14:09:30 +0100 Subject: Optimize QThreadStorage with QVector QThreadStorageData::get might be accessed very often. QMap lookup has proven to be too slow. We can expect about 20 instances of QThreadStorage in a typical applications (tested with some KDE applications) So a QVector is more suited Note: we now re-use the ids. Which means that if the QThreadStorage is destroyed before the QThread, and another one is created, we might get crashes (instead of a warning printed to the console) Reviewed-by: brad --- src/corelib/thread/qthread_p.h | 2 +- src/corelib/thread/qthreadstorage.cpp | 97 ++++++++-------- tests/benchmarks/benchmarks.pro | 3 +- tests/benchmarks/qthreadstorage/qthreadstorage.pro | 6 + .../qthreadstorage/tst_qthreadstorage.cpp | 124 +++++++++++++++++++++ 5 files changed, 183 insertions(+), 49 deletions(-) create mode 100644 tests/benchmarks/qthreadstorage/qthreadstorage.pro create mode 100644 tests/benchmarks/qthreadstorage/tst_qthreadstorage.cpp diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h index af68434..3db308f 100644 --- a/src/corelib/thread/qthread_p.h +++ b/src/corelib/thread/qthread_p.h @@ -202,7 +202,7 @@ public: QStack eventLoops; QPostEventList postEventList; bool canWait; - QMap tls; + QVector tls; QMutex mutex; diff --git a/src/corelib/thread/qthreadstorage.cpp b/src/corelib/thread/qthreadstorage.cpp index 8b1f255..a7d53d0 100644 --- a/src/corelib/thread/qthreadstorage.cpp +++ b/src/corelib/thread/qthreadstorage.cpp @@ -71,27 +71,32 @@ void qtsDebug(const char *fmt, ...) # define DEBUG_MSG if(false)qDebug #endif -static QBasicAtomicInt idCounter = Q_BASIC_ATOMIC_INITIALIZER(INT_MAX); Q_GLOBAL_STATIC(QMutex, mutex) -typedef QMap DestructorMap; +typedef QVector DestructorMap; Q_GLOBAL_STATIC(DestructorMap, destructors) QThreadStorageData::QThreadStorageData(void (*func)(void *)) - : id(idCounter.fetchAndAddRelaxed(-1)) { QMutexLocker locker(mutex()); - destructors()->insert(id, func); - + DestructorMap *destr = destructors(); + for (id = 0; id < destr->count(); id++) { + if (destr->at(id) == 0) + break; + } + if (id == destr->count()) { + destr->append(func); + } else { + (*destr)[id] = func; + } DEBUG_MSG("QThreadStorageData: Allocated id %d, destructor %p", id, func); } QThreadStorageData::~QThreadStorageData() { + DEBUG_MSG("QThreadStorageData: Released id %d", id); QMutexLocker locker(mutex()); if (destructors()) - destructors()->remove(id); - - DEBUG_MSG("QThreadStorageData: Released id %d", id); + (*destructors())[id] = 0; } void **QThreadStorageData::get() const @@ -101,14 +106,17 @@ void **QThreadStorageData::get() const qWarning("QThreadStorage::get: QThreadStorage can only be used with threads started with QThread"); return 0; } - QMap::const_iterator it = data->tls.constFind(id); + QVector &tls = data->tls; + if (tls.size() <= id) + tls.resize(id + 1); + void **v = &tls[id]; + DEBUG_MSG("QThreadStorageData: Returning storage %d, data %p, for thread %p", id, - it != data->tls.end() ? it.value() : 0, + *v, data->thread); - // const_cast below is a bit evil - but we have to make sure not to detach here - // otherwise we'll go bonkers in oom situations - return it != data->tls.constEnd() && it.value() != 0 ? const_cast(&it.value()) : 0; + + return *v ? v : 0; } void **QThreadStorageData::set(void *p) @@ -118,51 +126,46 @@ void **QThreadStorageData::set(void *p) qWarning("QThreadStorage::set: QThreadStorage can only be used with threads started with QThread"); return 0; } + QVector &tls = data->tls; + if (tls.size() <= id) + tls.resize(id + 1); + + void *&value = tls[id]; + // delete any previous data + if (value != 0) { + DEBUG_MSG("QThreadStorageData: Deleting previous storage %d, data %p, for thread %p", + id, + value, + data->thread); - QMap::iterator it = data->tls.find(id); - if (it != data->tls.end()) { - // delete any previous data - if (it.value() != 0) { - DEBUG_MSG("QThreadStorageData: Deleting previous storage %d, data %p, for thread %p", - id, - it.value(), - data->thread); - - void *q = it.value(); - it.value() = 0; - - QMutexLocker locker(mutex()); - void (*destructor)(void *) = destructors()->value(id); - locker.unlock(); + QMutexLocker locker(mutex()); + void (*destructor)(void *) = destructors()->value(id); + locker.unlock(); - destructor(q); - } + void *q = value; + value = 0; - // store new data - it.value() = p; - DEBUG_MSG("QThreadStorageData: Set storage %d for thread %p to %p", id, data->thread, p); - } else { - it = data->tls.insert(id, p); - DEBUG_MSG("QThreadStorageData: Inserted storage %d, data %p, for thread %p", id, p, data->thread); + destructor(q); } - return &it.value(); + // store new data + value = p; + DEBUG_MSG("QThreadStorageData: Set storage %d for thread %p to %p", id, data->thread, p); + return &value; } void QThreadStorageData::finish(void **p) { - QMap *tls = reinterpret_cast *>(p); + QVector *tls = reinterpret_cast *>(p); if (!tls || tls->isEmpty() || !mutex()) return; // nothing to do DEBUG_MSG("QThreadStorageData: Destroying storage for thread %p", QThread::currentThread()); - QMap::iterator it = tls->begin(); - while (it != tls->end()) { - int id = it.key(); - void *q = it.value(); - it.value() = 0; - ++it; + for(int i = tls->size() - 1; i >= 0; i--) { + void *&value = (*tls)[i]; + void *q = value; + value = 0; if (!q) { // data already deleted @@ -170,16 +173,16 @@ void QThreadStorageData::finish(void **p) } QMutexLocker locker(mutex()); - void (*destructor)(void *) = destructors()->value(id); + void (*destructor)(void *) = destructors()->value(i); locker.unlock(); if (!destructor) { if (QThread::currentThread()) qWarning("QThreadStorage: Thread %p exited after QThreadStorage %d destroyed", - QThread::currentThread(), id); + QThread::currentThread(), i); continue; } - destructor(q); + destructor(q); //crash here might mean the thread exited after qthreadstorage was destroyed } tls->clear(); } diff --git a/tests/benchmarks/benchmarks.pro b/tests/benchmarks/benchmarks.pro index bffa009..bb20dcf 100644 --- a/tests/benchmarks/benchmarks.pro +++ b/tests/benchmarks/benchmarks.pro @@ -36,7 +36,8 @@ SUBDIRS = containers-associative \ qstringbuilder \ qstylesheetstyle \ qsvgrenderer \ - qtableview + qtableview \ + qthreadstorage diff --git a/tests/benchmarks/qthreadstorage/qthreadstorage.pro b/tests/benchmarks/qthreadstorage/qthreadstorage.pro new file mode 100644 index 0000000..f9c1978 --- /dev/null +++ b/tests/benchmarks/qthreadstorage/qthreadstorage.pro @@ -0,0 +1,6 @@ +load(qttest_p4) +TEMPLATE = app +TARGET = tst_qthreadstorage + +SOURCES += tst_qthreadstorage.cpp + diff --git a/tests/benchmarks/qthreadstorage/tst_qthreadstorage.cpp b/tests/benchmarks/qthreadstorage/tst_qthreadstorage.cpp new file mode 100644 index 0000000..c00bf5a --- /dev/null +++ b/tests/benchmarks/qthreadstorage/tst_qthreadstorage.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +//TESTED_FILES= + +QThreadStorage dummy[8]; + +QThreadStorage tls1; + +class tst_QThreadStorage : public QObject +{ + Q_OBJECT + +public: + tst_QThreadStorage(); + virtual ~tst_QThreadStorage(); + +public slots: + void init(); + void cleanup(); + +private slots: + void construct(); + void get(); + void set(); +}; + +tst_QThreadStorage::tst_QThreadStorage() +{ +} + +tst_QThreadStorage::~tst_QThreadStorage() +{ +} + +void tst_QThreadStorage::init() +{ + dummy[1].setLocalData(new int(5)); + dummy[2].setLocalData(new int(4)); + dummy[3].setLocalData(new int(3)); + tls1.setLocalData(new QString()); +} + +void tst_QThreadStorage::cleanup() +{ +} + +void tst_QThreadStorage::construct() +{ + QBENCHMARK { + QThreadStorage ts; + } +} + + +void tst_QThreadStorage::get() +{ + QThreadStorage ts; + ts.setLocalData(new int(45)); + + int count = 0; + QBENCHMARK { + int *i = ts.localData(); + count += *i; + } + ts.setLocalData(0); +} + +void tst_QThreadStorage::set() +{ + QThreadStorage ts; + + int count = 0; + QBENCHMARK { + ts.setLocalData(new int(count)); + count++; + } + ts.setLocalData(0); +} + + +QTEST_MAIN(tst_QThreadStorage) +#include "tst_qthreadstorage.moc" -- cgit v0.12 From 1aa7d5922930c4195bc8747a19524a7e5021c4e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Thu, 17 Dec 2009 15:35:23 +0100 Subject: NEON configure detection and initial blend function implementations. Adds new NEON configure test and -no-neon configure option. NEON implementations can also be turned off by setting the QT_NO_NEON environment variable. Performance improvements (in frames per second): - Blending ARGB32 on RGB32/ARGB32, mostly opaque: 71 % - Blending ARGB32 on RGB32/ARGB32, no opaque pixels: 108 % - Blending ARGB32 on RGB32/ARGB32, with 0.5 opacity: 158 % - Blending RGB32 on RGB32/ARGB32, with 0.5 opacity: 189 % Task-number: QTBUG-6684 Reviewed-by: Gunnar Sletta Reviewed-by: Paul Olav Tvete --- config.tests/unix/neon/neon.cpp | 51 +++++++ config.tests/unix/neon/neon.pro | 2 + configure | 22 ++- src/gui/painting/painting.pri | 7 + src/gui/painting/qblendfunctions.cpp | 2 +- src/gui/painting/qdrawhelper.cpp | 16 ++- src/gui/painting/qdrawhelper_neon.cpp | 260 ++++++++++++++++++++++++++++++++++ src/gui/painting/qdrawhelper_neon_p.h | 76 ++++++++++ 8 files changed, 432 insertions(+), 4 deletions(-) create mode 100644 config.tests/unix/neon/neon.cpp create mode 100644 config.tests/unix/neon/neon.pro create mode 100644 src/gui/painting/qdrawhelper_neon.cpp create mode 100644 src/gui/painting/qdrawhelper_neon_p.h diff --git a/config.tests/unix/neon/neon.cpp b/config.tests/unix/neon/neon.cpp new file mode 100644 index 0000000..c31a9fd --- /dev/null +++ b/config.tests/unix/neon/neon.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the config.tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main(int, char**) +{ + int32x4_t null = vdupq_n_s32(0x0); + + int result; + vst1q_lane_s32(&result, null, 0); + return result; +} diff --git a/config.tests/unix/neon/neon.pro b/config.tests/unix/neon/neon.pro new file mode 100644 index 0000000..de20c4e --- /dev/null +++ b/config.tests/unix/neon/neon.pro @@ -0,0 +1,2 @@ +SOURCES = neon.cpp +CONFIG -= x11 qt diff --git a/configure b/configure index 2114863..22e6bd4 100755 --- a/configure +++ b/configure @@ -745,6 +745,7 @@ CFG_HOST_ENDIAN=auto CFG_DOUBLEFORMAT=auto CFG_ARMFPA=auto CFG_IWMMXT=no +CFG_NEON=auto CFG_CLOCK_GETTIME=auto CFG_CLOCK_MONOTONIC=auto CFG_MREMAP=auto @@ -1604,6 +1605,13 @@ while [ "$#" -gt 0 ]; do iwmmxt) CFG_IWMMXT="yes" ;; + neon) + if [ "$VAL" = "no" ]; then + CFG_NEON="$VAL" + else + UNKNOWN_OPT=yes + fi + ;; reduce-relocations) if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then CFG_REDUCE_RELOCATIONS="$VAL" @@ -3889,6 +3897,7 @@ Qt for Embedded Linux only: -iwmmxt ............ Compile using the iWMMXt instruction set (available on some XScale CPUs). + -no-neon ........... Do not compile with use of NEON instructions. EOF fi @@ -4509,6 +4518,15 @@ if [ "$CFG_IWMMXT" = "yes" ]; then fi fi +# detect neon support +if ([ "$CFG_ARCH" = "arm" ] || [ "$CFG_ARCH" = "armv6" ]) && [ "${CFG_NEON}" = "auto" ]; then + if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" config.tests/unix/neon "neon" $L_FLAGS $I_FLAGS $l_FLAGS "-mfpu=neon"; then + CFG_NEON=yes + else + CFG_NEON=no + fi +fi + # detect zlib if [ "$CFG_ZLIB" = "no" ]; then # Note: Qt no longer support builds without zlib @@ -6124,6 +6142,7 @@ fi [ "$CFG_SSE" = "yes" ] && QMAKE_CONFIG="$QMAKE_CONFIG sse" [ "$CFG_SSE2" = "yes" ] && QMAKE_CONFIG="$QMAKE_CONFIG sse2" [ "$CFG_IWMMXT" = "yes" ] && QMAKE_CONFIG="$QMAKE_CONFIG iwmmxt" +[ "$CFG_NEON" = "yes" ] && QMAKE_CONFIG="$QMAKE_CONFIG neon" [ "$PLATFORM_MAC" = "yes" ] && QMAKE_CONFIG="$QMAKE_CONFIG $CFG_MAC_ARCHS" if [ "$CFG_IPV6" = "yes" ]; then QT_CONFIG="$QT_CONFIG ipv6" @@ -7416,8 +7435,9 @@ echo "Declarative module .. $CFG_DECLARATIVE" echo "STL support ......... $CFG_STL" echo "PCH support ......... $CFG_PRECOMPILE" echo "MMX/3DNOW/SSE/SSE2.. ${CFG_MMX}/${CFG_3DNOW}/${CFG_SSE}/${CFG_SSE2}" -if [ "${CFG_ARCH}" = "arm" ]; then +if [ "$CFG_ARCH" = "arm" ] || [ "$CFG_ARCH" = "armv6" ]; then echo "iWMMXt support ...... ${CFG_IWMMXT}" + echo "NEON support ........ ${CFG_NEON}" fi [ "${PLATFORM_QWS}" != "yes" ] && echo "Graphics System ..... $CFG_GRAPHICS_SYSTEM" echo "IPv6 support ........ $CFG_IPV6" diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri index 628a109..0b1e79a 100644 --- a/src/gui/painting/painting.pri +++ b/src/gui/painting/painting.pri @@ -379,6 +379,13 @@ symbian { QMAKE_CXXFLAGS.ARMCC *= -O3 } +neon { + DEFINES += QT_HAVE_NEON + HEADERS += painting/qdrawhelper_neon_p.h + SOURCES += painting/qdrawhelper_neon.cpp + QMAKE.CXXFLAGS *= -mfpu=neon +} + contains(QT_CONFIG, zlib) { INCLUDEPATH += ../3rdparty/zlib } else:!contains(QT_CONFIG, no-zlib) { diff --git a/src/gui/painting/qblendfunctions.cpp b/src/gui/painting/qblendfunctions.cpp index 1d15dac..81d1515 100644 --- a/src/gui/painting/qblendfunctions.cpp +++ b/src/gui/painting/qblendfunctions.cpp @@ -605,7 +605,7 @@ static void qt_blend_argb32_on_argb32(uchar *destPixels, int dbpl, } -static void qt_blend_rgb32_on_rgb32(uchar *destPixels, int dbpl, +void qt_blend_rgb32_on_rgb32(uchar *destPixels, int dbpl, const uchar *srcPixels, int sbpl, int w, int h, int const_alpha) diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 23236ec..84cf5cc 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -7725,7 +7726,8 @@ enum CPUFeatures { SSE = 0x10, SSE2 = 0x20, CMOV = 0x40, - IWMMXT = 0x80 + IWMMXT = 0x80, + NEON = 0x100 }; static uint detectCPUFeatures() @@ -7751,6 +7753,9 @@ static uint detectCPUFeatures() // runtime detection only available when running as a previlegied process static const bool doIWMMXT = !qgetenv("QT_NO_IWMMXT").toInt(); return doIWMMXT ? IWMMXT : 0; +#elif defined(QT_HAVE_NEON) + static const bool doNEON = !qgetenv("QT_NO_NEON").toInt(); + return doNEON ? NEON : 0; #else uint features = 0; #if defined(__x86_64__) || defined(Q_OS_WIN64) @@ -8122,7 +8127,14 @@ void qInitDrawhelperAsm() qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_armv6; qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_armv6; qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_armv6; -#endif // Q_CC_RVCT && QT_HAVE_ARMV6 +#elif defined(QT_HAVE_NEON) + if (features & NEON) { + qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon; + qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon; + } +#endif if (functionForModeSolidAsm) { const int destinationMode = QPainter::CompositionMode_Destination; diff --git a/src/gui/painting/qdrawhelper_neon.cpp b/src/gui/painting/qdrawhelper_neon.cpp new file mode 100644 index 0000000..7fe11bf --- /dev/null +++ b/src/gui/painting/qdrawhelper_neon.cpp @@ -0,0 +1,260 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#ifdef QT_HAVE_NEON + +#include +#include + +QT_BEGIN_NAMESPACE + +static inline int16x8_t qvdiv_255_s16(int16x8_t x, int16x8_t half) +{ + // result = (x + (x >> 8) + 0x80) >> 8 + + const int16x8_t temp = vshrq_n_s16(x, 8); // x >> 8 + const int16x8_t sum_part = vaddq_s16(x, half); // x + 0x80 + const int16x8_t sum = vaddq_s16(temp, sum_part); + + return vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(sum), 8)); +} + +static inline int16x8_t qvbyte_mul_s16(int16x8_t x, int16x8_t alpha, int16x8_t half) +{ + // t = qRound(x * alpha / 255.0) + + const int16x8_t t = vmulq_s16(x, alpha); // t + return qvdiv_255_s16(t, half); +} + +static inline int16x8_t qvinterpolate_pixel_255(int16x8_t x, int16x8_t a, int16x8_t y, int16x8_t b, int16x8_t half) +{ + // t = x * a + y * b + + const int16x8_t ta = vmulq_s16(x, a); + const int16x8_t tb = vmulq_s16(y, b); + + return qvdiv_255_s16(vaddq_s16(ta, tb), half); +} + +static inline int16x8_t qvsource_over_s16(int16x8_t src16, int16x8_t dst16, int16x8_t half, int16x8_t full) +{ + const int16x4_t alpha16_high = vdup_lane_s16(vget_high_s16(src16), 3); + const int16x4_t alpha16_low = vdup_lane_s16(vget_low_s16(src16), 3); + + const int16x8_t alpha16 = vsubq_s16(full, vcombine_s16(alpha16_low, alpha16_high)); + + return vaddq_s16(src16, qvbyte_mul_s16(dst16, alpha16, half)); +} + +void qt_blend_argb32_on_argb32_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha) +{ + const uint *src = (const uint *) srcPixels; + uint *dst = (uint *) destPixels; + int16x8_t half = vdupq_n_s16(0x80); + int16x8_t full = vdupq_n_s16(0xff); + if (const_alpha == 256) { + for (int y = 0; y < h; ++y) { + int x = 0; + for (; x < w-3; x += 4) { + int32x4_t src32 = vld1q_s32((int32_t *)&src[x]); + if ((src[x] & src[x+1] & src[x+2] & src[x+3]) >= 0xff000000) { + // all opaque + vst1q_s32((int32_t *)&dst[x], src32); + } else if (src[x] | src[x+1] | src[x+2] | src[x+3]) { + int32x4_t dst32 = vld1q_s32((int32_t *)&dst[x]); + + const uint8x16_t src8 = vreinterpretq_u8_s32(src32); + const uint8x16_t dst8 = vreinterpretq_u8_s32(dst32); + + const uint8x8_t src8_low = vget_low_u8(src8); + const uint8x8_t dst8_low = vget_low_u8(dst8); + + const uint8x8_t src8_high = vget_high_u8(src8); + const uint8x8_t dst8_high = vget_high_u8(dst8); + + const int16x8_t src16_low = vreinterpretq_s16_u16(vmovl_u8(src8_low)); + const int16x8_t dst16_low = vreinterpretq_s16_u16(vmovl_u8(dst8_low)); + + const int16x8_t src16_high = vreinterpretq_s16_u16(vmovl_u8(src8_high)); + const int16x8_t dst16_high = vreinterpretq_s16_u16(vmovl_u8(dst8_high)); + + const int16x8_t result16_low = qvsource_over_s16(src16_low, dst16_low, half, full); + const int16x8_t result16_high = qvsource_over_s16(src16_high, dst16_high, half, full); + + const int32x2_t result32_low = vreinterpret_s32_s8(vmovn_s16(result16_low)); + const int32x2_t result32_high = vreinterpret_s32_s8(vmovn_s16(result16_high)); + + vst1q_s32((int32_t *)&dst[x], vcombine_s32(result32_low, result32_high)); + } + } + for (; x= 0xff000000) + dst[x] = s; + else if (s != 0) + dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); + } + dst = (quint32 *)(((uchar *) dst) + dbpl); + src = (const quint32 *)(((const uchar *) src) + sbpl); + } + } else if (const_alpha != 0) { + const_alpha = (const_alpha * 255) >> 8; + int16x8_t const_alpha16 = vdupq_n_s16(const_alpha); + for (int y = 0; y < h; ++y) { + int x = 0; + for (; x < w-3; x += 4) { + if (src[x] | src[x+1] | src[x+2] | src[x+3]) { + int32x4_t src32 = vld1q_s32((int32_t *)&src[x]); + int32x4_t dst32 = vld1q_s32((int32_t *)&dst[x]); + + const uint8x16_t src8 = vreinterpretq_u8_s32(src32); + const uint8x16_t dst8 = vreinterpretq_u8_s32(dst32); + + const uint8x8_t src8_low = vget_low_u8(src8); + const uint8x8_t dst8_low = vget_low_u8(dst8); + + const uint8x8_t src8_high = vget_high_u8(src8); + const uint8x8_t dst8_high = vget_high_u8(dst8); + + const int16x8_t src16_low = vreinterpretq_s16_u16(vmovl_u8(src8_low)); + const int16x8_t dst16_low = vreinterpretq_s16_u16(vmovl_u8(dst8_low)); + + const int16x8_t src16_high = vreinterpretq_s16_u16(vmovl_u8(src8_high)); + const int16x8_t dst16_high = vreinterpretq_s16_u16(vmovl_u8(dst8_high)); + + const int16x8_t srcalpha16_low = qvbyte_mul_s16(src16_low, const_alpha16, half); + const int16x8_t srcalpha16_high = qvbyte_mul_s16(src16_high, const_alpha16, half); + + const int16x8_t result16_low = qvsource_over_s16(srcalpha16_low, dst16_low, half, full); + const int16x8_t result16_high = qvsource_over_s16(srcalpha16_high, dst16_high, half, full); + + const int32x2_t result32_low = vreinterpret_s32_s8(vmovn_s16(result16_low)); + const int32x2_t result32_high = vreinterpret_s32_s8(vmovn_s16(result16_high)); + + vst1q_s32((int32_t *)&dst[x], vcombine_s32(result32_low, result32_high)); + } + } + for (; x> 8; + int one_minus_const_alpha = 255 - const_alpha; + int16x8_t const_alpha16 = vdupq_n_s16(const_alpha); + int16x8_t one_minus_const_alpha16 = vdupq_n_s16(255 - const_alpha); + for (int y = 0; y < h; ++y) { + int x = 0; + for (; x < w-3; x += 4) { + int32x4_t src32 = vld1q_s32((int32_t *)&src[x]); + int32x4_t dst32 = vld1q_s32((int32_t *)&dst[x]); + + const uint8x16_t src8 = vreinterpretq_u8_s32(src32); + const uint8x16_t dst8 = vreinterpretq_u8_s32(dst32); + + const uint8x8_t src8_low = vget_low_u8(src8); + const uint8x8_t dst8_low = vget_low_u8(dst8); + + const uint8x8_t src8_high = vget_high_u8(src8); + const uint8x8_t dst8_high = vget_high_u8(dst8); + + const int16x8_t src16_low = vreinterpretq_s16_u16(vmovl_u8(src8_low)); + const int16x8_t dst16_low = vreinterpretq_s16_u16(vmovl_u8(dst8_low)); + + const int16x8_t src16_high = vreinterpretq_s16_u16(vmovl_u8(src8_high)); + const int16x8_t dst16_high = vreinterpretq_s16_u16(vmovl_u8(dst8_high)); + + const int16x8_t result16_low = qvinterpolate_pixel_255(src16_low, const_alpha16, dst16_low, one_minus_const_alpha16, half); + const int16x8_t result16_high = qvinterpolate_pixel_255(src16_high, const_alpha16, dst16_high, one_minus_const_alpha16, half); + + const int32x2_t result32_low = vreinterpret_s32_s8(vmovn_s16(result16_low)); + const int32x2_t result32_high = vreinterpret_s32_s8(vmovn_s16(result16_high)); + + vst1q_s32((int32_t *)&dst[x], vcombine_s32(result32_low, result32_high)); + } + for (; x + +QT_BEGIN_NAMESPACE + +#ifdef QT_HAVE_NEON + +void qt_blend_argb32_on_argb32_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + +void qt_blend_rgb32_on_rgb32_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + +#endif // QT_HAVE_NEON + +QT_END_NAMESPACE + +#endif // QDRAWHELPER_NEON_P_H -- cgit v0.12 From 315c09c3873f7c5d8b785443372bf975bae47ee7 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Fri, 18 Dec 2009 14:17:44 +0100 Subject: Make sure mappings are created for columns when indexes newly become parents. Also move the modeltest related tests to the modeltest subdirectory Merge-request: 2262 Reviewed-by: Olivier Goffart --- src/gui/itemviews/qsortfilterproxymodel.cpp | 27 ++ tests/auto/headers/tst_headers.cpp | 4 +- tests/auto/modeltest/dynamictreemodel.cpp | 328 +++++++++++++++++++++ tests/auto/modeltest/dynamictreemodel.h | 199 +++++++++++++ tests/auto/modeltest/modeltest.cpp | 20 +- tests/auto/modeltest/modeltest.pro | 4 +- tests/auto/modeltest/tst_modeltest.cpp | 35 ++- tests/auto/qabstractitemmodel/dynamictreemodel.cpp | 328 --------------------- tests/auto/qabstractitemmodel/dynamictreemodel.h | 199 ------------- .../auto/qabstractitemmodel/qabstractitemmodel.pro | 7 +- 10 files changed, 607 insertions(+), 544 deletions(-) create mode 100644 tests/auto/modeltest/dynamictreemodel.cpp create mode 100644 tests/auto/modeltest/dynamictreemodel.h delete mode 100644 tests/auto/qabstractitemmodel/dynamictreemodel.cpp delete mode 100644 tests/auto/qabstractitemmodel/dynamictreemodel.h diff --git a/src/gui/itemviews/qsortfilterproxymodel.cpp b/src/gui/itemviews/qsortfilterproxymodel.cpp index fc82f30..95d6bc8 100644 --- a/src/gui/itemviews/qsortfilterproxymodel.cpp +++ b/src/gui/itemviews/qsortfilterproxymodel.cpp @@ -734,6 +734,33 @@ void QSortFilterProxyModelPrivate::source_items_inserted( } } + if (model->rowCount(source_parent) == delta_item_count) { + // Items were inserted where there were none before. + // If it was new rows make sure to create mappings for columns so that a + // valid mapping can be retreived later and vice-versa. + + QVector &orthogonal_proxy_to_source = (orient == Qt::Horizontal) ? m->source_rows : m->source_columns; + QVector &orthogonal_source_to_proxy = (orient == Qt::Horizontal) ? m->proxy_rows : m->proxy_columns; + + if (orthogonal_source_to_proxy.isEmpty()) { + const int ortho_end = (orient == Qt::Horizontal) ? model->rowCount(source_parent) : model->columnCount(source_parent); + + for (int ortho_item = 0; ortho_item < ortho_end; ++ortho_item) { + if ((orient == Qt::Horizontal) ? q->filterAcceptsRow(ortho_item, source_parent) + : q->filterAcceptsColumn(ortho_item, source_parent)) { + orthogonal_proxy_to_source.append(ortho_item); + } + } + orthogonal_source_to_proxy.resize(orthogonal_proxy_to_source.size()); + + if (orient == Qt::Horizontal) { + // We're reacting to columnsInserted, but we've just inserted new rows. Sort them. + sort_source_rows(orthogonal_proxy_to_source, source_parent); + } + build_source_to_proxy_mapping(orthogonal_proxy_to_source, orthogonal_source_to_proxy); + } + } + // Sort and insert the items if (orient == Qt::Vertical) // Only sort rows sort_source_rows(source_items, source_parent); diff --git a/tests/auto/headers/tst_headers.cpp b/tests/auto/headers/tst_headers.cpp index 36d25fe..329143d 100644 --- a/tests/auto/headers/tst_headers.cpp +++ b/tests/auto/headers/tst_headers.cpp @@ -217,8 +217,8 @@ void tst_Headers::licenseCheck() QCOMPARE(content.at(i++), QString("/****************************************************************************")); if (licenseType != "3RDPARTY") { QCOMPARE(content.at(i++), QString("**")); - if (sourceFile.endsWith("/tests/auto/qabstractitemmodel/dynamictreemodel.cpp") - || sourceFile.endsWith("/tests/auto/qabstractitemmodel/dynamictreemodel.h") + if (sourceFile.endsWith("/tests/auto/modeltest/dynamictreemodel.cpp") + || sourceFile.endsWith("/tests/auto/modeltest/dynamictreemodel.h") || sourceFile.endsWith("/src/network/kernel/qnetworkproxy_p.h")) { // These files are not copyrighted by Nokia. diff --git a/tests/auto/modeltest/dynamictreemodel.cpp b/tests/auto/modeltest/dynamictreemodel.cpp new file mode 100644 index 0000000..24d3ab3 --- /dev/null +++ b/tests/auto/modeltest/dynamictreemodel.cpp @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Stephen Kelly +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "dynamictreemodel.h" + +#include +#include +#include + + +DynamicTreeModel::DynamicTreeModel(QObject *parent) + : QAbstractItemModel(parent), + nextId(1) +{ +} + +QModelIndex DynamicTreeModel::index(int row, int column, const QModelIndex &parent) const +{ +// if (column != 0) +// return QModelIndex(); + + + if ( column < 0 || row < 0 ) + return QModelIndex(); + + QList > childIdColumns = m_childItems.value(parent.internalId()); + + + if (childIdColumns.size() == 0) + return QModelIndex(); + + if (column >= childIdColumns.size()) + return QModelIndex(); + + QList rowIds = childIdColumns.at(column); + + if ( row >= rowIds.size()) + return QModelIndex(); + + qint64 id = rowIds.at(row); + + return createIndex(row, column, reinterpret_cast(id)); + +} + +qint64 DynamicTreeModel::findParentId(qint64 searchId) const +{ + if (searchId <= 0) + return -1; + + QHashIterator > > i(m_childItems); + while (i.hasNext()) + { + i.next(); + QListIterator > j(i.value()); + while (j.hasNext()) + { + QList l = j.next(); + if (l.contains(searchId)) + { + return i.key(); + } + } + } + return -1; +} + +QModelIndex DynamicTreeModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + qint64 searchId = index.internalId(); + qint64 parentId = findParentId(searchId); + // Will never happen for valid index, but what the hey... + if (parentId <= 0) + return QModelIndex(); + + qint64 grandParentId = findParentId(parentId); + if (grandParentId < 0) + grandParentId = 0; + + int column = 0; + QList childList = m_childItems.value(grandParentId).at(column); + + int row = childList.indexOf(parentId); + + return createIndex(row, column, reinterpret_cast(parentId)); + +} + +int DynamicTreeModel::rowCount(const QModelIndex &index ) const +{ + QList > cols = m_childItems.value(index.internalId()); + + if (cols.size() == 0 ) + return 0; + + if (index.column() > 0) + return 0; + + return cols.at(0).size(); +} + +int DynamicTreeModel::columnCount(const QModelIndex &index ) const +{ +// Q_UNUSED(index); + return m_childItems.value(index.internalId()).size(); +} + +QVariant DynamicTreeModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (Qt::DisplayRole == role) + { + return m_items.value(index.internalId()); + } + return QVariant(); +} + +void DynamicTreeModel::clear() +{ + m_items.clear(); + m_childItems.clear(); + nextId = 1; + reset(); +} + + +ModelChangeCommand::ModelChangeCommand( DynamicTreeModel *model, QObject *parent ) + : QObject(parent), m_model(model), m_numCols(1), m_startRow(-1), m_endRow(-1) +{ + +} + +QModelIndex ModelChangeCommand::findIndex(QList rows) +{ + const int col = 0; + QModelIndex parent = QModelIndex(); + QListIterator i(rows); + while (i.hasNext()) + { + parent = m_model->index(i.next(), col, parent); + Q_ASSERT(parent.isValid()); + } + return parent; +} + +ModelInsertCommand::ModelInsertCommand(DynamicTreeModel *model, QObject *parent ) + : ModelChangeCommand(model, parent) +{ + +} + +void ModelInsertCommand::doCommand() +{ + QModelIndex parent = findIndex(m_rowNumbers); + m_model->beginInsertRows(parent, m_startRow, m_endRow); + qint64 parentId = parent.internalId(); + for (int row = m_startRow; row <= m_endRow; row++) + { + for(int col = 0; col < m_numCols; col++ ) + { + if (m_model->m_childItems[parentId].size() <= col) + { + m_model->m_childItems[parentId].append(QList()); + } +// QString name = QUuid::createUuid().toString(); + qint64 id = m_model->newId(); + QString name = QString::number(id); + + m_model->m_items.insert(id, name); + m_model->m_childItems[parentId][col].insert(row, id); + + } + } + m_model->endInsertRows(); +} + + +ModelMoveCommand::ModelMoveCommand(DynamicTreeModel *model, QObject *parent) + : ModelChangeCommand(model, parent) +{ + +} +bool ModelMoveCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) +{ + return m_model->beginMoveRows(srcParent, srcStart, srcEnd, destParent, destRow); +} + +void ModelMoveCommand::doCommand() +{ + QModelIndex srcParent = findIndex(m_rowNumbers); + QModelIndex destParent = findIndex(m_destRowNumbers); + + if (!emitPreSignal(srcParent, m_startRow, m_endRow, destParent, m_destRow)) + { + return; + } + + for (int column = 0; column < m_numCols; ++column) + { + QList l = m_model->m_childItems.value(srcParent.internalId())[column].mid(m_startRow, m_endRow - m_startRow + 1 ); + + for (int i = m_startRow; i <= m_endRow ; i++) + { + m_model->m_childItems[srcParent.internalId()][column].removeAt(m_startRow); + } + int d; + if (m_destRow < m_startRow) + d = m_destRow; + else + { + if (srcParent == destParent) + d = m_destRow - (m_endRow - m_startRow + 1); + else + d = m_destRow - (m_endRow - m_startRow) + 1; + } + + foreach(const qint64 id, l) + { + m_model->m_childItems[destParent.internalId()][column].insert(d++, id); + } + } + + emitPostSignal(); +} + +void ModelMoveCommand::emitPostSignal() +{ + m_model->endMoveRows(); +} + +ModelResetCommand::ModelResetCommand(DynamicTreeModel* model, QObject* parent) + : ModelMoveCommand(model, parent) +{ + +} + +ModelResetCommand::~ModelResetCommand() +{ + +} + +bool ModelResetCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) +{ + Q_UNUSED(srcParent); + Q_UNUSED(srcStart); + Q_UNUSED(srcEnd); + Q_UNUSED(destParent); + Q_UNUSED(destRow); + + return true; +} + +void ModelResetCommand::emitPostSignal() +{ + m_model->reset(); +} + +ModelResetCommandFixed::ModelResetCommandFixed(DynamicTreeModel* model, QObject* parent) + : ModelMoveCommand(model, parent) +{ + +} + +ModelResetCommandFixed::~ModelResetCommandFixed() +{ + +} + +bool ModelResetCommandFixed::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) +{ + Q_UNUSED(srcParent); + Q_UNUSED(srcStart); + Q_UNUSED(srcEnd); + Q_UNUSED(destParent); + Q_UNUSED(destRow); + + m_model->beginResetModel(); + return true; +} + +void ModelResetCommandFixed::emitPostSignal() +{ + m_model->endResetModel(); +} + diff --git a/tests/auto/modeltest/dynamictreemodel.h b/tests/auto/modeltest/dynamictreemodel.h new file mode 100644 index 0000000..3f0d90d --- /dev/null +++ b/tests/auto/modeltest/dynamictreemodel.h @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Stephen Kelly +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DYNAMICTREEMODEL_H +#define DYNAMICTREEMODEL_H + +#include + +#include +#include + + +template class QList; + +class DynamicTreeModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + DynamicTreeModel(QObject *parent = 0); + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + int rowCount(const QModelIndex &index = QModelIndex()) const; + int columnCount(const QModelIndex &index = QModelIndex()) const; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + void clear(); + +protected slots: + + /** + Finds the parent id of the string with id @p searchId. + + Returns -1 if not found. + */ + qint64 findParentId(qint64 searchId) const; + +private: + QHash m_items; + QHash > > m_childItems; + qint64 nextId; + qint64 newId() { return nextId++; }; + + QModelIndex m_nextParentIndex; + int m_nextRow; + + int m_depth; + int maxDepth; + + friend class ModelInsertCommand; + friend class ModelMoveCommand; + friend class ModelResetCommand; + friend class ModelResetCommandFixed; + +}; + + +class ModelChangeCommand : public QObject +{ + Q_OBJECT +public: + + ModelChangeCommand( DynamicTreeModel *model, QObject *parent = 0 ); + + virtual ~ModelChangeCommand() {} + + void setAncestorRowNumbers(QList rowNumbers) { m_rowNumbers = rowNumbers; } + + QModelIndex findIndex(QList rows); + + void setStartRow(int row) { m_startRow = row; } + + void setEndRow(int row) { m_endRow = row; } + + void setNumCols(int cols) { m_numCols = cols; } + + virtual void doCommand() = 0; + +protected: + DynamicTreeModel* m_model; + QList m_rowNumbers; + int m_numCols; + int m_startRow; + int m_endRow; + +}; + +typedef QList ModelChangeCommandList; + +class ModelInsertCommand : public ModelChangeCommand +{ + Q_OBJECT + +public: + + ModelInsertCommand(DynamicTreeModel *model, QObject *parent = 0 ); + virtual ~ModelInsertCommand() {} + + virtual void doCommand(); +}; + + +class ModelMoveCommand : public ModelChangeCommand +{ + Q_OBJECT +public: + ModelMoveCommand(DynamicTreeModel *model, QObject *parent); + + virtual ~ModelMoveCommand() {} + + virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow); + + virtual void doCommand(); + + virtual void emitPostSignal(); + + void setDestAncestors( QList rows ) { m_destRowNumbers = rows; } + + void setDestRow(int row) { m_destRow = row; } + +protected: + QList m_destRowNumbers; + int m_destRow; +}; + +/** + A command which does a move and emits a reset signal. +*/ +class ModelResetCommand : public ModelMoveCommand +{ + Q_OBJECT +public: + ModelResetCommand(DynamicTreeModel* model, QObject* parent = 0); + + virtual ~ModelResetCommand(); + + virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow); + virtual void emitPostSignal(); + +}; + +/** + A command which does a move and emits a beginResetModel and endResetModel signals. +*/ +class ModelResetCommandFixed : public ModelMoveCommand +{ + Q_OBJECT +public: + ModelResetCommandFixed(DynamicTreeModel* model, QObject* parent = 0); + + virtual ~ModelResetCommandFixed(); + + virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow); + virtual void emitPostSignal(); + +}; + + +#endif diff --git a/tests/auto/modeltest/modeltest.cpp b/tests/auto/modeltest/modeltest.cpp index 39d00f6..0ca9e14 100644 --- a/tests/auto/modeltest/modeltest.cpp +++ b/tests/auto/modeltest/modeltest.cpp @@ -475,8 +475,8 @@ void ModelTest::data() void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int end ) { // Q_UNUSED(end); - qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString() - << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) ); +// qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString() +// << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) ); // qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) ); Changing c; c.parent = parent; @@ -495,14 +495,14 @@ void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end ) { Changing c = insert.pop(); Q_ASSERT ( c.parent == parent ); - qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize - << "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent ); - - for (int ii=start; ii <= end; ii++) - { - qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent )); - } - qDebug(); +// qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize +// << "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent ); + +// for (int ii=start; ii <= end; ii++) +// { +// qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent )); +// } +// qDebug(); Q_ASSERT ( c.oldSize + ( end - start + 1 ) == model->rowCount ( parent ) ); Q_ASSERT ( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) ); diff --git a/tests/auto/modeltest/modeltest.pro b/tests/auto/modeltest/modeltest.pro index 6655d85..f157da0 100644 --- a/tests/auto/modeltest/modeltest.pro +++ b/tests/auto/modeltest/modeltest.pro @@ -1,6 +1,6 @@ load(qttest_p4) -SOURCES += tst_modeltest.cpp modeltest.cpp -HEADERS += modeltest.h +SOURCES += tst_modeltest.cpp modeltest.cpp dynamictreemodel.cpp +HEADERS += modeltest.h dynamictreemodel.h diff --git a/tests/auto/modeltest/tst_modeltest.cpp b/tests/auto/modeltest/tst_modeltest.cpp index a719168..78aa79f 100644 --- a/tests/auto/modeltest/tst_modeltest.cpp +++ b/tests/auto/modeltest/tst_modeltest.cpp @@ -44,6 +44,7 @@ #include #include "modeltest.h" +#include "dynamictreemodel.h" class tst_ModelTest : public QObject @@ -64,6 +65,7 @@ private slots: void stringListModel(); void treeWidgetModel(); void standardItemModel(); + void testInsertThroughProxy(); }; @@ -143,7 +145,38 @@ void tst_ModelTest::standardItemModel() model.insertRows(0,5, model.index(1,1)); model.insertColumns(0,5, model.index(1,3)); - +} + +void tst_ModelTest::testInsertThroughProxy() +{ + DynamicTreeModel *model = new DynamicTreeModel(this); + + QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this); + proxy->setSourceModel(model); + + new ModelTest(proxy, this); + + ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this); + insertCommand->setNumCols(4); + insertCommand->setStartRow(0); + insertCommand->setEndRow(9); + // Parent is QModelIndex() + insertCommand->doCommand(); + + insertCommand = new ModelInsertCommand(model, this); + insertCommand->setNumCols(4); + insertCommand->setAncestorRowNumbers(QList() << 5); + insertCommand->setStartRow(0); + insertCommand->setEndRow(9); + insertCommand->doCommand(); + + ModelMoveCommand *moveCommand = new ModelMoveCommand(model, this); + moveCommand->setNumCols(4); + moveCommand->setStartRow(0); + moveCommand->setEndRow(0); + moveCommand->setDestRow(9); + moveCommand->setDestAncestors(QList() << 5); + moveCommand->doCommand(); } QTEST_MAIN(tst_ModelTest) diff --git a/tests/auto/qabstractitemmodel/dynamictreemodel.cpp b/tests/auto/qabstractitemmodel/dynamictreemodel.cpp deleted file mode 100644 index 24d3ab3..0000000 --- a/tests/auto/qabstractitemmodel/dynamictreemodel.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Stephen Kelly -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "dynamictreemodel.h" - -#include -#include -#include - - -DynamicTreeModel::DynamicTreeModel(QObject *parent) - : QAbstractItemModel(parent), - nextId(1) -{ -} - -QModelIndex DynamicTreeModel::index(int row, int column, const QModelIndex &parent) const -{ -// if (column != 0) -// return QModelIndex(); - - - if ( column < 0 || row < 0 ) - return QModelIndex(); - - QList > childIdColumns = m_childItems.value(parent.internalId()); - - - if (childIdColumns.size() == 0) - return QModelIndex(); - - if (column >= childIdColumns.size()) - return QModelIndex(); - - QList rowIds = childIdColumns.at(column); - - if ( row >= rowIds.size()) - return QModelIndex(); - - qint64 id = rowIds.at(row); - - return createIndex(row, column, reinterpret_cast(id)); - -} - -qint64 DynamicTreeModel::findParentId(qint64 searchId) const -{ - if (searchId <= 0) - return -1; - - QHashIterator > > i(m_childItems); - while (i.hasNext()) - { - i.next(); - QListIterator > j(i.value()); - while (j.hasNext()) - { - QList l = j.next(); - if (l.contains(searchId)) - { - return i.key(); - } - } - } - return -1; -} - -QModelIndex DynamicTreeModel::parent(const QModelIndex &index) const -{ - if (!index.isValid()) - return QModelIndex(); - - qint64 searchId = index.internalId(); - qint64 parentId = findParentId(searchId); - // Will never happen for valid index, but what the hey... - if (parentId <= 0) - return QModelIndex(); - - qint64 grandParentId = findParentId(parentId); - if (grandParentId < 0) - grandParentId = 0; - - int column = 0; - QList childList = m_childItems.value(grandParentId).at(column); - - int row = childList.indexOf(parentId); - - return createIndex(row, column, reinterpret_cast(parentId)); - -} - -int DynamicTreeModel::rowCount(const QModelIndex &index ) const -{ - QList > cols = m_childItems.value(index.internalId()); - - if (cols.size() == 0 ) - return 0; - - if (index.column() > 0) - return 0; - - return cols.at(0).size(); -} - -int DynamicTreeModel::columnCount(const QModelIndex &index ) const -{ -// Q_UNUSED(index); - return m_childItems.value(index.internalId()).size(); -} - -QVariant DynamicTreeModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - if (Qt::DisplayRole == role) - { - return m_items.value(index.internalId()); - } - return QVariant(); -} - -void DynamicTreeModel::clear() -{ - m_items.clear(); - m_childItems.clear(); - nextId = 1; - reset(); -} - - -ModelChangeCommand::ModelChangeCommand( DynamicTreeModel *model, QObject *parent ) - : QObject(parent), m_model(model), m_numCols(1), m_startRow(-1), m_endRow(-1) -{ - -} - -QModelIndex ModelChangeCommand::findIndex(QList rows) -{ - const int col = 0; - QModelIndex parent = QModelIndex(); - QListIterator i(rows); - while (i.hasNext()) - { - parent = m_model->index(i.next(), col, parent); - Q_ASSERT(parent.isValid()); - } - return parent; -} - -ModelInsertCommand::ModelInsertCommand(DynamicTreeModel *model, QObject *parent ) - : ModelChangeCommand(model, parent) -{ - -} - -void ModelInsertCommand::doCommand() -{ - QModelIndex parent = findIndex(m_rowNumbers); - m_model->beginInsertRows(parent, m_startRow, m_endRow); - qint64 parentId = parent.internalId(); - for (int row = m_startRow; row <= m_endRow; row++) - { - for(int col = 0; col < m_numCols; col++ ) - { - if (m_model->m_childItems[parentId].size() <= col) - { - m_model->m_childItems[parentId].append(QList()); - } -// QString name = QUuid::createUuid().toString(); - qint64 id = m_model->newId(); - QString name = QString::number(id); - - m_model->m_items.insert(id, name); - m_model->m_childItems[parentId][col].insert(row, id); - - } - } - m_model->endInsertRows(); -} - - -ModelMoveCommand::ModelMoveCommand(DynamicTreeModel *model, QObject *parent) - : ModelChangeCommand(model, parent) -{ - -} -bool ModelMoveCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) -{ - return m_model->beginMoveRows(srcParent, srcStart, srcEnd, destParent, destRow); -} - -void ModelMoveCommand::doCommand() -{ - QModelIndex srcParent = findIndex(m_rowNumbers); - QModelIndex destParent = findIndex(m_destRowNumbers); - - if (!emitPreSignal(srcParent, m_startRow, m_endRow, destParent, m_destRow)) - { - return; - } - - for (int column = 0; column < m_numCols; ++column) - { - QList l = m_model->m_childItems.value(srcParent.internalId())[column].mid(m_startRow, m_endRow - m_startRow + 1 ); - - for (int i = m_startRow; i <= m_endRow ; i++) - { - m_model->m_childItems[srcParent.internalId()][column].removeAt(m_startRow); - } - int d; - if (m_destRow < m_startRow) - d = m_destRow; - else - { - if (srcParent == destParent) - d = m_destRow - (m_endRow - m_startRow + 1); - else - d = m_destRow - (m_endRow - m_startRow) + 1; - } - - foreach(const qint64 id, l) - { - m_model->m_childItems[destParent.internalId()][column].insert(d++, id); - } - } - - emitPostSignal(); -} - -void ModelMoveCommand::emitPostSignal() -{ - m_model->endMoveRows(); -} - -ModelResetCommand::ModelResetCommand(DynamicTreeModel* model, QObject* parent) - : ModelMoveCommand(model, parent) -{ - -} - -ModelResetCommand::~ModelResetCommand() -{ - -} - -bool ModelResetCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) -{ - Q_UNUSED(srcParent); - Q_UNUSED(srcStart); - Q_UNUSED(srcEnd); - Q_UNUSED(destParent); - Q_UNUSED(destRow); - - return true; -} - -void ModelResetCommand::emitPostSignal() -{ - m_model->reset(); -} - -ModelResetCommandFixed::ModelResetCommandFixed(DynamicTreeModel* model, QObject* parent) - : ModelMoveCommand(model, parent) -{ - -} - -ModelResetCommandFixed::~ModelResetCommandFixed() -{ - -} - -bool ModelResetCommandFixed::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) -{ - Q_UNUSED(srcParent); - Q_UNUSED(srcStart); - Q_UNUSED(srcEnd); - Q_UNUSED(destParent); - Q_UNUSED(destRow); - - m_model->beginResetModel(); - return true; -} - -void ModelResetCommandFixed::emitPostSignal() -{ - m_model->endResetModel(); -} - diff --git a/tests/auto/qabstractitemmodel/dynamictreemodel.h b/tests/auto/qabstractitemmodel/dynamictreemodel.h deleted file mode 100644 index 3f0d90d..0000000 --- a/tests/auto/qabstractitemmodel/dynamictreemodel.h +++ /dev/null @@ -1,199 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Stephen Kelly -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef DYNAMICTREEMODEL_H -#define DYNAMICTREEMODEL_H - -#include - -#include -#include - - -template class QList; - -class DynamicTreeModel : public QAbstractItemModel -{ - Q_OBJECT - -public: - DynamicTreeModel(QObject *parent = 0); - - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; - QModelIndex parent(const QModelIndex &index) const; - int rowCount(const QModelIndex &index = QModelIndex()) const; - int columnCount(const QModelIndex &index = QModelIndex()) const; - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - - void clear(); - -protected slots: - - /** - Finds the parent id of the string with id @p searchId. - - Returns -1 if not found. - */ - qint64 findParentId(qint64 searchId) const; - -private: - QHash m_items; - QHash > > m_childItems; - qint64 nextId; - qint64 newId() { return nextId++; }; - - QModelIndex m_nextParentIndex; - int m_nextRow; - - int m_depth; - int maxDepth; - - friend class ModelInsertCommand; - friend class ModelMoveCommand; - friend class ModelResetCommand; - friend class ModelResetCommandFixed; - -}; - - -class ModelChangeCommand : public QObject -{ - Q_OBJECT -public: - - ModelChangeCommand( DynamicTreeModel *model, QObject *parent = 0 ); - - virtual ~ModelChangeCommand() {} - - void setAncestorRowNumbers(QList rowNumbers) { m_rowNumbers = rowNumbers; } - - QModelIndex findIndex(QList rows); - - void setStartRow(int row) { m_startRow = row; } - - void setEndRow(int row) { m_endRow = row; } - - void setNumCols(int cols) { m_numCols = cols; } - - virtual void doCommand() = 0; - -protected: - DynamicTreeModel* m_model; - QList m_rowNumbers; - int m_numCols; - int m_startRow; - int m_endRow; - -}; - -typedef QList ModelChangeCommandList; - -class ModelInsertCommand : public ModelChangeCommand -{ - Q_OBJECT - -public: - - ModelInsertCommand(DynamicTreeModel *model, QObject *parent = 0 ); - virtual ~ModelInsertCommand() {} - - virtual void doCommand(); -}; - - -class ModelMoveCommand : public ModelChangeCommand -{ - Q_OBJECT -public: - ModelMoveCommand(DynamicTreeModel *model, QObject *parent); - - virtual ~ModelMoveCommand() {} - - virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow); - - virtual void doCommand(); - - virtual void emitPostSignal(); - - void setDestAncestors( QList rows ) { m_destRowNumbers = rows; } - - void setDestRow(int row) { m_destRow = row; } - -protected: - QList m_destRowNumbers; - int m_destRow; -}; - -/** - A command which does a move and emits a reset signal. -*/ -class ModelResetCommand : public ModelMoveCommand -{ - Q_OBJECT -public: - ModelResetCommand(DynamicTreeModel* model, QObject* parent = 0); - - virtual ~ModelResetCommand(); - - virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow); - virtual void emitPostSignal(); - -}; - -/** - A command which does a move and emits a beginResetModel and endResetModel signals. -*/ -class ModelResetCommandFixed : public ModelMoveCommand -{ - Q_OBJECT -public: - ModelResetCommandFixed(DynamicTreeModel* model, QObject* parent = 0); - - virtual ~ModelResetCommandFixed(); - - virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow); - virtual void emitPostSignal(); - -}; - - -#endif diff --git a/tests/auto/qabstractitemmodel/qabstractitemmodel.pro b/tests/auto/qabstractitemmodel/qabstractitemmodel.pro index a31868b..52c4e96 100644 --- a/tests/auto/qabstractitemmodel/qabstractitemmodel.pro +++ b/tests/auto/qabstractitemmodel/qabstractitemmodel.pro @@ -1,6 +1,9 @@ load(qttest_p4) -SOURCES += tst_qabstractitemmodel.cpp dynamictreemodel.cpp -HEADERS += dynamictreemodel.h + +INCLUDEPATH += $$PWD/../modeltest + +SOURCES += tst_qabstractitemmodel.cpp ../modeltest/dynamictreemodel.cpp ../modeltest/modeltest.cpp +HEADERS += ../modeltest/dynamictreemodel.h ../modeltest/modeltest.h -- cgit v0.12 From 82a35ed3f1dede556e9ab6e30b86997c4a8f0fd1 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Fri, 18 Dec 2009 15:13:09 +0000 Subject: Make the layout{,AboutToBe}Changed signals in the QSortFilterProxyModel match its source. Merge-request: 2266 Reviewed-by: Olivier Goffart --- src/gui/itemviews/qsortfilterproxymodel.cpp | 6 +- tests/auto/modeltest/tst_modeltest.cpp | 122 +++++++++++++++++++++++++--- 2 files changed, 115 insertions(+), 13 deletions(-) diff --git a/src/gui/itemviews/qsortfilterproxymodel.cpp b/src/gui/itemviews/qsortfilterproxymodel.cpp index 95d6bc8..646a3a1 100644 --- a/src/gui/itemviews/qsortfilterproxymodel.cpp +++ b/src/gui/itemviews/qsortfilterproxymodel.cpp @@ -1198,9 +1198,10 @@ void QSortFilterProxyModelPrivate::_q_sourceLayoutAboutToBeChanged() { Q_Q(QSortFilterProxyModel); saved_persistent_indexes.clear(); + emit q->layoutAboutToBeChanged(); if (persistent.indexes.isEmpty()) return; - emit q->layoutAboutToBeChanged(); + saved_persistent_indexes = store_persistent_indexes(); } @@ -1208,7 +1209,8 @@ void QSortFilterProxyModelPrivate::_q_sourceLayoutChanged() { Q_Q(QSortFilterProxyModel); if (saved_persistent_indexes.isEmpty()) { - q->invalidate(); + clear_mapping(); + emit q->layoutChanged(); return; } diff --git a/tests/auto/modeltest/tst_modeltest.cpp b/tests/auto/modeltest/tst_modeltest.cpp index 78aa79f..262b966 100644 --- a/tests/auto/modeltest/tst_modeltest.cpp +++ b/tests/auto/modeltest/tst_modeltest.cpp @@ -66,6 +66,7 @@ private slots: void treeWidgetModel(); void standardItemModel(); void testInsertThroughProxy(); + void moveSourceItems(); }; @@ -94,15 +95,15 @@ void tst_ModelTest::stringListModel() { QStringListModel model; QSortFilterProxyModel proxy; - + ModelTest t1(&model); ModelTest t2(&proxy); - + proxy.setSourceModel(&model); - + model.setStringList(QStringList() << "2" << "3" << "1"); model.setStringList(QStringList() << "a" << "e" << "plop" << "b" << "c" ); - + proxy.setDynamicSortFilter(true); proxy.setFilterRegExp(QRegExp("[^b]")); } @@ -112,7 +113,7 @@ void tst_ModelTest::treeWidgetModel() QTreeWidget widget; ModelTest t1(widget.model()); - + QTreeWidgetItem *root = new QTreeWidgetItem(&widget, QStringList("root")); for (int i = 0; i < 20; ++i) { new QTreeWidgetItem(root, QStringList(QString::number(i))); @@ -122,7 +123,7 @@ void tst_ModelTest::treeWidgetModel() QTreeWidgetItem *parent = new QTreeWidgetItem(&widget, QStringList("parent")); new QTreeWidgetItem(parent, QStringList("child")); widget.setItemHidden(parent, true); - + widget.sortByColumn(0); } @@ -130,19 +131,19 @@ void tst_ModelTest::standardItemModel() { QStandardItemModel model(10,10); QSortFilterProxyModel proxy; - - + + ModelTest t1(&model); ModelTest t2(&proxy); - + proxy.setSourceModel(&model); - + model.insertRows(2, 5); model.removeRows(4, 5); model.insertColumns(2, 5); model.removeColumns(4, 5); - + model.insertRows(0,5, model.index(1,1)); model.insertColumns(0,5, model.index(1,3)); } @@ -179,5 +180,104 @@ void tst_ModelTest::testInsertThroughProxy() moveCommand->doCommand(); } +/** + Makes the persistent index list publicly accessible +*/ +class AccessibleProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + AccessibleProxyModel(QObject *parent = 0) : QSortFilterProxyModel(parent) {} + + QModelIndexList persistent() + { + return persistentIndexList(); + } +}; + +class ObservingObject : public QObject +{ + Q_OBJECT +public: + ObservingObject(AccessibleProxyModel *proxy, QObject *parent = 0) + : QObject(parent), + m_proxy(proxy) + { + connect(m_proxy, SIGNAL(layoutAboutToBeChanged()), SLOT(storePersistent())); + connect(m_proxy, SIGNAL(layoutChanged()), SLOT(checkPersistent())); + } + +public slots: + + void storePersistent(const QModelIndex &parent) + { + for (int row = 0; row < m_proxy->rowCount(parent); ++row) { + QModelIndex proxyIndex = m_proxy->index(row, 0, parent); + QModelIndex sourceIndex = m_proxy->mapToSource(proxyIndex); + Q_ASSERT(proxyIndex.isValid()); + Q_ASSERT(sourceIndex.isValid()); + m_persistentSourceIndexes.append(sourceIndex); + m_persistentProxyIndexes.append(proxyIndex); + if (m_proxy->hasChildren(proxyIndex)) + storePersistent(proxyIndex); + } + } + + void storePersistent() + { + m_persistentSourceIndexes.clear(); + m_persistentProxyIndexes.clear(); + Q_ASSERT(m_proxy->persistent().isEmpty()); + storePersistent(QModelIndex()); + Q_ASSERT(!m_proxy->persistent().isEmpty()); + } + + void checkPersistent() + { + for (int row = 0; row < m_persistentProxyIndexes.size(); ++row) { + QModelIndex updatedProxy = m_persistentProxyIndexes.at(row); + QModelIndex updatedSource = m_persistentSourceIndexes.at(row); + } + for (int row = 0; row < m_persistentProxyIndexes.size(); ++row) { + QModelIndex updatedProxy = m_persistentProxyIndexes.at(row); + QModelIndex updatedSource = m_persistentSourceIndexes.at(row); + QCOMPARE(m_proxy->mapToSource(updatedProxy), updatedSource); + } + } + +private: + AccessibleProxyModel *m_proxy; + QList m_persistentSourceIndexes; + QList m_persistentProxyIndexes; +}; + +void tst_ModelTest::moveSourceItems() +{ + DynamicTreeModel *model = new DynamicTreeModel(this); + AccessibleProxyModel *proxy = new AccessibleProxyModel(this); + proxy->setSourceModel(model); + + ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this); + insertCommand->setStartRow(0); + insertCommand->setEndRow(2); + insertCommand->doCommand(); + + insertCommand = new ModelInsertCommand(model, this); + insertCommand->setAncestorRowNumbers(QList() << 1); + insertCommand->setStartRow(0); + insertCommand->setEndRow(2); + insertCommand->doCommand(); + + ObservingObject observer(proxy); + + ModelMoveCommand *moveCommand = new ModelMoveCommand(model, this); + moveCommand->setStartRow(0); + moveCommand->setEndRow(0); + moveCommand->setDestAncestors(QList() << 1); + moveCommand->setDestRow(0); + moveCommand->doCommand(); +} + + QTEST_MAIN(tst_ModelTest) #include "tst_modeltest.moc" -- cgit v0.12 From 51be60532af93ac6526e76db4085283465c5baa6 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Fri, 18 Dec 2009 16:54:16 +0100 Subject: Fixes bug when using AnchorUnderMouse for GraphicsView transformation. We now automatically enable mouse tracking on the viewport of the GraphicsView when setting AnchorUnderMouse as transformationAnchor or resizeAnchor. Autotest included. Task-number: QTBUG-6835 Reviewed-by: bnilsen --- src/gui/graphicsview/qgraphicsview.cpp | 19 ++++- tests/auto/qgraphicsview/tst_qgraphicsview.cpp | 97 ++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 3 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index ffe64aa..3bb40fb 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -1208,6 +1208,11 @@ void QGraphicsView::setTransformationAnchor(ViewportAnchor anchor) { Q_D(QGraphicsView); d->transformationAnchor = anchor; + + // Ensure mouse tracking is enabled in the case we are using AnchorUnderMouse + // in order to have up-to-date information for centering the view. + if (d->transformationAnchor == AnchorUnderMouse) + d->viewport->setMouseTracking(true); } /*! @@ -1235,6 +1240,11 @@ void QGraphicsView::setResizeAnchor(ViewportAnchor anchor) { Q_D(QGraphicsView); d->resizeAnchor = anchor; + + // Ensure mouse tracking is enabled in the case we are using AnchorUnderMouse + // in order to have up-to-date information for centering the view. + if (d->resizeAnchor == AnchorUnderMouse) + d->viewport->setMouseTracking(true); } /*! @@ -2597,9 +2607,12 @@ void QGraphicsView::setupViewport(QWidget *widget) } // We are only interested in mouse tracking if items - // accept hover events or use non-default cursors. - if (d->scene && (!d->scene->d_func()->allItemsIgnoreHoverEvents - || !d->scene->d_func()->allItemsUseDefaultCursor)) { + // accept hover events or use non-default cursors or if + // AnchorUnderMouse is used as transformation or resize anchor. + if ((d->scene && (!d->scene->d_func()->allItemsIgnoreHoverEvents + || !d->scene->d_func()->allItemsUseDefaultCursor)) + || d->transformationAnchor == AnchorUnderMouse + || d->resizeAnchor == AnchorUnderMouse) { widget->setMouseTracking(true); } diff --git a/tests/auto/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/qgraphicsview/tst_qgraphicsview.cpp index 69df39b..9e4321b 100644 --- a/tests/auto/qgraphicsview/tst_qgraphicsview.cpp +++ b/tests/auto/qgraphicsview/tst_qgraphicsview.cpp @@ -211,6 +211,7 @@ private slots: void centerOnDirtyItem(); void mouseTracking(); void mouseTracking2(); + void mouseTracking3(); void render(); void exposeRegion(); void update_data(); @@ -3403,6 +3404,102 @@ void tst_QGraphicsView::mouseTracking2() QCOMPARE(spy.count(), 1); } +void tst_QGraphicsView::mouseTracking3() +{ + // Mouse tracking should be automatically enabled if AnchorUnderMouse is used for + // view transform or resize. We never disable mouse tracking if it is already enabled. + + { // Make sure we enable mouse tracking when using AnchorUnderMouse for view transformation. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsView view(&scene); + QVERIFY(!view.viewport()->hasMouseTracking()); + + view.setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + QVERIFY(view.viewport()->hasMouseTracking()); + } + + { // Make sure we enable mouse tracking when using AnchorUnderMouse for view resizing. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsView view(&scene); + QVERIFY(!view.viewport()->hasMouseTracking()); + + view.setResizeAnchor(QGraphicsView::AnchorUnderMouse); + QVERIFY(view.viewport()->hasMouseTracking()); + } + + { // Make sure we don't disable mouse tracking in setViewport/setScene (transformation anchor). + QGraphicsView view; + view.setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + QVERIFY(view.viewport()->hasMouseTracking()); + + QWidget *viewport = new QWidget; + view.setViewport(viewport); + QVERIFY(viewport->hasMouseTracking()); + + QGraphicsScene scene(-10000, -10000, 20000, 20000); + view.setScene(&scene); + QVERIFY(viewport->hasMouseTracking()); + } + + { // Make sure we don't disable mouse tracking in setViewport/setScene (resize anchor). + QGraphicsView view; + view.setResizeAnchor(QGraphicsView::AnchorUnderMouse); + QVERIFY(view.viewport()->hasMouseTracking()); + + QWidget *viewport = new QWidget; + view.setViewport(viewport); + QVERIFY(viewport->hasMouseTracking()); + + QGraphicsScene scene(-10000, -10000, 20000, 20000); + view.setScene(&scene); + QVERIFY(viewport->hasMouseTracking()); + } + + // Make sure we don't disable mouse tracking when adding an item (transformation anchor). + { // Adding an item to the scene before the scene is set on the view. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsRectItem *item = new QGraphicsRectItem(10, 10, 10, 10); + scene.addItem(item); + + QGraphicsView view; + view.setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + view.setScene(&scene); + QVERIFY(view.viewport()->hasMouseTracking()); + } + + { // Adding an item to the scene after the scene is set on the view. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsView view(&scene); + view.setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + + QGraphicsRectItem *item = new QGraphicsRectItem(10, 10, 10, 10); + scene.addItem(item); + QVERIFY(view.viewport()->hasMouseTracking()); + } + + // Make sure we don't disable mouse tracking when adding an item (resize anchor). + { // Adding an item to the scene before the scene is set on the view. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsRectItem *item = new QGraphicsRectItem(10, 10, 10, 10); + scene.addItem(item); + + QGraphicsView view; + view.setResizeAnchor(QGraphicsView::AnchorUnderMouse); + view.setScene(&scene); + QVERIFY(view.viewport()->hasMouseTracking()); + } + + { // Adding an item to the scene after the scene is set on the view. + QGraphicsScene scene(-10000, -10000, 20000, 20000); + QGraphicsView view(&scene); + view.setResizeAnchor(QGraphicsView::AnchorUnderMouse); + + QGraphicsRectItem *item = new QGraphicsRectItem(10, 10, 10, 10); + scene.addItem(item); + QVERIFY(view.viewport()->hasMouseTracking()); + } +} + class RenderTester : public QGraphicsRectItem { public: -- cgit v0.12 From f708b248aec810a1dcad1f13e1c16390244c9834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Fri, 18 Dec 2009 17:48:10 +0100 Subject: Fixed QPixmap::load() to not modify referenced copies. This is a bad regression from 4.5. QPixmap::load() would modify all references to the same QPixmap object. Task-number: QTBUG-6840 Reviewed-by: Kim --- src/gui/image/qpixmap.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index 617cfe5..7e4597e 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -831,14 +831,13 @@ bool QPixmap::load(const QString &fileName, const char *format, Qt::ImageConvers if (QPixmapCache::find(key, *this)) return true; - if (!data) - data = QPixmapData::create(0, 0, QPixmapData::PixmapType); - - if (data->fromFile(fileName, format, flags)) { + QPixmapData *tmp = QPixmapData::create(0, 0, QPixmapData::PixmapType); + if (tmp->fromFile(fileName, format, flags)) { + data = tmp; QPixmapCache::insert(key, *this); return true; } - + delete tmp; return false; } -- cgit v0.12