diff options
author | David Boddie <dboddie@trolltech.com> | 2009-07-13 11:49:08 (GMT) |
---|---|---|
committer | David Boddie <dboddie@trolltech.com> | 2009-07-13 11:49:08 (GMT) |
commit | 821c7c1486beb6d5e65bd3ac8ba74752fec37fe8 (patch) | |
tree | 042ad778ccdfacd2dc1b4751a98e83889e5e10e3 /src | |
parent | 85fbffa12bcd38b08030561335305c3226312bc6 (diff) | |
parent | cc24c46c117248ecb98200416e7f25375e6bb476 (diff) | |
download | Qt-821c7c1486beb6d5e65bd3ac8ba74752fec37fe8.zip Qt-821c7c1486beb6d5e65bd3ac8ba74752fec37fe8.tar.gz Qt-821c7c1486beb6d5e65bd3ac8ba74752fec37fe8.tar.bz2 |
Merge branch 'master' of git@scm.dev.nokia.troll.no:qt/qt
Diffstat (limited to 'src')
98 files changed, 3850 insertions, 2464 deletions
diff --git a/src/3rdparty/phonon/ds9/videowidget.cpp b/src/3rdparty/phonon/ds9/videowidget.cpp index 0ef653f..34ff8cb 100644 --- a/src/3rdparty/phonon/ds9/videowidget.cpp +++ b/src/3rdparty/phonon/ds9/videowidget.cpp @@ -153,7 +153,7 @@ namespace Phonon } } else if (!isEmbedded()) { m_currentRenderer = m_node->switchRendering(m_currentRenderer); - setAttribute(Qt::WA_PaintOnScreen, true); + setAttribute(Qt::WA_PaintOnScreen, false); } } diff --git a/src/corelib/animation/qpropertyanimation.cpp b/src/corelib/animation/qpropertyanimation.cpp index 7526a81..5f224aa 100644 --- a/src/corelib/animation/qpropertyanimation.cpp +++ b/src/corelib/animation/qpropertyanimation.cpp @@ -100,10 +100,6 @@ QT_BEGIN_NAMESPACE -typedef QPair<QObject *, QByteArray> QPropertyAnimationPair; -typedef QHash<QPropertyAnimationPair, QPropertyAnimation*> QPropertyAnimationHash; -Q_GLOBAL_STATIC(QPropertyAnimationHash, _q_runningAnimations) - void QPropertyAnimationPrivate::updateMetaProperty() { if (!target || propertyName.isEmpty()) @@ -286,19 +282,21 @@ void QPropertyAnimation::updateState(QAbstractAnimation::State oldState, QPropertyAnimation *animToStop = 0; { - QPropertyAnimationHash * hash = _q_runningAnimations(); - QMutexLocker locker(QMutexPool::globalInstanceGet(hash)); + QMutexLocker locker(QMutexPool::globalInstanceGet(&staticMetaObject)); + typedef QPair<QObject *, QByteArray> QPropertyAnimationPair; + typedef QHash<QPropertyAnimationPair, QPropertyAnimation*> QPropertyAnimationHash; + static QPropertyAnimationHash hash; QPropertyAnimationPair key(d->target, d->propertyName); if (newState == Running) { d->updateMetaProperty(); - animToStop = hash->value(key, 0); - hash->insert(key, this); + animToStop = hash.value(key, 0); + hash.insert(key, this); // update the default start value if (oldState == Stopped) { d->setDefaultStartValue(d->target->property(d->propertyName.constData())); } - } else if (hash->value(key) == this) { - hash->remove(key); + } else if (hash.value(key) == this) { + hash.remove(key); } } diff --git a/src/corelib/codecs/qiconvcodec.cpp b/src/corelib/codecs/qiconvcodec.cpp index 1bf76ea..188ac8c 100644 --- a/src/corelib/codecs/qiconvcodec.cpp +++ b/src/corelib/codecs/qiconvcodec.cpp @@ -225,10 +225,11 @@ QString QIconvCodec::convertToUnicode(const char* chars, int len, ConverterState char *inBytes = const_cast<char *>(chars); #endif + QByteArray in; if (remainingCount) { // we have to prepend the remaining bytes from the previous conversion inBytesLeft += remainingCount; - QByteArray in(inBytesLeft, Qt::Uninitialized); + in.resize(inBytesLeft); inBytes = in.data(); memcpy(in.data(), remainingBuffer, remainingCount); @@ -362,9 +363,10 @@ QByteArray QIconvCodec::convertFromUnicode(const QChar *uc, int len, ConverterSt inBytes = const_cast<char *>(reinterpret_cast<const char *>(uc)); inBytesLeft = len * sizeof(QChar); + QByteArray in; if (convState && convState->remainingChars) { // we have one surrogate char to be prepended - QByteArray in(sizeof(QChar) + len, Qt::Uninitialized); + in.resize(sizeof(QChar) + len); inBytes = in.data(); QChar remaining = convState->state_data[0]; diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index a139a55..d6c708c 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -2062,7 +2062,7 @@ public: inline bool operator!() const { return !i; } - inline bool testFlag(Enum f) const { return (i & f) == f; } + inline bool testFlag(Enum f) const { return (i & f) == f && (f != 0 || i == f ); } }; #define Q_DECLARE_FLAGS(Flags, Enum)\ diff --git a/src/corelib/io/qbuffer.cpp b/src/corelib/io/qbuffer.cpp index aed5b82..3883d30 100644 --- a/src/corelib/io/qbuffer.cpp +++ b/src/corelib/io/qbuffer.cpp @@ -452,7 +452,7 @@ qint64 QBuffer::writeData(const char *data, qint64 len) */ void QBuffer::connectNotify(const char *signal) { - if (strcmp(signal + 1, "readyRead()") == 0 || strcmp(signal + 1, "bytesWritten(qint64)")) + if (strcmp(signal + 1, "readyRead()") == 0 || strcmp(signal + 1, "bytesWritten(qint64)") == 0) d_func()->signalConnectionCount++; } diff --git a/src/corelib/kernel/qabstracteventdispatcher.cpp b/src/corelib/kernel/qabstracteventdispatcher.cpp index a98d005..e2682f5 100644 --- a/src/corelib/kernel/qabstracteventdispatcher.cpp +++ b/src/corelib/kernel/qabstracteventdispatcher.cpp @@ -398,20 +398,23 @@ void QAbstractEventDispatcher::closingDown() */ /*! - Sets the event filter \a filter. Returns a pointer to the filter - function previously defined. - - The event filter is a function that receives all messages taken - from the system event loop before the event is dispatched to the - respective target. This includes messages that are not sent to Qt + Replaces the event filter function for this + QAbstractEventDispatcher with \a filter and returns the replaced + event filter function. Only the current event filter function is + called. If you want to use both filter functions, save the + replaced EventFilter in a place where yours can call it. + + The event filter function set here is called for all messages + taken from the system event loop before the event is dispatched to + the respective target, including the messages not meant for Qt objects. - The function can return true to stop the event to be processed by - Qt, or false to continue with the standard event processing. + The event filter function should return true if the message should + be filtered, (i.e. stopped). It should return false to allow + processing the message to continue. - Only one filter can be defined, but the filter can use the return - value to call the previously set event filter. By default, no - filter is set (i.e. the function returns 0). + By default, no event filter function is set (i.e., this function + returns a null EventFilter the first time it is called). */ QAbstractEventDispatcher::EventFilter QAbstractEventDispatcher::setEventFilter(EventFilter filter) { diff --git a/src/corelib/kernel/qcore_unix_p.h b/src/corelib/kernel/qcore_unix_p.h index 1bf2425..8d43897 100644 --- a/src/corelib/kernel/qcore_unix_p.h +++ b/src/corelib/kernel/qcore_unix_p.h @@ -69,19 +69,19 @@ struct sockaddr; -QT_BEGIN_NAMESPACE - #if defined(Q_OS_LINUX) && defined(O_CLOEXEC) && defined(__GLIBC__) && (__GLIBC__ * 0x100 + __GLIBC_MINOR__) >= 0x0204 // Linux supports thread-safe FD_CLOEXEC # define QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC 1 +QT_BEGIN_NAMESPACE namespace QtLibcSupplement { Q_CORE_EXPORT int accept4(int, sockaddr *, QT_SOCKLEN_T *, int flags); Q_CORE_EXPORT int dup3(int oldfd, int newfd, int flags); Q_CORE_EXPORT int pipe2(int pipes[], int flags); } +QT_END_NAMESPACE +using namespace QT_PREPEND_NAMESPACE(QtLibcSupplement); -using namespace QtLibcSupplement; #else # define QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC 0 #endif @@ -91,6 +91,7 @@ using namespace QtLibcSupplement; var = cmd; \ } while (var == -1 && errno == EINTR) +QT_BEGIN_NAMESPACE // don't call QT_OPEN or ::open // call qt_safe_open diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 054be70..706dc54 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -2183,21 +2183,37 @@ void QCoreApplication::removeLibraryPath(const QString &path) /*! \fn EventFilter QCoreApplication::setEventFilter(EventFilter filter) - Sets the event filter \a filter. Returns a pointer to the filter - function previously defined. - - The event filter is a function that is called for every message - received in all threads. This does \e not include messages to + Replaces the event filter function for the QCoreApplication with + \a filter and returns the pointer to the replaced event filter + function. Only the current event filter function is called. If you + want to use both filter functions, save the replaced EventFilter + in a place where yours can call it. + + The event filter function set here is called for all messages + received by all threads meant for all Qt objects. It is \e not + called for messages that are not meant for Qt objects. + + The event filter function should return true if the message should + be filtered, (i.e. stopped). It should return false to allow + processing the message to continue. + + By default, no event filter function is set (i.e., this function + returns a null EventFilter the first time it is called). + + \note The filter function set here receives native messages, + i.e. MSG or XEvent structs, that are going to Qt objects. It is + called by QCoreApplication::filterEvent(). If the filter function + returns false to indicate the message should be processed further, + the native message can then be translated into a QEvent and + handled by the standard Qt \l{QEvent} {event} filering, e.g. + QObject::installEventFilter(). + + \note The filter function set here is different form the filter + function set via QAbstractEventDispatcher::setEventFilter(), which + gets all messages received by its thread, even messages meant for objects that are not handled by Qt. - The function can return true to stop the event to be processed by - Qt, or false to continue with the standard event processing. - - Only one filter can be defined, but the filter can use the return - value to call the previously set event filter. By default, no - filter is set (i.e., the function returns 0). - - \sa installEventFilter() + \sa QObject::installEventFilter(), QAbstractEventDispatcher::setEventFilter() */ QCoreApplication::EventFilter QCoreApplication::setEventFilter(QCoreApplication::EventFilter filter) diff --git a/src/corelib/plugin/qplugin.h b/src/corelib/plugin/qplugin.h index ff2b412..233b4f9 100644 --- a/src/corelib/plugin/qplugin.h +++ b/src/corelib/plugin/qplugin.h @@ -63,21 +63,6 @@ typedef QObject *(*QtPluginInstanceFunction)(); void Q_CORE_EXPORT qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunction function); -struct qt_plugin_instance_deleter -{ - qt_plugin_instance_deleter(QPointer<QObject> &instance) - : instance_(instance) - { - } - - ~qt_plugin_instance_deleter() - { - delete instance_; - } - - QPointer<QObject> &instance_; -}; - #define Q_IMPORT_PLUGIN(PLUGIN) \ extern QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance_##PLUGIN(); \ class Static##PLUGIN##PluginInstance{ \ @@ -91,10 +76,8 @@ struct qt_plugin_instance_deleter #define Q_PLUGIN_INSTANCE(IMPLEMENTATION) \ { \ static QT_PREPEND_NAMESPACE(QPointer)<QT_PREPEND_NAMESPACE(QObject)> _instance; \ - if (!_instance) { \ - static QT_PREPEND_NAMESPACE(qt_plugin_instance_deleter) deleter(_instance); \ + if (!_instance) \ _instance = new IMPLEMENTATION; \ - } \ return _instance; \ } diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index 24522f2..2e31c6d 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -261,9 +261,14 @@ void QAdoptedThread::run() Returns the thread handle of the currently executing thread. \warning The handle returned by this function is used for internal - purposes and should not be used in any application code. On - Windows, the returned value is a pseudo-handle for the current - thread that cannot be used for numerical comparison. + purposes and should not be used in any application code. + + \warning On Windows, the returned value is a pseudo-handle for the + current thread. It can't be used for numerical comparison. i.e., + this function returns the DWORD (Windows-Thread ID) returned by + the Win32 function getCurrentThreadId(), not the HANDLE + (Windows-Thread HANDLE) returned by the Win32 function + getCurrentThread(). */ /*! diff --git a/src/corelib/tools/qringbuffer_p.h b/src/corelib/tools/qringbuffer_p.h index f3daca7..008c068 100644 --- a/src/corelib/tools/qringbuffer_p.h +++ b/src/corelib/tools/qringbuffer_p.h @@ -303,6 +303,9 @@ public: // read an unspecified amount (will read the first buffer) inline QByteArray read() { + if (bufferSize == 0) + return QByteArray(); + // multiple buffers, just take the first one if (head == 0 && tailBuffer != 0) { QByteArray qba = buffers.takeFirst(); @@ -325,7 +328,7 @@ public: // We can avoid by initializing the QRingBuffer with basicBlockSize of 0 // and only using this read() function. QByteArray qba(readPointer(), nextDataBlockSize()); - buffers.takeFirst(); + buffers.removeFirst(); head = 0; if (tailBuffer == 0) { buffers << QByteArray(); diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 6bb0d8e..235c603 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -1235,6 +1235,8 @@ inline int QStringRef::localeAwareCompare(const QStringRef &s1, const QStringRef QT_END_NAMESPACE +QT_END_HEADER + #ifdef QT_USE_FAST_CONCATENATION #include <QtCore/qstringbuilder.h> #endif diff --git a/src/corelib/tools/qstringbuilder.h b/src/corelib/tools/qstringbuilder.h index 852c072..3b43253 100644 --- a/src/corelib/tools/qstringbuilder.h +++ b/src/corelib/tools/qstringbuilder.h @@ -69,7 +69,7 @@ private: }; -template <typename T> class QConcatenable {}; +template <typename T> struct QConcatenable {}; template <typename A, typename B> class QStringBuilder @@ -202,6 +202,18 @@ template <> struct QConcatenable<const char *> *out++ = QLatin1Char(*a++); } }; + +template <> struct QConcatenable<QByteArray> +{ + typedef QByteArray type; + static int size(const QByteArray &ba) { return qstrnlen(ba.constData(), ba.size()); } + static inline void appendTo(const QByteArray &ba, QChar *&out) + { + const char *data = ba.constData(); + while (*data) + *out++ = QLatin1Char(*data++); + } +}; #endif template <typename A, typename B> diff --git a/src/dbus/qdbusconnection.cpp b/src/dbus/qdbusconnection.cpp index 6777aa5..14cadd9 100644 --- a/src/dbus/qdbusconnection.cpp +++ b/src/dbus/qdbusconnection.cpp @@ -973,8 +973,15 @@ QDBusConnection QDBusConnection::systemBus() } /*! + \nonreentrant + Returns the connection that sent the signal, if called in a slot activated by QDBus; otherwise it returns 0. + + \note Please avoid this function. This function is not thread-safe, so if + there's any other thread delivering a D-Bus call, this function may return + the wrong connection. In new code, please use QDBusContext::connection() + (see that class for a description on how to use it). */ QDBusConnection QDBusConnection::sender() { diff --git a/src/gui/dialogs/qfiledialog_p.h b/src/gui/dialogs/qfiledialog_p.h index d798f9d..4c599cc 100644 --- a/src/gui/dialogs/qfiledialog_p.h +++ b/src/gui/dialogs/qfiledialog_p.h @@ -73,7 +73,6 @@ #include <qabstractproxymodel.h> #include <qcompleter.h> #include <qpointer.h> -#include <qtimeline.h> #include <qdebug.h> #include "qsidebar_p.h" diff --git a/src/gui/dialogs/qsidebar.cpp b/src/gui/dialogs/qsidebar.cpp index 1915e21..e4821ad 100644 --- a/src/gui/dialogs/qsidebar.cpp +++ b/src/gui/dialogs/qsidebar.cpp @@ -247,11 +247,16 @@ void QUrlModel::addUrls(const QList<QUrl> &list, int row, bool move) QUrl url = list.at(i); if (!url.isValid() || url.scheme() != QLatin1String("file")) continue; + //this makes sure the url is clean + const QString cleanUrl = QDir::cleanPath(url.toLocalFile()); + url = QUrl::fromLocalFile(cleanUrl); + for (int j = 0; move && j < rowCount(); ++j) { + QString local = index(j, 0).data(UrlRole).toUrl().toLocalFile(); #if defined(Q_OS_WIN) - if (QDir::cleanPath(index(j, 0).data(UrlRole).toUrl().toLocalFile()).toLower() == QDir::cleanPath(url.toLocalFile()).toLower()) { + if (index(j, 0).data(UrlRole).toUrl().toLocalFile().toLower() == cleanUrl.toLower()) { #else - if (QDir::cleanPath(index(j, 0).data(UrlRole).toUrl().toLocalFile()) == QDir::cleanPath(url.toLocalFile())) { + if (index(j, 0).data(UrlRole).toUrl().toLocalFile() == cleanUrl) { #endif removeRow(j); if (j <= row) @@ -260,12 +265,12 @@ void QUrlModel::addUrls(const QList<QUrl> &list, int row, bool move) } } row = qMax(row, 0); - QModelIndex idx = fileSystemModel->index(url.toLocalFile()); + QModelIndex idx = fileSystemModel->index(cleanUrl); if (!fileSystemModel->isDir(idx)) continue; insertRows(row, 1); setUrl(index(row, 0), url, idx); - watching.append(QPair<QModelIndex, QString>(idx, url.toLocalFile())); + watching.append(qMakePair(idx, cleanUrl)); } } diff --git a/src/gui/graphicsview/graphicsview.pri b/src/gui/graphicsview/graphicsview.pri index 4cee6d6..0c0747e 100644 --- a/src/gui/graphicsview/graphicsview.pri +++ b/src/gui/graphicsview/graphicsview.pri @@ -1,37 +1,42 @@ # Qt graphicsview module -HEADERS += graphicsview/qgraphicsitem.h \ +HEADERS += graphicsview/qgraphicsgridlayout.h \ + graphicsview/qgraphicsitem.h \ graphicsview/qgraphicsitem_p.h \ graphicsview/qgraphicsitemanimation.h \ - graphicsview/qgraphicsscene.h \ - graphicsview/qgraphicsscene_p.h \ - graphicsview/qgraphicsscene_bsp_p.h \ - graphicsview/qgraphicssceneevent.h \ - graphicsview/qgraphicsview_p.h \ - graphicsview/qgraphicsview.h -SOURCES += graphicsview/qgraphicsitem.cpp \ - graphicsview/qgraphicsitemanimation.cpp \ - graphicsview/qgraphicsscene.cpp \ - graphicsview/qgraphicsscene_bsp.cpp \ - graphicsview/qgraphicssceneevent.cpp \ - graphicsview/qgraphicsview.cpp - -# Widgets on the canvas -HEADERS += graphicsview/qgraphicslayout.h \ + graphicsview/qgraphicslayout.h \ graphicsview/qgraphicslayout_p.h \ graphicsview/qgraphicslayoutitem.h \ graphicsview/qgraphicslayoutitem_p.h \ graphicsview/qgraphicslinearlayout.h \ + graphicsview/qgraphicsproxywidget.h \ + graphicsview/qgraphicsscene.h \ + graphicsview/qgraphicsscene_bsp_p.h \ + graphicsview/qgraphicsscene_p.h \ + graphicsview/qgraphicsscenebsptreeindex_p.h \ + graphicsview/qgraphicssceneevent.h \ + graphicsview/qgraphicssceneindex_p.h \ + graphicsview/qgraphicsscenelinearindex_p.h \ + graphicsview/qgraphicsview.h \ + graphicsview/qgraphicsview_p.h \ graphicsview/qgraphicswidget.h \ graphicsview/qgraphicswidget_p.h \ - graphicsview/qgridlayoutengine_p.h \ - graphicsview/qgraphicsproxywidget.h \ - graphicsview/qgraphicsgridlayout.h -SOURCES += graphicsview/qgraphicslayout.cpp \ + graphicsview/qgridlayoutengine_p.h + +SOURCES += graphicsview/qgraphicsgridlayout.cpp \ + graphicsview/qgraphicsitem.cpp \ + graphicsview/qgraphicsitemanimation.cpp \ + graphicsview/qgraphicslayout.cpp \ graphicsview/qgraphicslayout_p.cpp \ graphicsview/qgraphicslayoutitem.cpp \ graphicsview/qgraphicslinearlayout.cpp \ + graphicsview/qgraphicsproxywidget.cpp \ + graphicsview/qgraphicsscene.cpp \ + graphicsview/qgraphicsscene_bsp.cpp \ + graphicsview/qgraphicsscenebsptreeindex.cpp \ + graphicsview/qgraphicssceneevent.cpp \ + graphicsview/qgraphicssceneindex.cpp \ + graphicsview/qgraphicsscenelinearindex.cpp \ + graphicsview/qgraphicsview.cpp \ graphicsview/qgraphicswidget.cpp \ graphicsview/qgraphicswidget_p.cpp \ - graphicsview/qgridlayoutengine.cpp \ - graphicsview/qgraphicsproxywidget.cpp \ - graphicsview/qgraphicsgridlayout.cpp + graphicsview/qgridlayoutengine.cpp diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index a5ee7e6..03014d8 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -558,6 +558,7 @@ #include "qgraphicsview.h" #include "qgraphicswidget.h" #include "qgraphicsproxywidget.h" +#include "qgraphicsscenebsptreeindex_p.h" #include <QtCore/qbitarray.h> #include <QtCore/qdebug.h> #include <QtCore/qpoint.h> @@ -588,17 +589,6 @@ QT_BEGIN_NAMESPACE -// QRectF::intersects() returns false always if either the source or target -// rectangle's width or height are 0. This works around that problem. -static inline void _q_adjustRect(QRectF *rect) -{ - Q_ASSERT(rect); - if (!rect->width()) - rect->adjust(-0.00001, 0, 0.00001, 0); - if (!rect->height()) - rect->adjust(0, -0.00001, 0, 0.00001); -} - static inline void _q_adjustRect(QRect *rect) { Q_ASSERT(rect); @@ -832,6 +822,42 @@ void QGraphicsItemPrivate::combineTransformFromParent(QTransform *x, const QTran } } +void QGraphicsItemPrivate::updateSceneTransformFromParent() +{ + if (parent) { + Q_ASSERT(!parent->d_ptr->dirtySceneTransform); + if (parent->d_ptr->sceneTransformTranslateOnly) { + sceneTransform = QTransform::fromTranslate(parent->d_ptr->sceneTransform.dx() + pos.x(), + parent->d_ptr->sceneTransform.dy() + pos.y()); + } else { + sceneTransform = parent->d_ptr->sceneTransform; + sceneTransform.translate(pos.x(), pos.y()); + } + if (transformData) { + sceneTransform = transformData->computedFullTransform(&sceneTransform); + sceneTransformTranslateOnly = (sceneTransform.type() <= QTransform::TxTranslate); + } else { + sceneTransformTranslateOnly = parent->d_ptr->sceneTransformTranslateOnly; + } + } else if (!transformData) { + sceneTransform = QTransform::fromTranslate(pos.x(), pos.y()); + sceneTransformTranslateOnly = 1; + } else if (transformData->onlyTransform) { + sceneTransform = transformData->transform; + if (!pos.isNull()) + sceneTransform *= QTransform::fromTranslate(pos.x(), pos.y()); + sceneTransformTranslateOnly = (sceneTransform.type() <= QTransform::TxTranslate); + } else if (pos.isNull()) { + sceneTransform = transformData->computedFullTransform(); + sceneTransformTranslateOnly = (sceneTransform.type() <= QTransform::TxTranslate); + } else { + sceneTransform = QTransform::fromTranslate(pos.x(), pos.y()); + sceneTransform = transformData->computedFullTransform(&sceneTransform); + sceneTransformTranslateOnly = (sceneTransform.type() <= QTransform::TxTranslate); + } + dirtySceneTransform = 0; +} + /*! \internal @@ -871,6 +897,11 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) if (newParent == parent) return; + if (scene) { + // Deliver the change to the index + scene->d_func()->index->itemChange(q, QGraphicsItem::ItemParentChange, newParentVariant); + } + if (QGraphicsWidget *w = isWidget ? static_cast<QGraphicsWidget *>(q) : q->parentWidget()) { // Update the child focus chain; when reparenting a widget that has a // focus child, ensure that that focus child clears its focus child @@ -958,12 +989,6 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) } } - if (scene) { - // Invalidate any sort caching; arrival of a new item means we need to - // resort. - scene->d_func()->invalidateSortCache(); - } - // Resolve depth. resolveDepth(parent ? parent->d_ptr->depth : -1); dirtySceneTransform = 1; @@ -1442,6 +1467,8 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags) flags = GraphicsItemFlags(itemChange(ItemFlagsChange, quint32(flags)).toUInt()); if (quint32(d_ptr->flags) == quint32(flags)) return; + if (d_ptr->scene) + d_ptr->scene->d_func()->index->itemChange(this, ItemFlagsChange, quint32(flags)); // Flags that alter the geometry of the item (or its children). const quint32 geomChangeFlagsMask = (ItemClipsChildrenToShape | ItemClipsToShape | ItemIgnoresTransformations); @@ -1800,6 +1827,8 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bo if (q_ptr->isSelected()) q_ptr->setSelected(false); } else { + geometryChanged = 1; + paintedViewBoundingRectsNeedRepaint = 1; if (isWidget && scene) { QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(q_ptr); if (widget->windowType() == Qt::Popup) @@ -3160,7 +3189,8 @@ void QGraphicsItem::setTransformOrigin(const QPointF &origin) */ QMatrix QGraphicsItem::sceneMatrix() const { - return sceneTransform().toAffine(); + d_ptr->ensureSceneTransform(); + return d_ptr->sceneTransform.toAffine(); } @@ -3183,16 +3213,7 @@ QMatrix QGraphicsItem::sceneMatrix() const */ QTransform QGraphicsItem::sceneTransform() const { - if (d_ptr->dirtySceneTransform) { - // This item and all its descendants have dirty scene transforms. - // We're about to validate this item's scene transform, so we have to - // invalidate all the children; otherwise there's no way for the descendants - // to detect that the ancestor has changed. - d_ptr->invalidateChildrenSceneTransform(); - } - - QGraphicsItem *that = const_cast<QGraphicsItem *>(this); - d_ptr->ensureSceneTransformRecursive(&that); + d_ptr->ensureSceneTransform(); return d_ptr->sceneTransform; } @@ -3222,8 +3243,10 @@ QTransform QGraphicsItem::sceneTransform() const QTransform QGraphicsItem::deviceTransform(const QTransform &viewportTransform) const { // Ensure we return the standard transform if we're not untransformable. - if (!d_ptr->itemIsUntransformable()) - return sceneTransform() * viewportTransform; + if (!d_ptr->itemIsUntransformable()) { + d_ptr->ensureSceneTransform(); + return d_ptr->sceneTransform * viewportTransform; + } // Find the topmost item that ignores view transformations. const QGraphicsItem *untransformedAncestor = this; @@ -3242,7 +3265,8 @@ QTransform QGraphicsItem::deviceTransform(const QTransform &viewportTransform) c } // First translate the base untransformable item. - QPointF mappedPoint = (untransformedAncestor->sceneTransform() * viewportTransform).map(QPointF(0, 0)); + untransformedAncestor->d_ptr->ensureSceneTransform(); + QPointF mappedPoint = (untransformedAncestor->d_ptr->sceneTransform * viewportTransform).map(QPointF(0, 0)); // COMBINE QTransform matrix = QTransform::fromTranslate(mappedPoint.x(), mappedPoint.y()); @@ -3335,8 +3359,11 @@ QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) co // Find the closest common ancestor. If the two items don't share an // ancestor, then the only way is to combine their scene transforms. const QGraphicsItem *commonAncestor = commonAncestorItem(other); - if (!commonAncestor) - return sceneTransform() * other->sceneTransform().inverted(ok); + if (!commonAncestor) { + d_ptr->ensureSceneTransform(); + other->d_ptr->ensureSceneTransform(); + return d_ptr->sceneTransform * other->d_ptr->sceneTransform.inverted(ok); + } // If the two items are cousins (in sibling branches), map both to the // common ancestor, and combine the two transforms. @@ -3627,18 +3654,20 @@ void QGraphicsItem::setZValue(qreal z) qreal newZ = qreal(newZVariant.toDouble()); if (newZ == d_ptr->z) return; + + if (d_ptr->scene) { + // Z Value has changed, we have to notify the index. + d_ptr->scene->d_func()->index->itemChange(this, ItemZValueChange, newZVariant); + } + d_ptr->z = newZ; if (d_ptr->parent) d_ptr->parent->d_ptr->needSortChildren = 1; else if (d_ptr->scene) d_ptr->scene->d_func()->needSortTopLevelItems = 1; - if (d_ptr->scene) { + if (d_ptr->scene) d_ptr->scene->d_func()->markDirty(this, QRectF(), /*invalidateChildren=*/true); - // Invalidate any sort caching; arrival of a new item means we need to - // resort. - d_ptr->scene->d_func()->invalidateSortCache(); - } itemChange(ItemZValueHasChanged, newZVariant); @@ -3729,7 +3758,13 @@ QRectF QGraphicsItem::sceneBoundingRect() const QRectF br = boundingRect(); br.translate(offset); - return !parentItem ? br : parentItem->sceneTransform().mapRect(br); + if (!parentItem) + return br; + if (parentItem->d_ptr->hasTranslateOnlySceneTransform()) { + br.translate(parentItem->d_ptr->sceneTransform.dx(), parentItem->d_ptr->sceneTransform.dy()); + return br; + } + return parentItem->d_ptr->sceneTransform.mapRect(br); } /*! @@ -4104,7 +4139,7 @@ bool QGraphicsItem::isObscuredBy(const QGraphicsItem *item) const { if (!item) return false; - return QGraphicsScenePrivate::closestItemFirst_withoutCache(item, this) + return QGraphicsSceneBspTreeIndexPrivate::closestItemFirst_withoutCache(item, this) && qt_QGraphicsItem_isObscured(this, item, boundingRect()); } @@ -4297,12 +4332,11 @@ bool QGraphicsItemPrivate::discardUpdateRequest(bool ignoreClipping, bool ignore { // No scene, or if the scene is updating everything, means we have nothing // to do. The only exception is if the scene tracks the growing scene rect. - return (!visible && !ignoreVisibleBit) + return !scene + || (!visible && !ignoreVisibleBit && !this->ignoreVisible) || (!ignoreDirtyBit && fullUpdatePending) - || !scene - || (scene->d_func()->updateAll && scene->d_func()->hasSceneRect) || (!ignoreClipping && (childrenClippedToShape() && isClippedAway())) - || (!ignoreOpacity && childrenCombineOpacity() && isFullyTransparent()); + || (!ignoreOpacity && !this->ignoreOpacity && childrenCombineOpacity() && isFullyTransparent()); } /*! @@ -4323,6 +4357,7 @@ void QGraphicsItemPrivate::resolveDepth(int parentDepth) void QGraphicsItemPrivate::addChild(QGraphicsItem *child) { needSortChildren = 1; + child->d_ptr->siblingIndex = children.size(); children.append(child); } @@ -4332,6 +4367,10 @@ void QGraphicsItemPrivate::addChild(QGraphicsItem *child) void QGraphicsItemPrivate::removeChild(QGraphicsItem *child) { children.removeOne(child); + // NB! Do not use children.removeAt(child->d_ptr->siblingIndex) because + // the child is not guaranteed to be at the index after the list is sorted. + // (see ensureSortedChildren()). + child->d_ptr->siblingIndex = -1; } /*! @@ -4492,9 +4531,22 @@ void QGraphicsItemPrivate::ensureSceneTransformRecursive(QGraphicsItem **topMost } // COMBINE my transform with the parent's scene transform. - sceneTransform = parent ? parent->d_ptr->sceneTransform : QTransform(); - combineTransformFromParent(&sceneTransform); - dirtySceneTransform = 0; + updateSceneTransformFromParent(); + Q_ASSERT(!dirtySceneTransform); +} + +void QGraphicsItemPrivate::ensureSceneTransform() +{ + if (dirtySceneTransform) { + // This item and all its descendants have dirty scene transforms. + // We're about to validate this item's scene transform, so we have to + // invalidate all the children; otherwise there's no way for the descendants + // to detect that the ancestor has changed. + invalidateChildrenSceneTransform(); + } + + QGraphicsItem *that = q_func(); + ensureSceneTransformRecursive(&that); } /*! @@ -4835,7 +4887,9 @@ QPointF QGraphicsItem::mapToParent(const QPointF &point) const */ QPointF QGraphicsItem::mapToScene(const QPointF &point) const { - return sceneTransform().map(point); + if (d_ptr->hasTranslateOnlySceneTransform()) + return QPointF(point.x() + d_ptr->sceneTransform.dx(), point.y() + d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.map(point); } /*! @@ -4902,7 +4956,9 @@ QPolygonF QGraphicsItem::mapToParent(const QRectF &rect) const */ QPolygonF QGraphicsItem::mapToScene(const QRectF &rect) const { - return sceneTransform().map(rect); + if (d_ptr->hasTranslateOnlySceneTransform()) + return rect.translated(d_ptr->sceneTransform.dx(), d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.map(rect); } /*! @@ -4975,7 +5031,9 @@ QRectF QGraphicsItem::mapRectToParent(const QRectF &rect) const */ QRectF QGraphicsItem::mapRectToScene(const QRectF &rect) const { - return sceneTransform().mapRect(rect); + if (d_ptr->hasTranslateOnlySceneTransform()) + return rect.translated(d_ptr->sceneTransform.dx(), d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.mapRect(rect); } /*! @@ -5049,7 +5107,9 @@ QRectF QGraphicsItem::mapRectFromParent(const QRectF &rect) const */ QRectF QGraphicsItem::mapRectFromScene(const QRectF &rect) const { - return sceneTransform().inverted().mapRect(rect); + if (d_ptr->hasTranslateOnlySceneTransform()) + return rect.translated(-d_ptr->sceneTransform.dx(), -d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.inverted().mapRect(rect); } /*! @@ -5101,7 +5161,9 @@ QPolygonF QGraphicsItem::mapToParent(const QPolygonF &polygon) const */ QPolygonF QGraphicsItem::mapToScene(const QPolygonF &polygon) const { - return sceneTransform().map(polygon); + if (d_ptr->hasTranslateOnlySceneTransform()) + return polygon.translated(d_ptr->sceneTransform.dx(), d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.map(polygon); } /*! @@ -5146,7 +5208,9 @@ QPainterPath QGraphicsItem::mapToParent(const QPainterPath &path) const */ QPainterPath QGraphicsItem::mapToScene(const QPainterPath &path) const { - return sceneTransform().map(path); + if (d_ptr->hasTranslateOnlySceneTransform()) + return path.translated(d_ptr->sceneTransform.dx(), d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.map(path); } /*! @@ -5207,7 +5271,9 @@ QPointF QGraphicsItem::mapFromParent(const QPointF &point) const */ QPointF QGraphicsItem::mapFromScene(const QPointF &point) const { - return sceneTransform().inverted().map(point); + if (d_ptr->hasTranslateOnlySceneTransform()) + return QPointF(point.x() - d_ptr->sceneTransform.dx(), point.y() - d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.inverted().map(point); } /*! @@ -5275,7 +5341,9 @@ QPolygonF QGraphicsItem::mapFromParent(const QRectF &rect) const */ QPolygonF QGraphicsItem::mapFromScene(const QRectF &rect) const { - return sceneTransform().inverted().map(rect); + if (d_ptr->hasTranslateOnlySceneTransform()) + return rect.translated(-d_ptr->sceneTransform.dx(), -d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.inverted().map(rect); } /*! @@ -5325,7 +5393,9 @@ QPolygonF QGraphicsItem::mapFromParent(const QPolygonF &polygon) const */ QPolygonF QGraphicsItem::mapFromScene(const QPolygonF &polygon) const { - return sceneTransform().inverted().map(polygon); + if (d_ptr->hasTranslateOnlySceneTransform()) + return polygon.translated(-d_ptr->sceneTransform.dx(), -d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.inverted().map(polygon); } /*! @@ -5368,7 +5438,9 @@ QPainterPath QGraphicsItem::mapFromParent(const QPainterPath &path) const */ QPainterPath QGraphicsItem::mapFromScene(const QPainterPath &path) const { - return sceneTransform().inverted().map(path); + if (d_ptr->hasTranslateOnlySceneTransform()) + return path.translated(-d_ptr->sceneTransform.dx(), -d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.inverted().map(path); } /*! @@ -6333,7 +6405,7 @@ void QGraphicsItem::addToIndex() return; } if (d_ptr->scene) - d_ptr->scene->d_func()->addToIndex(this); + d_ptr->scene->d_func()->index->addItem(this); } /*! @@ -6350,7 +6422,7 @@ void QGraphicsItem::removeFromIndex() return; } if (d_ptr->scene) - d_ptr->scene->d_func()->removeFromIndex(this); + d_ptr->scene->d_func()->index->removeItem(this); } /*! @@ -6369,10 +6441,12 @@ void QGraphicsItem::removeFromIndex() void QGraphicsItem::prepareGeometryChange() { if (d_ptr->scene) { + d_ptr->scene->d_func()->dirtyGrowingItemsBoundingRect = true; d_ptr->geometryChanged = 1; d_ptr->paintedViewBoundingRectsNeedRepaint = 1; QGraphicsScenePrivate *scenePrivate = d_ptr->scene->d_func(); + scenePrivate->index->prepareBoundingRectChange(this); scenePrivate->markDirty(this, QRectF(), /*invalidateChildren=*/true, /*maybeDirtyClipPath=*/!d_ptr->inSetPosHelper); @@ -6383,10 +6457,13 @@ void QGraphicsItem::prepareGeometryChange() // _q_processDirtyItems is called before _q_emitUpdated. if ((scenePrivate->connectedSignals & scenePrivate->changedSignalMask) || scenePrivate->views.isEmpty()) { - d_ptr->scene->update(sceneTransform().mapRect(boundingRect())); + if (d_ptr->hasTranslateOnlySceneTransform()) { + d_ptr->scene->update(boundingRect().translated(d_ptr->sceneTransform.dx(), + d_ptr->sceneTransform.dy())); + } else { + d_ptr->scene->update(d_ptr->sceneTransform.mapRect(boundingRect())); + } } - - scenePrivate->removeFromIndex(this); } QGraphicsItem *parent = this; @@ -9706,17 +9783,11 @@ QDebug operator<<(QDebug debug, QGraphicsItem *item) return debug; } - QStringList flags; - if (item->isVisible()) flags << QLatin1String("isVisible"); - if (item->isEnabled()) flags << QLatin1String("isEnabled"); - if (item->isSelected()) flags << QLatin1String("isSelected"); - if (item->hasFocus()) flags << QLatin1String("HasFocus"); - debug << "QGraphicsItem(this =" << ((void*)item) << ", parent =" << ((void*)item->parentItem()) << ", pos =" << item->pos() - << ", z =" << item->zValue() << ", flags = {" - << flags.join(QLatin1String("|")) << " })"; + << ", z =" << item->zValue() << ", flags = " + << item->flags() << ")"; return debug; } diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index d26110a..72c4830 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -142,7 +142,7 @@ public: QGraphicsItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -209,8 +209,8 @@ public: Qt::MouseButtons acceptedMouseButtons() const; void setAcceptedMouseButtons(Qt::MouseButtons buttons); - bool acceptsHoverEvents() const; // obsolete - void setAcceptsHoverEvents(bool enabled); // obsolete + bool acceptsHoverEvents() const; // ### obsolete + void setAcceptsHoverEvents(bool enabled); // ### obsolete bool acceptHoverEvents() const; void setAcceptHoverEvents(bool enabled); bool acceptTouchEvents() const; @@ -437,11 +437,16 @@ private: friend class QGraphicsScene; friend class QGraphicsScenePrivate; friend class QGraphicsSceneFindItemBspTreeVisitor; + friend class QGraphicsSceneBspTree; friend class QGraphicsView; friend class QGraphicsViewPrivate; friend class QGraphicsWidget; friend class QGraphicsWidgetPrivate; friend class QGraphicsProxyWidgetPrivate; + friend class QGraphicsSceneIndex; + friend class QGraphicsSceneIndexPrivate; + friend class QGraphicsSceneBspTreeIndex; + friend class QGraphicsSceneBspTreeIndexPrivate; friend class ::tst_QGraphicsItem; friend bool qt_closestLeaf(const QGraphicsItem *, const QGraphicsItem *); friend bool qt_closestItemFirst(const QGraphicsItem *, const QGraphicsItem *); @@ -533,7 +538,7 @@ class Q_GUI_EXPORT QAbstractGraphicsShapeItem : public QGraphicsItem public: QAbstractGraphicsShapeItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -563,13 +568,13 @@ class Q_GUI_EXPORT QGraphicsPathItem : public QAbstractGraphicsShapeItem public: QGraphicsPathItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsPathItem(const QPainterPath &path, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -606,19 +611,19 @@ class Q_GUI_EXPORT QGraphicsRectItem : public QAbstractGraphicsShapeItem public: QGraphicsRectItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsRectItem(const QRectF &rect, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsRectItem(qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -659,19 +664,19 @@ class Q_GUI_EXPORT QGraphicsEllipseItem : public QAbstractGraphicsShapeItem public: QGraphicsEllipseItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsEllipseItem(const QRectF &rect, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsEllipseItem(qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -718,14 +723,14 @@ class Q_GUI_EXPORT QGraphicsPolygonItem : public QAbstractGraphicsShapeItem public: QGraphicsPolygonItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsPolygonItem(const QPolygonF &polygon, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -765,19 +770,19 @@ class Q_GUI_EXPORT QGraphicsLineItem : public QGraphicsItem public: QGraphicsLineItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsLineItem(const QLineF &line, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsLineItem(qreal x1, qreal y1, qreal x2, qreal y2, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -825,13 +830,13 @@ public: QGraphicsPixmapItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsPixmapItem(const QPixmap &pixmap, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -887,13 +892,13 @@ class Q_GUI_EXPORT QGraphicsTextItem : public QGraphicsObject public: QGraphicsTextItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsTextItem(const QString &text, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -988,13 +993,13 @@ class Q_GUI_EXPORT QGraphicsSimpleTextItem : public QAbstractGraphicsShapeItem public: QGraphicsSimpleTextItem(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); QGraphicsSimpleTextItem(const QString &text, QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); @@ -1034,7 +1039,7 @@ class Q_GUI_EXPORT QGraphicsItemGroup : public QGraphicsItem public: QGraphicsItemGroup(QGraphicsItem *parent = 0 #ifndef Q_QDOC - // obsolete argument + // ### obsolete argument , QGraphicsScene *scene = 0 #endif ); diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 46ec6fe..9bdd273 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -119,6 +119,7 @@ public: parent(0), transformData(0), index(-1), + siblingIndex(-1), depth(0), acceptedMouseButtons(0x1f), visible(1), @@ -149,13 +150,14 @@ public: dirtyChildrenBoundingRect(1), paintedViewBoundingRectsNeedRepaint(0), dirtySceneTransform(1), - geometryChanged(0), + geometryChanged(1), inDestructor(0), isObject(0), ignoreVisible(0), ignoreOpacity(0), acceptTouchEvents(0), acceptedTouchBeginEvent(0), + sceneTransformTranslateOnly(0), globalStackingOrder(-1), q_ptr(0) { @@ -164,6 +166,15 @@ public: inline virtual ~QGraphicsItemPrivate() { } + static const QGraphicsItemPrivate *get(const QGraphicsItem *item) + { + return item->d_ptr; + } + static QGraphicsItemPrivate *get(QGraphicsItem *item) + { + return item->d_ptr; + } + void updateAncestorFlag(QGraphicsItem::GraphicsItemFlag childFlag, AncestorFlag flag = NoFlag, bool enabled = false, bool root = true); void setIsMemberOfGroup(bool enabled); @@ -177,6 +188,7 @@ public: void combineTransformToParent(QTransform *x, const QTransform *viewTransform = 0) const; void combineTransformFromParent(QTransform *x, const QTransform *viewTransform = 0) const; + void updateSceneTransformFromParent(); // ### Qt 5: Remove. Workaround for reimplementation added after Qt 4.4. virtual QVariant inputMethodQueryHelper(Qt::InputMethodQuery query) const; @@ -286,6 +298,13 @@ public: void invalidateCachedClipPathRecursively(bool childrenOnly = false, const QRectF &emptyIfOutsideThisRect = QRectF()); void updateCachedClipPathFromSetPosHelper(const QPointF &newPos); void ensureSceneTransformRecursive(QGraphicsItem **topMostDirtyItem); + void ensureSceneTransform(); + + inline bool hasTranslateOnlySceneTransform() + { + ensureSceneTransform(); + return sceneTransformTranslateOnly; + } inline void invalidateChildrenSceneTransform() { @@ -370,6 +389,7 @@ public: } inline QTransform transformToParent() const; + inline void ensureSortedChildren(); QPainterPath cachedClipPath; QRectF childrenBoundingRect; @@ -385,6 +405,7 @@ public: TransformData *transformData; QTransform sceneTransform; int index; + int siblingIndex; int depth; // Packed 32 bytes @@ -426,7 +447,8 @@ public: quint32 ignoreOpacity : 1; quint32 acceptTouchEvents : 1; quint32 acceptedTouchBeginEvent : 1; - quint32 unused : 9; // feel free to use + quint32 sceneTransformTranslateOnly : 1; + quint32 unused : 8; // feel free to use // Optional stacking order int globalStackingOrder; @@ -457,6 +479,10 @@ struct QGraphicsItemPrivate::TransformData { if (onlyTransform) { if (!postmultiplyTransform) return transform; + if (postmultiplyTransform->isIdentity()) + return transform; + if (transform.isIdentity()) + return *postmultiplyTransform; QTransform x(transform); x *= *postmultiplyTransform; return x; @@ -477,6 +503,29 @@ struct QGraphicsItemPrivate::TransformData { } }; +/*! + \internal +*/ +inline bool qt_closestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2) +{ + // Return true if sibling item1 is on top of item2. + const QGraphicsItemPrivate *d1 = item1->d_ptr; + const QGraphicsItemPrivate *d2 = item2->d_ptr; + bool f1 = d1->flags & QGraphicsItem::ItemStacksBehindParent; + bool f2 = d2->flags & QGraphicsItem::ItemStacksBehindParent; + if (f1 != f2) + return f2; + if (d1->z != d2->z) + return d1->z > d2->z; + return d1->siblingIndex > d2->siblingIndex; +} + +/*! + \internal +*/ +static inline bool qt_notclosestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2) +{ return qt_closestLeaf(item2, item1); } + /* return the full transform of the item to the parent. This include the position and all the transform data */ @@ -487,6 +536,14 @@ inline QTransform QGraphicsItemPrivate::transformToParent() const return matrix; } +inline void QGraphicsItemPrivate::ensureSortedChildren() +{ + if (needSortChildren) { + qSort(children.begin(), children.end(), qt_notclosestLeaf); + needSortChildren = 0; + } +} + QT_END_NAMESPACE #endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 0c3abd4..c8e178a 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -39,7 +39,6 @@ ** ****************************************************************************/ - /*! \class QGraphicsScene \brief The QGraphicsScene class provides a surface for managing a large @@ -218,6 +217,9 @@ #include "qgraphicsview_p.h" #include "qgraphicswidget.h" #include "qgraphicswidget_p.h" +#include "qgraphicssceneindex_p.h" +#include "qgraphicsscenebsptreeindex_p.h" +#include "qgraphicsscenelinearindex_p.h" #include <QtCore/qdebug.h> #include <QtCore/qlist.h> @@ -251,67 +253,6 @@ QT_BEGIN_NAMESPACE -static inline bool qt_notclosestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2); - -static inline bool QRectF_intersects(const QRectF &s, const QRectF &r) -{ - qreal xp = s.left(); - qreal yp = s.top(); - qreal w = s.width(); - qreal h = s.height(); - qreal l1 = xp; - qreal r1 = xp; - if (w < 0) - l1 += w; - else - r1 += w; - - qreal l2 = r.left(); - qreal r2 = r.left(); - if (w < 0) - l2 += r.width(); - else - r2 += r.width(); - - if (l1 >= r2 || l2 >= r1) - return false; - - qreal t1 = yp; - qreal b1 = yp; - if (h < 0) - t1 += h; - else - b1 += h; - - qreal t2 = r.top(); - qreal b2 = r.top(); - if (r.height() < 0) - t2 += r.height(); - else - b2 += r.height(); - - return !(t1 >= b2 || t2 >= b1); -} - -// QRectF::intersects() returns false always if either the source or target -// rectangle's width or height are 0. This works around that problem. -static inline void _q_adjustRect(QRectF *rect) -{ - Q_ASSERT(rect); - if (!rect->width()) - rect->adjust(-0.00001, 0, 0.00001, 0); - if (!rect->height()) - rect->adjust(0, -0.00001, 0, 0.00001); -} - -static inline QRectF adjustedItemBoundingRect(const QGraphicsItem *item) -{ - Q_ASSERT(item); - QRectF boundingRect(item->boundingRect()); - _q_adjustRect(&boundingRect); - return boundingRect; -} - static void _q_hoverFromMouseEvent(QGraphicsSceneHoverEvent *hover, const QGraphicsSceneMouseEvent *mouseEvent) { hover->setWidget(mouseEvent->widget()); @@ -331,18 +272,15 @@ static void _q_hoverFromMouseEvent(QGraphicsSceneHoverEvent *hover, const QGraph QGraphicsScenePrivate::QGraphicsScenePrivate() : changedSignalMask(0), indexMethod(QGraphicsScene::BspTreeIndex), - bspTreeDepth(0), + index(0), lastItemCount(0), hasSceneRect(false), + dirtyGrowingItemsBoundingRect(true), updateAll(false), calledEmitUpdated(false), processDirtyItemsEmitted(false), selectionChanging(0), needSortTopLevelItems(true), - regenerateIndex(true), - purgePending(false), - indexTimerId(0), - restartIndexTimer(false), stickyFocus(false), hasFocus(false), focusItem(0), @@ -359,7 +297,6 @@ QGraphicsScenePrivate::QGraphicsScenePrivate() allItemsUseDefaultCursor(true), painterStateProtection(true), sortCacheEnabled(false), - updatingSortCache(false), style(0), allItemsIgnoreTouchEvents(true) { @@ -372,6 +309,8 @@ void QGraphicsScenePrivate::init() { Q_Q(QGraphicsScene); + index = new QGraphicsSceneBspTreeIndex(q); + // Keep this index so we can check for connected slots later on. changedSignalMask = (1 << q->metaObject()->indexOfSignal("changed(QList<QRectF>)")); qApp->d_func()->scene_list.append(q); @@ -381,223 +320,25 @@ void QGraphicsScenePrivate::init() /*! \internal */ -QList<QGraphicsItem *> QGraphicsScenePrivate::estimateItemsInRect(const QRectF &rect) const -{ - const_cast<QGraphicsScenePrivate *>(this)->purgeRemovedItems(); - const_cast<QGraphicsScenePrivate *>(this)->_q_updateSortCache(); - - if (indexMethod == QGraphicsScene::BspTreeIndex) { - // ### Only do this once in a while. - QGraphicsScenePrivate *that = const_cast<QGraphicsScenePrivate *>(this); - - // Get items from BSP tree - QList<QGraphicsItem *> items = that->bspTree.items(rect); - - // Fill in with any unindexed items - for (int i = 0; i < unindexedItems.size(); ++i) { - if (QGraphicsItem *item = unindexedItems.at(i)) { - if (!item->d_ptr->itemDiscovered && item->d_ptr->visible && !(item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) { - QRectF boundingRect = item->sceneBoundingRect(); - if (QRectF_intersects(boundingRect, rect)) { - item->d_ptr->itemDiscovered = 1; - items << item; - } - } - } - } - - // Reset the discovered state of all discovered items - for (int i = 0; i < items.size(); ++i) - items.at(i)->d_func()->itemDiscovered = 0; - return items; - } - - QList<QGraphicsItem *> itemsInRect; - for (int i = 0; i < unindexedItems.size(); ++i) { - if (QGraphicsItem *item = unindexedItems.at(i)) { - if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) - continue; - if (item->d_ptr->visible && !item->d_ptr->isFullyTransparent()) - itemsInRect << item; - } - } - for (int i = 0; i < indexedItems.size(); ++i) { - if (QGraphicsItem *item = indexedItems.at(i)) { - if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) - continue; - if (item->d_ptr->visible && !item->d_ptr->isFullyTransparent()) - itemsInRect << item; - } - } - - return itemsInRect; -} - -/*! - \internal -*/ -void QGraphicsScenePrivate::addToIndex(QGraphicsItem *item) -{ - if (indexMethod == QGraphicsScene::BspTreeIndex) { - if (item->d_func()->index != -1) { - bspTree.insertItem(item, item->sceneBoundingRect()); - foreach (QGraphicsItem *child, item->children()) - child->addToIndex(); - } else { - // The BSP tree is regenerated if the number of items grows to a - // certain threshold, or if the bounding rect of the graph doubles in - // size. - startIndexTimer(); - } - } -} - -/*! - \internal -*/ -void QGraphicsScenePrivate::removeFromIndex(QGraphicsItem *item) -{ - if (indexMethod == QGraphicsScene::BspTreeIndex) { - int index = item->d_func()->index; - if (index != -1) { - bspTree.removeItem(item, item->sceneBoundingRect()); - freeItemIndexes << index; - indexedItems[index] = 0; - item->d_func()->index = -1; - unindexedItems << item; - - foreach (QGraphicsItem *child, item->children()) - child->removeFromIndex(); - } - - startIndexTimer(); - } -} - -/*! - \internal -*/ -void QGraphicsScenePrivate::resetIndex() -{ - purgeRemovedItems(); - if (indexMethod == QGraphicsScene::BspTreeIndex) { - for (int i = 0; i < indexedItems.size(); ++i) { - if (QGraphicsItem *item = indexedItems.at(i)) { - item->d_ptr->index = -1; - unindexedItems << item; - } - } - indexedItems.clear(); - freeItemIndexes.clear(); - regenerateIndex = true; - startIndexTimer(); - } -} - -static inline int intmaxlog(int n) +QGraphicsScenePrivate *QGraphicsScenePrivate::get(QGraphicsScene *q) { - return (n > 0 ? qMax(qCeil(qLn(qreal(n)) / qLn(qreal(2))), 5) : 0); + return q->d_func(); } -/*! - \internal -*/ -void QGraphicsScenePrivate::_q_updateIndex() +void QGraphicsScenePrivate::_q_emitUpdated() { - if (!indexTimerId) - return; - Q_Q(QGraphicsScene); - q->killTimer(indexTimerId); - indexTimerId = 0; - - purgeRemovedItems(); - - // Add unindexedItems to indexedItems - QRectF unindexedItemsBoundingRect; - for (int i = 0; i < unindexedItems.size(); ++i) { - if (QGraphicsItem *item = unindexedItems.at(i)) { - unindexedItemsBoundingRect |= item->sceneBoundingRect(); - if (!freeItemIndexes.isEmpty()) { - int freeIndex = freeItemIndexes.takeFirst(); - item->d_func()->index = freeIndex; - indexedItems[freeIndex] = item; - } else { - item->d_func()->index = indexedItems.size(); - indexedItems << item; - } - } - } - - // Update growing scene rect. - QRectF oldGrowingItemsBoundingRect = growingItemsBoundingRect; - growingItemsBoundingRect |= unindexedItemsBoundingRect; - - // Determine whether we should regenerate the BSP tree. - if (indexMethod == QGraphicsScene::BspTreeIndex) { - int depth = bspTreeDepth; - if (depth == 0) { - int oldDepth = intmaxlog(lastItemCount); - depth = intmaxlog(indexedItems.size()); - static const int slack = 100; - if (bspTree.leafCount() == 0 || (oldDepth != depth && qAbs(lastItemCount - indexedItems.size()) > slack)) { - // ### Crude algorithm. - regenerateIndex = true; - } - } - - // Regenerate the tree. - if (regenerateIndex) { - regenerateIndex = false; - bspTree.initialize(q->sceneRect(), depth); - unindexedItems = indexedItems; - lastItemCount = indexedItems.size(); - q->update(); - - // Take this opportunity to reset our largest-item counter for - // untransformable items. When the items are inserted into the BSP - // tree, we'll get an accurate calculation. - largestUntransformableItem = QRectF(); - } - } + calledEmitUpdated = false; - // Insert all unindexed items into the tree. - for (int i = 0; i < unindexedItems.size(); ++i) { - if (QGraphicsItem *item = unindexedItems.at(i)) { - QRectF rect = item->sceneBoundingRect(); - if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) - continue; - if (indexMethod == QGraphicsScene::BspTreeIndex) - bspTree.insertItem(item, rect); - - // If the item ignores view transformations, update our - // largest-item-counter to ensure that the view can accurately - // discover untransformable items when drawing. - if (item->d_ptr->itemIsUntransformable()) { - QGraphicsItem *topmostUntransformable = item; - while (topmostUntransformable && (topmostUntransformable->d_ptr->ancestorFlags - & QGraphicsItemPrivate::AncestorIgnoresTransformations)) { - topmostUntransformable = topmostUntransformable->parentItem(); - } - // ### Verify that this is the correct largest untransformable rectangle. - largestUntransformableItem |= item->mapToItem(topmostUntransformable, item->boundingRect()).boundingRect(); - } + if (dirtyGrowingItemsBoundingRect) { + if (!hasSceneRect) { + const QRectF oldGrowingItemsBoundingRect = growingItemsBoundingRect; + growingItemsBoundingRect |= q->itemsBoundingRect(); + if (oldGrowingItemsBoundingRect != growingItemsBoundingRect) + emit q->sceneRectChanged(growingItemsBoundingRect); } + dirtyGrowingItemsBoundingRect = false; } - unindexedItems.clear(); - - // Notify scene rect changes. - if (!hasSceneRect && growingItemsBoundingRect != oldGrowingItemsBoundingRect) - emit q->sceneRectChanged(growingItemsBoundingRect); -} - -/*! - \internal -*/ -void QGraphicsScenePrivate::_q_emitUpdated() -{ - Q_Q(QGraphicsScene); - calledEmitUpdated = false; // Ensure all views are connected if anything is connected. This disables // the optimization that items send updates directly to the views, but it @@ -633,6 +374,7 @@ void QGraphicsScenePrivate::_q_emitUpdated() void QGraphicsScenePrivate::registerTopLevelItem(QGraphicsItem *item) { needSortTopLevelItems = true; + item->d_ptr->siblingIndex = topLevelItems.size(); topLevelItems.append(item); } @@ -642,19 +384,10 @@ void QGraphicsScenePrivate::registerTopLevelItem(QGraphicsItem *item) void QGraphicsScenePrivate::unregisterTopLevelItem(QGraphicsItem *item) { topLevelItems.removeOne(item); -} - -/*! - \internal - - Updates all items in the pending update list. At this point, the list is - unlikely to contain partially constructed items. -*/ -void QGraphicsScenePrivate::_q_updateLater() -{ - foreach (QGraphicsItem *item, pendingUpdateItems) - item->update(); - pendingUpdateItems.clear(); + // NB! Do not use topLevelItems.removeAt(item->d_ptr->siblingIndex) because + // the item is not guaranteed to be at the index after the list is sorted + // (see ensureSortedTopLevelItems()). + item->d_ptr->siblingIndex = -1; } /*! @@ -680,9 +413,23 @@ void QGraphicsScenePrivate::_q_processDirtyItems() { processDirtyItemsEmitted = false; + if (updateAll) { + Q_ASSERT(calledEmitUpdated); + // No need for further processing (except resetting the dirty states). + // The growingItemsBoundingRect is updated in _q_emitUpdated. + for (int i = 0; i < topLevelItems.size(); ++i) + resetDirtyItem(topLevelItems.at(i), /*recursive=*/true); + return; + } + const bool wasPendingSceneUpdate = calledEmitUpdated; const QRectF oldGrowingItemsBoundingRect = growingItemsBoundingRect; - processDirtyItemsRecursive(0); + + // Process items recursively. + for (int i = 0; i < topLevelItems.size(); ++i) + processDirtyItemsRecursive(topLevelItems.at(i)); + + dirtyGrowingItemsBoundingRect = false; if (!hasSceneRect && oldGrowingItemsBoundingRect != growingItemsBoundingRect) emit q_func()->sceneRectChanged(growingItemsBoundingRect); @@ -727,22 +474,16 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item) // Clear focus on the item to remove any reference in the focusWidget chain. item->clearFocus(); + markDirty(item, QRectF(), false, false, false, false, /*removingItemFromScene=*/true); - if (!item->d_ptr->inDestructor) { + if (item->d_ptr->inDestructor) { + // The item is actually in its destructor, we call the special method in the index. + index->deleteItem(item); + } else { // Can potentially call item->boundingRect() (virtual function), that's why // we only can call this function if the item is not in its destructor. - removeFromIndex(item); - } else if (item->d_ptr->index != -1) { - // Important: The index is useless until purgeRemovedItems() is called. - indexedItems[item->d_ptr->index] = (QGraphicsItem *)0; - if (!purgePending) - purgePending = true; - removedItems << item; - } else { - // Recently added items are purged immediately. unindexedItems() never - // contains stale items. - unindexedItems.removeAll(item); + index->removeItem(item); } if (!item->d_ptr->inDestructor && item == tabFocusFirst) { @@ -763,17 +504,6 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item) unregisterTopLevelItem(item); } - if (!item->d_ptr->inDestructor) { - // Remove from our item lists. - int index = item->d_func()->index; - if (index != -1) { - freeItemIndexes << index; - indexedItems[index] = 0; - } else { - unindexedItems.removeAll(item); - } - } - // Reset the mouse grabber and focus item data. if (item == focusItem) focusItem = 0; @@ -793,7 +523,6 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item) hoverItems.removeAll(item); cachedItemsUnderMouse.removeAll(item); unpolishedItems.removeAll(item); - pendingUpdateItems.removeAll(item); resetDirtyItem(item); //We remove all references of item from the sceneEventFilter arrays @@ -831,45 +560,6 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item) /*! \internal - - Removes stale pointers from all data structures. -*/ -void QGraphicsScenePrivate::purgeRemovedItems() -{ - if (!purgePending && removedItems.isEmpty()) - return; - - // Remove stale items from the BSP tree. - if (indexMethod != QGraphicsScene::NoIndex) - bspTree.removeItems(removedItems); - - // Purge this list. - removedItems.clear(); - freeItemIndexes.clear(); - for (int i = 0; i < indexedItems.size(); ++i) { - if (!indexedItems.at(i)) - freeItemIndexes << i; - } - purgePending = false; -} - -/*! - \internal - - Starts or restarts the timer used for reindexing unindexed items. -*/ -void QGraphicsScenePrivate::startIndexTimer(int interval) -{ - Q_Q(QGraphicsScene); - if (indexTimerId) { - restartIndexTimer = true; - } else { - indexTimerId = q->startTimer(interval); - } -} - -/*! - \internal */ void QGraphicsScenePrivate::addPopup(QGraphicsWidget *widget) { @@ -1104,41 +794,20 @@ QList<QGraphicsItem *> QGraphicsScenePrivate::itemsAtPosition(const QPoint &scre { Q_Q(const QGraphicsScene); QGraphicsView *view = widget ? qobject_cast<QGraphicsView *>(widget->parentWidget()) : 0; - QList<QGraphicsItem *> items; - if (view) - items = view->items(view->viewport()->mapFromGlobal(screenPos)); - else - items = q->items(scenePos); - return items; -} + if (!view) + return q->items(scenePos, Qt::IntersectsItemShape, Qt::AscendingOrder, QTransform()); -/*! - \internal + const QRectF pointRect(QPointF(widget->mapFromGlobal(screenPos)), QSizeF(1, 1)); + if (!view->isTransformed()) + return q->items(pointRect, Qt::IntersectsItemShape, Qt::AscendingOrder); - Checks if item collides with the path and mode, but also checks that if it - doesn't collide, maybe its frame rect will. -*/ -bool QGraphicsScenePrivate::itemCollidesWithPath(QGraphicsItem *item, - const QPainterPath &path, - Qt::ItemSelectionMode mode) -{ - if (item->collidesWithPath(path, mode)) - return true; - if (item->isWidget()) { - // Check if this is a window, and if its frame rect collides. - QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item); - if (widget->isWindow()) { - QRectF frameRect = widget->windowFrameRect(); - QPainterPath framePath; - framePath.addRect(frameRect); - bool intersects = path.intersects(frameRect); - if (mode == Qt::IntersectsItemShape || mode == Qt::IntersectsItemBoundingRect) - return intersects || path.contains(frameRect.topLeft()) - || framePath.contains(path.elementAt(0)); - return !intersects && path.contains(frameRect.topLeft()); - } + const QTransform viewTransform = view->viewportTransform(); + if (viewTransform.type() <= QTransform::TxScale) { + return q->items(viewTransform.inverted().mapRect(pointRect), Qt::IntersectsItemShape, + Qt::AscendingOrder, viewTransform); } - return false; + return q->items(viewTransform.inverted().map(pointRect), Qt::IntersectsItemShape, + Qt::AscendingOrder, viewTransform); } /*! @@ -1150,7 +819,7 @@ void QGraphicsScenePrivate::storeMouseButtonsForMouseGrabber(QGraphicsSceneMouse if (event->buttons() & i) { mouseGrabberButtonDownPos.insert(Qt::MouseButton(i), mouseGrabberItems.last()->d_ptr->genericMapFromScene(event->scenePos(), - event->widget())); + event->widget())); mouseGrabberButtonDownScenePos.insert(Qt::MouseButton(i), event->scenePos()); mouseGrabberButtonDownScreenPos.insert(Qt::MouseButton(i), event->screenPos()); } @@ -1428,779 +1097,6 @@ QGraphicsWidget *QGraphicsScenePrivate::windowForItem(const QGraphicsItem *item) return 0; } -QList<QGraphicsItem *> QGraphicsScenePrivate::topLevelItemsInStackingOrder(const QTransform *const viewTransform, - QRegion *exposedRegion) -{ - if (indexMethod == QGraphicsScene::NoIndex || !exposedRegion) { - if (needSortTopLevelItems) { - needSortTopLevelItems = false; - qStableSort(topLevelItems.begin(), topLevelItems.end(), qt_notclosestLeaf); - } - return topLevelItems; - } - - const QRectF exposedRect = exposedRegion->boundingRect().adjusted(-1, -1, 1, 1); - QRectF sceneRect; - QTransform invertedViewTransform(Qt::Uninitialized); - if (!viewTransform) { - sceneRect = exposedRect; - } else { - invertedViewTransform = viewTransform->inverted(); - sceneRect = invertedViewTransform.mapRect(exposedRect); - } - if (!largestUntransformableItem.isEmpty()) { - // ### Nuke this when we move the indexing code into a separate - // class. All the largestUntransformableItem code should then go - // away, and the estimate function should return untransformable - // items as well. - QRectF untr = largestUntransformableItem; - QRectF ltri = !viewTransform ? untr : invertedViewTransform.mapRect(untr); - ltri.adjust(-untr.width(), -untr.height(), untr.width(), untr.height()); - sceneRect.adjust(-ltri.width(), -ltri.height(), ltri.width(), ltri.height()); - } - - QList<QGraphicsItem *> tmp = estimateItemsInRect(sceneRect); - for (int i = 0; i < tmp.size(); ++i) - tmp.at(i)->topLevelItem()->d_ptr->itemDiscovered = 1; - - // Sort if the toplevel list is unsorted. - if (needSortTopLevelItems) { - needSortTopLevelItems = false; - qStableSort(topLevelItems.begin(), topLevelItems.end(), qt_notclosestLeaf); - } - - QList<QGraphicsItem *> tli; - for (int i = 0; i < topLevelItems.size(); ++i) { - // ### Investigate smarter ways. Looping through all top level - // items is not optimal. If the BSP tree is to have maximum - // effect, it should be possible to sort the subset of items - // quickly. We must use this approach for now, as it's the only - // current way to keep the stable sorting order (insertion order). - QGraphicsItem *item = topLevelItems.at(i); - if (item->d_ptr->itemDiscovered) { - item->d_ptr->itemDiscovered = 0; - tli << item; - } - } - return tli; -} - -void QGraphicsScenePrivate::recursive_items_helper(QGraphicsItem *item, QRectF rect, - QList<QGraphicsItem *> *items, - const QTransform &parentTransform, - const QTransform &viewTransform, - Qt::ItemSelectionMode mode, Qt::SortOrder order, - qreal parentOpacity) const -{ - // Calculate opacity. - qreal opacity; - if (item) { - if (!item->d_ptr->visible) - return; - QGraphicsItem *p = item->d_ptr->parent; - bool itemIgnoresParentOpacity = item->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity; - bool parentDoesntPropagateOpacity = (p && (p->d_ptr->flags & QGraphicsItem::ItemDoesntPropagateOpacityToChildren)); - if (!itemIgnoresParentOpacity && !parentDoesntPropagateOpacity) { - opacity = parentOpacity * item->opacity(); - } else { - opacity = item->d_ptr->opacity; - } - if (opacity == 0.0 && !(item->d_ptr->flags & QGraphicsItem::ItemDoesntPropagateOpacityToChildren)) - return; - } else { - opacity = parentOpacity; - } - - // Calculate the full transform for this item. - QTransform transform = parentTransform; - bool keep = false; - if (item) { - item->d_ptr->combineTransformFromParent(&transform, &viewTransform); - - // ### This does not take the clip into account. - QRectF brect = item->boundingRect(); - _q_adjustRect(&brect); - - keep = true; - if (mode == Qt::ContainsItemShape || mode == Qt::ContainsItemBoundingRect) - keep = rect.contains(transform.mapRect(brect)) && rect != brect; - else - keep = rect.intersects(transform.mapRect(brect)); - - if (keep && (mode == Qt::ContainsItemShape || mode == Qt::IntersectsItemShape)) { - QPainterPath rectPath; - rectPath.addRect(rect); - keep = itemCollidesWithPath(item, transform.inverted().map(rectPath), mode); - } - } - - bool childClip = (item && (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape)); - bool dontProcessItem = !item || !keep; - bool dontProcessChildren = item && dontProcessItem && childClip; - - // Find and sort children. - QList<QGraphicsItem *> &children = item ? item->d_ptr->children : const_cast<QGraphicsScenePrivate *>(this)->topLevelItems; - if (!dontProcessChildren) { - if (item && item->d_ptr->needSortChildren) { - item->d_ptr->needSortChildren = 0; - qStableSort(children.begin(), children.end(), qt_notclosestLeaf); - } else if (!item && needSortTopLevelItems) { - const_cast<QGraphicsScenePrivate *>(this)->needSortTopLevelItems = false; - qStableSort(children.begin(), children.end(), qt_notclosestLeaf); - } - } - - childClip &= !dontProcessChildren & !children.isEmpty(); - - // Clip. - if (childClip) - rect &= transform.map(item->shape()).controlPointRect(); - - // Process children behind - int i = 0; - if (!dontProcessChildren) { - for (i = 0; i < children.size(); ++i) { - QGraphicsItem *child = children.at(i); - if (!(child->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent)) - break; - recursive_items_helper(child, rect, items, transform, viewTransform, - mode, order, opacity); - } - } - - // Process item - if (!dontProcessItem) - items->append(item); - - // Process children in front - if (!dontProcessChildren) { - for (; i < children.size(); ++i) - recursive_items_helper(children.at(i), rect, items, transform, viewTransform, - mode, order, opacity); - } - - if (!item && order == Qt::AscendingOrder) { - int n = items->size(); - for (int i = 0; i < n / 2; ++i) { - QGraphicsItem *tmp = (*items)[n - i - 1]; - (*items)[n - i - 1] = (*items)[i]; - (*items)[i] = tmp; - } - } -} - -QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QPointF &pos) const -{ - QList<QGraphicsItem *> items; - - // The index returns a rough estimate of what items are inside the rect. - // Refine it by iterating through all returned items. - QRectF adjustedRect = QRectF(pos, QSize(1,1)); - foreach (QGraphicsItem *item, estimateItemsInRect(adjustedRect)) { - // Find the item's scene transform in a clever way. - QTransform x = item->sceneTransform(); - bool keep = false; - - // ### _q_adjustedRect is only needed because QRectF::intersects, - // QRectF::contains and QTransform::map() and friends don't work with - // flat rectangles. - const QRectF br(adjustedItemBoundingRect(item)); - // Rect intersects/contains item's shape - if (QRectF_intersects(adjustedRect, x.mapRect(br))) { - bool ok; - QTransform xinv = x.inverted(&ok); - if (ok) { - if (item->contains(xinv.map(pos))) { - items << item; - keep = true; - } - } - } - - if (keep && (item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) { - // Recurse into children that clip children. - bool ok; - QTransform xinv = x.inverted(&ok); - if (ok) - childItems_helper(&items, item, xinv.map(pos)); - } - } - - sortItems(&items, Qt::AscendingOrder, sortCacheEnabled); - return items; -} - -QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QRectF &rect, - Qt::ItemSelectionMode mode, - Qt::SortOrder order) const -{ - QList<QGraphicsItem *> items; - - QPainterPath path; - - // The index returns a rough estimate of what items are inside the rect. - // Refine it by iterating through all returned items. - QRectF adjustedRect(rect); - _q_adjustRect(&adjustedRect); - foreach (QGraphicsItem *item, estimateItemsInRect(adjustedRect)) { - // Find the item's scene transform in a clever way. - QTransform x = item->sceneTransform(); - bool keep = false; - - // ### _q_adjustedRect is only needed because QRectF::intersects, - // QRectF::contains and QTransform::map() and friends don't work with - // flat rectangles. - const QRectF br(adjustedItemBoundingRect(item)); - if (mode >= Qt::ContainsItemBoundingRect) { - // Rect intersects/contains item's bounding rect - QRectF mbr = x.mapRect(br); - if ((mode == Qt::IntersectsItemBoundingRect && QRectF_intersects(rect, mbr)) - || (mode == Qt::ContainsItemBoundingRect && rect != mbr && rect.contains(mbr))) { - items << item; - keep = true; - } - } else { - // Rect intersects/contains item's shape - if (QRectF_intersects(adjustedRect, x.mapRect(br))) { - bool ok; - QTransform xinv = x.inverted(&ok); - if (ok) { - if (path.isEmpty()) - path.addRect(rect); - if (itemCollidesWithPath(item, xinv.map(path), mode)) { - items << item; - keep = true; - } - } - } - } - - if (keep && (item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) { - // Recurse into children that clip children. - bool ok; - QTransform xinv = x.inverted(&ok); - if (ok) { - if (x.type() <= QTransform::TxScale) { - // Rect - childItems_helper(&items, item, xinv.mapRect(rect), mode); - } else { - // Polygon - childItems_helper(&items, item, xinv.map(rect), mode); - } - } - } - } - - if (order != Qt::SortOrder(-1)) - sortItems(&items, order, sortCacheEnabled); - return items; -} - -QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QPolygonF &polygon, - Qt::ItemSelectionMode mode, - Qt::SortOrder order) const -{ - QList<QGraphicsItem *> items; - - QRectF polyRect(polygon.boundingRect()); - _q_adjustRect(&polyRect); - QPainterPath path; - - // The index returns a rough estimate of what items are inside the rect. - // Refine it by iterating through all returned items. - foreach (QGraphicsItem *item, estimateItemsInRect(polyRect)) { - // Find the item's scene transform in a clever way. - QTransform x = item->sceneTransform(); - bool keep = false; - - // ### _q_adjustedRect is only needed because QRectF::intersects, - // QRectF::contains and QTransform::map() and friends don't work with - // flat rectangles. - const QRectF br(adjustedItemBoundingRect(item)); - if (mode >= Qt::ContainsItemBoundingRect) { - // Polygon contains/intersects item's bounding rect - if (path == QPainterPath()) - path.addPolygon(polygon); - if ((mode == Qt::IntersectsItemBoundingRect && path.intersects(x.mapRect(br))) - || (mode == Qt::ContainsItemBoundingRect && path.contains(x.mapRect(br)))) { - items << item; - keep = true; - } - } else { - // Polygon contains/intersects item's shape - if (QRectF_intersects(polyRect, x.mapRect(br))) { - bool ok; - QTransform xinv = x.inverted(&ok); - if (ok) { - if (path == QPainterPath()) - path.addPolygon(polygon); - if (itemCollidesWithPath(item, xinv.map(path), mode)) { - items << item; - keep = true; - } - } - } - } - - if (keep && (item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) { - // Recurse into children that clip children. - bool ok; - QTransform xinv = x.inverted(&ok); - if (ok) - childItems_helper(&items, item, xinv.map(polygon), mode); - } - } - - if (order != Qt::SortOrder(-1)) - sortItems(&items, order, sortCacheEnabled); - return items; -} - -QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QPainterPath &path, - Qt::ItemSelectionMode mode, - Qt::SortOrder order) const -{ - QList<QGraphicsItem *> items; - QRectF pathRect(path.controlPointRect()); - _q_adjustRect(&pathRect); - - // The index returns a rough estimate of what items are inside the rect. - // Refine it by iterating through all returned items. - foreach (QGraphicsItem *item, estimateItemsInRect(pathRect)) { - // Find the item's scene transform in a clever way. - QTransform x = item->sceneTransform(); - bool keep = false; - - // ### _q_adjustedRect is only needed because QRectF::intersects, - // QRectF::contains and QTransform::map() and friends don't work with - // flat rectangles. - const QRectF br(adjustedItemBoundingRect(item)); - if (mode >= Qt::ContainsItemBoundingRect) { - // Path contains/intersects item's bounding rect - if ((mode == Qt::IntersectsItemBoundingRect && path.intersects(x.mapRect(br))) - || (mode == Qt::ContainsItemBoundingRect && path.contains(x.mapRect(br)))) { - items << item; - keep = true; - } - } else { - // Path contains/intersects item's shape - if (QRectF_intersects(pathRect, x.mapRect(br))) { - bool ok; - QTransform xinv = x.inverted(&ok); - if (ok) { - if (itemCollidesWithPath(item, xinv.map(path), mode)) { - items << item; - keep = true; - } - } - } - } - - if (keep && (item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) { - bool ok; - QTransform xinv = x.inverted(&ok); - if (ok) - childItems_helper(&items, item, xinv.map(path), mode); - } - } - - if (order != Qt::SortOrder(-1)) - sortItems(&items, order, sortCacheEnabled); - return items; -} - -void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items, - const QGraphicsItem *parent, - const QPointF &pos) const -{ - bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape); - if (parentClip && parent->d_ptr->isClippedAway()) - return; - // ### is this needed? - if (parentClip && !parent->boundingRect().contains(pos)) - return; - - QList<QGraphicsItem *> &children = parent->d_ptr->children; - for (int i = 0; i < children.size(); ++i) { - QGraphicsItem *item = children.at(i); - if (item->d_ptr->transformData && !item->d_ptr->transformData->computedFullTransform().isInvertible()) - continue; - - // Skip invisible items and all their children. - if (item->d_ptr->isInvisible()) - continue; - - bool keep = false; - if (!item->d_ptr->isClippedAway()) { - if (item->contains(item->mapFromParent(pos))) { - items->append(item); - keep = true; - } - } - - if ((keep || !(item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) && !item->d_ptr->children.isEmpty()) - // Recurse into children. - childItems_helper(items, item, item->mapFromParent(pos)); - } -} - - -void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items, - const QGraphicsItem *parent, - const QRectF &rect, - Qt::ItemSelectionMode mode) const -{ - bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape); - if (parentClip && parent->d_ptr->isClippedAway()) - return; - QRectF adjustedRect(rect); - _q_adjustRect(&adjustedRect); - QRectF r = !parentClip ? adjustedRect : adjustedRect.intersected(adjustedItemBoundingRect(parent)); - if (r.isEmpty()) - return; - - QPainterPath path; - QList<QGraphicsItem *> &children = parent->d_ptr->children; - for (int i = 0; i < children.size(); ++i) { - QGraphicsItem *item = children.at(i); - if (item->d_ptr->transformData && !item->d_ptr->transformData->computedFullTransform().isInvertible()) - continue; - - // Skip invisible items and all their children. - if (item->d_ptr->isInvisible()) - continue; - - bool keep = false; - if (!item->d_ptr->isClippedAway()) { - // ### _q_adjustedRect is only needed because QRectF::intersects, - // QRectF::contains and QTransform::map() and friends don't work with - // flat rectangles. - const QRectF br(adjustedItemBoundingRect(item)); - QRectF mbr = item->mapRectToParent(br); - if (mode >= Qt::ContainsItemBoundingRect) { - // Rect intersects/contains item's bounding rect - if ((mode == Qt::IntersectsItemBoundingRect && QRectF_intersects(rect, mbr)) - || (mode == Qt::ContainsItemBoundingRect && rect != mbr && rect.contains(br))) { - items->append(item); - keep = true; - } - } else { - // Rect intersects/contains item's shape - if (QRectF_intersects(rect, mbr)) { - if (path == QPainterPath()) - path.addRect(rect); - if (itemCollidesWithPath(item, item->mapFromParent(path), mode)) { - items->append(item); - keep = true; - } - } - } - } - - if ((keep || !(item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) && !item->d_ptr->children.isEmpty()) { - // Recurse into children. - if (!item->d_ptr->transformData || item->d_ptr->transformData->computedFullTransform().type() <= QTransform::TxScale) { - // Rect - childItems_helper(items, item, item->mapRectFromParent(rect), mode); - } else { - // Polygon - childItems_helper(items, item, item->mapFromParent(rect), mode); - } - } - } -} - - -void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items, - const QGraphicsItem *parent, - const QPolygonF &polygon, - Qt::ItemSelectionMode mode) const -{ - bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape); - if (parentClip && parent->d_ptr->isClippedAway()) - return; - QRectF polyRect(polygon.boundingRect()); - _q_adjustRect(&polyRect); - QRectF r = !parentClip ? polyRect : polyRect.intersected(adjustedItemBoundingRect(parent)); - if (r.isEmpty()) - return; - - QPainterPath path; - QList<QGraphicsItem *> &children = parent->d_ptr->children; - for (int i = 0; i < children.size(); ++i) { - QGraphicsItem *item = children.at(i); - if (item->d_ptr->transformData && !item->d_ptr->transformData->computedFullTransform().isInvertible()) - continue; - - // Skip invisible items. - if (item->d_ptr->isInvisible()) - continue; - - bool keep = false; - if (!item->d_ptr->isClippedAway()) { - // ### _q_adjustedRect is only needed because QRectF::intersects, - // QRectF::contains and QTransform::map() and friends don't work with - // flat rectangles. - const QRectF br(adjustedItemBoundingRect(item)); - if (mode >= Qt::ContainsItemBoundingRect) { - // Polygon contains/intersects item's bounding rect - if (path == QPainterPath()) - path.addPolygon(polygon); - if ((mode == Qt::IntersectsItemBoundingRect && path.intersects(item->mapRectToParent(br))) - || (mode == Qt::ContainsItemBoundingRect && path.contains(item->mapRectToParent(br)))) { - items->append(item); - keep = true; - } - } else { - // Polygon contains/intersects item's shape - if (QRectF_intersects(polyRect, item->mapRectToParent(br))) { - if (path == QPainterPath()) - path.addPolygon(polygon); - if (itemCollidesWithPath(item, item->mapFromParent(path), mode)) { - items->append(item); - keep = true; - } - } - } - } - - if ((keep || !(item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) && !item->d_ptr->children.isEmpty()) { - // Recurse into children that clip children. - childItems_helper(items, item, item->mapFromParent(polygon), mode); - } - } -} - -void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items, - const QGraphicsItem *parent, - const QPainterPath &path, - Qt::ItemSelectionMode mode) const -{ - bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape); - if (parentClip && parent->d_ptr->isClippedAway()) - return; - QRectF pathRect(path.boundingRect()); - _q_adjustRect(&pathRect); - QRectF r = !parentClip ? pathRect : pathRect.intersected(adjustedItemBoundingRect(parent)); - if (r.isEmpty()) - return; - - QList<QGraphicsItem *> &children = parent->d_ptr->children; - for (int i = 0; i < children.size(); ++i) { - QGraphicsItem *item = children.at(i); - if (item->d_ptr->transformData && !item->d_ptr->transformData->computedFullTransform().isInvertible()) - continue; - - // Skip invisible items. - if (item->d_ptr->isInvisible()) - continue; - - bool keep = false; - if (!item->d_ptr->isClippedAway()) { - // ### _q_adjustedRect is only needed because QRectF::intersects, - // QRectF::contains and QTransform::map() and friends don't work with - // flat rectangles. - const QRectF br(adjustedItemBoundingRect(item)); - if (mode >= Qt::ContainsItemBoundingRect) { - // Polygon contains/intersects item's bounding rect - if ((mode == Qt::IntersectsItemBoundingRect && path.intersects(item->mapRectToParent(br))) - || (mode == Qt::ContainsItemBoundingRect && path.contains(item->mapRectToParent(br)))) { - items->append(item); - keep = true; - } - } else { - // Path contains/intersects item's shape - if (QRectF_intersects(pathRect, item->mapRectToParent(br))) { - if (itemCollidesWithPath(item, item->mapFromParent(path), mode)) { - items->append(item); - keep = true; - } - } - } - } - - if ((keep || !(item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) && !item->d_ptr->children.isEmpty()) { - // Recurse into children that clip children. - childItems_helper(items, item, item->mapFromParent(path), mode); - } - } -} - -void QGraphicsScenePrivate::invalidateSortCache() -{ - Q_Q(QGraphicsScene); - if (!sortCacheEnabled || updatingSortCache) - return; - - updatingSortCache = true; - QMetaObject::invokeMethod(q, "_q_updateSortCache", Qt::QueuedConnection); -} - -/*! - \internal - - Should not be exported, but we can't change that now. - ### Qt 5: Remove symbol / make static -*/ -inline bool qt_closestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2) -{ - // Return true if sibling item1 is on top of item2. - const QGraphicsItemPrivate *d1 = item1->d_ptr; - const QGraphicsItemPrivate *d2 = item2->d_ptr; - bool f1 = d1->flags & QGraphicsItem::ItemStacksBehindParent; - bool f2 = d2->flags & QGraphicsItem::ItemStacksBehindParent; - if (f1 != f2) return f2; - qreal z1 = d1->z; - qreal z2 = d2->z; - return z1 > z2; -} - -static inline bool qt_notclosestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2) -{ - return qt_closestLeaf(item2, item1); -} - -/*! - \internal - - Should not be exported, but we can't change that now. -*/ -inline bool qt_closestItemFirst(const QGraphicsItem *item1, const QGraphicsItem *item2) -{ - return QGraphicsScenePrivate::closestItemFirst_withoutCache(item1, item2); -} - -/*! - Returns true if \a item1 is on top of \a item2. - - \internal -*/ -bool QGraphicsScenePrivate::closestItemFirst_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2) -{ - // Siblings? Just check their z-values. - const QGraphicsItemPrivate *d1 = item1->d_ptr; - const QGraphicsItemPrivate *d2 = item2->d_ptr; - if (d1->parent == d2->parent) - return qt_closestLeaf(item1, item2); - - // Find common ancestor, and each item's ancestor closest to the common - // ancestor. - int item1Depth = d1->depth; - int item2Depth = d2->depth; - const QGraphicsItem *p = item1; - const QGraphicsItem *t1 = item1; - while (item1Depth > item2Depth && (p = p->d_ptr->parent)) { - if (p == item2) { - // item2 is one of item1's ancestors; item1 is on top - return !(t1->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent); - } - t1 = p; - --item1Depth; - } - p = item2; - const QGraphicsItem *t2 = item2; - while (item2Depth > item1Depth && (p = p->d_ptr->parent)) { - if (p == item1) { - // item1 is one of item2's ancestors; item1 is not on top - return (t2->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent); - } - t2 = p; - --item2Depth; - } - - // item1Ancestor is now at the same level as item2Ancestor, but not the same. - const QGraphicsItem *a1 = t1; - const QGraphicsItem *a2 = t2; - while (a1) { - const QGraphicsItem *p1 = a1; - const QGraphicsItem *p2 = a2; - a1 = a1->parentItem(); - a2 = a2->parentItem(); - if (a1 && a1 == a2) - return qt_closestLeaf(p1, p2); - } - - // No common ancestor? Then just compare the items' toplevels directly. - return qt_closestLeaf(t1->topLevelItem(), t2->topLevelItem()); -} - -/*! - Returns true if \a item2 is on top of \a item1. - - \internal -*/ -bool QGraphicsScenePrivate::closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2) -{ - return closestItemFirst_withoutCache(item2, item1); -} - -void QGraphicsScenePrivate::climbTree(QGraphicsItem *item, int *stackingOrder) -{ - if (!item->d_ptr->children.isEmpty()) { - QList<QGraphicsItem *> childList = item->d_ptr->children; - qSort(childList.begin(), childList.end(), qt_closestLeaf); - for (int i = 0; i < childList.size(); ++i) { - QGraphicsItem *item = childList.at(i); - if (!(item->flags() & QGraphicsItem::ItemStacksBehindParent)) - climbTree(childList.at(i), stackingOrder); - } - item->d_ptr->globalStackingOrder = (*stackingOrder)++; - for (int i = 0; i < childList.size(); ++i) { - QGraphicsItem *item = childList.at(i); - if (item->flags() & QGraphicsItem::ItemStacksBehindParent) - climbTree(childList.at(i), stackingOrder); - } - } else { - item->d_ptr->globalStackingOrder = (*stackingOrder)++; - } -} - -void QGraphicsScenePrivate::_q_updateSortCache() -{ - _q_updateIndex(); - - if (!sortCacheEnabled || !updatingSortCache) - return; - - updatingSortCache = false; - int stackingOrder = 0; - - QList<QGraphicsItem *> topLevels; - - for (int i = 0; i < indexedItems.size(); ++i) { - QGraphicsItem *item = indexedItems.at(i); - if (item && item->parentItem() == 0) - topLevels << item; - } - for (int i = 0; i < unindexedItems.size(); ++i) { - QGraphicsItem *item = unindexedItems.at(i); - if (item->parentItem() == 0) - topLevels << item; - } - - qSort(topLevels.begin(), topLevels.end(), qt_closestLeaf); - for (int i = 0; i < topLevels.size(); ++i) - climbTree(topLevels.at(i), &stackingOrder); -} - -void QGraphicsScenePrivate::sortItems(QList<QGraphicsItem *> *itemList, Qt::SortOrder order, - bool sortCacheEnabled) -{ - if (sortCacheEnabled) { - if (order == Qt::AscendingOrder) { - qSort(itemList->begin(), itemList->end(), closestItemFirst_withCache); - } else if (order == Qt::DescendingOrder) { - qSort(itemList->begin(), itemList->end(), closestItemLast_withCache); - } - } else { - if (order == Qt::AscendingOrder) { - qSort(itemList->begin(), itemList->end(), closestItemFirst_withoutCache); - } else if (order == Qt::DescendingOrder) { - qSort(itemList->begin(), itemList->end(), closestItemLast_withoutCache); - } - } -} - /*! \internal @@ -2333,8 +1229,8 @@ QGraphicsScene::QGraphicsScene(QObject *parent) QGraphicsScene::QGraphicsScene(const QRectF &sceneRect, QObject *parent) : QObject(*new QGraphicsScenePrivate, parent) { - setSceneRect(sceneRect); d_func()->init(); + setSceneRect(sceneRect); } /*! @@ -2348,8 +1244,8 @@ QGraphicsScene::QGraphicsScene(const QRectF &sceneRect, QObject *parent) QGraphicsScene::QGraphicsScene(qreal x, qreal y, qreal width, qreal height, QObject *parent) : QObject(*new QGraphicsScenePrivate, parent) { - setSceneRect(x, y, width, height); d_func()->init(); + setSceneRect(x, y, width, height); } /*! @@ -2386,8 +1282,19 @@ QGraphicsScene::~QGraphicsScene() QRectF QGraphicsScene::sceneRect() const { Q_D(const QGraphicsScene); - const_cast<QGraphicsScenePrivate *>(d)->_q_updateIndex(); - return d->hasSceneRect ? d->sceneRect : d->growingItemsBoundingRect; + if (d->hasSceneRect) + return d->sceneRect; + + if (d->dirtyGrowingItemsBoundingRect) { + // Lazily update the growing items bounding rect + QGraphicsScenePrivate *thatd = const_cast<QGraphicsScenePrivate *>(d); + QRectF oldGrowingBoundingRect = thatd->growingItemsBoundingRect; + thatd->growingItemsBoundingRect |= itemsBoundingRect(); + thatd->dirtyGrowingItemsBoundingRect = false; + if (oldGrowingBoundingRect != thatd->growingItemsBoundingRect) + emit const_cast<QGraphicsScene *>(this)->sceneRectChanged(thatd->growingItemsBoundingRect); + } + return d->growingItemsBoundingRect; } void QGraphicsScene::setSceneRect(const QRectF &rect) { @@ -2395,8 +1302,7 @@ void QGraphicsScene::setSceneRect(const QRectF &rect) if (rect != d->sceneRect) { d->hasSceneRect = !rect.isNull(); d->sceneRect = rect; - d->resetIndex(); - emit sceneRectChanged(rect); + emit sceneRectChanged(d->hasSceneRect ? rect : d->growingItemsBoundingRect); } } @@ -2437,6 +1343,8 @@ void QGraphicsScene::setSceneRect(const QRectF &rect) void QGraphicsScene::render(QPainter *painter, const QRectF &target, const QRectF &source, Qt::AspectRatioMode aspectRatioMode) { + // ### Switch to using the recursive rendering algorithm instead. + // Default source rect = scene rect QRectF sourceRect = source; if (sourceRect.isNull()) @@ -2531,8 +1439,19 @@ QGraphicsScene::ItemIndexMethod QGraphicsScene::itemIndexMethod() const void QGraphicsScene::setItemIndexMethod(ItemIndexMethod method) { Q_D(QGraphicsScene); - d->resetIndex(); + if (d->indexMethod == method) + return; + d->indexMethod = method; + + QList<QGraphicsItem *> oldItems = d->index->items(Qt::AscendingOrder); + delete d->index; + if (method == BspTreeIndex) + d->index = new QGraphicsSceneBspTreeIndex(this); + else + d->index = new QGraphicsSceneLinearIndex(this); + for (int i = oldItems.size() - 1; i >= 0; --i) + d->index->addItem(oldItems.at(i)); } /*! @@ -2570,35 +1489,32 @@ void QGraphicsScene::setItemIndexMethod(ItemIndexMethod method) int QGraphicsScene::bspTreeDepth() const { Q_D(const QGraphicsScene); - return d->bspTreeDepth; + QGraphicsSceneBspTreeIndex *bspTree = qobject_cast<QGraphicsSceneBspTreeIndex *>(d->index); + return bspTree ? bspTree->bspTreeDepth() : 0; } void QGraphicsScene::setBspTreeDepth(int depth) { Q_D(QGraphicsScene); - if (d->bspTreeDepth == depth) - return; - if (depth < 0) { qWarning("QGraphicsScene::setBspTreeDepth: invalid depth %d ignored; must be >= 0", depth); return; } - d->bspTreeDepth = depth; - d->resetIndex(); + QGraphicsSceneBspTreeIndex *bspTree = qobject_cast<QGraphicsSceneBspTreeIndex *>(d->index); + if (!bspTree) { + qWarning("QGraphicsScene::setBspTreeDepth: can not apply if indexing method is not BSP"); + return; + } + bspTree->setBspTreeDepth(depth); } /*! \property QGraphicsScene::sortCacheEnabled \brief whether sort caching is enabled \since 4.5 + \obsolete - When enabled, this property adds a cache that speeds up sorting and - transformations for scenes with deep hierarchies (i.e., items with many - levels of descendents), at the cost of using more memory (approx. 100 more - bytes of memory per item). - - Items that are not part of a deep hierarchy suffer no penalty from this - cache. + Since Qt 4.6, this property has no effect. */ bool QGraphicsScene::isSortCacheEnabled() const { @@ -2608,10 +1524,9 @@ bool QGraphicsScene::isSortCacheEnabled() const void QGraphicsScene::setSortCacheEnabled(bool enabled) { Q_D(QGraphicsScene); - if (enabled == d->sortCacheEnabled) + if (d->sortCacheEnabled == enabled) return; - if ((d->sortCacheEnabled = enabled)) - d->invalidateSortCache(); + d->sortCacheEnabled = enabled; } /*! @@ -2623,6 +1538,7 @@ void QGraphicsScene::setSortCacheEnabled(bool enabled) */ QRectF QGraphicsScene::itemsBoundingRect() const { + // Does not take untransformable items into account. QRectF boundingRect; foreach (QGraphicsItem *item, items()) boundingRect |= item->sceneBoundingRect(); @@ -2637,29 +1553,24 @@ QRectF QGraphicsScene::itemsBoundingRect() const QList<QGraphicsItem *> QGraphicsScene::items() const { Q_D(const QGraphicsScene); - const_cast<QGraphicsScenePrivate *>(d)->purgeRemovedItems(); + return d->index->items(Qt::AscendingOrder); +} - // If freeItemIndexes is empty, we know there are no holes in indexedItems and - // unindexedItems. - if (d->freeItemIndexes.isEmpty()) { - if (d->unindexedItems.isEmpty()) - return d->indexedItems; - return d->indexedItems + d->unindexedItems; - } +/*! + Returns an ordered list of all items on the scene. \a order decides the + sorting. - // Rebuild the list of items to avoid holes. ### We could also just - // compress the item lists at this point. - QList<QGraphicsItem *> itemList; - foreach (QGraphicsItem *item, d->indexedItems + d->unindexedItems) { - if (item) - itemList << item; - } - return itemList; + \sa addItem(), removeItem() +*/ +QList<QGraphicsItem *> QGraphicsScene::items(Qt::SortOrder order) const +{ + Q_D(const QGraphicsScene); + return d->index->items(order); } /*! Returns all visible items at position \a pos in the scene. The items are - listed in descending Z order (i.e., the first item in the list is the + listed in descending stacking order (i.e., the first item in the list is the top-most item, and the last item is the bottom-most item). \sa itemAt() @@ -2667,7 +1578,7 @@ QList<QGraphicsItem *> QGraphicsScene::items() const QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos) const { Q_D(const QGraphicsScene); - return d->items_helper(pos); + return d->index->items(pos, Qt::IntersectsItemShape, Qt::AscendingOrder); } /*! @@ -2686,9 +1597,7 @@ QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos) const QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rect, Qt::ItemSelectionMode mode) const { Q_D(const QGraphicsScene); - QList<QGraphicsItem *> itemList; - d->recursive_items_helper(0, rect, &itemList, QTransform(), QTransform(), mode, Qt::AscendingOrder); - return itemList; + return d->index->items(rect, mode, Qt::AscendingOrder); } /*! @@ -2712,7 +1621,7 @@ QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rect, Qt::ItemSelecti QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode) const { Q_D(const QGraphicsScene); - return d->items_helper(polygon, mode, Qt::AscendingOrder); + return d->index->items(polygon, mode, Qt::AscendingOrder); } /*! @@ -2729,7 +1638,85 @@ QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemS QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode) const { Q_D(const QGraphicsScene); - return d->items_helper(path, mode, Qt::AscendingOrder); + return d->index->items(path, mode, Qt::AscendingOrder); +} + +/*! + Returns all visible items that, depending on \a mode, are at the specified \a pos + and return a list sorted using \a order. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with \a pos are returned. + + \a deviceTransform is the transformation apply to the view. + + \sa itemAt() +*/ +QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform) const +{ + Q_D(const QGraphicsScene); + return d->index->items(pos, mode, order, deviceTransform); +} + +/*! + \overload + + Returns all visible items that, depending on \a mode, are either inside or + intersect with the specified \a rect and return a list sorted using \a order. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a rect are returned. + + \a deviceTransform is the transformation apply to the view. + + \sa itemAt() +*/ +QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rect, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform) const +{ + Q_D(const QGraphicsScene); + return d->index->items(rect, mode, order, deviceTransform); +} + +/*! + \overload + + Returns all visible items that, depending on \a mode, are either inside or + intersect with the specified \a polygon and return a list sorted using \a order. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a polygon are returned. + + \a deviceTransform is the transformation apply to the view. + + \sa itemAt() +*/ +QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform) const +{ + Q_D(const QGraphicsScene); + return d->index->items(polygon, mode, order, deviceTransform); +} + +/*! + \overload + + Returns all visible items that, depending on \a mode, are either inside or + intersect with the specified \a path and return a list sorted using \a order. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a path are returned. + + \a deviceTransform is the transformation apply to the view. + + \sa itemAt() +*/ +QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform) const +{ + Q_D(const QGraphicsScene); + return d->index->items(path, mode, order, deviceTransform); } /*! @@ -2752,12 +1739,12 @@ QList<QGraphicsItem *> QGraphicsScene::collidingItems(const QGraphicsItem *item, return QList<QGraphicsItem *>(); } + // Does not support ItemIgnoresTransformations. QList<QGraphicsItem *> tmp; - foreach (QGraphicsItem *itemInVicinity, d->estimateItemsInRect(item->sceneBoundingRect())) { + foreach (QGraphicsItem *itemInVicinity, d->index->estimateItems(item->sceneBoundingRect(), Qt::AscendingOrder)) { if (item != itemInVicinity && item->collidesWithItem(itemInVicinity, mode)) tmp << itemInVicinity; } - d->sortItems(&tmp, Qt::AscendingOrder, d->sortCacheEnabled); return tmp; } @@ -2777,6 +1764,13 @@ QGraphicsItem *QGraphicsScene::itemAt(const QPointF &pos) const return itemsAtPoint.isEmpty() ? 0 : itemsAtPoint.first(); } +QGraphicsItem *QGraphicsScene::itemAt(const QPointF &pos, const QTransform &deviceTransform) const +{ + QList<QGraphicsItem *> itemsAtPoint = items(pos, Qt::IntersectsItemShape, + Qt::AscendingOrder, deviceTransform); + return itemsAtPoint.isEmpty() ? 0 : itemsAtPoint.first(); +} + /*! \fn QGraphicsScene::itemAt(qreal x, qreal y) const \overload @@ -2852,6 +1846,21 @@ void QGraphicsScene::setSelectionArea(const QPainterPath &path) */ void QGraphicsScene::setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode) { + setSelectionArea(path, mode, QTransform()); +} + +/*! + \overload + \since 4.3 + + Sets the selection area to \a path using \a mode to determine if items are + included in the selection area. + + \sa clearSelection(), selectionArea() +*/ +void QGraphicsScene::setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode, + const QTransform &deviceTransform) +{ Q_D(QGraphicsScene); // Note: with boolean path operations, we can improve performance here @@ -2867,7 +1876,7 @@ void QGraphicsScene::setSelectionArea(const QPainterPath &path, Qt::ItemSelectio bool changed = false; // Set all items in path to selected. - foreach (QGraphicsItem *item, items(path, mode)) { + foreach (QGraphicsItem *item, items(path, mode, Qt::AscendingOrder, deviceTransform)) { if (item->flags() & QGraphicsItem::ItemIsSelectable) { if (!item->isSelected()) changed = true; @@ -2924,26 +1933,13 @@ void QGraphicsScene::clearSelection() void QGraphicsScene::clear() { Q_D(QGraphicsScene); - // Recursive descent delete - for (int i = 0; i < d->indexedItems.size(); ++i) { - if (QGraphicsItem *item = d->indexedItems.at(i)) { - if (!item->parentItem()) - delete item; - } - } - QList<QGraphicsItem *> unindexedParents; - for (int i = 0; i < d->unindexedItems.size(); ++i) { - QGraphicsItem *item = d->unindexedItems.at(i); - if (!item->parentItem()) - unindexedParents << item; - } - d->unindexedItems.clear(); - qDeleteAll(unindexedParents); - d->indexedItems.clear(); - d->freeItemIndexes.clear(); + // NB! We have to clear the index before deleting items; otherwise the + // index might try to access dangling item pointers. + d->index->clear(); + const QList<QGraphicsItem *> items = d->topLevelItems; + qDeleteAll(items); + Q_ASSERT(d->topLevelItems.isEmpty()); d->lastItemCount = 0; - d->bspTree.clear(); - d->largestUntransformableItem = QRectF(); d->allItemsIgnoreHoverEvents = true; d->allItemsUseDefaultCursor = true; d->allItemsIgnoreTouchEvents = true; @@ -3065,14 +2061,6 @@ void QGraphicsScene::addItem(QGraphicsItem *item) return; } - // Prevent reusing a recently deleted pointer: purge all removed items - // from our lists. - d->purgeRemovedItems(); - - // Invalidate any sort caching; arrival of a new item means we need to - // resort. - d->invalidateSortCache(); - // Detach this item from its parent if the parent's scene is different // from this scene. if (QGraphicsItem *itemParent = item->parentItem()) { @@ -3083,29 +2071,18 @@ void QGraphicsScene::addItem(QGraphicsItem *item) // Add the item to this scene item->d_func()->scene = targetScene; - // Indexing requires sceneBoundingRect(), but because \a item might - // not be completely constructed at this point, we need to store it in - // a temporary list and schedule an indexing for later. - d->unindexedItems << item; - item->d_func()->index = -1; - d->startIndexTimer(0); + // Add the item in the index + d->index->addItem(item); // Add to list of toplevels if this item is a toplevel. if (!item->d_ptr->parent) d->registerTopLevelItem(item); - // Update the scene's sort cache settings. - item->d_ptr->globalStackingOrder = -1; - d->invalidateSortCache(); - // Add to list of items that require an update. We cannot assume that the // item is fully constructed, so calling item->update() can lead to a pure // virtual function call to boundingRect(). - if (!d->updateAll) { - if (d->pendingUpdateItems.isEmpty()) - QMetaObject::invokeMethod(this, "_q_updateLater", Qt::QueuedConnection); - d->pendingUpdateItems << item; - } + d->markDirty(item); + d->dirtyGrowingItemsBoundingRect = true; // Disable selectionChanged() for individual items ++d->selectionChanging; @@ -4039,16 +3016,6 @@ bool QGraphicsScene::event(QEvent *event) case QEvent::TouchEnd: d->touchEventHandler(static_cast<QTouchEvent *>(event)); break; - case QEvent::Timer: - if (d->indexTimerId && static_cast<QTimerEvent *>(event)->timerId() == d->indexTimerId) { - if (d->restartIndexTimer) { - d->restartIndexTimer = false; - } else { - // this call will kill the timer - d->_q_updateIndex(); - } - } - // Fallthrough intended - support timers in subclasses. default: return QObject::event(event); } @@ -5137,6 +4104,20 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte } } +void QGraphicsScenePrivate::drawItems(QPainter *painter, const QTransform *const viewTransform, + QRegion *exposedRegion, QWidget *widget) +{ + QRectF exposedSceneRect; + if (exposedRegion && indexMethod != QGraphicsScene::NoIndex) { + exposedSceneRect = exposedRegion->boundingRect().adjusted(-1, -1, 1, 1); + if (viewTransform) + exposedSceneRect = viewTransform->inverted().mapRect(exposedSceneRect); + } + const QList<QGraphicsItem *> tli = index->estimateTopLevelItems(exposedSceneRect, Qt::DescendingOrder); + for (int i = 0; i < tli.size(); ++i) + drawSubtreeRecursive(tli.at(i), painter, viewTransform, exposedRegion, widget); +} + void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter *painter, const QTransform *const viewTransform, QRegion *exposedRegion, QWidget *widget, @@ -5159,6 +4140,7 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * QTransform transform(Qt::Uninitialized); QTransform *transformPtr = 0; + bool translateOnlyTransform = false; #define ENSURE_TRANSFORM_PTR \ if (!transformPtr) { \ Q_ASSERT(!itemIsUntransformable); \ @@ -5168,6 +4150,7 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * transformPtr = &transform; \ } else { \ transformPtr = &item->d_ptr->sceneTransform; \ + translateOnlyTransform = item->d_ptr->sceneTransformTranslateOnly; \ } \ } @@ -5179,10 +4162,8 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * transform = item->deviceTransform(viewTransform ? *viewTransform : QTransform()); transformPtr = &transform; } else if (item->d_ptr->dirtySceneTransform) { - item->d_ptr->sceneTransform = item->d_ptr->parent ? item->d_ptr->parent->d_ptr->sceneTransform - : QTransform(); - item->d_ptr->combineTransformFromParent(&item->d_ptr->sceneTransform); - item->d_ptr->dirtySceneTransform = 0; + item->d_ptr->updateSceneTransformFromParent(); + Q_ASSERT(!item->d_ptr->dirtySceneTransform); wasDirtyParentSceneTransform = true; } @@ -5191,7 +4172,8 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * if (drawItem) { const QRectF brect = adjustedItemBoundingRect(item); ENSURE_TRANSFORM_PTR - QRect viewBoundingRect = transformPtr->mapRect(brect).toRect(); + QRect viewBoundingRect = translateOnlyTransform ? brect.translated(transformPtr->dx(), transformPtr->dy()).toRect() + : transformPtr->mapRect(brect).toRect(); item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect); viewBoundingRect.adjust(-1, -1, 1, 1); drawItem = exposedRegion ? exposedRegion->intersects(viewBoundingRect) : !viewBoundingRect.isEmpty(); @@ -5208,10 +4190,7 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * int i = 0; if (itemHasChildren) { - if (item->d_ptr->needSortChildren) { - item->d_ptr->needSortChildren = 0; - qStableSort(item->d_ptr->children.begin(), item->d_ptr->children.end(), qt_notclosestLeaf); - } + item->d_ptr->ensureSortedChildren(); if (itemClipsChildrenToShape) { painter->save(); @@ -5237,7 +4216,8 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * if (drawItem) { Q_ASSERT(!itemIsFullyTransparent); Q_ASSERT(itemHasContents); - item->d_ptr->initStyleOption(&styleOptionTmp, transform, exposedRegion + ENSURE_TRANSFORM_PTR + item->d_ptr->initStyleOption(&styleOptionTmp, *transformPtr, exposedRegion ? *exposedRegion : QRegion(), exposedRegion == 0); const bool itemClipsToShape = item->d_ptr->flags & QGraphicsItem::ItemClipsToShape; @@ -5245,10 +4225,9 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * if (savePainter) painter->save(); - if (!itemHasChildren || !itemClipsChildrenToShape) { - ENSURE_TRANSFORM_PTR + if (!itemHasChildren || !itemClipsChildrenToShape) painter->setWorldTransform(*transformPtr); - } + if (itemClipsToShape) painter->setClipPath(item->shape(), Qt::IntersectClip); painter->setOpacity(opacity); @@ -5291,6 +4270,16 @@ void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, b /*ignoreVisibleBit=*/force, /*ignoreDirtyBit=*/removingItemFromScene || invalidateChildren, /*ignoreOpacity=*/ignoreOpacity)) { + if (item->d_ptr->dirty) { + // The item is already marked as dirty and will be processed later. However, + // we have to make sure ignoreVisible and ignoreOpacity are set properly; + // otherwise things like: item->update(); item->hide() (force is now true) + // won't work as expected. + if (force) + item->d_ptr->ignoreVisible = 1; + if (ignoreOpacity) + item->d_ptr->ignoreOpacity = 1; + } return; } @@ -5350,65 +4339,121 @@ void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, b } static inline bool updateHelper(QGraphicsViewPrivate *view, QGraphicsItemPrivate *item, - const QRectF &rect, const QTransform &xform) + const QRectF &rect, bool itemIsUntransformable) { Q_ASSERT(view); Q_ASSERT(item); - if (item->hasBoundingRegionGranularity) + + QGraphicsItem *itemq = static_cast<QGraphicsItem *>(item->q_ptr); + QGraphicsView *viewq = static_cast<QGraphicsView *>(view->q_ptr); + + if (itemIsUntransformable) { + const QTransform xform = itemq->deviceTransform(viewq->viewportTransform()); + if (!item->hasBoundingRegionGranularity) + return view->updateRect(xform.mapRect(rect).toRect()); return view->updateRegion(xform.map(QRegion(rect.toRect()))); - return view->updateRect(xform.mapRect(rect).toRect()); + } + + if (item->sceneTransformTranslateOnly && view->identityMatrix) { + const qreal dx = item->sceneTransform.dx(); + const qreal dy = item->sceneTransform.dy(); + if (!item->hasBoundingRegionGranularity) { + QRectF r(rect); + r.translate(dx - view->horizontalScroll(), dy - view->verticalScroll()); + return view->updateRect(r.toRect()); + } + QRegion r(rect.toRect()); + r.translate(qRound(dx) - view->horizontalScroll(), qRound(dy) - view->verticalScroll()); + return view->updateRegion(r); + } + + if (!viewq->isTransformed()) { + if (!item->hasBoundingRegionGranularity) + return view->updateRect(item->sceneTransform.mapRect(rect).toRect()); + return view->updateRegion(item->sceneTransform.map(QRegion(rect.toRect()))); + } + + QTransform xform = item->sceneTransform; + xform *= viewq->viewportTransform(); + if (!item->hasBoundingRegionGranularity) + return view->updateRect(xform.mapRect(rect).toRect()); + return view->updateRegion(xform.map(QRegion(rect.toRect()))); } void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool dirtyAncestorContainsChildren, qreal parentOpacity) { Q_Q(QGraphicsScene); + Q_ASSERT(item); + Q_ASSERT(!updateAll); - bool wasDirtyParentViewBoundingRects = false; - bool wasDirtyParentSceneTransform = false; - qreal opacity = parentOpacity; + if (!item->d_ptr->dirty && !item->d_ptr->dirtyChildren) { + resetDirtyItem(item); + return; + } - if (item) { - wasDirtyParentViewBoundingRects = item->d_ptr->paintedViewBoundingRectsNeedRepaint; - opacity = item->d_ptr->combineOpacityFromParent(parentOpacity); - const bool itemIsHidden = !item->d_ptr->ignoreVisible && !item->d_ptr->visible; - const bool itemIsFullyTransparent = !item->d_ptr->ignoreOpacity && opacity == 0.0; - - if (item->d_ptr->dirtySceneTransform && !itemIsHidden && !item->d_ptr->itemIsUntransformable() - && !(itemIsFullyTransparent && item->d_ptr->childrenCombineOpacity())) { - // Calculate the full scene transform for this item. - item->d_ptr->sceneTransform = item->d_ptr->parent ? item->d_ptr->parent->d_ptr->sceneTransform - : QTransform(); - item->d_ptr->combineTransformFromParent(&item->d_ptr->sceneTransform); - item->d_ptr->dirtySceneTransform = 0; - wasDirtyParentSceneTransform = true; - } + const bool itemIsHidden = !item->d_ptr->ignoreVisible && !item->d_ptr->visible; + if (itemIsHidden) { + resetDirtyItem(item, /*recursive=*/true); + return; + } + + const bool itemHasContents = !(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents); + const bool itemHasChildren = !item->d_ptr->children.isEmpty(); + if (!itemHasContents && !itemHasChildren) { + resetDirtyItem(item); + return; // Item has neither contents nor children!(?) + } + + const qreal opacity = item->d_ptr->combineOpacityFromParent(parentOpacity); + const bool itemIsFullyTransparent = !item->d_ptr->ignoreOpacity && opacity < 0.0001; + if (itemIsFullyTransparent && (!itemHasChildren || item->d_ptr->childrenCombineOpacity())) { + resetDirtyItem(item, /*recursive=*/itemHasChildren); + return; + } - if (itemIsHidden || itemIsFullyTransparent || (item->d_ptr->flags & QGraphicsItem::ItemHasNoContents)) { - // Make sure we don't process invisible items or items with no content. - item->d_ptr->dirty = 0; + bool wasDirtyParentSceneTransform = item->d_ptr->dirtySceneTransform; + const bool itemIsUntransformable = item->d_ptr->itemIsUntransformable(); + if (wasDirtyParentSceneTransform && !itemIsUntransformable) { + item->d_ptr->updateSceneTransformFromParent(); + Q_ASSERT(!item->d_ptr->dirtySceneTransform); + } + + const bool wasDirtyParentViewBoundingRects = item->d_ptr->paintedViewBoundingRectsNeedRepaint; + if (itemIsFullyTransparent || !itemHasContents || dirtyAncestorContainsChildren) { + // Make sure we don't process invisible items or items with no content. + item->d_ptr->dirty = 0; + item->d_ptr->fullUpdatePending = 0; + // Might have a dirty view bounding rect otherwise. + if (itemIsFullyTransparent || !itemHasContents) item->d_ptr->paintedViewBoundingRectsNeedRepaint = 0; + } + + if (!hasSceneRect && item->d_ptr->geometryChanged && item->d_ptr->visible) { + // Update growingItemsBoundingRect. + if (item->d_ptr->sceneTransformTranslateOnly) { + growingItemsBoundingRect |= item->boundingRect().translated(item->d_ptr->sceneTransform.dx(), + item->d_ptr->sceneTransform.dy()); + } else { + growingItemsBoundingRect |= item->d_ptr->sceneTransform.mapRect(item->boundingRect()); } } // Process item. - if (item && (item->d_ptr->dirty || item->d_ptr->paintedViewBoundingRectsNeedRepaint)) { + if (item->d_ptr->dirty || item->d_ptr->paintedViewBoundingRectsNeedRepaint) { const bool useCompatUpdate = views.isEmpty() || (connectedSignals & changedSignalMask); - const bool untransformableItem = item->d_ptr->itemIsUntransformable(); const QRectF itemBoundingRect = adjustedItemBoundingRect(item); - if (item->d_ptr->geometryChanged) { - // Update growingItemsBoundingRect. - if (!hasSceneRect) - growingItemsBoundingRect |= item->d_ptr->sceneTransform.mapRect(itemBoundingRect); - item->d_ptr->geometryChanged = 0; - } - - if (useCompatUpdate && !untransformableItem && qFuzzyIsNull(item->boundingRegionGranularity())) { + if (useCompatUpdate && !itemIsUntransformable && qFuzzyIsNull(item->boundingRegionGranularity())) { // This block of code is kept for compatibility. Since 4.5, by default // QGraphicsView does not connect the signal and we use the below // method of delivering updates. - q->update(item->d_ptr->sceneTransform.mapRect(itemBoundingRect)); + if (item->d_ptr->sceneTransformTranslateOnly) { + q->update(itemBoundingRect.translated(item->d_ptr->sceneTransform.dx(), + item->d_ptr->sceneTransform.dy())); + } else { + q->update(item->d_ptr->sceneTransform.mapRect(itemBoundingRect)); + } } else { QRectF dirtyRect; bool uninitializedDirtyRect = true; @@ -5416,30 +4461,31 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool for (int j = 0; j < views.size(); ++j) { QGraphicsView *view = views.at(j); QGraphicsViewPrivate *viewPrivate = view->d_func(); - if (viewPrivate->fullUpdatePending) - continue; - switch (viewPrivate->viewportUpdateMode) { - case QGraphicsView::NoViewportUpdate: - continue; - case QGraphicsView::FullViewportUpdate: - view->viewport()->update(); - viewPrivate->fullUpdatePending = 1; + QRect &paintedViewBoundingRect = item->d_ptr->paintedViewBoundingRects[viewPrivate->viewport]; + if (viewPrivate->fullUpdatePending + || viewPrivate->viewportUpdateMode == QGraphicsView::NoViewportUpdate) { + // Okay, if we have a full update pending or no viewport update, this item's + // paintedViewBoundingRect will be updated correctly in the next paintEvent if + // it is inside the viewport, but for now we can pretend that it is outside. + paintedViewBoundingRect = QRect(-1, -1, -1, -1); continue; - default: - break; } - QRect &paintedViewBoundingRect = item->d_ptr->paintedViewBoundingRects[viewPrivate->viewport]; - if (item->d_ptr->paintedViewBoundingRectsNeedRepaint) { - wasDirtyParentViewBoundingRects = true; + if (item->d_ptr->paintedViewBoundingRectsNeedRepaint && !paintedViewBoundingRect.isEmpty()) { paintedViewBoundingRect.translate(viewPrivate->dirtyScrollOffset); if (!viewPrivate->updateRect(paintedViewBoundingRect)) - paintedViewBoundingRect = QRect(); + paintedViewBoundingRect = QRect(-1, -1, -1, -1); // Outside viewport. } if (!item->d_ptr->dirty) continue; + if (!item->d_ptr->paintedViewBoundingRectsNeedRepaint + && paintedViewBoundingRect.x() == -1 && paintedViewBoundingRect.y() == -1 + && paintedViewBoundingRect.width() == -1 && paintedViewBoundingRect.height() == -1) { + continue; // Outside viewport. + } + if (uninitializedDirtyRect) { dirtyRect = itemBoundingRect; if (!item->d_ptr->fullUpdatePending) { @@ -5452,35 +4498,25 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool if (dirtyRect.isEmpty()) continue; // Discard updates outside the bounding rect. - bool valid = false; - if (untransformableItem) { - valid = updateHelper(viewPrivate, item->d_ptr, dirtyRect, - item->deviceTransform(view->viewportTransform())); - } else if (!view->isTransformed()) { - valid = updateHelper(viewPrivate, item->d_ptr, dirtyRect, item->d_ptr->sceneTransform); - } else { - QTransform deviceTransform = item->d_ptr->sceneTransform; - deviceTransform *= view->viewportTransform(); - valid = updateHelper(viewPrivate, item->d_ptr, dirtyRect, deviceTransform); + if (!updateHelper(viewPrivate, item->d_ptr, dirtyRect, itemIsUntransformable) + && item->d_ptr->paintedViewBoundingRectsNeedRepaint) { + paintedViewBoundingRect = QRect(-1, -1, -1, -1); // Outside viewport. } - if (!valid) - paintedViewBoundingRect = QRect(); } } } - // Process root items / children. - if (!item || item->d_ptr->dirtyChildren) { - QList<QGraphicsItem *> *children = item ? &item->d_ptr->children : &topLevelItems; - const bool allChildrenDirty = item && item->d_ptr->allChildrenDirty; + // Process children. + if (itemHasChildren && item->d_ptr->dirtyChildren) { if (!dirtyAncestorContainsChildren) { - dirtyAncestorContainsChildren = item && item->d_ptr->fullUpdatePending + dirtyAncestorContainsChildren = item->d_ptr->fullUpdatePending && (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape); } - const bool parentIgnoresVisible = item && item->d_ptr->ignoreVisible; - const bool parentIgnoresOpacity = item && item->d_ptr->ignoreOpacity; - for (int i = 0; i < children->size(); ++i) { - QGraphicsItem *child = children->at(i); + const bool allChildrenDirty = item->d_ptr->allChildrenDirty; + const bool parentIgnoresVisible = item->d_ptr->ignoreVisible; + const bool parentIgnoresOpacity = item->d_ptr->ignoreOpacity; + for (int i = 0; i < item->d_ptr->children.size(); ++i) { + QGraphicsItem *child = item->d_ptr->children.at(i); if (wasDirtyParentSceneTransform) child->d_ptr->dirtySceneTransform = 1; if (wasDirtyParentViewBoundingRects) @@ -5489,36 +4525,19 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool child->d_ptr->ignoreVisible = 1; if (parentIgnoresOpacity) child->d_ptr->ignoreOpacity = 1; - if (allChildrenDirty) { child->d_ptr->dirty = 1; child->d_ptr->fullUpdatePending = 1; child->d_ptr->dirtyChildren = 1; child->d_ptr->allChildrenDirty = 1; - } else if (!child->d_ptr->dirty && !child->d_ptr->dirtyChildren) { - resetDirtyItem(child); - continue; - } - - if (dirtyAncestorContainsChildren || updateAll) { - // No need to process this child's dirty rect, hence reset the dirty state. - // However, we have to continue the recursion because it might have a dirty - // view bounding rect that needs repaint. We also have to reset the dirty - // state of its descendants. - child->d_ptr->dirty = 0; - child->d_ptr->fullUpdatePending = 0; - if (updateAll) - child->d_ptr->paintedViewBoundingRectsNeedRepaint = 0; } - processDirtyItemsRecursive(child, dirtyAncestorContainsChildren, opacity); } } else if (wasDirtyParentSceneTransform) { item->d_ptr->invalidateChildrenSceneTransform(); } - if (item) - resetDirtyItem(item); + resetDirtyItem(item); } /*! diff --git a/src/gui/graphicsview/qgraphicsscene.h b/src/gui/graphicsview/qgraphicsscene.h index 6aaeb91..d790f90 100644 --- a/src/gui/graphicsview/qgraphicsscene.h +++ b/src/gui/graphicsview/qgraphicsscene.h @@ -83,6 +83,7 @@ class QGraphicsSimpleTextItem; class QGraphicsTextItem; class QGraphicsView; class QGraphicsWidget; +class QGraphicsSceneIndex; class QHelpEvent; class QInputMethodEvent; class QKeyEvent; @@ -152,22 +153,38 @@ public: QRectF itemsBoundingRect() const; QList<QGraphicsItem *> items() const; - QList<QGraphicsItem *> items(const QPointF &pos) const; - QList<QGraphicsItem *> items(const QRectF &rect, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; - QList<QGraphicsItem *> items(const QPolygonF &polygon, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; - QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; + QList<QGraphicsItem *> items(Qt::SortOrder order) const; // ### Qt 5: unify + + QList<QGraphicsItem *> items(const QPointF &pos, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const; + QList<QGraphicsItem *> items(const QRectF &rect, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const; + QList<QGraphicsItem *> items(const QPolygonF &polygon, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const; + QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const; + + QList<QGraphicsItem *> items(const QPointF &pos) const; // ### obsolete + QList<QGraphicsItem *> items(const QRectF &rect, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; // ### obsolete + QList<QGraphicsItem *> items(const QPolygonF &polygon, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; // ### obsolete + QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; // ### obsolete + QList<QGraphicsItem *> collidingItems(const QGraphicsItem *item, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; - QGraphicsItem *itemAt(const QPointF &pos) const; + + QGraphicsItem *itemAt(const QPointF &pos) const; // ### obsolete + QGraphicsItem *itemAt(const QPointF &pos, const QTransform &deviceTransform) const; inline QList<QGraphicsItem *> items(qreal x, qreal y, qreal w, qreal h, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const - { return items(QRectF(x, y, w, h), mode); } - inline QGraphicsItem *itemAt(qreal x, qreal y) const + { return items(QRectF(x, y, w, h), mode); } // ### obsolete + inline QList<QGraphicsItem *> items(qreal x, qreal y, qreal w, qreal h, Qt::ItemSelectionMode mode, Qt::SortOrder order, + const QTransform &deviceTransform = QTransform()) const + { return items(QRectF(x, y, w, h), mode, order, deviceTransform); } + inline QGraphicsItem *itemAt(qreal x, qreal y) const // ### obsolete { return itemAt(QPointF(x, y)); } + inline QGraphicsItem *itemAt(qreal x, qreal y, const QTransform &deviceTransform) const + { return itemAt(QPointF(x, y), deviceTransform); } QList<QGraphicsItem *> selectedItems() const; QPainterPath selectionArea() const; void setSelectionArea(const QPainterPath &path); - void setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode); + void setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode); + void setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode, const QTransform &deviceTransform); QGraphicsItemGroup *createItemGroup(const QList<QGraphicsItem *> &items); void destroyItemGroup(QGraphicsItemGroup *group); @@ -275,11 +292,8 @@ Q_SIGNALS: private: Q_DECLARE_PRIVATE(QGraphicsScene) Q_DISABLE_COPY(QGraphicsScene) - Q_PRIVATE_SLOT(d_func(), void _q_updateIndex()) Q_PRIVATE_SLOT(d_func(), void _q_emitUpdated()) - Q_PRIVATE_SLOT(d_func(), void _q_updateLater()) Q_PRIVATE_SLOT(d_func(), void _q_polishItems()) - Q_PRIVATE_SLOT(d_func(), void _q_updateSortCache()) Q_PRIVATE_SLOT(d_func(), void _q_processDirtyItems()) friend class QGraphicsItem; friend class QGraphicsItemPrivate; @@ -287,6 +301,10 @@ private: friend class QGraphicsViewPrivate; friend class QGraphicsWidget; friend class QGraphicsWidgetPrivate; + friend class QGraphicsSceneIndex; + friend class QGraphicsSceneIndexPrivate; + friend class QGraphicsSceneBspTreeIndex; + friend class QGraphicsSceneBspTreeIndexPrivate; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsScene::SceneLayers) diff --git a/src/gui/graphicsview/qgraphicsscene_bsp.cpp b/src/gui/graphicsview/qgraphicsscene_bsp.cpp index 3f3e58b..fb4b9a4 100644 --- a/src/gui/graphicsview/qgraphicsscene_bsp.cpp +++ b/src/gui/graphicsview/qgraphicsscene_bsp.cpp @@ -70,12 +70,15 @@ class QGraphicsSceneFindItemBspTreeVisitor : public QGraphicsSceneBspTreeVisitor { public: QList<QGraphicsItem *> *foundItems; + bool onlyTopLevelItems; void visit(QList<QGraphicsItem *> *items) { for (int i = 0; i < items->size(); ++i) { QGraphicsItem *item = items->at(i); - if (!item->d_func()->itemDiscovered && item->isVisible()) { + if (onlyTopLevelItems && item->d_ptr->parent) + item = item->topLevelItem(); + if (!item->d_func()->itemDiscovered && item->d_ptr->visible) { item->d_func()->itemDiscovered = 1; foundItems->prepend(item); } @@ -143,19 +146,15 @@ void QGraphicsSceneBspTree::removeItems(const QSet<QGraphicsItem *> &items) } } -QList<QGraphicsItem *> QGraphicsSceneBspTree::items(const QRectF &rect) +QList<QGraphicsItem *> QGraphicsSceneBspTree::items(const QRectF &rect, bool onlyTopLevelItems) const { QList<QGraphicsItem *> tmp; findVisitor->foundItems = &tmp; + findVisitor->onlyTopLevelItems = onlyTopLevelItems; climbTree(findVisitor, rect); - return tmp; -} - -QList<QGraphicsItem *> QGraphicsSceneBspTree::items(const QPointF &pos) -{ - QList<QGraphicsItem *> tmp; - findVisitor->foundItems = &tmp; - climbTree(findVisitor, pos); + // Reset discovery bits. + for (int i = 0; i < tmp.size(); ++i) + tmp.at(i)->d_ptr->itemDiscovered = 0; return tmp; } @@ -235,47 +234,17 @@ void QGraphicsSceneBspTree::initialize(const QRectF &rect, int depth, int index) } } -void QGraphicsSceneBspTree::climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QPointF &pos, int index) -{ - if (nodes.isEmpty()) - return; - - const Node &node = nodes.at(index); - int childIndex = firstChildIndex(index); - - switch (node.type) { - case Node::Leaf: { - visitor->visit(&leaves[node.leafIndex]); - break; - } - case Node::Vertical: - if (pos.x() < node.offset) { - climbTree(visitor, pos, childIndex); - } else { - climbTree(visitor, pos, childIndex + 1); - } - break; - case Node::Horizontal: - if (pos.y() < node.offset) { - climbTree(visitor, pos, childIndex); - } else { - climbTree(visitor, pos, childIndex + 1); - } - break; - } -} - -void QGraphicsSceneBspTree::climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QRectF &rect, int index) +void QGraphicsSceneBspTree::climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QRectF &rect, int index) const { if (nodes.isEmpty()) return; const Node &node = nodes.at(index); - int childIndex = firstChildIndex(index); + const int childIndex = firstChildIndex(index); switch (node.type) { case Node::Leaf: { - visitor->visit(&leaves[node.leafIndex]); + visitor->visit(const_cast<QList<QGraphicsItem*>*>(&leaves[node.leafIndex])); break; } case Node::Vertical: @@ -288,7 +257,6 @@ void QGraphicsSceneBspTree::climbTree(QGraphicsSceneBspTreeVisitor *visitor, con } break; case Node::Horizontal: - int childIndex = firstChildIndex(index); if (rect.top() < node.offset) { climbTree(visitor, rect, childIndex); if (rect.bottom() >= node.offset) diff --git a/src/gui/graphicsview/qgraphicsscene_bsp_p.h b/src/gui/graphicsview/qgraphicsscene_bsp_p.h index 73a937f..4cac64a 100644 --- a/src/gui/graphicsview/qgraphicsscene_bsp_p.h +++ b/src/gui/graphicsview/qgraphicsscene_bsp_p.h @@ -92,8 +92,7 @@ public: void removeItem(QGraphicsItem *item, const QRectF &rect); void removeItems(const QSet<QGraphicsItem *> &items); - QList<QGraphicsItem *> items(const QRectF &rect); - QList<QGraphicsItem *> items(const QPointF &pos); + QList<QGraphicsItem *> items(const QRectF &rect, bool onlyTopLevelItems = false) const; int leafCount() const; inline int firstChildIndex(int index) const @@ -106,11 +105,7 @@ public: private: void initialize(const QRectF &rect, int depth, int index); - void climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QPointF &pos, int index = 0); - void climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QRectF &rect, int index = 0); - - void findItems(QList<QGraphicsItem *> *foundItems, const QRectF &rect, int index); - void findItems(QList<QGraphicsItem *> *foundItems, const QPointF &pos, int index); + void climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QRectF &rect, int index = 0) const; QRectF rectForIndex(int index) const; QVector<Node> nodes; diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index dc720a7..ffd62d5 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -59,7 +59,6 @@ #include "qgraphicssceneevent.h" #include "qgraphicsview.h" -#include "qgraphicsscene_bsp_p.h" #include "qgraphicsitem_p.h" #include <private/qobject_p.h> @@ -72,10 +71,9 @@ #include <QtGui/qstyle.h> #include <QtGui/qstyleoption.h> -static const int QGRAPHICSSCENE_INDEXTIMER_TIMEOUT = 2000; - QT_BEGIN_NAMESPACE +class QGraphicsSceneIndex; class QGraphicsView; class QGraphicsWidget; @@ -86,24 +84,19 @@ public: QGraphicsScenePrivate(); void init(); + static QGraphicsScenePrivate *get(QGraphicsScene *q); + quint32 changedSignalMask; QGraphicsScene::ItemIndexMethod indexMethod; - int bspTreeDepth; - - QList<QGraphicsItem *> estimateItemsInRect(const QRectF &rect) const; - void addToIndex(QGraphicsItem *item); - void removeFromIndex(QGraphicsItem *item); - void resetIndex(); + QGraphicsSceneIndex *index; - QGraphicsSceneBspTree bspTree; - void _q_updateIndex(); int lastItemCount; QRectF sceneRect; bool hasSceneRect; + bool dirtyGrowingItemsBoundingRect; QRectF growingItemsBoundingRect; - QRectF largestUntransformableItem; void _q_emitUpdated(); QList<QRectF> updatedRects; @@ -114,9 +107,6 @@ public: QPainterPath selectionArea; int selectionChanging; QSet<QGraphicsItem *> selectedItems; - QList<QGraphicsItem *> unindexedItems; - QList<QGraphicsItem *> indexedItems; - QList<QGraphicsItem *> pendingUpdateItems; QList<QGraphicsItem *> unpolishedItems; QList<QGraphicsItem *> topLevelItems; bool needSortTopLevelItems; @@ -128,21 +118,11 @@ public: void _q_processDirtyItems(); - QList<int> freeItemIndexes; - bool regenerateIndex; - - bool purgePending; void removeItemHelper(QGraphicsItem *item); - QSet<QGraphicsItem *> removedItems; - void purgeRemovedItems(); QBrush backgroundBrush; QBrush foregroundBrush; - int indexTimerId; - bool restartIndexTimer; - void startIndexTimer(int interval = QGRAPHICSSCENE_INDEXTIMER_TIMEOUT); - bool stickyFocus; bool hasFocus; QGraphicsItem *focusItem; @@ -181,7 +161,6 @@ public: QList<QGraphicsItem *> itemsAtPosition(const QPoint &screenPos, const QPointF &scenePos, QWidget *widget) const; - static bool itemCollidesWithPath(QGraphicsItem *item, const QPainterPath &path, Qt::ItemSelectionMode mode); void storeMouseButtonsForMouseGrabber(QGraphicsSceneMouseEvent *event); QList<QGraphicsView *> views; @@ -210,69 +189,14 @@ public: void mousePressEventHandler(QGraphicsSceneMouseEvent *mouseEvent); QGraphicsWidget *windowForItem(const QGraphicsItem *item) const; - QList<QGraphicsItem *> topLevelItemsInStackingOrder(const QTransform *const, QRegion *); - void recursive_items_helper(QGraphicsItem *item, QRectF rect, QList<QGraphicsItem *> *items, - const QTransform &parentTransform, const QTransform &viewTransform, - Qt::ItemSelectionMode mode, Qt::SortOrder order, qreal parentOpacity = 1.0) const; - - QList<QGraphicsItem *> items_helper(const QPointF &pos) const; - QList<QGraphicsItem *> items_helper(const QRectF &rect, - Qt::ItemSelectionMode mode, - Qt::SortOrder order) const; - QList<QGraphicsItem *> items_helper(const QPolygonF &rect, - Qt::ItemSelectionMode mode, - Qt::SortOrder order) const; - QList<QGraphicsItem *> items_helper(const QPainterPath &rect, - Qt::ItemSelectionMode mode, - Qt::SortOrder order) const; - void childItems_helper(QList<QGraphicsItem *> *items, - const QGraphicsItem *parent, - const QPointF &pos) const; - void childItems_helper(QList<QGraphicsItem *> *items, - const QGraphicsItem *parent, - const QRectF &rect, - Qt::ItemSelectionMode mode) const; - void childItems_helper(QList<QGraphicsItem *> *items, - const QGraphicsItem *parent, - const QPolygonF &polygon, - Qt::ItemSelectionMode mode) const; - void childItems_helper(QList<QGraphicsItem *> *items, - const QGraphicsItem *parent, - const QPainterPath &path, - Qt::ItemSelectionMode mode) const; - - bool sortCacheEnabled; - bool updatingSortCache; - void invalidateSortCache(); - static void climbTree(QGraphicsItem *item, int *stackingOrder); - void _q_updateSortCache(); - - static bool closestItemFirst_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2); - static bool closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2); - - static inline bool closestItemFirst_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2) - { - return item1->d_ptr->globalStackingOrder < item2->d_ptr->globalStackingOrder; - } - static inline bool closestItemLast_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2) - { - return item1->d_ptr->globalStackingOrder >= item2->d_ptr->globalStackingOrder; - } - - static void sortItems(QList<QGraphicsItem *> *itemList, Qt::SortOrder order, bool cached); + bool sortCacheEnabled; // for compatibility void drawItemHelper(QGraphicsItem *item, QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget, bool painterStateProtection); - inline void drawItems(QPainter *painter, const QTransform *const viewTransform, - QRegion *exposedRegion, QWidget *widget) - { - const QList<QGraphicsItem *> tli = topLevelItemsInStackingOrder(viewTransform, exposedRegion); - for (int i = 0; i < tli.size(); ++i) - drawSubtreeRecursive(tli.at(i), painter, viewTransform, exposedRegion, widget); - return; - } + void drawItems(QPainter *painter, const QTransform *const viewTransform, + QRegion *exposedRegion, QWidget *widget); void drawSubtreeRecursive(QGraphicsItem *item, QPainter *painter, const QTransform *const, QRegion *exposedRegion, QWidget *widget, qreal parentOpacity = qreal(1.0)); @@ -282,18 +206,32 @@ public: void processDirtyItemsRecursive(QGraphicsItem *item, bool dirtyAncestorContainsChildren = false, qreal parentOpacity = qreal(1.0)); - inline void resetDirtyItem(QGraphicsItem *item) + inline void resetDirtyItem(QGraphicsItem *item, bool recursive = false) { Q_ASSERT(item); item->d_ptr->dirty = 0; item->d_ptr->paintedViewBoundingRectsNeedRepaint = 0; item->d_ptr->geometryChanged = 0; + if (!item->d_ptr->dirtyChildren) + recursive = false; item->d_ptr->dirtyChildren = 0; item->d_ptr->needsRepaint = QRectF(); item->d_ptr->allChildrenDirty = 0; item->d_ptr->fullUpdatePending = 0; item->d_ptr->ignoreVisible = 0; item->d_ptr->ignoreOpacity = 0; + if (recursive) { + for (int i = 0; i < item->d_ptr->children.size(); ++i) + resetDirtyItem(item->d_ptr->children.at(i), recursive); + } + } + + inline void ensureSortedTopLevelItems() + { + if (needSortTopLevelItems) { + qSort(topLevelItems.begin(), topLevelItems.end(), qt_notclosestLeaf); + needSortTopLevelItems = false; + } } QStyle *style; @@ -320,6 +258,25 @@ public: void updateInputMethodSensitivityInViews(); }; +// QRectF::intersects() returns false always if either the source or target +// rectangle's width or height are 0. This works around that problem. +static inline void _q_adjustRect(QRectF *rect) +{ + Q_ASSERT(rect); + if (!rect->width()) + rect->adjust(-0.00001, 0, 0.00001, 0); + if (!rect->height()) + rect->adjust(0, -0.00001, 0, 0.00001); +} + +static inline QRectF adjustedItemBoundingRect(const QGraphicsItem *item) +{ + Q_ASSERT(item); + QRectF boundingRect(item->boundingRect()); + _q_adjustRect(&boundingRect); + return boundingRect; +} + QT_END_NAMESPACE #endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp new file mode 100644 index 0000000..a54ade9 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp @@ -0,0 +1,782 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QGraphicsSceneBspTreeIndex + \brief The QGraphicsSceneBspTreeIndex class provides an implementation of + a BSP indexing algorithm for discovering items in QGraphicsScene. + \since 4.6 + \ingroup multimedia + \ingroup graphicsview-api + \mainclass + \internal + + QGraphicsSceneBspTreeIndex index use a BSP(Binary Space Partitioning) + implementation to discover items quickly. This implementation is + very efficient for static scene. It has a depth that you can set. + The depth directly affects performance and memory usage; the latter + growing exponentially with the depth of the tree. With an optimal tree + depth, the index can instantly determine the locality of items, even + for scenes with thousands or millions of items. This also greatly improves + rendering performance. + + By default, the value is 0, in which case Qt will guess a reasonable + default depth based on the size, location and number of items in the + scene. If these parameters change frequently, however, you may experience + slowdowns as the index retunes the depth internally. You can avoid + potential slowdowns by fixating the tree depth through setting this + property. + + The depth of the tree and the size of the scene rectangle decide the + granularity of the scene's partitioning. The size of each scene segment is + determined by the following algorithm: + + The BSP tree has an optimal size when each segment contains between 0 and + 10 items. + + \sa QGraphicsScene, QGraphicsView, QGraphicsSceneIndex +*/ + +#ifndef QT_NO_GRAPHICSVIEW + +#include <private/qgraphicsscene_p.h> +#include <private/qgraphicsscenebsptreeindex_p.h> +#include <private/qgraphicssceneindex_p.h> + +#include <QtCore/qmath.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +static inline int intmaxlog(int n) +{ + return (n > 0 ? qMax(qCeil(qLn(qreal(n)) / qLn(qreal(2))), 5) : 0); +} + +/*! + Constructs a private scene bsp index. +*/ +QGraphicsSceneBspTreeIndexPrivate::QGraphicsSceneBspTreeIndexPrivate(QGraphicsScene *scene) + : QGraphicsSceneIndexPrivate(scene), + bspTreeDepth(0), + indexTimerId(0), + restartIndexTimer(false), + regenerateIndex(true), + lastItemCount(0), + purgePending(false), + sortCacheEnabled(false), + updatingSortCache(false) +{ +} + + +/*! + This method will update the BSP index by removing the items from the temporary + unindexed list and add them in the indexedItems list. This will also + update the growingItemsBoundingRect if needed. This will update the BSP + implementation as well. + + \internal +*/ +void QGraphicsSceneBspTreeIndexPrivate::_q_updateIndex() +{ + Q_Q(QGraphicsSceneBspTreeIndex); + if (!indexTimerId) + return; + + q->killTimer(indexTimerId); + indexTimerId = 0; + + purgeRemovedItems(); + + // Add unindexedItems to indexedItems + for (int i = 0; i < unindexedItems.size(); ++i) { + if (QGraphicsItem *item = unindexedItems.at(i)) { + Q_ASSERT(!item->d_ptr->itemDiscovered); + if (!freeItemIndexes.isEmpty()) { + int freeIndex = freeItemIndexes.takeFirst(); + item->d_func()->index = freeIndex; + indexedItems[freeIndex] = item; + } else { + item->d_func()->index = indexedItems.size(); + indexedItems << item; + } + } + } + + // Determine whether we should regenerate the BSP tree. + if (bspTreeDepth == 0) { + int oldDepth = intmaxlog(lastItemCount); + bspTreeDepth = intmaxlog(indexedItems.size()); + static const int slack = 100; + if (bsp.leafCount() == 0 || (oldDepth != bspTreeDepth && qAbs(lastItemCount - indexedItems.size()) > slack)) { + // ### Crude algorithm. + regenerateIndex = true; + } + } + + // Regenerate the tree. + if (regenerateIndex) { + regenerateIndex = false; + bsp.initialize(sceneRect, bspTreeDepth); + unindexedItems = indexedItems; + lastItemCount = indexedItems.size(); + } + + // Insert all unindexed items into the tree. + for (int i = 0; i < unindexedItems.size(); ++i) { + if (QGraphicsItem *item = unindexedItems.at(i)) { + if (item->d_ptr->itemIsUntransformable()) { + untransformableItems << item; + continue; + } + if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) + continue; + + bsp.insertItem(item, item->sceneBoundingRect()); + } + } + unindexedItems.clear(); +} + + +/*! + \internal + + Removes stale pointers from all data structures. +*/ +void QGraphicsSceneBspTreeIndexPrivate::purgeRemovedItems() +{ + if (!purgePending && removedItems.isEmpty()) + return; + + // Remove stale items from the BSP tree. + bsp.removeItems(removedItems); + // Purge this list. + removedItems.clear(); + freeItemIndexes.clear(); + for (int i = 0; i < indexedItems.size(); ++i) { + if (!indexedItems.at(i)) + freeItemIndexes << i; + } + purgePending = false; +} + +/*! + \internal + + Starts or restarts the timer used for reindexing unindexed items. +*/ +void QGraphicsSceneBspTreeIndexPrivate::startIndexTimer(int interval) +{ + Q_Q(QGraphicsSceneBspTreeIndex); + if (indexTimerId) { + restartIndexTimer = true; + } else { + indexTimerId = q->startTimer(interval); + } +} + +/*! + \internal +*/ +void QGraphicsSceneBspTreeIndexPrivate::resetIndex() +{ + purgeRemovedItems(); + for (int i = 0; i < indexedItems.size(); ++i) { + if (QGraphicsItem *item = indexedItems.at(i)) { + item->d_ptr->index = -1; + Q_ASSERT(!item->d_ptr->itemDiscovered); + unindexedItems << item; + } + } + indexedItems.clear(); + freeItemIndexes.clear(); + untransformableItems.clear(); + regenerateIndex = true; + startIndexTimer(); +} + +/*! + \internal +*/ +void QGraphicsSceneBspTreeIndexPrivate::climbTree(QGraphicsItem *item, int *stackingOrder) +{ + if (!item->d_ptr->children.isEmpty()) { + QList<QGraphicsItem *> childList = item->d_ptr->children; + qSort(childList.begin(), childList.end(), qt_closestLeaf); + for (int i = 0; i < childList.size(); ++i) { + QGraphicsItem *item = childList.at(i); + if (!(item->flags() & QGraphicsItem::ItemStacksBehindParent)) + climbTree(childList.at(i), stackingOrder); + } + item->d_ptr->globalStackingOrder = (*stackingOrder)++; + for (int i = 0; i < childList.size(); ++i) { + QGraphicsItem *item = childList.at(i); + if (item->flags() & QGraphicsItem::ItemStacksBehindParent) + climbTree(childList.at(i), stackingOrder); + } + } else { + item->d_ptr->globalStackingOrder = (*stackingOrder)++; + } +} + +/*! + \internal +*/ +void QGraphicsSceneBspTreeIndexPrivate::_q_updateSortCache() +{ + Q_Q(QGraphicsSceneBspTreeIndex); + _q_updateIndex(); + + if (!sortCacheEnabled || !updatingSortCache) + return; + + updatingSortCache = false; + int stackingOrder = 0; + + QList<QGraphicsItem *> topLevels; + const QList<QGraphicsItem *> items = q->items(); + for (int i = 0; i < items.size(); ++i) { + QGraphicsItem *item = items.at(i); + if (item && !item->d_ptr->parent) + topLevels << item; + } + + qSort(topLevels.begin(), topLevels.end(), qt_closestLeaf); + for (int i = 0; i < topLevels.size(); ++i) + climbTree(topLevels.at(i), &stackingOrder); +} + +/*! + \internal +*/ +void QGraphicsSceneBspTreeIndexPrivate::invalidateSortCache() +{ + Q_Q(QGraphicsSceneBspTreeIndex); + if (!sortCacheEnabled || updatingSortCache) + return; + + updatingSortCache = true; + QMetaObject::invokeMethod(q, "_q_updateSortCache", Qt::QueuedConnection); +} + +void QGraphicsSceneBspTreeIndexPrivate::addItem(QGraphicsItem *item, bool recursive) +{ + if (!item) + return; + + // Prevent reusing a recently deleted pointer: purge all removed item from our lists. + purgeRemovedItems(); + + // Invalidate any sort caching; arrival of a new item means we need to resort. + // Update the scene's sort cache settings. + item->d_ptr->globalStackingOrder = -1; + invalidateSortCache(); + + // Indexing requires sceneBoundingRect(), but because \a item might + // not be completely constructed at this point, we need to store it in + // a temporary list and schedule an indexing for later. + if (item->d_ptr->index == -1) { + Q_ASSERT(!unindexedItems.contains(item)); + unindexedItems << item; + startIndexTimer(0); + } else { + Q_ASSERT(indexedItems.contains(item)); + qWarning("QGraphicsSceneBspTreeIndex::addItem: item has already been added to this BSP"); + } + + if (recursive) { + for (int i = 0; i < item->d_ptr->children.size(); ++i) + addItem(item->d_ptr->children.at(i), recursive); + } +} + +void QGraphicsSceneBspTreeIndexPrivate::removeItem(QGraphicsItem *item, bool recursive, + bool moveToUnindexedItems) +{ + if (!item) + return; + + if (item->d_ptr->index != -1) { + Q_ASSERT(item->d_ptr->index < indexedItems.size()); + Q_ASSERT(indexedItems.at(item->d_ptr->index) == item); + Q_ASSERT(!item->d_ptr->itemDiscovered); + freeItemIndexes << item->d_ptr->index; + indexedItems[item->d_ptr->index] = 0; + item->d_ptr->index = -1; + + if (item->d_ptr->itemIsUntransformable()) { + untransformableItems.removeOne(item); + } else if (item->d_ptr->inDestructor) { + // Avoid virtual function calls from the destructor. + purgePending = true; + removedItems << item; + } else if (!(item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) { + bsp.removeItem(item, item->sceneBoundingRect()); + } + } else { + unindexedItems.removeOne(item); + } + invalidateSortCache(); // ### Only do this when removing from BSP? + + Q_ASSERT(item->d_ptr->index == -1); + Q_ASSERT(!indexedItems.contains(item)); + Q_ASSERT(!unindexedItems.contains(item)); + Q_ASSERT(!untransformableItems.contains(item)); + + if (moveToUnindexedItems) + addItem(item); + + if (recursive) { + for (int i = 0; i < item->d_ptr->children.size(); ++i) + removeItem(item->d_ptr->children.at(i), recursive, moveToUnindexedItems); + } +} + +QList<QGraphicsItem *> QGraphicsSceneBspTreeIndexPrivate::estimateItems(const QRectF &rect, Qt::SortOrder order, + bool onlyTopLevelItems) +{ + Q_Q(QGraphicsSceneBspTreeIndex); + if (onlyTopLevelItems && rect.isNull()) + return q->QGraphicsSceneIndex::estimateTopLevelItems(rect, order); + + purgeRemovedItems(); + _q_updateSortCache(); + Q_ASSERT(unindexedItems.isEmpty()); + + QList<QGraphicsItem *> rectItems = bsp.items(rect, onlyTopLevelItems); + if (onlyTopLevelItems) { + for (int i = 0; i < untransformableItems.size(); ++i) { + QGraphicsItem *item = untransformableItems.at(i); + if (!item->d_ptr->parent) { + rectItems << item; + } else { + item = item->topLevelItem(); + if (!rectItems.contains(item)) + rectItems << item; + } + } + } else { + rectItems += untransformableItems; + } + + sortItems(&rectItems, order, sortCacheEnabled, onlyTopLevelItems); + return rectItems; +} + +/*! + Returns true if \a item1 is on top of \a item2. + + \internal +*/ +bool QGraphicsSceneBspTreeIndexPrivate::closestItemFirst_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2) +{ + // Siblings? Just check their z-values. + const QGraphicsItemPrivate *d1 = item1->d_ptr; + const QGraphicsItemPrivate *d2 = item2->d_ptr; + if (d1->parent == d2->parent) + return qt_closestLeaf(item1, item2); + + // Find common ancestor, and each item's ancestor closest to the common + // ancestor. + int item1Depth = d1->depth; + int item2Depth = d2->depth; + const QGraphicsItem *p = item1; + const QGraphicsItem *t1 = item1; + while (item1Depth > item2Depth && (p = p->d_ptr->parent)) { + if (p == item2) { + // item2 is one of item1's ancestors; item1 is on top + return !(t1->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent); + } + t1 = p; + --item1Depth; + } + p = item2; + const QGraphicsItem *t2 = item2; + while (item2Depth > item1Depth && (p = p->d_ptr->parent)) { + if (p == item1) { + // item1 is one of item2's ancestors; item1 is not on top + return (t2->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent); + } + t2 = p; + --item2Depth; + } + + // item1Ancestor is now at the same level as item2Ancestor, but not the same. + const QGraphicsItem *a1 = t1; + const QGraphicsItem *a2 = t2; + while (a1) { + const QGraphicsItem *p1 = a1; + const QGraphicsItem *p2 = a2; + a1 = a1->parentItem(); + a2 = a2->parentItem(); + if (a1 && a1 == a2) + return qt_closestLeaf(p1, p2); + } + + // No common ancestor? Then just compare the items' toplevels directly. + return qt_closestLeaf(t1->topLevelItem(), t2->topLevelItem()); +} + +/*! + Returns true if \a item2 is on top of \a item1. + + \internal +*/ +bool QGraphicsSceneBspTreeIndexPrivate::closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2) +{ + return closestItemFirst_withoutCache(item2, item1); +} + +/*! + Sort a list of \a itemList in a specific \a order and use the cache if requested. + + \internal +*/ +void QGraphicsSceneBspTreeIndexPrivate::sortItems(QList<QGraphicsItem *> *itemList, Qt::SortOrder order, + bool sortCacheEnabled, bool onlyTopLevelItems) +{ + if (order == Qt::SortOrder(-1)) + return; + + if (onlyTopLevelItems) { + if (order == Qt::AscendingOrder) + qSort(itemList->begin(), itemList->end(), qt_closestLeaf); + else if (order == Qt::DescendingOrder) + qSort(itemList->begin(), itemList->end(), qt_notclosestLeaf); + return; + } + + if (sortCacheEnabled) { + if (order == Qt::AscendingOrder) { + qSort(itemList->begin(), itemList->end(), closestItemFirst_withCache); + } else if (order == Qt::DescendingOrder) { + qSort(itemList->begin(), itemList->end(), closestItemLast_withCache); + } + } else { + if (order == Qt::AscendingOrder) { + qSort(itemList->begin(), itemList->end(), closestItemFirst_withoutCache); + } else if (order == Qt::DescendingOrder) { + qSort(itemList->begin(), itemList->end(), closestItemLast_withoutCache); + } + } +} + +/*! + Constructs a BSP scene index for the given \a scene. +*/ +QGraphicsSceneBspTreeIndex::QGraphicsSceneBspTreeIndex(QGraphicsScene *scene) + : QGraphicsSceneIndex(*new QGraphicsSceneBspTreeIndexPrivate(scene), scene) +{ + +} + +QGraphicsSceneBspTreeIndex::~QGraphicsSceneBspTreeIndex() +{ + Q_D(QGraphicsSceneBspTreeIndex); + for (int i = 0; i < d->indexedItems.size(); ++i) { + // Ensure item bits are reset properly. + if (QGraphicsItem *item = d->indexedItems.at(i)) { + Q_ASSERT(!item->d_ptr->itemDiscovered); + item->d_ptr->index = -1; + } + } +} + +/*! + \reimp + Clear the all the BSP index. +*/ +void QGraphicsSceneBspTreeIndex::clear() +{ + Q_D(QGraphicsSceneBspTreeIndex); + d->bsp.clear(); + d->lastItemCount = 0; + d->freeItemIndexes.clear(); + for (int i = 0; i < d->indexedItems.size(); ++i) { + // Ensure item bits are reset properly. + if (QGraphicsItem *item = d->indexedItems.at(i)) { + Q_ASSERT(!item->d_ptr->itemDiscovered); + item->d_ptr->index = -1; + } + } + d->indexedItems.clear(); + d->unindexedItems.clear(); + d->untransformableItems.clear(); +} + +/*! + Add the \a item into the BSP index. +*/ +void QGraphicsSceneBspTreeIndex::addItem(QGraphicsItem *item) +{ + Q_D(QGraphicsSceneBspTreeIndex); + d->addItem(item); +} + +/*! + Remove the \a item from the BSP index. +*/ +void QGraphicsSceneBspTreeIndex::removeItem(QGraphicsItem *item) +{ + Q_D(QGraphicsSceneBspTreeIndex); + d->removeItem(item); +} + +/*! + \reimp + Update the BSP when the \a item 's bounding rect has changed. +*/ +void QGraphicsSceneBspTreeIndex::prepareBoundingRectChange(const QGraphicsItem *item) +{ + if (!item) + return; + + if (item->d_ptr->index == -1 || item->d_ptr->itemIsUntransformable() + || (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) { + return; // Item is not in BSP tree; nothing to do. + } + + Q_D(QGraphicsSceneBspTreeIndex); + QGraphicsItem *thatItem = const_cast<QGraphicsItem *>(item); + d->removeItem(thatItem, /*recursive=*/false, /*moveToUnindexedItems=*/true); + for (int i = 0; i < item->d_ptr->children.size(); ++i) // ### Do we really need this? + prepareBoundingRectChange(item->d_ptr->children.at(i)); +} + +/*! + Returns an estimation visible items that are either inside or + intersect with the specified \a rect and return a list sorted using \a order. + + \a deviceTransform is the transformation apply to the view. + +*/ +QList<QGraphicsItem *> QGraphicsSceneBspTreeIndex::estimateItems(const QRectF &rect, Qt::SortOrder order) const +{ + Q_D(const QGraphicsSceneBspTreeIndex); + return const_cast<QGraphicsSceneBspTreeIndexPrivate*>(d)->estimateItems(rect, order); +} + +QList<QGraphicsItem *> QGraphicsSceneBspTreeIndex::estimateTopLevelItems(const QRectF &rect, Qt::SortOrder order) const +{ + Q_D(const QGraphicsSceneBspTreeIndex); + return const_cast<QGraphicsSceneBspTreeIndexPrivate*>(d)->estimateItems(rect, order, /*onlyTopLevels=*/true); +} + +/*! + \fn QList<QGraphicsItem *> QGraphicsSceneBspTreeIndex::items(Qt::SortOrder order = Qt::AscendingOrder) const; + + Return all items in the BSP index and sort them using \a order. +*/ +QList<QGraphicsItem *> QGraphicsSceneBspTreeIndex::items(Qt::SortOrder order) const +{ + Q_D(const QGraphicsSceneBspTreeIndex); + const_cast<QGraphicsSceneBspTreeIndexPrivate*>(d)->purgeRemovedItems(); + QList<QGraphicsItem *> itemList; + + // If freeItemIndexes is empty, we know there are no holes in indexedItems and + // unindexedItems. + if (d->freeItemIndexes.isEmpty()) { + if (d->unindexedItems.isEmpty()) { + itemList = d->indexedItems; + } else { + itemList = d->indexedItems + d->unindexedItems; + } + } else { + // Rebuild the list of items to avoid holes. ### We could also just + // compress the item lists at this point. + foreach (QGraphicsItem *item, d->indexedItems + d->unindexedItems) { + if (item) + itemList << item; + } + } + if (order != -1) { + //We sort descending order + d->sortItems(&itemList, order, d->sortCacheEnabled); + } + return itemList; +} + +/*! + \property QGraphicsSceneBspTreeIndex::bspTreeDepth + \brief the depth of the BSP index tree + \since 4.6 + + This value determines the depth of BSP tree. The depth + directly affects performance and memory usage; the latter + growing exponentially with the depth of the tree. With an optimal tree + depth, the index can instantly determine the locality of items, even + for scenes with thousands or millions of items. This also greatly improves + rendering performance. + + By default, the value is 0, in which case Qt will guess a reasonable + default depth based on the size, location and number of items in the + scene. If these parameters change frequently, however, you may experience + slowdowns as the index retunes the depth internally. You can avoid + potential slowdowns by fixating the tree depth through setting this + property. + + The depth of the tree and the size of the scene rectangle decide the + granularity of the scene's partitioning. The size of each scene segment is + determined by the following algorithm: + + The BSP tree has an optimal size when each segment contains between 0 and + 10 items. + +*/ +int QGraphicsSceneBspTreeIndex::bspTreeDepth() +{ + Q_D(const QGraphicsSceneBspTreeIndex); + return d->bspTreeDepth; +} + +void QGraphicsSceneBspTreeIndex::setBspTreeDepth(int depth) +{ + Q_D(QGraphicsSceneBspTreeIndex); + if (d->bspTreeDepth == depth) + return; + d->bspTreeDepth = depth; + d->resetIndex(); +} + +/*! + \reimp + + This method react to the \a rect change of the scene and + reset the BSP tree index. +*/ +void QGraphicsSceneBspTreeIndex::updateSceneRect(const QRectF &rect) +{ + Q_D(QGraphicsSceneBspTreeIndex); + d->sceneRect = rect; + d->resetIndex(); +} + +/*! + \reimp + + This method react to the \a change of the \a item and use the \a value to + update the BSP tree if necessary. + +*/ +void QGraphicsSceneBspTreeIndex::itemChange(const QGraphicsItem *item, QGraphicsItem::GraphicsItemChange change, const QVariant &value) +{ + Q_D(QGraphicsSceneBspTreeIndex); + switch (change) { + case QGraphicsItem::ItemFlagsChange: { + // Handle ItemIgnoresTransformations + bool ignoredTransform = item->d_ptr->flags & QGraphicsItem::ItemIgnoresTransformations; + bool willIgnoreTransform = value.toUInt() & QGraphicsItem::ItemIgnoresTransformations; + bool clipsChildren = item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape; + bool willClipChildren = value.toUInt() & QGraphicsItem::ItemClipsChildrenToShape; + if ((ignoredTransform != willIgnoreTransform) || (clipsChildren != willClipChildren)) { + QGraphicsItem *thatItem = const_cast<QGraphicsItem *>(item); + // Remove item and its descendants from the index and append + // them to the list of unindexed items. Then, when the index + // is updated, they will be put into the bsp-tree or the list + // of untransformable items. + d->removeItem(thatItem, /*recursive=*/true, /*moveToUnidexedItems=*/true); + } + break; + } + case QGraphicsItem::ItemZValueChange: + d->invalidateSortCache(); + break; + case QGraphicsItem::ItemParentChange: { + d->invalidateSortCache(); + // Handle ItemIgnoresTransformations + QGraphicsItem *newParent = qVariantValue<QGraphicsItem *>(value); + bool ignoredTransform = item->d_ptr->itemIsUntransformable(); + bool willIgnoreTransform = (item->d_ptr->flags & QGraphicsItem::ItemIgnoresTransformations) + || (newParent && newParent->d_ptr->itemIsUntransformable()); + bool ancestorClippedChildren = item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren; + bool ancestorWillClipChildren = newParent + && ((newParent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape) + || (newParent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)); + if ((ignoredTransform != willIgnoreTransform) || (ancestorClippedChildren != ancestorWillClipChildren)) { + QGraphicsItem *thatItem = const_cast<QGraphicsItem *>(item); + // Remove item and its descendants from the index and append + // them to the list of unindexed items. Then, when the index + // is updated, they will be put into the bsp-tree or the list + // of untransformable items. + d->removeItem(thatItem, /*recursive=*/true, /*moveToUnidexedItems=*/true); + } + break; + } + default: + break; + } + return QGraphicsSceneIndex::itemChange(item, change, value); +} +/*! + \reimp + + Used to catch the timer event. + + \internal +*/ +bool QGraphicsSceneBspTreeIndex::event(QEvent *event) +{ + Q_D(QGraphicsSceneBspTreeIndex); + switch (event->type()) { + case QEvent::Timer: + if (d->indexTimerId && static_cast<QTimerEvent *>(event)->timerId() == d->indexTimerId) { + if (d->restartIndexTimer) { + d->restartIndexTimer = false; + } else { + // this call will kill the timer + d->_q_updateIndex(); + } + } + // Fallthrough intended - support timers in subclasses. + default: + return QObject::event(event); + } + return true; +} + +QT_END_NAMESPACE + +#include "moc_qgraphicsscenebsptreeindex_p.cpp" + +#endif // QT_NO_GRAPHICSVIEW + diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h b/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h new file mode 100644 index 0000000..2e02458 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QGRAPHICSBSPTREEINDEX_H +#define QGRAPHICSBSPTREEINDEX_H + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +#include "qgraphicssceneindex_p.h" +#include "qgraphicsitem_p.h" +#include "qgraphicsscene_bsp_p.h" + +#include <QtCore/qglobal.h> +#include <QtCore/qrect.h> +#include <QtCore/qlist.h> + +static const int QGRAPHICSSCENE_INDEXTIMER_TIMEOUT = 2000; + +class QGraphicsScene; +class QGraphicsSceneBspTreeIndexPrivate; + +class Q_AUTOTEST_EXPORT QGraphicsSceneBspTreeIndex : public QGraphicsSceneIndex +{ + Q_OBJECT + Q_PROPERTY(int bspTreeDepth READ bspTreeDepth WRITE setBspTreeDepth) +public: + QGraphicsSceneBspTreeIndex(QGraphicsScene *scene = 0); + ~QGraphicsSceneBspTreeIndex(); + + QList<QGraphicsItem *> estimateItems(const QRectF &rect, Qt::SortOrder order) const; + QList<QGraphicsItem *> estimateTopLevelItems(const QRectF &rect, Qt::SortOrder order) const; + QList<QGraphicsItem *> items(Qt::SortOrder order = Qt::AscendingOrder) const; + + int bspTreeDepth(); + void setBspTreeDepth(int depth); + +protected Q_SLOTS: + void updateSceneRect(const QRectF &rect); + +protected: + bool event(QEvent *event); + void clear(); + + void addItem(QGraphicsItem *item); + void removeItem(QGraphicsItem *item); + void prepareBoundingRectChange(const QGraphicsItem *item); + + void itemChange(const QGraphicsItem *item, QGraphicsItem::GraphicsItemChange change, const QVariant &value); + +private : + Q_DECLARE_PRIVATE(QGraphicsSceneBspTreeIndex) + Q_DISABLE_COPY(QGraphicsSceneBspTreeIndex) + Q_PRIVATE_SLOT(d_func(), void _q_updateSortCache()) + Q_PRIVATE_SLOT(d_func(), void _q_updateIndex()) + + friend class QGraphicsScene; + friend class QGraphicsScenePrivate; +}; + +class QGraphicsSceneBspTreeIndexPrivate : public QGraphicsSceneIndexPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsSceneBspTreeIndex) +public: + QGraphicsSceneBspTreeIndexPrivate(QGraphicsScene *scene); + + QGraphicsSceneBspTree bsp; + QRectF sceneRect; + int bspTreeDepth; + int indexTimerId; + bool restartIndexTimer; + bool regenerateIndex; + int lastItemCount; + + QList<QGraphicsItem *> indexedItems; + QList<QGraphicsItem *> unindexedItems; + QList<QGraphicsItem *> untransformableItems; + QList<int> freeItemIndexes; + + bool purgePending; + QSet<QGraphicsItem *> removedItems; + void purgeRemovedItems(); + + void _q_updateIndex(); + void startIndexTimer(int interval = QGRAPHICSSCENE_INDEXTIMER_TIMEOUT); + void resetIndex(); + + void _q_updateSortCache(); + bool sortCacheEnabled; + bool updatingSortCache; + void invalidateSortCache(); + void addItem(QGraphicsItem *item, bool recursive = false); + void removeItem(QGraphicsItem *item, bool recursive = false, bool moveToUnindexedItems = false); + QList<QGraphicsItem *> estimateItems(const QRectF &, Qt::SortOrder, bool b = false); + + static void climbTree(QGraphicsItem *item, int *stackingOrder); + static bool closestItemFirst_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2); + static bool closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2); + + static inline bool closestItemFirst_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2) + { + return item1->d_ptr->globalStackingOrder < item2->d_ptr->globalStackingOrder; + } + static inline bool closestItemLast_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2) + { + return item1->d_ptr->globalStackingOrder >= item2->d_ptr->globalStackingOrder; + } + + static void sortItems(QList<QGraphicsItem *> *itemList, Qt::SortOrder order, + bool cached, bool onlyTopLevelItems = false); +}; + +static inline bool QRectF_intersects(const QRectF &s, const QRectF &r) +{ + qreal xp = s.left(); + qreal yp = s.top(); + qreal w = s.width(); + qreal h = s.height(); + qreal l1 = xp; + qreal r1 = xp; + if (w < 0) + l1 += w; + else + r1 += w; + + qreal l2 = r.left(); + qreal r2 = r.left(); + if (w < 0) + l2 += r.width(); + else + r2 += r.width(); + + if (l1 >= r2 || l2 >= r1) + return false; + + qreal t1 = yp; + qreal b1 = yp; + if (h < 0) + t1 += h; + else + b1 += h; + + qreal t2 = r.top(); + qreal b2 = r.top(); + if (r.height() < 0) + t2 += r.height(); + else + b2 += r.height(); + + return !(t1 >= b2 || t2 >= b1); +} + +#endif // QT_NO_GRAPHICSVIEW + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGRAPHICSBSPTREEINDEX_H diff --git a/src/gui/graphicsview/qgraphicssceneindex.cpp b/src/gui/graphicsview/qgraphicssceneindex.cpp new file mode 100644 index 0000000..01efde4 --- /dev/null +++ b/src/gui/graphicsview/qgraphicssceneindex.cpp @@ -0,0 +1,659 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \class QGraphicsSceneIndex + \brief The QGraphicsSceneIndex class provides a base class to implement + a custom indexing algorithm for discovering items in QGraphicsScene. + \since 4.6 + \ingroup multimedia + \ingroup graphicsview-api + \mainclass + \internal + + The QGraphicsSceneIndex class provides a base class to implement + a custom indexing algorithm for discovering items in QGraphicsScene. You + need to subclass it and reimplement addItem, removeItem, estimateItems + and items in order to have an functional indexing. + + \sa QGraphicsScene, QGraphicsView +*/ + +#include "qdebug.h" +#include "qgraphicsscene.h" +#include "qgraphicsitem_p.h" +#include "qgraphicsscene_p.h" +#include "qgraphicswidget.h" +#include "qgraphicssceneindex_p.h" +#include "qgraphicsscenebsptreeindex_p.h" + +#ifndef QT_NO_GRAPHICSVIEW + +QT_BEGIN_NAMESPACE + +class QGraphicsSceneIndexRectIntersector : public QGraphicsSceneIndexIntersector +{ +public: + bool intersect(const QGraphicsItem *item, const QRectF &exposeRect, Qt::ItemSelectionMode mode, + const QTransform &deviceTransform) const + { + QRectF brect = item->boundingRect(); + _q_adjustRect(&brect); + + // ### Add test for this (without making things slower?) + Q_UNUSED(exposeRect); + + bool keep = true; + const QGraphicsItemPrivate *itemd = QGraphicsItemPrivate::get(item); + if (itemd->itemIsUntransformable()) { + // Untransformable items; map the scene rect to item coordinates. + const QTransform transform = item->deviceTransform(deviceTransform); + QRectF itemRect = (deviceTransform * transform.inverted()).mapRect(sceneRect); + if (mode == Qt::ContainsItemShape || mode == Qt::ContainsItemBoundingRect) + keep = itemRect.contains(brect) && itemRect != brect; + else + keep = itemRect.intersects(brect); + if (keep && (mode == Qt::ContainsItemShape || mode == Qt::IntersectsItemShape)) { + QPainterPath itemPath; + itemPath.addRect(itemRect); + keep = QGraphicsSceneIndexPrivate::itemCollidesWithPath(item, itemPath, mode); + } + } else { + Q_ASSERT(!itemd->dirtySceneTransform); + const QRectF itemSceneBoundingRect = itemd->sceneTransformTranslateOnly + ? brect.translated(itemd->sceneTransform.dx(), + itemd->sceneTransform.dy()) + : itemd->sceneTransform.mapRect(brect); + if (mode == Qt::ContainsItemShape || mode == Qt::ContainsItemBoundingRect) + keep = sceneRect != brect && sceneRect.contains(itemSceneBoundingRect); + else + keep = sceneRect.intersects(itemSceneBoundingRect); + if (keep && (mode == Qt::ContainsItemShape || mode == Qt::IntersectsItemShape)) { + QPainterPath rectPath; + rectPath.addRect(sceneRect); + if (itemd->sceneTransformTranslateOnly) + rectPath.translate(-itemd->sceneTransform.dx(), -itemd->sceneTransform.dy()); + else + rectPath = itemd->sceneTransform.inverted().map(rectPath); + keep = QGraphicsSceneIndexPrivate::itemCollidesWithPath(item, rectPath, mode); + } + } + return keep; + } + + QRectF sceneRect; +}; + +class QGraphicsSceneIndexPointIntersector : public QGraphicsSceneIndexIntersector +{ +public: + bool intersect(const QGraphicsItem *item, const QRectF &exposeRect, Qt::ItemSelectionMode mode, + const QTransform &deviceTransform) const + { + QRectF brect = item->boundingRect(); + _q_adjustRect(&brect); + + // ### Add test for this (without making things slower?) + Q_UNUSED(exposeRect); + + bool keep = false; + const QGraphicsItemPrivate *itemd = QGraphicsItemPrivate::get(item); + if (itemd->itemIsUntransformable()) { + // Untransformable items; map the scene point to item coordinates. + const QTransform transform = item->deviceTransform(deviceTransform); + QPointF itemPoint = (deviceTransform * transform.inverted()).map(scenePoint); + keep = brect.contains(itemPoint); + if (keep && (mode == Qt::ContainsItemShape || mode == Qt::IntersectsItemShape)) { + QPainterPath pointPath; + pointPath.addRect(QRectF(itemPoint, QSizeF(1, 1))); + keep = QGraphicsSceneIndexPrivate::itemCollidesWithPath(item, pointPath, mode); + } + } else { + Q_ASSERT(!itemd->dirtySceneTransform); + QRectF sceneBoundingRect = itemd->sceneTransformTranslateOnly + ? brect.translated(itemd->sceneTransform.dx(), + itemd->sceneTransform.dy()) + : itemd->sceneTransform.mapRect(brect); + keep = sceneBoundingRect.intersects(QRectF(scenePoint, QSizeF(1, 1))); + if (keep) { + QPointF p = itemd->sceneTransformTranslateOnly + ? QPointF(scenePoint.x() - itemd->sceneTransform.dx(), + scenePoint.y() - itemd->sceneTransform.dy()) + : itemd->sceneTransform.inverted().map(scenePoint); + keep = item->contains(p); + } + } + + return keep; + } + + QPointF scenePoint; +}; + +class QGraphicsSceneIndexPathIntersector : public QGraphicsSceneIndexIntersector +{ +public: + bool intersect(const QGraphicsItem *item, const QRectF &exposeRect, Qt::ItemSelectionMode mode, + const QTransform &deviceTransform) const + { + QRectF brect = item->boundingRect(); + _q_adjustRect(&brect); + + // ### Add test for this (without making things slower?) + Q_UNUSED(exposeRect); + + bool keep = true; + const QGraphicsItemPrivate *itemd = QGraphicsItemPrivate::get(item); + if (itemd->itemIsUntransformable()) { + // Untransformable items; map the scene rect to item coordinates. + const QTransform transform = item->deviceTransform(deviceTransform); + QPainterPath itemPath = (deviceTransform * transform.inverted()).map(scenePath); + if (mode == Qt::ContainsItemShape || mode == Qt::ContainsItemBoundingRect) + keep = itemPath.contains(brect); + else + keep = itemPath.intersects(brect); + if (keep && (mode == Qt::ContainsItemShape || mode == Qt::IntersectsItemShape)) + keep = QGraphicsSceneIndexPrivate::itemCollidesWithPath(item, itemPath, mode); + } else { + Q_ASSERT(!itemd->dirtySceneTransform); + const QRectF itemSceneBoundingRect = itemd->sceneTransformTranslateOnly + ? brect.translated(itemd->sceneTransform.dx(), + itemd->sceneTransform.dy()) + : itemd->sceneTransform.mapRect(brect); + if (mode == Qt::ContainsItemShape || mode == Qt::ContainsItemBoundingRect) + keep = scenePath.contains(itemSceneBoundingRect); + else + keep = scenePath.intersects(itemSceneBoundingRect); + if (keep && (mode == Qt::ContainsItemShape || mode == Qt::IntersectsItemShape)) { + QPainterPath itemPath = itemd->sceneTransformTranslateOnly + ? scenePath.translated(-itemd->sceneTransform.dx(), + -itemd->sceneTransform.dy()) + : itemd->sceneTransform.inverted().map(scenePath); + keep = QGraphicsSceneIndexPrivate::itemCollidesWithPath(item, itemPath, mode); + } + } + return keep; + } + + QPainterPath scenePath; +}; + +/*! + Constructs a private scene index. +*/ +QGraphicsSceneIndexPrivate::QGraphicsSceneIndexPrivate(QGraphicsScene *scene) : scene(scene) +{ + pointIntersector = new QGraphicsSceneIndexPointIntersector; + rectIntersector = new QGraphicsSceneIndexRectIntersector; + pathIntersector = new QGraphicsSceneIndexPathIntersector; +} + +/*! + Destructor of private scene index. +*/ +QGraphicsSceneIndexPrivate::~QGraphicsSceneIndexPrivate() +{ + delete pointIntersector; + delete rectIntersector; + delete pathIntersector; +} + +/*! + \internal + + Checks if item collides with the path and mode, but also checks that if it + doesn't collide, maybe its frame rect will. +*/ +bool QGraphicsSceneIndexPrivate::itemCollidesWithPath(const QGraphicsItem *item, + const QPainterPath &path, + Qt::ItemSelectionMode mode) +{ + if (item->collidesWithPath(path, mode)) + return true; + if (item->isWidget()) { + // Check if this is a window, and if its frame rect collides. + const QGraphicsWidget *widget = static_cast<const QGraphicsWidget *>(item); + if (widget->isWindow()) { + QRectF frameRect = widget->windowFrameRect(); + QPainterPath framePath; + framePath.addRect(frameRect); + bool intersects = path.intersects(frameRect); + if (mode == Qt::IntersectsItemShape || mode == Qt::IntersectsItemBoundingRect) + return intersects || path.contains(frameRect.topLeft()) + || framePath.contains(path.elementAt(0)); + return !intersects && path.contains(frameRect.topLeft()); + } + } + return false; +} + +/*! + \internal +*/ +void QGraphicsSceneIndexPrivate::recursive_items_helper(QGraphicsItem *item, QRectF exposeRect, + QGraphicsSceneIndexIntersector *intersector, + QList<QGraphicsItem *> *items, + const QTransform &viewTransform, + Qt::ItemSelectionMode mode, Qt::SortOrder order, + qreal parentOpacity) const +{ + Q_ASSERT(item); + if (!item->d_ptr->visible) + return; + + const qreal opacity = item->d_ptr->combineOpacityFromParent(parentOpacity); + const bool itemIsFullyTransparent = (opacity < 0.0001); + const bool itemHasChildren = !item->d_ptr->children.isEmpty(); + if (itemIsFullyTransparent && (!itemHasChildren || item->d_ptr->childrenCombineOpacity())) + return; + + // Update the item's scene transform if dirty. + const bool itemIsUntransformable = item->d_ptr->itemIsUntransformable(); + const bool wasDirtyParentSceneTransform = item->d_ptr->dirtySceneTransform && !itemIsUntransformable; + if (wasDirtyParentSceneTransform) { + item->d_ptr->updateSceneTransformFromParent(); + Q_ASSERT(!item->d_ptr->dirtySceneTransform); + } + + const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape); + bool processItem = !itemIsFullyTransparent; + if (processItem) { + processItem = intersector->intersect(item, exposeRect, mode, viewTransform); + if (!processItem && (!itemHasChildren || itemClipsChildrenToShape)) { + if (wasDirtyParentSceneTransform) + item->d_ptr->invalidateChildrenSceneTransform(); + return; + } + } // else we know for sure this item has children we must process. + + int i = 0; + if (itemHasChildren) { + // Sort children. + item->d_ptr->ensureSortedChildren(); + + // Clip to shape. + if (itemClipsChildrenToShape && !itemIsUntransformable) { + QPainterPath mappedShape = item->d_ptr->sceneTransformTranslateOnly + ? item->shape().translated(item->d_ptr->sceneTransform.dx(), + item->d_ptr->sceneTransform.dy()) + : item->d_ptr->sceneTransform.map(item->shape()); + exposeRect &= mappedShape.controlPointRect(); + } + + // Process children behind + for (i = 0; i < item->d_ptr->children.size(); ++i) { + QGraphicsItem *child = item->d_ptr->children.at(i); + if (wasDirtyParentSceneTransform) + child->d_ptr->dirtySceneTransform = 1; + if (!(child->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent)) + break; + if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity)) + continue; + recursive_items_helper(child, exposeRect, intersector, items, viewTransform, + mode, order, opacity); + } + } + + // Process item + if (processItem) + items->append(item); + + // Process children in front + if (itemHasChildren) { + for (; i < item->d_ptr->children.size(); ++i) { + QGraphicsItem *child = item->d_ptr->children.at(i); + if (wasDirtyParentSceneTransform) + child->d_ptr->dirtySceneTransform = 1; + if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity)) + continue; + recursive_items_helper(child, exposeRect, intersector, items, viewTransform, + mode, order, opacity); + } + } +} + +void QGraphicsSceneIndexPrivate::init() +{ + if (!scene) + return; + + QObject::connect(scene, SIGNAL(sceneRectChanged(const QRectF&)), + q_func(), SLOT(updateSceneRect(const QRectF&))); +} + +/*! + Constructs an abstract scene index for a given \a scene. +*/ +QGraphicsSceneIndex::QGraphicsSceneIndex(QGraphicsScene *scene) +: QObject(*new QGraphicsSceneIndexPrivate(scene), scene) +{ + d_func()->init(); +} + +/*! + \internal +*/ +QGraphicsSceneIndex::QGraphicsSceneIndex(QGraphicsSceneIndexPrivate &dd, QGraphicsScene *scene) + : QObject(dd, scene) +{ + d_func()->init(); +} + +/*! + Destroys the scene index. +*/ +QGraphicsSceneIndex::~QGraphicsSceneIndex() +{ + +} + +/*! + Returns the scene of this index. +*/ +QGraphicsScene* QGraphicsSceneIndex::scene() const +{ + Q_D(const QGraphicsSceneIndex); + return d->scene; +} + +/*! + \fn QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPointF &pos, + Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform + &deviceTransform) const + + Returns all visible items that, depending on \a mode, are at the specified + \a pos and return a list sorted using \a order. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with \a pos are returned. + + \a deviceTransform is the transformation apply to the view. + + This method use the estimation of the index (estimateItems) and refine the + list to get an exact result. If you want to implement your own refinement + algorithm you can reimplement this method. + + \sa estimateItems() + +*/ +QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPointF &pos, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform) const +{ + + Q_D(const QGraphicsSceneIndex); + QList<QGraphicsItem *> itemList; + d->pointIntersector->scenePoint = pos; + d->items_helper(QRectF(pos, QSizeF(1, 1)), d->pointIntersector, &itemList, deviceTransform, mode, order); + return itemList; +} + +/*! + \fn QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QRectF &rect, + Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform + &deviceTransform) const + + \overload + + Returns all visible items that, depending on \a mode, are either inside or + intersect with the specified \a rect and return a list sorted using \a order. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a rect are returned. + + \a deviceTransform is the transformation apply to the view. + + This method use the estimation of the index (estimateItems) and refine + the list to get an exact result. If you want to implement your own + refinement algorithm you can reimplement this method. + + \sa estimateItems() + +*/ +QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QRectF &rect, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform) const +{ + Q_D(const QGraphicsSceneIndex); + QRectF exposeRect = rect; + _q_adjustRect(&exposeRect); + QList<QGraphicsItem *> itemList; + d->rectIntersector->sceneRect = rect; + d->items_helper(exposeRect, d->rectIntersector, &itemList, deviceTransform, mode, order); + return itemList; +} + +/*! + \fn QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPolygonF + &polygon, Qt::ItemSelectionMode mode, Qt::SortOrder order, const + QTransform &deviceTransform) const + + \overload + + Returns all visible items that, depending on \a mode, are either inside or + intersect with the specified \a polygon and return a list sorted using \a order. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a polygon are returned. + + \a deviceTransform is the transformation apply to the view. + + This method use the estimation of the index (estimateItems) and refine + the list to get an exact result. If you want to implement your own + refinement algorithm you can reimplement this method. + + \sa estimateItems() + +*/ +QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform) const +{ + Q_D(const QGraphicsSceneIndex); + QList<QGraphicsItem *> itemList; + QRectF exposeRect = polygon.boundingRect(); + _q_adjustRect(&exposeRect); + QPainterPath path; + path.addPolygon(polygon); + d->pathIntersector->scenePath = path; + d->items_helper(exposeRect, d->pathIntersector, &itemList, deviceTransform, mode, order); + return itemList; +} + +/*! + \fn QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPainterPath + &path, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform + &deviceTransform) const + + \overload + + Returns all visible items that, depending on \a mode, are either inside or + intersect with the specified \a path and return a list sorted using \a order. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a path are returned. + + \a deviceTransform is the transformation apply to the view. + + This method use the estimation of the index (estimateItems) and refine + the list to get an exact result. If you want to implement your own + refinement algorithm you can reimplement this method. + + \sa estimateItems() + +*/ +QList<QGraphicsItem *> QGraphicsSceneIndex::items(const QPainterPath &path, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform) const +{ + Q_D(const QGraphicsSceneIndex); + QList<QGraphicsItem *> itemList; + QRectF exposeRect = path.controlPointRect(); + _q_adjustRect(&exposeRect); + d->pathIntersector->scenePath = path; + d->items_helper(exposeRect, d->pathIntersector, &itemList, deviceTransform, mode, order); + return itemList; +} + +/*! + This virtual function return an estimation of items at position \a point. + This method return a list sorted using \a order. +*/ +QList<QGraphicsItem *> QGraphicsSceneIndex::estimateItems(const QPointF &point, Qt::SortOrder order) const +{ + return estimateItems(QRectF(point, QSize(1, 1)), order); +} + +QList<QGraphicsItem *> QGraphicsSceneIndex::estimateTopLevelItems(const QRectF &rect, Qt::SortOrder order) const +{ + Q_D(const QGraphicsSceneIndex); + Q_UNUSED(rect); + QGraphicsScenePrivate *scened = d->scene->d_func(); + scened->ensureSortedTopLevelItems(); + if (order == Qt::AscendingOrder) { + QList<QGraphicsItem *> sorted; + for (int i = scened->topLevelItems.size() - 1; i >= 0; --i) + sorted << scened->topLevelItems.at(i); + return sorted; + } + return scened->topLevelItems; +} + +/*! + \fn virtual QList<QGraphicsItem *> + QGraphicsSceneIndex::estimateItems(const QRectF &rect, Qt::SortOrder + order, const QTransform &deviceTransform) const = 0 + + This pure virtual function return an estimation of items in the \a rect. + This method return a list sorted using \a order. + + \a deviceTransform is the transformation apply to the view. +*/ + +/*! + \fn virtual QList<QGraphicsItem *> + QGraphicsSceneIndex::items(Qt::SortOrder order = Qt::AscendingOrder) const + = 0; This pure virtual function all items in the index and sort them using + \a order. +*/ + + +/*! + Notifies the index that the scene's scene rect has changed. \a rect + is thew new scene rect. + + \sa QGraphicsScene::sceneRect() +*/ +void QGraphicsSceneIndex::updateSceneRect(const QRectF &rect) +{ + Q_UNUSED(rect); +} + +/*! + This virtual function removes all items in the scene index. +*/ +void QGraphicsSceneIndex::clear() +{ + const QList<QGraphicsItem *> allItems = items(); + for (int i = 0 ; i < allItems.size(); ++i) + removeItem(allItems.at(i)); +} + +/*! + \fn virtual void QGraphicsSceneIndex::addItem(QGraphicsItem *item) = 0 + + This pure virtual function inserts an \a item to the scene index. + + \sa removeItem(), deleteItem() +*/ + +/*! + \fn virtual void QGraphicsSceneIndex::removeItem(QGraphicsItem *item) = 0 + + This pure virtual function removes an \a item to the scene index. + + \sa addItem(), deleteItem() +*/ + +/*! + This method is called when an \a item has been deleted. + The default implementation call removeItem. Be carefull, + if your implementation of removeItem use pure virtual method + of QGraphicsItem like boundingRect(), then you should reimplement + this method. + + \sa addItem(), removeItem() +*/ +void QGraphicsSceneIndex::deleteItem(QGraphicsItem *item) +{ + removeItem(item); +} + +/*! + This virtual function is called by QGraphicsItem to notify the index + that some part of the \a item 's state changes. By reimplementing this + function, your can react to a change, and in some cases, (depending on \a + change,) adjustments in the index can be made. + + \a change is the parameter of the item that is changing. \a value is the + value that changed; the type of the value depends on \a change. + + The default implementation does nothing. + + \sa QGraphicsItem::GraphicsItemChange +*/ +void QGraphicsSceneIndex::itemChange(const QGraphicsItem *item, QGraphicsItem::GraphicsItemChange change, const QVariant &value) +{ + Q_UNUSED(item); + Q_UNUSED(change); + Q_UNUSED(value); +} + +/*! + Notify the index for a geometry change of an \a item. + + \sa QGraphicsItem::prepareGeometryChange() +*/ +void QGraphicsSceneIndex::prepareBoundingRectChange(const QGraphicsItem *item) +{ + Q_UNUSED(item); +} + +QT_END_NAMESPACE + +#include "moc_qgraphicssceneindex_p.cpp" + +#endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicssceneindex_p.h b/src/gui/graphicsview/qgraphicssceneindex_p.h new file mode 100644 index 0000000..8cf0294 --- /dev/null +++ b/src/gui/graphicsview/qgraphicssceneindex_p.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSSCENEINDEX_H +#define QGRAPHICSSCENEINDEX_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qgraphicsscene_p.h" +#include "qgraphicsscene.h" +#include <private/qobject_p.h> + +#include <QtCore/qnamespace.h> +#include <QtCore/qobject.h> +#include <QtGui/qtransform.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +class QGraphicsSceneIndexIntersector; +class QGraphicsSceneIndexPointIntersector; +class QGraphicsSceneIndexRectIntersector; +class QGraphicsSceneIndexPathIntersector; +class QGraphicsSceneIndexPrivate; +class QPointF; +class QRectF; +template<typename T> class QList; + +class Q_AUTOTEST_EXPORT QGraphicsSceneIndex : public QObject +{ + Q_OBJECT + +public: + QGraphicsSceneIndex(QGraphicsScene *scene = 0); + virtual ~QGraphicsSceneIndex(); + + QGraphicsScene *scene() const; + + virtual QList<QGraphicsItem *> items(Qt::SortOrder order = Qt::AscendingOrder) const = 0; + virtual QList<QGraphicsItem *> items(const QPointF &pos, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const; + virtual QList<QGraphicsItem *> items(const QRectF &rect, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const; + virtual QList<QGraphicsItem *> items(const QPolygonF &polygon, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const; + virtual QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode, + Qt::SortOrder order, const QTransform &deviceTransform = QTransform()) const; + virtual QList<QGraphicsItem *> estimateItems(const QPointF &point, Qt::SortOrder order) const; + virtual QList<QGraphicsItem *> estimateItems(const QRectF &rect, Qt::SortOrder order) const = 0; + virtual QList<QGraphicsItem *> estimateTopLevelItems(const QRectF &, Qt::SortOrder order) const; + +protected Q_SLOTS: + virtual void updateSceneRect(const QRectF &rect); + +protected: + virtual void clear(); + virtual void addItem(QGraphicsItem *item) = 0; + virtual void removeItem(QGraphicsItem *item) = 0; + virtual void deleteItem(QGraphicsItem *item); + + virtual void itemChange(const QGraphicsItem *item, QGraphicsItem::GraphicsItemChange, const QVariant &value); + virtual void prepareBoundingRectChange(const QGraphicsItem *item); + + QGraphicsSceneIndex(QGraphicsSceneIndexPrivate &dd, QGraphicsScene *scene); + + friend class QGraphicsScene; + friend class QGraphicsScenePrivate; + friend class QGraphicsItem; + friend class QGraphicsItemPrivate; + friend class QGraphicsSceneBspTreeIndex; +private: + Q_DISABLE_COPY(QGraphicsSceneIndex) + Q_DECLARE_PRIVATE(QGraphicsSceneIndex) +}; + +class QGraphicsSceneIndexPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsSceneIndex) +public: + QGraphicsSceneIndexPrivate(QGraphicsScene *scene); + ~QGraphicsSceneIndexPrivate(); + + void init(); + static bool itemCollidesWithPath(const QGraphicsItem *item, const QPainterPath &path, Qt::ItemSelectionMode mode); + + void recursive_items_helper(QGraphicsItem *item, QRectF exposeRect, + QGraphicsSceneIndexIntersector *intersector, QList<QGraphicsItem *> *items, + const QTransform &viewTransform, + Qt::ItemSelectionMode mode, Qt::SortOrder order, qreal parentOpacity = 1.0) const; + inline void items_helper(const QRectF &rect, QGraphicsSceneIndexIntersector *intersector, + QList<QGraphicsItem *> *items, const QTransform &viewTransform, + Qt::ItemSelectionMode mode, Qt::SortOrder order) const; + + QGraphicsScene *scene; + QGraphicsSceneIndexPointIntersector *pointIntersector; + QGraphicsSceneIndexRectIntersector *rectIntersector; + QGraphicsSceneIndexPathIntersector *pathIntersector; +}; + +inline void QGraphicsSceneIndexPrivate::items_helper(const QRectF &rect, QGraphicsSceneIndexIntersector *intersector, + QList<QGraphicsItem *> *items, const QTransform &viewTransform, + Qt::ItemSelectionMode mode, Qt::SortOrder order) const +{ + Q_Q(const QGraphicsSceneIndex); + const QList<QGraphicsItem *> tli = q->estimateTopLevelItems(rect, Qt::DescendingOrder); + for (int i = 0; i < tli.size(); ++i) + recursive_items_helper(tli.at(i), rect, intersector, items, viewTransform, mode, order); + if (order == Qt::AscendingOrder) { + const int n = items->size(); + for (int i = 0; i < n / 2; ++i) + items->swap(i, n - i - 1); + } +} + +class QGraphicsSceneIndexIntersector +{ +public: + QGraphicsSceneIndexIntersector() { } + virtual ~QGraphicsSceneIndexIntersector() { } + virtual bool intersect(const QGraphicsItem *item, const QRectF &exposeRect, Qt::ItemSelectionMode mode, + const QTransform &deviceTransform) const = 0; +}; + +#endif // QT_NO_GRAPHICSVIEW + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGRAPHICSSCENEINDEX_H diff --git a/src/gui/graphicsview/qgraphicsscenelinearindex.cpp b/src/gui/graphicsview/qgraphicsscenelinearindex.cpp new file mode 100644 index 0000000..5e6ac30 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsscenelinearindex.cpp @@ -0,0 +1,62 @@ +/*! + \class QGraphicsSceneLinearIndex + \brief The QGraphicsSceneLinearIndex class provides an implementation of + a linear indexing algorithm for discovering items in QGraphicsScene. + \since 4.6 + \ingroup multimedia + \ingroup graphicsview-api + \mainclass + \internal + + QGraphicsSceneLinearIndex index is default linear implementation to discover items. + It basically store all items in a list and return them to the scene. + + \sa QGraphicsScene, QGraphicsView, QGraphicsSceneIndex, QGraphicsSceneBspTreeIndex +*/ + +#include <private/qgraphicsscenelinearindex_p.h> + +/*! + \fn QGraphicsSceneLinearIndex::QGraphicsSceneLinearIndex(QGraphicsScene *scene = 0): + + Construct a linear index for the given \a scene. +*/ + +/*! + \fn QList<QGraphicsItem *> QGraphicsSceneLinearIndex::items(Qt::SortOrder order = Qt::AscendingOrder) const; + + Return all items in the index and sort them using \a order. +*/ + + +/*! + \fn virtual QList<QGraphicsItem *> QGraphicsSceneLinearIndex::estimateItems(const QRectF &rect, Qt::SortOrder order) const; + + Returns an estimation visible items that are either inside or + intersect with the specified \a rect and return a list sorted using \a order. +*/ + +/*! + \fn QRectF QGraphicsSceneLinearIndex::indexedRect() const; + \reimp + Return the rect indexed by the the index. +*/ + +/*! + \fn void QGraphicsSceneLinearIndex::clear(); + \reimp + Clear the all the BSP index. +*/ + +/*! + \fn virtual void QGraphicsSceneLinearIndex::addItem(QGraphicsItem *item); + + Add the \a item into the index. +*/ + +/*! + \fn virtual void QGraphicsSceneLinearIndex::removeItem(QGraphicsItem *item); + + Add the \a item from the index. +*/ + diff --git a/src/gui/graphicsview/qgraphicsscenelinearindex_p.h b/src/gui/graphicsview/qgraphicsscenelinearindex_p.h new file mode 100644 index 0000000..56dde3a --- /dev/null +++ b/src/gui/graphicsview/qgraphicsscenelinearindex_p.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSSCENELINEARINDEX_H +#define QGRAPHICSSCENELINEARINDEX_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +#include <QtCore/qrect.h> +#include <QtCore/qlist.h> +#include <QtGui/qgraphicsitem.h> +#include <private/qgraphicssceneindex_p.h> + +class Q_AUTOTEST_EXPORT QGraphicsSceneLinearIndex : public QGraphicsSceneIndex +{ + Q_OBJECT + +public: + QGraphicsSceneLinearIndex(QGraphicsScene *scene = 0) : QGraphicsSceneIndex(scene) + { } + + QList<QGraphicsItem *> items(Qt::SortOrder order = Qt::AscendingOrder) const + { Q_UNUSED(order); return m_items; } + + virtual QList<QGraphicsItem *> estimateItems(const QRectF &rect, Qt::SortOrder order) const + { + Q_UNUSED(rect); + Q_UNUSED(order); + return m_items; + } + +protected : + virtual void clear() + { m_items.clear(); } + + virtual void addItem(QGraphicsItem *item) + { m_items << item; } + + virtual void removeItem(QGraphicsItem *item) + { m_items.removeOne(item); } + +private: + QList<QGraphicsItem*> m_items; +}; + +#endif // QT_NO_GRAPHICSVIEW + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGRAPHICSSCENELINEARINDEX_H diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index 3b29552..bcfd68c 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -813,19 +813,9 @@ QRegion QGraphicsViewPrivate::mapToViewRegion(const QGraphicsItem *item, const Q return item->boundingRegion(itv) & itv.mapRect(rect).toAlignedRect(); } -// QRectF::intersects() returns false always if either the source or target -// rectangle's width or height are 0. This works around that problem. -static inline QRectF adjustedItemBoundingRect(const QGraphicsItem *item) -{ - Q_ASSERT(item); - QRectF boundingRect(item->boundingRect()); - if (!boundingRect.width()) - boundingRect.adjust(-0.00001, 0, 0.00001, 0); - if (!boundingRect.height()) - boundingRect.adjust(0, -0.00001, 0, 0.00001); - return boundingRect; -} - +/*! + \internal +*/ void QGraphicsViewPrivate::processPendingUpdates() { if (!scene) @@ -850,13 +840,29 @@ void QGraphicsViewPrivate::processPendingUpdates() dirtyRegion = QRegion(); } +static inline bool intersectsViewport(const QRect &r, int width, int height) +{ return !(r.left() > width) && !(r.right() < 0) && !(r.top() >= height) && !(r.bottom() < 0); } + +static inline bool containsViewport(const QRect &r, int width, int height) +{ return r.left() <= 0 && r.top() <= 0 && r.right() >= width - 1 && r.bottom() >= height - 1; } + +static inline void QRect_unite(QRect *rect, const QRect &other) +{ + if (rect->isEmpty()) { + *rect = other; + } else { + rect->setCoords(qMin(rect->left(), other.left()), qMin(rect->top(), other.top()), + qMax(rect->right(), other.right()), qMax(rect->bottom(), other.bottom())); + } +} + bool QGraphicsViewPrivate::updateRegion(const QRegion &r) { if (fullUpdatePending || viewportUpdateMode == QGraphicsView::NoViewportUpdate || r.isEmpty()) return false; const QRect boundingRect = r.boundingRect(); - if (!boundingRect.intersects(viewport->rect())) + if (!intersectsViewport(boundingRect, viewport->width(), viewport->height())) return false; // Update region outside viewport. switch (viewportUpdateMode) { @@ -865,8 +871,8 @@ bool QGraphicsViewPrivate::updateRegion(const QRegion &r) viewport->update(); break; case QGraphicsView::BoundingRectViewportUpdate: - dirtyBoundingRect |= boundingRect; - if (dirtyBoundingRect.contains(viewport->rect())) { + QRect_unite(&dirtyBoundingRect, boundingRect); + if (containsViewport(dirtyBoundingRect, viewport->width(), viewport->height())) { fullUpdatePending = true; viewport->update(); } @@ -893,7 +899,7 @@ bool QGraphicsViewPrivate::updateRegion(const QRegion &r) bool QGraphicsViewPrivate::updateRect(const QRect &r) { if (fullUpdatePending || viewportUpdateMode == QGraphicsView::NoViewportUpdate - || !r.intersects(viewport->rect())) { + || !intersectsViewport(r, viewport->width(), viewport->height())) { return false; } @@ -903,8 +909,8 @@ bool QGraphicsViewPrivate::updateRect(const QRect &r) viewport->update(); break; case QGraphicsView::BoundingRectViewportUpdate: - dirtyBoundingRect |= r; - if (dirtyBoundingRect.contains(viewport->rect())) { + QRect_unite(&dirtyBoundingRect, r); + if (containsViewport(dirtyBoundingRect, viewport->width(), viewport->height())) { fullUpdatePending = true; viewport->update(); } @@ -955,47 +961,32 @@ extern QPainterPath qt_regionToPath(const QRegion ®ion); is at risk of painting 1 pixel outside the bounding rect. Therefore we must search for items with an adjustment of (-1, -1, 1, 1). */ -QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedRegion, bool *allItems) const +QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedRegion, bool *allItems, + const QTransform &viewTransform) const { Q_Q(const QGraphicsView); // Step 1) If all items are contained within the expose region, then - // return a list of all visible items. + // return a list of all visible items. ### the scene's growing bounding + // rect does not take into account untransformable items. const QRectF exposedRegionSceneBounds = q->mapToScene(exposedRegion.boundingRect().adjusted(-1, -1, 1, 1)) .boundingRect(); - if (exposedRegionSceneBounds.contains(scene->d_func()->growingItemsBoundingRect)) { + if (exposedRegionSceneBounds.contains(scene->sceneRect())) { Q_ASSERT(allItems); *allItems = true; - // All items are guaranteed within the exposed region, don't bother using the index. - QList<QGraphicsItem *> itemList(scene->items()); - int i = 0; - while (i < itemList.size()) { - const QGraphicsItem *item = itemList.at(i); - // But we only want to include items that are visible - // The following check is basically the same as item->d_ptr->isInvisible(), except - // that we don't check whether the item clips children to shape or propagates its - // opacity (we loop through all items, so those checks are wrong in this context). - if (!item->isVisible() || item->d_ptr->isClippedAway() || item->d_ptr->isFullyTransparent()) - itemList.removeAt(i); - else - ++i; - } - - // Sort the items. - QGraphicsScenePrivate::sortItems(&itemList, Qt::DescendingOrder, scene->d_func()->sortCacheEnabled); - return itemList; + // All items are guaranteed within the exposed region. + return scene->items(Qt::DescendingOrder); } // Step 2) If the expose region is a simple rect and the view is only // translated or scaled, search for items using // QGraphicsScene::items(QRectF). - bool simpleRectLookup = (scene->d_func()->largestUntransformableItem.isNull() - && exposedRegion.numRects() == 1 && matrix.type() <= QTransform::TxScale); + bool simpleRectLookup = exposedRegion.numRects() == 1 && matrix.type() <= QTransform::TxScale; if (simpleRectLookup) { - return scene->d_func()->items_helper(exposedRegionSceneBounds, - Qt::IntersectsItemBoundingRect, - Qt::DescendingOrder); + return scene->items(exposedRegionSceneBounds, + Qt::IntersectsItemBoundingRect, + Qt::DescendingOrder, viewTransform); } // If the region is complex or the view has a complex transform, adjust @@ -1005,16 +996,9 @@ QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedReg foreach (const QRect &r, exposedRegion.rects()) adjustedRegion += r.adjusted(-1, -1, 1, 1); - const QPainterPath exposedPath(qt_regionToPath(adjustedRegion)); - if (scene->d_func()->largestUntransformableItem.isNull()) { - const QPainterPath exposedScenePath(q->mapToScene(exposedPath)); - return scene->d_func()->items_helper(exposedScenePath, - Qt::IntersectsItemBoundingRect, - Qt::DescendingOrder); - } - - // NB! Path must be in viewport coordinates. - return itemsInArea(exposedPath, Qt::IntersectsItemBoundingRect, Qt::DescendingOrder); + const QPainterPath exposedScenePath(q->mapToScene(qt_regionToPath(adjustedRegion))); + return scene->items(exposedScenePath, Qt::IntersectsItemBoundingRect, + Qt::DescendingOrder, viewTransform); } /*! @@ -1915,7 +1899,12 @@ void QGraphicsView::fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRati void QGraphicsView::fitInView(const QGraphicsItem *item, Qt::AspectRatioMode aspectRatioMode) { QPainterPath path = item->isClipped() ? item->clipPath() : item->shape(); - fitInView(item->sceneTransform().map(path).boundingRect(), aspectRatioMode); + if (item->d_ptr->hasTranslateOnlySceneTransform()) { + path.translate(item->d_ptr->sceneTransform.dx(), item->d_ptr->sceneTransform.dy()); + fitInView(path.boundingRect(), aspectRatioMode); + } else { + fitInView(item->d_ptr->sceneTransform.map(path).boundingRect(), aspectRatioMode); + } } /*! @@ -1941,6 +1930,8 @@ void QGraphicsView::fitInView(const QGraphicsItem *item, Qt::AspectRatioMode asp void QGraphicsView::render(QPainter *painter, const QRectF &target, const QRect &source, Qt::AspectRatioMode aspectRatioMode) { + // ### Switch to using the recursive rendering algorithm instead. + Q_D(QGraphicsView); if (!d->scene || !(painter && painter->isActive())) return; @@ -2037,69 +2028,6 @@ QList<QGraphicsItem *> QGraphicsView::items() const } /*! - Returns all items in the area \a path, which is in viewport coordinates, - also taking untransformable items into consideration. This function is - considerably slower than just checking the scene directly. There is - certainly room for improvement. -*/ -QList<QGraphicsItem *> QGraphicsViewPrivate::itemsInArea(const QPainterPath &path, - Qt::ItemSelectionMode mode, - Qt::SortOrder order) const -{ - Q_Q(const QGraphicsView); - - // Determine the size of the largest untransformable subtree of children - // mapped to scene coordinates. - QRectF untr = scene->d_func()->largestUntransformableItem; - QRectF ltri = matrix.inverted().mapRect(untr); - ltri.adjust(-untr.width(), -untr.height(), untr.width(), untr.height()); - - QRectF rect = path.controlPointRect(); - - // Find all possible items in the relevant area. - // ### Improve this algorithm; it might be searching a too large area. - QRectF adjustedRect = q->mapToScene(rect.adjusted(-1, -1, 1, 1).toRect()).boundingRect(); - adjustedRect.adjust(-ltri.width(), -ltri.height(), ltri.width(), ltri.height()); - - // First build a (potentially large) list of all items in the vicinity - // that might be untransformable. - QList<QGraphicsItem *> allCandidates = scene->d_func()->estimateItemsInRect(adjustedRect); - - // Then find the minimal list of items that are inside \a path, and - // convert it to a set. - QList<QGraphicsItem *> regularCandidates = scene->items(q->mapToScene(path), mode); - QSet<QGraphicsItem *> candSet = QSet<QGraphicsItem *>::fromList(regularCandidates); - - QTransform viewMatrix = q->viewportTransform(); - - QList<QGraphicsItem *> result; - - // Run through all candidates and keep all items that are in candSet, or - // are untransformable and collide with \a path. ### We can improve this - // algorithm. - QList<QGraphicsItem *>::Iterator it = allCandidates.begin(); - while (it != allCandidates.end()) { - QGraphicsItem *item = *it; - if (item->d_ptr->itemIsUntransformable()) { - // Check if this untransformable item collides with the - // original selection rect. - QTransform itemTransform = item->deviceTransform(viewMatrix); - if (QGraphicsScenePrivate::itemCollidesWithPath(item, itemTransform.inverted().map(path), mode)) - result << item; - } else { - if (candSet.contains(item)) - result << item; - } - ++it; - } - - // ### Insertion sort would be faster. - if (order != Qt::SortOrder(-1)) - QGraphicsScenePrivate::sortItems(&result, order, scene->d_func()->sortCacheEnabled); - return result; -} - -/*! Returns a list of all the items at the position \a pos in the view. The items are listed in descending Z order (i.e., the first item in the list is the top-most item, and the last item is the bottom-most item). \a pos @@ -2118,17 +2046,22 @@ QList<QGraphicsItem *> QGraphicsView::items(const QPoint &pos) const Q_D(const QGraphicsView); if (!d->scene) return QList<QGraphicsItem *>(); - if (d->scene->d_func()->largestUntransformableItem.isNull()) { - if ((d->identityMatrix || d->matrix.type() <= QTransform::TxScale)) { - QTransform xinv = viewportTransform().inverted(); - return d->scene->items(xinv.mapRect(QRectF(pos.x(), pos.y(), 1, 1))); - } - return d->scene->items(mapToScene(pos.x(), pos.y(), 1, 1)); + // ### Unify these two, and use the items(QPointF) version in + // QGraphicsScene instead. The scene items function could use the viewport + // transform to map the point to a rect/polygon. + if ((d->identityMatrix || d->matrix.type() <= QTransform::TxScale)) { + // Use the rect version + QTransform xinv = viewportTransform().inverted(); + return d->scene->items(xinv.mapRect(QRectF(pos.x(), pos.y(), 1, 1)), + Qt::IntersectsItemShape, + Qt::AscendingOrder, + viewportTransform()); } - - QPainterPath path; - path.addRect(QRectF(pos.x(), pos.y(), 1, 1)); - return d->itemsInArea(path); + // Use the polygon version + return d->scene->items(mapToScene(pos.x(), pos.y(), 1, 1), + Qt::IntersectsItemShape, + Qt::AscendingOrder, + viewportTransform()); } /*! @@ -2155,12 +2088,7 @@ QList<QGraphicsItem *> QGraphicsView::items(const QRect &rect, Qt::ItemSelection Q_D(const QGraphicsView); if (!d->scene) return QList<QGraphicsItem *>(); - if (d->scene->d_func()->largestUntransformableItem.isNull()) - return d->scene->items(mapToScene(rect), mode); - - QPainterPath path; - path.addRect(rect); - return d->itemsInArea(path); + return d->scene->items(mapToScene(rect), mode, Qt::AscendingOrder, viewportTransform()); } /*! @@ -2188,13 +2116,7 @@ QList<QGraphicsItem *> QGraphicsView::items(const QPolygon &polygon, Qt::ItemSel Q_D(const QGraphicsView); if (!d->scene) return QList<QGraphicsItem *>(); - if (d->scene->d_func()->largestUntransformableItem.isNull()) - return d->scene->items(mapToScene(polygon), mode); - - QPainterPath path; - path.addPolygon(polygon); - path.closeSubpath(); - return d->itemsInArea(path); + return d->scene->items(mapToScene(polygon), mode, Qt::AscendingOrder, viewportTransform()); } /*! @@ -2214,9 +2136,7 @@ QList<QGraphicsItem *> QGraphicsView::items(const QPainterPath &path, Qt::ItemSe Q_D(const QGraphicsView); if (!d->scene) return QList<QGraphicsItem *>(); - if (d->scene->d_func()->largestUntransformableItem.isNull()) - return d->scene->items(mapToScene(path), mode); - return d->itemsInArea(path); + return d->scene->items(mapToScene(path), mode, Qt::AscendingOrder, viewportTransform()); } /*! @@ -3169,7 +3089,8 @@ void QGraphicsView::mouseMoveEvent(QMouseEvent *event) selectionArea.addPolygon(mapToScene(d->rubberBandRect)); selectionArea.closeSubpath(); if (d->scene) - d->scene->setSelectionArea(selectionArea, d->rubberBandSelectionMode); + d->scene->setSelectionArea(selectionArea, d->rubberBandSelectionMode, + viewportTransform()); return; } } else @@ -3382,8 +3303,7 @@ void QGraphicsView::paintEvent(QPaintEvent *event) } else { // Find all exposed items bool allItems = false; - QList<QGraphicsItem *> itemList = d->findItems(d->exposedRegion, &allItems); - + QList<QGraphicsItem *> itemList = d->findItems(d->exposedRegion, &allItems, viewTransform); if (!itemList.isEmpty()) { // Generate the style options. const int numItems = itemList.size(); diff --git a/src/gui/graphicsview/qgraphicsview_p.h b/src/gui/graphicsview/qgraphicsview_p.h index 09d842d..0fa8b34 100644 --- a/src/gui/graphicsview/qgraphicsview_p.h +++ b/src/gui/graphicsview/qgraphicsview_p.h @@ -59,11 +59,12 @@ #include <QtGui/qevent.h> #include "qgraphicssceneevent.h" +#include <QtGui/qstyleoption.h> #include <private/qabstractscrollarea_p.h> QT_BEGIN_NAMESPACE -class QGraphicsViewPrivate : public QAbstractScrollAreaPrivate +class Q_AUTOTEST_EXPORT QGraphicsViewPrivate : public QAbstractScrollAreaPrivate { Q_DECLARE_PUBLIC(QGraphicsView) public: @@ -84,10 +85,6 @@ public: qint64 horizontalScroll() const; qint64 verticalScroll() const; - QList<QGraphicsItem *> itemsInArea(const QPainterPath &path, - Qt::ItemSelectionMode mode = Qt::IntersectsItemShape, - Qt::SortOrder = Qt::AscendingOrder) const; - QPointF mousePressItemPoint; QPointF mousePressScenePoint; QPoint mousePressViewPoint; @@ -176,7 +173,8 @@ public: bool updateSceneSlotReimplementedChecked; QRegion exposedRegion; - QList<QGraphicsItem *> findItems(const QRegion &exposedRegion, bool *allItems) const; + QList<QGraphicsItem *> findItems(const QRegion &exposedRegion, bool *allItems, + const QTransform &viewTransform) const; QPointF mapToScene(const QPointF &point) const; QRectF mapToScene(const QRectF &rect) const; diff --git a/src/gui/itemviews/qabstractitemview.cpp b/src/gui/itemviews/qabstractitemview.cpp index dd84304..a4a69c3 100644 --- a/src/gui/itemviews/qabstractitemview.cpp +++ b/src/gui/itemviews/qabstractitemview.cpp @@ -3881,30 +3881,48 @@ bool QAbstractItemViewPrivate::openEditor(const QModelIndex &index, QEvent *even return true; } -QPixmap QAbstractItemViewPrivate::renderToPixmap(const QModelIndexList &indexes, QRect *r) const +/* + \internal + + returns the pair QRect/QModelIndex that should be painted on the viewports's rect + */ + +QItemViewPaintPairs QAbstractItemViewPrivate::draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const { + Q_ASSERT(r); Q_Q(const QAbstractItemView); - QRect rect = q->visualRect(indexes.at(0)); - QList<QRect> rects; + QRect &rect = *r; + const QRect viewportRect = viewport->rect(); + QItemViewPaintPairs ret; for (int i = 0; i < indexes.count(); ++i) { - rects.append(q->visualRect(indexes.at(i))); - rect |= rects.at(i); + const QModelIndex &index = indexes.at(i); + const QRect current = q->visualRect(index); + if (current.intersects(viewportRect)) { + ret += qMakePair(current, index); + rect |= current; + } } - rect = rect.intersected(viewport->rect()); - if (rect.width() <= 0 || rect.height() <= 0) + rect &= viewportRect; + return ret; +} + +QPixmap QAbstractItemViewPrivate::renderToPixmap(const QModelIndexList &indexes, QRect *r) const +{ + Q_ASSERT(r); + QItemViewPaintPairs paintPairs = draggablePaintPairs(indexes, r); + if (paintPairs.isEmpty()) return QPixmap(); - QImage image(rect.size(), QImage::Format_ARGB32_Premultiplied); - image.fill(0); - QPainter painter(&image); + QPixmap pixmap(r->size()); + pixmap.fill(Qt::transparent); + QPainter painter(&pixmap); QStyleOptionViewItemV4 option = viewOptionsV4(); option.state |= QStyle::State_Selected; - for (int j = 0; j < indexes.count(); ++j) { - option.rect = QRect(rects.at(j).topLeft() - rect.topLeft(), rects.at(j).size()); - delegateForIndex(indexes.at(j))->paint(&painter, option, indexes.at(j)); + for (int j = 0; j < paintPairs.count(); ++j) { + option.rect = paintPairs.at(j).first.translated(r->topLeft()); + const QModelIndex ¤t = paintPairs.at(j).second; + delegateForIndex(current)->paint(&painter, option, current); } - painter.end(); - if (r) *r = rect; - return QPixmap::fromImage(image); + return pixmap; } void QAbstractItemViewPrivate::selectAll(QItemSelectionModel::SelectionFlags command) diff --git a/src/gui/itemviews/qabstractitemview_p.h b/src/gui/itemviews/qabstractitemview_p.h index 2950bcd..557e98b 100644 --- a/src/gui/itemviews/qabstractitemview_p.h +++ b/src/gui/itemviews/qabstractitemview_p.h @@ -61,11 +61,10 @@ #include "QtGui/qmime.h" #include "QtGui/qpainter.h" #include "QtCore/qpair.h" -#include "QtCore/qtimer.h" -#include "QtCore/qtimeline.h" #include "QtGui/qregion.h" #include "QtCore/qdebug.h" #include "QtGui/qpainter.h" +#include "QtCore/qbasictimer.h" #ifndef QT_NO_ITEMVIEWS @@ -87,6 +86,9 @@ struct QEditorInfo }; +typedef QPair<QRect, QModelIndex> QItemViewPaintPair; +typedef QList<QItemViewPaintPair> QItemViewPaintPairs; + class QEmptyModel : public QAbstractItemModel { public: @@ -177,7 +179,9 @@ public: q_func()->style()->drawPrimitive(QStyle::PE_IndicatorItemViewItemDrop, &opt, painter, q_func()); } } + #endif + virtual QItemViewPaintPairs draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const; inline void releaseEditor(QWidget *editor) const { if (editor) { @@ -222,7 +226,7 @@ public: void clearOrRemove(); void checkPersistentEditorFocus(); - QPixmap renderToPixmap(const QModelIndexList &indexes, QRect *r = 0) const; + QPixmap renderToPixmap(const QModelIndexList &indexes, QRect *r) const; inline QPoint offset() const { const Q_Q(QAbstractItemView); @@ -376,7 +380,6 @@ public: QBasicTimer updateTimer; QBasicTimer delayedEditing; QBasicTimer delayedAutoScroll; //used when an item is clicked - QTimeLine timeline; QAbstractItemView::ScrollMode verticalScrollMode; QAbstractItemView::ScrollMode horizontalScrollMode; diff --git a/src/gui/itemviews/qitemselectionmodel.cpp b/src/gui/itemviews/qitemselectionmodel.cpp index e4cb0f0..87825d9 100644 --- a/src/gui/itemviews/qitemselectionmodel.cpp +++ b/src/gui/itemviews/qitemselectionmodel.cpp @@ -270,24 +270,35 @@ QItemSelectionRange QItemSelectionRange::intersect(const QItemSelectionRange &ot */ -/*! - Returns the list of model index items stored in the selection. -*/ +/* + \internal -QModelIndexList QItemSelectionRange::indexes() const + utility function for getting the indexes from a range + it avoid concatenating list and works on one + */ + +static void indexesFromRange(const QItemSelectionRange &range, QModelIndexList &result) { - QModelIndex index; - QModelIndexList result; - if (isValid() && model()) { - for (int column = left(); column <= right(); ++column) { - for (int row = top(); row <= bottom(); ++row) { - index = model()->index(row, column, parent()); - Qt::ItemFlags flags = model()->flags(index); + if (range.isValid() && range.model()) { + for (int column = range.left(); column <= range.right(); ++column) { + for (int row = range.top(); row <= range.bottom(); ++row) { + QModelIndex index = range.model()->index(row, column, range.parent()); + Qt::ItemFlags flags = range.model()->flags(index); if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled)) result.append(index); } } } +} + +/*! + Returns the list of model index items stored in the selection. +*/ + +QModelIndexList QItemSelectionRange::indexes() const +{ + QModelIndexList result; + indexesFromRange(*this, result); return result; } @@ -404,7 +415,7 @@ QModelIndexList QItemSelection::indexes() const QModelIndexList result; QList<QItemSelectionRange>::const_iterator it = begin(); for (; it != end(); ++it) - result += (*it).indexes(); + indexesFromRange(*it, result); return result; } diff --git a/src/gui/itemviews/qlistview.cpp b/src/gui/itemviews/qlistview.cpp index 4652b91..9a94b31 100644 --- a/src/gui/itemviews/qlistview.cpp +++ b/src/gui/itemviews/qlistview.cpp @@ -709,6 +709,32 @@ void QListViewPrivate::selectAll(QItemSelectionModel::SelectionFlags command) selectionModel->select(selection, command); } +/*! + \reimp + + We have a QListView way of knowing what elements are on the viewport + through the intersectingSet function +*/ +QItemViewPaintPairs QListViewPrivate::draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const +{ + Q_ASSERT(r); + Q_Q(const QListView); + QRect &rect = *r; + const QRect viewportRect = viewport->rect(); + QItemViewPaintPairs ret; + intersectingSet(viewportRect); + const QSet<QModelIndex> visibleIndexes = intersectingSet(viewportRect).toList().toSet(); + for (int i = 0; i < indexes.count(); ++i) { + const QModelIndex &index = indexes.at(i); + if (visibleIndexes.contains(index)) { + const QRect current = q->visualRect(index); + ret += qMakePair(current, index); + rect |= current; + } + } + rect &= viewportRect; + return ret; +} /*! \internal @@ -925,9 +951,9 @@ void QListView::dragMoveEvent(QDragMoveEvent *e) QModelIndex index; if (d->movement == Snap) { QRect rect(d->dynamicListView->snapToGrid(e->pos() + d->offset()), d->gridSize()); - d->intersectingSet(rect); - index = d->intersectVector.count() > 0 - ? d->intersectVector.last() : QModelIndex(); + const QVector<QModelIndex> intersectVector = d->intersectingSet(rect); + index = intersectVector.count() > 0 + ? intersectVector.last() : QModelIndex(); } else { index = indexAt(e->pos()); } @@ -1100,10 +1126,8 @@ void QListView::paintEvent(QPaintEvent *e) return; QStyleOptionViewItemV4 option = d->viewOptionsV4(); QPainter painter(d->viewport); - QRect area = e->rect(); - d->intersectingSet(e->rect().translated(horizontalOffset(), verticalOffset()), false); - const QVector<QModelIndex> toBeRendered = d->intersectVector; + const QVector<QModelIndex> toBeRendered = d->intersectingSet(e->rect().translated(horizontalOffset(), verticalOffset()), false); const QModelIndex current = currentIndex(); const QModelIndex hover = d->hover; @@ -1225,9 +1249,9 @@ QModelIndex QListView::indexAt(const QPoint &p) const { Q_D(const QListView); QRect rect(p.x() + horizontalOffset(), p.y() + verticalOffset(), 1, 1); - d->intersectingSet(rect); - QModelIndex index = d->intersectVector.count() > 0 - ? d->intersectVector.last() : QModelIndex(); + const QVector<QModelIndex> intersectVector = d->intersectingSet(rect); + QModelIndex index = intersectVector.count() > 0 + ? intersectVector.last() : QModelIndex(); if (index.isValid() && visualRect(index).contains(p)) return index; return QModelIndex(); @@ -1325,38 +1349,38 @@ QModelIndex QListView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifie if (d->gridSize().isValid()) rect.setSize(d->gridSize()); QSize contents = d->contentsSize(); - d->intersectVector.clear(); + QVector<QModelIndex> intersectVector; switch (cursorAction) { case MoveLeft: - while (d->intersectVector.isEmpty()) { + while (intersectVector.isEmpty()) { rect.translate(-rect.width(), 0); if (rect.right() <= 0) return current; if (rect.left() < 0) rect.setLeft(0); - d->intersectingSet(rect); - d->removeCurrentAndDisabled(&d->intersectVector, current); + intersectVector = d->intersectingSet(rect); + d->removeCurrentAndDisabled(&intersectVector, current); } - return d->closestIndex(initialRect, d->intersectVector); + return d->closestIndex(initialRect, intersectVector); case MoveRight: - while (d->intersectVector.isEmpty()) { + while (intersectVector.isEmpty()) { rect.translate(rect.width(), 0); if (rect.left() >= contents.width()) return current; if (rect.right() > contents.width()) rect.setRight(contents.width()); - d->intersectingSet(rect); - d->removeCurrentAndDisabled(&d->intersectVector, current); + intersectVector = d->intersectingSet(rect); + d->removeCurrentAndDisabled(&intersectVector, current); } - return d->closestIndex(initialRect, d->intersectVector); + return d->closestIndex(initialRect, intersectVector); case MovePageUp: rect.moveTop(rect.top() - d->viewport->height()); if (rect.top() < rect.height()) rect.moveTop(rect.height()); case MovePrevious: case MoveUp: - while (d->intersectVector.isEmpty()) { + while (intersectVector.isEmpty()) { rect.translate(0, -rect.height()); if (rect.bottom() <= 0) { #ifdef QT_KEYPAD_NAVIGATION @@ -1372,17 +1396,17 @@ QModelIndex QListView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifie } if (rect.top() < 0) rect.setTop(0); - d->intersectingSet(rect); - d->removeCurrentAndDisabled(&d->intersectVector, current); + intersectVector = d->intersectingSet(rect); + d->removeCurrentAndDisabled(&intersectVector, current); } - return d->closestIndex(initialRect, d->intersectVector); + return d->closestIndex(initialRect, intersectVector); case MovePageDown: rect.moveTop(rect.top() + d->viewport->height()); if (rect.bottom() > contents.height() - rect.height()) rect.moveBottom(contents.height() - rect.height()); case MoveNext: case MoveDown: - while (d->intersectVector.isEmpty()) { + while (intersectVector.isEmpty()) { rect.translate(0, rect.height()); if (rect.top() >= contents.height()) { #ifdef QT_KEYPAD_NAVIGATION @@ -1399,10 +1423,10 @@ QModelIndex QListView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifie } if (rect.bottom() > contents.height()) rect.setBottom(contents.height()); - d->intersectingSet(rect); - d->removeCurrentAndDisabled(&d->intersectVector, current); + intersectVector = d->intersectingSet(rect); + d->removeCurrentAndDisabled(&intersectVector, current); } - return d->closestIndex(initialRect, d->intersectVector); + return d->closestIndex(initialRect, intersectVector); case MoveHome: return d->model->index(0, d->column, d->root); case MoveEnd: @@ -1477,10 +1501,10 @@ void QListView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFl QItemSelection selection; if (rect.width() == 1 && rect.height() == 1) { - d->intersectingSet(rect.translated(horizontalOffset(), verticalOffset())); + const QVector<QModelIndex> intersectVector = d->intersectingSet(rect.translated(horizontalOffset(), verticalOffset())); QModelIndex tl; - if (!d->intersectVector.isEmpty()) - tl = d->intersectVector.last(); // special case for mouse press; only select the top item + if (!intersectVector.isEmpty()) + tl = intersectVector.last(); // special case for mouse press; only select the top item if (tl.isValid() && d->isIndexEnabled(tl)) selection.select(tl, tl); } else { @@ -1490,14 +1514,14 @@ void QListView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFl QModelIndex tl, br; // get the first item const QRect topLeft(rect.left() + horizontalOffset(), rect.top() + verticalOffset(), 1, 1); - d->intersectingSet(topLeft); - if (!d->intersectVector.isEmpty()) - tl = d->intersectVector.last(); + QVector<QModelIndex> intersectVector = d->intersectingSet(topLeft); + if (!intersectVector.isEmpty()) + tl = intersectVector.last(); // get the last item const QRect bottomRight(rect.right() + horizontalOffset(), rect.bottom() + verticalOffset(), 1, 1); - d->intersectingSet(bottomRight); - if (!d->intersectVector.isEmpty()) - br = d->intersectVector.last(); + intersectVector = d->intersectingSet(bottomRight); + if (!intersectVector.isEmpty()) + br = intersectVector.last(); // get the ranges if (tl.isValid() && br.isValid() @@ -1633,14 +1657,16 @@ QRegion QListView::visualRegionForSelection(const QItemSelection &selection) con QModelIndexList QListView::selectedIndexes() const { Q_D(const QListView); - QModelIndexList viewSelected; - QModelIndexList modelSelected; - if (d->selectionModel) - modelSelected = d->selectionModel->selectedIndexes(); - for (int i = 0; i < modelSelected.count(); ++i) { - QModelIndex index = modelSelected.at(i); + if (!d->selectionModel) + return QModelIndexList(); + + QModelIndexList viewSelected = d->selectionModel->selectedIndexes(); + for (int i = 0; i < viewSelected.count(); ++i) { + const QModelIndex &index = viewSelected.at(i); if (!isIndexHidden(index) && index.parent() == d->root && index.column() == d->column) - viewSelected.append(index); + ++i; + else + viewSelected.removeAt(i); } return viewSelected; } @@ -2116,8 +2142,8 @@ QItemSelection QListViewPrivate::selection(const QRect &rect) const { QItemSelection selection; QModelIndex tl, br; - intersectingSet(rect); - QVector<QModelIndex>::iterator it = intersectVector.begin(); + const QVector<QModelIndex> intersectVector = intersectingSet(rect); + QVector<QModelIndex>::const_iterator it = intersectVector.begin(); for (; it != intersectVector.end(); ++it) { if (!tl.isValid() && !br.isValid()) { tl = br = *it; @@ -2408,9 +2434,9 @@ void QStaticListViewBase::doStaticLayout(const QListViewLayoutInfo &info) Finds the set of items intersecting with \a area. In this function, itemsize is counted from topleft to the start of the next item. */ -void QStaticListViewBase::intersectingStaticSet(const QRect &area) const +QVector<QModelIndex> QStaticListViewBase::intersectingStaticSet(const QRect &area) const { - clearIntersections(); + QVector<QModelIndex> ret; int segStartPosition; int segEndPosition; int flowStartPosition; @@ -2427,7 +2453,7 @@ void QStaticListViewBase::intersectingStaticSet(const QRect &area) const flowEndPosition = area.bottom(); } if (segmentPositions.count() < 2 || flowPositions.isEmpty()) - return; + return ret; // the last segment position is actually the edge of the last segment const int segLast = segmentPositions.count() - 2; int seg = qBinarySearch<int>(segmentPositions, segStartPosition, 0, segLast + 1); @@ -2442,13 +2468,14 @@ void QStaticListViewBase::intersectingStaticSet(const QRect &area) const continue; QModelIndex index = modelIndex(row); if (index.isValid()) - appendToIntersections(index); + ret += index; #if 0 // for debugging else qWarning("intersectingStaticSet: row %d was invalid", row); #endif } } + return ret; } int QStaticListViewBase::itemIndex(const QListViewItem &item) const @@ -2769,12 +2796,15 @@ void QDynamicListViewBase::doDynamicLayout(const QListViewLayoutInfo &info) viewport()->update(); } -void QDynamicListViewBase::intersectingDynamicSet(const QRect &area) const +QVector<QModelIndex> QDynamicListViewBase::intersectingDynamicSet(const QRect &area) const { - clearIntersections(); - QListViewPrivate *that = const_cast<QListViewPrivate*>(dd); + QDynamicListViewBase *that = const_cast<QDynamicListViewBase*>(this); QBspTree::Data data(static_cast<void*>(that)); - that->dynamicListView->tree.climbTree(area, &QDynamicListViewBase::addLeaf, data); + QVector<QModelIndex> res; + that->interSectingVector = &res; + that->tree.climbTree(area, &QDynamicListViewBase::addLeaf, data); + that->interSectingVector = 0; + return res; } void QDynamicListViewBase::createItems(int to) @@ -2851,20 +2881,20 @@ int QDynamicListViewBase::itemIndex(const QListViewItem &item) const } void QDynamicListViewBase::addLeaf(QVector<int> &leaf, const QRect &area, - uint visited, QBspTree::Data data) + uint visited, QBspTree::Data data) { QListViewItem *vi; - QListViewPrivate *_this = static_cast<QListViewPrivate *>(data.ptr); + QDynamicListViewBase *_this = static_cast<QDynamicListViewBase *>(data.ptr); for (int i = 0; i < leaf.count(); ++i) { int idx = leaf.at(i); - if (idx < 0 || idx >= _this->dynamicListView->items.count()) + if (idx < 0 || idx >= _this->items.count()) continue; - vi = &_this->dynamicListView->items[idx]; + vi = &_this->items[idx]; Q_ASSERT(vi); if (vi->isValid() && vi->rect().intersects(area) && vi->visited != visited) { - QModelIndex index = _this->listViewItemToIndex(*vi); + QModelIndex index = _this->dd->listViewItemToIndex(*vi); Q_ASSERT(index.isValid()); - _this->intersectVector.append(index); + _this->interSectingVector->append(index); vi->visited = visited; } } diff --git a/src/gui/itemviews/qlistview_p.h b/src/gui/itemviews/qlistview_p.h index a7a7000..1727ba4 100644 --- a/src/gui/itemviews/qlistview_p.h +++ b/src/gui/itemviews/qlistview_p.h @@ -153,9 +153,6 @@ public: inline bool isHidden(int row) const; inline int hiddenCount() const; - inline void clearIntersections() const; - inline void appendToIntersections(const QModelIndex &idx) const; - inline bool isRightToLeft() const; QListViewPrivate *dd; @@ -186,7 +183,7 @@ public: QPoint initStaticLayout(const QListViewLayoutInfo &info); void doStaticLayout(const QListViewLayoutInfo &info); - void intersectingStaticSet(const QRect &area) const; + QVector<QModelIndex> intersectingStaticSet(const QRect &area) const; int itemIndex(const QListViewItem &item) const; @@ -216,7 +213,7 @@ class QDynamicListViewBase : public QCommonListViewBase friend class QListViewPrivate; public: QDynamicListViewBase(QListView *q, QListViewPrivate *d) : QCommonListViewBase(q, d), - batchStartRow(0), batchSavedDeltaSeg(0) {} + batchStartRow(0), batchSavedDeltaSeg(0), interSectingVector(0) {} QBspTree tree; QVector<QListViewItem> items; @@ -230,6 +227,7 @@ public: // used when laying out in batches int batchStartRow; int batchSavedDeltaSeg; + QVector<QModelIndex> *interSectingVector; //used from within intersectingDynamicSet void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); bool doBatchedItemLayout(const QListViewLayoutInfo &info, int max); @@ -237,7 +235,7 @@ public: void initBspTree(const QSize &contents); QPoint initDynamicLayout(const QListViewLayoutInfo &info); void doDynamicLayout(const QListViewLayoutInfo &info); - void intersectingDynamicSet(const QRect &area) const; + QVector<QModelIndex> intersectingDynamicSet(const QRect &area) const; static void addLeaf(QVector<int> &leaf, const QRect &area, uint visited, QBspTree::Data data); @@ -277,11 +275,11 @@ public: bool doItemsLayout(int num); - inline void intersectingSet(const QRect &area, bool doLayout = true) const { + inline QVector<QModelIndex> intersectingSet(const QRect &area, bool doLayout = true) const { if (doLayout) executePostedLayout(); QRect a = (q_func()->isRightToLeft() ? flipX(area.normalized()) : area.normalized()); - if (viewMode == QListView::ListMode) staticListView->intersectingStaticSet(a); - else dynamicListView->intersectingDynamicSet(a); + return (viewMode == QListView::ListMode) ? staticListView->intersectingStaticSet(a) + : dynamicListView->intersectingDynamicSet(a); } // ### FIXME: @@ -351,6 +349,8 @@ public: void scrollElasticBandBy(int dx, int dy); + QItemViewPaintPairs draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const; + // ### FIXME: we only need one at a time QDynamicListViewBase *dynamicListView; QStaticListViewBase *staticListView; @@ -383,9 +383,6 @@ public: QRect layoutBounds; - // used for intersecting set - mutable QVector<QModelIndex> intersectVector; - // timers QBasicTimer batchLayoutTimer; @@ -438,9 +435,6 @@ inline QAbstractItemDelegate *QCommonListViewBase::delegate(const QModelIndex &i inline bool QCommonListViewBase::isHidden(int row) const { return dd->isHidden(row); } inline int QCommonListViewBase::hiddenCount() const { return dd->hiddenRows.count(); } -inline void QCommonListViewBase::clearIntersections() const { dd->intersectVector.clear(); } -inline void QCommonListViewBase::appendToIntersections(const QModelIndex &idx) const { dd->intersectVector.append(idx); } - inline bool QCommonListViewBase::isRightToLeft() const { return qq->isRightToLeft(); } QT_END_NAMESPACE diff --git a/src/gui/itemviews/qtableview.cpp b/src/gui/itemviews/qtableview.cpp index c676237..2009499 100644 --- a/src/gui/itemviews/qtableview.cpp +++ b/src/gui/itemviews/qtableview.cpp @@ -1528,6 +1528,8 @@ void QTableView::updateGeometries() ++columnsInViewport; } } + columnsInViewport = qMax(columnsInViewport, 1); //there must be always at least 1 column + if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) { const int visibleColumns = columnCount - d->horizontalHeader->hiddenSectionCount(); horizontalScrollBar()->setRange(0, visibleColumns - columnsInViewport); @@ -1554,6 +1556,8 @@ void QTableView::updateGeometries() ++rowsInViewport; } } + rowsInViewport = qMax(rowsInViewport, 1); //there must be always at least 1 row + if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) { const int visibleRows = rowCount - d->verticalHeader->hiddenSectionCount(); verticalScrollBar()->setRange(0, visibleRows - rowsInViewport); @@ -2036,7 +2040,7 @@ void QTableView::scrollTo(const QModelIndex &index, ScrollHint hint) if (positionAtRight || hint == PositionAtCenter || positionAtLeft) { int hiddenSections = 0; if (d->horizontalHeader->sectionsHidden()) { - for (int s = horizontalIndex; s >= 0; --s) { + for (int s = horizontalIndex - 1; s >= 0; --s) { int column = d->horizontalHeader->logicalIndex(s); if (d->horizontalHeader->isSectionHidden(column)) ++hiddenSections; @@ -2091,7 +2095,7 @@ void QTableView::scrollTo(const QModelIndex &index, ScrollHint hint) if (hint == PositionAtBottom || hint == PositionAtCenter || hint == PositionAtTop) { int hiddenSections = 0; if (d->verticalHeader->sectionsHidden()) { - for (int s = verticalIndex; s >= 0; --s) { + for (int s = verticalIndex - 1; s >= 0; --s) { int row = d->verticalHeader->logicalIndex(s); if (d->verticalHeader->isSectionHidden(row)) ++hiddenSections; diff --git a/src/gui/itemviews/qtreeview.cpp b/src/gui/itemviews/qtreeview.cpp index 7c319dc..f13ff0c 100644 --- a/src/gui/itemviews/qtreeview.cpp +++ b/src/gui/itemviews/qtreeview.cpp @@ -262,10 +262,6 @@ void QTreeView::setSelectionModel(QItemSelectionModel *selectionModel) Q_D(QTreeView); Q_ASSERT(selectionModel); if (d->selectionModel) { - if (d->allColumnsShowFocus) { - QObject::disconnect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this, SLOT(_q_currentChanged(QModelIndex,QModelIndex))); - } // support row editing disconnect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), d->model, SLOT(submit())); @@ -275,10 +271,6 @@ void QTreeView::setSelectionModel(QItemSelectionModel *selectionModel) QAbstractItemView::setSelectionModel(selectionModel); if (d->selectionModel) { - if (d->allColumnsShowFocus) { - QObject::connect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this, SLOT(_q_currentChanged(QModelIndex,QModelIndex))); - } // support row editing connect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), d->model, SLOT(submit())); @@ -901,15 +893,6 @@ void QTreeView::setAllColumnsShowFocus(bool enable) Q_D(QTreeView); if (d->allColumnsShowFocus == enable) return; - if (d->selectionModel) { - if (enable) { - QObject::connect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this, SLOT(_q_currentChanged(QModelIndex,QModelIndex))); - } else { - QObject::disconnect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this, SLOT(_q_currentChanged(QModelIndex,QModelIndex))); - } - } d->allColumnsShowFocus = enable; d->viewport->update(); } @@ -1267,10 +1250,13 @@ void QTreeView::paintEvent(QPaintEvent *event) Q_D(QTreeView); d->executePostedLayout(); QPainter painter(viewport()); +#ifndef QT_NO_ANIMATION if (d->isAnimating()) { - drawTree(&painter, event->region() - d->animationRect()); + drawTree(&painter, event->region() - d->animatedOperation.rect()); d->drawAnimatedOperation(&painter); - } else { + } else +#endif //QT_NO_ANIMATION + { drawTree(&painter, event->region()); #ifndef QT_NO_DRAGANDDROP d->paintDropIndicator(&painter); @@ -1306,13 +1292,13 @@ bool QTreeViewPrivate::expandOrCollapseItemAtPos(const QPoint &pos) { Q_Q(QTreeView); // we want to handle mousePress in EditingState (persistent editors) - if ((q->state() != QAbstractItemView::NoState - && q->state() != QAbstractItemView::EditingState) + if ((state != QAbstractItemView::NoState + && state != QAbstractItemView::EditingState) || !viewport->rect().contains(pos)) return true; int i = itemDecorationAt(pos); - if ((i != -1) && q->itemsExpandable() && hasVisibleChildren(viewItems.at(i).index)) { + if ((i != -1) && itemsExpandable && hasVisibleChildren(viewItems.at(i).index)) { if (viewItems.at(i).expanded) collapse(i, true); else @@ -2851,10 +2837,9 @@ void QTreeViewPrivate::initialize() header->setStretchLastSection(true); header->setDefaultAlignment(Qt::AlignLeft|Qt::AlignVCenter); q->setHeader(header); - - // animation - QObject::connect(&timeline, SIGNAL(frameChanged(int)), q, SLOT(_q_animate())); - QObject::connect(&timeline, SIGNAL(finished()), q, SLOT(_q_endAnimatedOperation()), Qt::QueuedConnection); +#ifndef QT_NO_ANIMATION + QObject::connect(&animatedOperation, SIGNAL(finished()), q, SLOT(_q_endAnimatedOperation())); +#endif //QT_NO_ANIMATION } void QTreeViewPrivate::expand(int item, bool emitSignal) @@ -2864,10 +2849,11 @@ void QTreeViewPrivate::expand(int item, bool emitSignal) if (item == -1 || viewItems.at(item).expanded) return; +#ifndef QT_NO_ANIMATION if (emitSignal && animationsEnabled) - prepareAnimatedOperation(item, AnimatedOperation::Expand); - - QAbstractItemView::State oldState = q->state(); + prepareAnimatedOperation(item, QVariantAnimation::Forward); +#endif //QT_NO_ANIMATION + QAbstractItemView::State oldState = state; q->setState(QAbstractItemView::ExpandingState); const QModelIndex index = viewItems.at(item).index; storeExpanded(index); @@ -2877,8 +2863,10 @@ void QTreeViewPrivate::expand(int item, bool emitSignal) if (emitSignal) { emit q->expanded(index); +#ifndef QT_NO_ANIMATION if (animationsEnabled) beginAnimatedOperation(); +#endif //QT_NO_ANIMATION } if (model->canFetchMore(index)) model->fetchMore(index); @@ -2902,10 +2890,12 @@ void QTreeViewPrivate::collapse(int item, bool emitSignal) if (it == expandedIndexes.end() || viewItems.at(item).expanded == false) return; // nothing to do +#ifndef QT_NO_ANIMATION if (emitSignal && animationsEnabled) - prepareAnimatedOperation(item, AnimatedOperation::Collapse); + prepareAnimatedOperation(item, QVariantAnimation::Backward); +#endif //QT_NO_ANIMATION - QAbstractItemView::State oldState = q->state(); + QAbstractItemView::State oldState = state; q->setState(QAbstractItemView::CollapsingState); expandedIndexes.erase(it); viewItems[item].expanded = false; @@ -2922,29 +2912,33 @@ void QTreeViewPrivate::collapse(int item, bool emitSignal) if (emitSignal) { emit q->collapsed(modelIndex); +#ifndef QT_NO_ANIMATION if (animationsEnabled) beginAnimatedOperation(); +#endif //QT_NO_ANIMATION } } -void QTreeViewPrivate::prepareAnimatedOperation(int item, AnimatedOperation::Type type) +#ifndef QT_NO_ANIMATION +void QTreeViewPrivate::prepareAnimatedOperation(int item, QVariantAnimation::Direction direction) { animatedOperation.item = item; - animatedOperation.type = type; + animatedOperation.viewport = viewport; + animatedOperation.setDirection(direction); int top = coordinateForItem(item) + itemHeight(item); QRect rect = viewport->rect(); rect.setTop(top); - if (type == AnimatedOperation::Collapse) { + if (direction == QVariantAnimation::Backward) { const int limit = rect.height() * 2; int h = 0; int c = item + viewItems.at(item).total + 1; for (int i = item + 1; i < c && h < limit; ++i) h += itemHeight(i); rect.setHeight(h); - animatedOperation.duration = h; + animatedOperation.setEndValue(top + h); } - animatedOperation.top = top; + animatedOperation.setStartValue(top); animatedOperation.before = renderTreeToPixmapForAnimation(rect); } @@ -2953,50 +2947,29 @@ void QTreeViewPrivate::beginAnimatedOperation() Q_Q(QTreeView); QRect rect = viewport->rect(); - rect.setTop(animatedOperation.top); - if (animatedOperation.type == AnimatedOperation::Expand) { + rect.setTop(animatedOperation.top()); + if (animatedOperation.direction() == QVariantAnimation::Forward) { const int limit = rect.height() * 2; int h = 0; int c = animatedOperation.item + viewItems.at(animatedOperation.item).total + 1; for (int i = animatedOperation.item + 1; i < c && h < limit; ++i) h += itemHeight(i); rect.setHeight(h); - animatedOperation.duration = h; + animatedOperation.setEndValue(animatedOperation.top() + h); } animatedOperation.after = renderTreeToPixmapForAnimation(rect); q->setState(QAbstractItemView::AnimatingState); - - timeline.stop(); - timeline.setDuration(250); - timeline.setFrameRange(animatedOperation.top, animatedOperation.top + animatedOperation.duration); - timeline.start(); -} - -void QTreeViewPrivate::_q_endAnimatedOperation() -{ - Q_Q(QTreeView); - animatedOperation.before = QPixmap(); - animatedOperation.after = QPixmap(); - q->setState(QAbstractItemView::NoState); - q->updateGeometries(); - viewport->update(); -} - -void QTreeViewPrivate::_q_animate() -{ - QRect rect = viewport->rect(); - rect.moveTop(animatedOperation.top); - viewport->repaint(rect); + animatedOperation.start(); //let's start the animation } void QTreeViewPrivate::drawAnimatedOperation(QPainter *painter) const { - int start = timeline.startFrame(); - int end = timeline.endFrame(); - bool collapsing = animatedOperation.type == AnimatedOperation::Collapse; - int current = collapsing ? end - timeline.currentFrame() + start : timeline.currentFrame(); + const int start = animatedOperation.startValue().toInt(), + end = animatedOperation.endValue().toInt(), + current = animatedOperation.currentValue().toInt(); + bool collapsing = animatedOperation.direction() == QVariantAnimation::Backward; const QPixmap top = collapsing ? animatedOperation.before : animatedOperation.after; painter->drawPixmap(0, start, top, 0, end - current - 1, top.width(), top.height()); const QPixmap bottom = collapsing ? animatedOperation.after : animatedOperation.before; @@ -3039,26 +3012,14 @@ QPixmap QTreeViewPrivate::renderTreeToPixmapForAnimation(const QRect &rect) cons return pixmap; } -void QTreeViewPrivate::_q_currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +void QTreeViewPrivate::_q_endAnimatedOperation() { Q_Q(QTreeView); - if (previous.isValid()) { - QRect previousRect = q->visualRect(previous); - if (allColumnsShowFocus) { - previousRect.setX(0); - previousRect.setWidth(viewport->width()); - } - viewport->update(previousRect); - } - if (current.isValid()) { - QRect currentRect = q->visualRect(current); - if (allColumnsShowFocus) { - currentRect.setX(0); - currentRect.setWidth(viewport->width()); - } - viewport->update(currentRect); - } + q->setState(QAbstractItemView::NoState); + q->updateGeometries(); + viewport->update(); } +#endif //QT_NO_ANIMATION void QTreeViewPrivate::_q_modelAboutToBeReset() { @@ -3787,6 +3748,21 @@ void QTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &pr } #endif QAbstractItemView::currentChanged(current, previous); + + if (allColumnsShowFocus()) { + if (previous.isValid()) { + QRect previousRect = visualRect(previous); + previousRect.setX(0); + previousRect.setWidth(viewport()->width()); + viewport()->update(previousRect); + } + if (current.isValid()) { + QRect currentRect = visualRect(current); + currentRect.setX(0); + currentRect.setWidth(viewport()->width()); + viewport()->update(currentRect); + } + } } /*! diff --git a/src/gui/itemviews/qtreeview.h b/src/gui/itemviews/qtreeview.h index 0347645..4411781 100644 --- a/src/gui/itemviews/qtreeview.h +++ b/src/gui/itemviews/qtreeview.h @@ -223,12 +223,11 @@ private: Q_DECLARE_PRIVATE(QTreeView) Q_DISABLE_COPY(QTreeView) +#ifndef QT_NO_ANIMATION Q_PRIVATE_SLOT(d_func(), void _q_endAnimatedOperation()) - Q_PRIVATE_SLOT(d_func(), void _q_animate()) - Q_PRIVATE_SLOT(d_func(), void _q_currentChanged(const QModelIndex&, const QModelIndex &)) +#endif //QT_NO_ANIMATION Q_PRIVATE_SLOT(d_func(), void _q_modelAboutToBeReset()) Q_PRIVATE_SLOT(d_func(), void _q_sortIndicatorChanged(int column, Qt::SortOrder order)) - Q_PRIVATE_SLOT(d_func(), void _q_modelDestroyed()) }; #endif // QT_NO_TREEVIEW diff --git a/src/gui/itemviews/qtreeview_p.h b/src/gui/itemviews/qtreeview_p.h index 6a1dfe5..6fb2e41 100644 --- a/src/gui/itemviews/qtreeview_p.h +++ b/src/gui/itemviews/qtreeview_p.h @@ -54,6 +54,7 @@ // #include "private/qabstractitemview_p.h" +#include <QtCore/qvariantanimation.h> #ifndef QT_NO_TREEVIEW @@ -81,42 +82,39 @@ public: uniformRowHeights(false), rootDecoration(true), itemsExpandable(true), sortingEnabled(false), expandsOnDoubleClick(true), - allColumnsShowFocus(false), + allColumnsShowFocus(false), current(0), animationsEnabled(false), columnResizeTimerID(0), autoExpandDelay(-1), hoverBranch(-1), geometryRecursionBlock(false) {} ~QTreeViewPrivate() {} void initialize(); - struct AnimatedOperation +#ifndef QT_NO_ANIMATION + struct AnimatedOperation : public QVariantAnimation { - enum Type { Expand, Collapse }; int item; - int top; - int duration; - Type type; QPixmap before; QPixmap after; - }; - - void expand(int item, bool emitSignal); - void collapse(int item, bool emitSignal); - - void prepareAnimatedOperation(int item, AnimatedOperation::Type type); + QWidget *viewport; + AnimatedOperation() : item(0) { setEasingCurve(QEasingCurve::InOutQuad); } + int top() const { return startValue().toInt(); } + QRect rect() const { QRect rect = viewport->rect(); rect.moveTop(top()); return rect; } + void updateCurrentValue(const QVariant &) { viewport->update(rect()); } + void updateState(State, State state) { if (state == Stopped) before = after = QPixmap(); } + } animatedOperation; + void prepareAnimatedOperation(int item, QVariantAnimation::Direction d); void beginAnimatedOperation(); - void _q_endAnimatedOperation(); void drawAnimatedOperation(QPainter *painter) const; QPixmap renderTreeToPixmapForAnimation(const QRect &rect) const; + void _q_endAnimatedOperation(); +#endif //QT_NO_ANIMATION - inline QRect animationRect() const - { return QRect(0, animatedOperation.top, viewport->width(), - viewport->height() - animatedOperation.top); } + void expand(int item, bool emitSignal); + void collapse(int item, bool emitSignal); - void _q_currentChanged(const QModelIndex&, const QModelIndex&); void _q_columnsAboutToBeRemoved(const QModelIndex &, int, int); void _q_columnsRemoved(const QModelIndex &, int, int); void _q_modelAboutToBeReset(); - void _q_animate(); void _q_sortIndicatorChanged(int column, Qt::SortOrder order); void _q_modelDestroyed(); @@ -177,8 +175,6 @@ public: // used when expanding and collapsing items QSet<QPersistentModelIndex> expandedIndexes; - QStack<bool> expandParent; - AnimatedOperation animatedOperation; bool animationsEnabled; inline bool storeExpanded(const QPersistentModelIndex &idx) { diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 839e465..b168188 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -126,7 +126,7 @@ int QApplicationPrivate::app_compile_version = 0x040000; //we don't know exactly QApplication::Type qt_appType=QApplication::Tty; QApplicationPrivate *QApplicationPrivate::self = 0; -QInputContext *QApplicationPrivate::inputContext; +QInputContext *QApplicationPrivate::inputContext = 0; bool QApplicationPrivate::quitOnLastWindowClosed = true; @@ -3716,6 +3716,13 @@ bool QApplication::notify(QObject *receiver, QEvent *e) Qt::MouseFocusReason); } + // ### Qt 5 These dynamic tool tips should be an OPT-IN feature. Some platforms + // like Mac OS X (probably others too), can optimize their views by not + // dispatching mouse move events. We have attributes to control hover, + // and mouse tracking, but as long as we are deciding to implement this + // feature without choice of opting-in or out, you ALWAYS have to have + // tracking enabled. Therefore, the other properties give a false sense of + // performance enhancement. if (e->type() == QEvent::MouseMove && mouse->buttons() == 0) { d->toolTipWidget = w; d->toolTipPos = relpos; diff --git a/src/gui/kernel/qapplication_mac.mm b/src/gui/kernel/qapplication_mac.mm index 0884976..beccfb0 100644 --- a/src/gui/kernel/qapplication_mac.mm +++ b/src/gui/kernel/qapplication_mac.mm @@ -1316,8 +1316,13 @@ void QApplication::setOverrideCursor(const QCursor &cursor) { qApp->d_func()->cursor_list.prepend(cursor); +#ifdef QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; + [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursor)) push]; +#else if (qApp && qApp->activeWindow()) qt_mac_set_cursor(&qApp->d_func()->cursor_list.first(), QCursor::pos()); +#endif } void QApplication::restoreOverrideCursor() @@ -1326,12 +1331,17 @@ void QApplication::restoreOverrideCursor() return; qApp->d_func()->cursor_list.removeFirst(); +#ifdef QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; + [NSCursor pop]; +#else if (qApp && qApp->activeWindow()) { const QCursor def(Qt::ArrowCursor); qt_mac_set_cursor(qApp->d_func()->cursor_list.isEmpty() ? &def : &qApp->d_func()->cursor_list.first(), QCursor::pos()); } -} #endif +} +#endif // QT_NO_CURSOR QWidget *QApplication::topLevelAt(const QPoint &p) { diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index db77b07..90eaba0 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -158,17 +158,19 @@ inline QPointF QTabletDeviceData::scaleCoord(int coordX, int coordY, int outOriginY, int outExtentY) const { QPointF ret; + if (sign(outExtentX) == sign(maxX)) - ret.setX(((coordX - minX) * qAbs(outExtentX) / qAbs(qreal(maxX))) + outOriginX); + ret.setX(((coordX - minX) * qAbs(outExtentX) / qAbs(qreal(maxX - minX))) + outOriginX); else - ret.setX(((qAbs(maxX) - (coordX - minX)) * qAbs(outExtentX) / qAbs(qreal(maxX))) + ret.setX(((qAbs(maxX) - (coordX - minX)) * qAbs(outExtentX) / qAbs(qreal(maxX - minX))) + outOriginX); if (sign(outExtentY) == sign(maxY)) - ret.setY(((coordY - minY) * qAbs(outExtentY) / qAbs(qreal(maxY))) + outOriginY); + ret.setY(((coordY - minY) * qAbs(outExtentY) / qAbs(qreal(maxY - minY))) + outOriginY); else - ret.setY(((qAbs(maxY) - (coordY - minY)) * qAbs(outExtentY) / qAbs(qreal(maxY))) + ret.setY(((qAbs(maxY) - (coordY - minY)) * qAbs(outExtentY) / qAbs(qreal(maxY - minY))) + outOriginY); + return ret; } #endif diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index e0eda82..e0c62b7 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -2956,7 +2956,9 @@ bool QETWidget::translateMouseEvent(const MSG &msg) if (alienWidget && alienWidget->internalWinId()) alienWidget = 0; - if (type == QEvent::MouseMove || type == QEvent::NonClientAreaMouseMove) { + if (type == QEvent::MouseMove || type == QEvent::NonClientAreaMouseMove + || type == QEvent::TabletMove) { + if (!(state & Qt::MouseButtonMask)) qt_button_down = 0; #ifndef QT_NO_CURSOR @@ -3087,6 +3089,8 @@ bool QETWidget::translateMouseEvent(const MSG &msg) popupButtonFocus = popupChild; break; case QEvent::MouseButtonRelease: + case QEvent::TabletRelease: + releaseAfter = true; break; default: @@ -3324,17 +3328,19 @@ static void tabletInit(UINT wActiveCsr, HCTX hTab) tdd.minTanPressure = int(np.axMin); tdd.maxTanPressure = int(np.axMax); - ptrWTInfo(WTI_DEVICES + lc.lcDevice, DVC_X, &np); - tdd.minX = int(np.axMin); - tdd.maxX = int(np.axMax); + LOGCONTEXT lcMine; + + /* get default region */ + ptrWTInfo(WTI_DEFCONTEXT, 0, &lcMine); + + tdd.minX = 0; + tdd.maxX = int(lcMine.lcInExtX) - int(lcMine.lcInOrgX); - ptrWTInfo(WTI_DEVICES + lc.lcDevice, DVC_Y, &np); - tdd.minY = int(np.axMin); - tdd.maxY = int(np.axMax); + tdd.minY = 0; + tdd.maxY = int(lcMine.lcInExtY) - int(lcMine.lcInOrgY); - ptrWTInfo(WTI_DEVICES + lc.lcDevice, DVC_Z, &np); - tdd.minZ = int(np.axMin); - tdd.maxZ = int(np.axMax); + tdd.minZ = 0; + tdd.maxZ = int(lcMine.lcInExtZ) - int(lcMine.lcInOrgZ); int csr_type, csr_physid; @@ -3444,13 +3450,34 @@ bool QETWidget::translateTabletEvent(const MSG &msg, PACKET *localPacketBuf, } QPoint globalPos(qRound(hiResGlobal.x()), qRound(hiResGlobal.y())); + if (t == QEvent::TabletPress) + { + qt_button_down = QApplication::widgetAt(globalPos); + } + // make sure the tablet event get's sent to the proper widget... - QWidget *w = QApplication::widgetAt(globalPos); + QWidget *w = 0; + if (qt_button_down) w = qt_button_down; // Pass it to the thing that's grabbed it. + else + w = QApplication::widgetAt(globalPos); if (!w) w = this; + + if (t == QEvent::TabletRelease) + { + if (qt_win_ignoreNextMouseReleaseEvent) { + qt_win_ignoreNextMouseReleaseEvent = false; + if (qt_button_down && qt_button_down->internalWinId() == autoCaptureWnd) { + releaseAutoCapture(); + qt_button_down = 0; + } + } + + } + QPoint localPos = w->mapFromGlobal(globalPos); #ifndef QT_NO_TABLETEVENT if (currentTabletPointer.currentDevice == QTabletEvent::Airbrush) { diff --git a/src/gui/kernel/qcocoapanel_mac.mm b/src/gui/kernel/qcocoapanel_mac.mm index bdc7ecb..1e0bbdf 100644 --- a/src/gui/kernel/qcocoapanel_mac.mm +++ b/src/gui/kernel/qcocoapanel_mac.mm @@ -85,6 +85,12 @@ QT_USE_NAMESPACE last resort (i.e., this is code that can potentially be removed). */ +- (void)toggleToolbarShown:(id)sender +{ + macSendToolbarChangeEvent([self QT_MANGLE_NAMESPACE(qt_qwidget)]); + [super toggleToolbarShown:sender]; +} + - (void)keyDown:(NSEvent *)theEvent { bool keyOK = qt_dispatchKeyEvent(theEvent, [self QT_MANGLE_NAMESPACE(qt_qwidget)]); diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm index 64cdae6..52685ca 100644 --- a/src/gui/kernel/qcocoaview_mac.mm +++ b/src/gui/kernel/qcocoaview_mac.mm @@ -290,11 +290,18 @@ extern "C" { { if (qwidget->testAttribute(Qt::WA_DropSiteRegistered) == false) return NSDragOperationNone; + NSPoint windowPoint = [sender draggingLocation]; + if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents)) { + // pass the drag enter event to the view underneath. + NSView *candidateView = [[[self window] contentView] hitTest:windowPoint]; + if (candidateView && candidateView != self) + return [candidateView draggingEntered:sender]; + } + dragEnterSequence = [sender draggingSequenceNumber]; [self addDropData:sender]; QMimeData *mimeData = dropData; if (QDragManager::self()->source()) mimeData = QDragManager::self()->dragPrivate()->data; - NSPoint windowPoint = [sender draggingLocation]; NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint]; NSPoint localPoint = [self convertPoint:windowPoint fromView:nil]; QPoint posDrag(localPoint.x, localPoint.y); @@ -318,6 +325,9 @@ extern "C" { [self removeDropData]; return NSDragOperationNone; } else { + // save the mouse position, used by draggingExited handler. + DnDParams *dndParams = [QCocoaView currentMouseEvent]; + dndParams->activeDragEnterPos = windowPoint; // send a drag move event immediately after a drag enter event (as per documentation). QDragMoveEvent qDMEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); qDMEvent.setDropAction(qDEEvent.dropAction()); @@ -338,11 +348,22 @@ extern "C" { - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender { - // drag enter event was rejected, so ignore the move event. + NSPoint windowPoint = [sender draggingLocation]; + if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents)) { + // pass the drag move event to the view underneath. + NSView *candidateView = [[[self window] contentView] hitTest:windowPoint]; + if (candidateView && candidateView != self) + return [candidateView draggingUpdated:sender]; + } + // in cases like QFocusFrame, the view under the mouse might + // not have received the drag enter. Generate a synthetic + // drag enter event for that view. + if (dragEnterSequence != [sender draggingSequenceNumber]) + [self draggingEntered:sender]; + // drag enter event was rejected, so ignore the move event. if (dropData == 0) return NSDragOperationNone; // return last value, if we are still in the answerRect. - NSPoint windowPoint = [sender draggingLocation]; NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint]; NSPoint localPoint = [self convertPoint:windowPoint fromView:nil]; NSDragOperation nsActions = [sender draggingSourceOperationMask]; @@ -381,21 +402,34 @@ extern "C" { - (void)draggingExited:(id < NSDraggingInfo >)sender { - Q_UNUSED(sender) - // drag enter event was rejected, so ignore the move event. + dragEnterSequence = -1; + if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents)) { + // try sending the leave event to the last view which accepted drag enter. + DnDParams *dndParams = [QCocoaView currentMouseEvent]; + NSView *candidateView = [[[self window] contentView] hitTest:dndParams->activeDragEnterPos]; + if (candidateView && candidateView != self) + return [candidateView draggingExited:sender]; + } + // drag enter event was rejected, so ignore the move event. if (dropData) { QDragLeaveEvent de; QApplication::sendEvent(qwidget, &de); [self removeDropData]; } - } - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender { + NSPoint windowPoint = [sender draggingLocation]; + dragEnterSequence = -1; + if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents)) { + // pass the drop event to the view underneath. + NSView *candidateView = [[[self window] contentView] hitTest:windowPoint]; + if (candidateView && candidateView != self) + return [candidateView performDragOperation:sender]; + } [self addDropData:sender]; - NSPoint windowPoint = [sender draggingLocation]; NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint]; NSPoint localPoint = [self convertPoint:windowPoint fromView:nil]; QPoint posDrop(localPoint.x, localPoint.y); @@ -574,11 +608,15 @@ extern "C" { [self removeTrackingArea:t]; } } + + // Ideally, we shouldn't have NSTrackingMouseMoved events included below, it should + // only be turned on if mouseTracking, hover is on or a tool tip is set. + // Unfortunately, Qt will send "tooltip" events on mouse moves, so we need to + // turn it on in ALL case. That means EVERY QCocoaView gets to pay the cost of + // mouse moves delivered to it (Apple recommends keeping it OFF because there + // is a performance hit). So it goes. NSUInteger trackingOptions = NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp - | NSTrackingInVisibleRect; - if (qwidget->hasMouseTracking() || !qwidgetprivate->toolTip.isEmpty() - || qwidget->testAttribute(Qt::WA_Hover)) - trackingOptions |= NSTrackingMouseMoved; + | NSTrackingInVisibleRect | NSTrackingMouseMoved; NSTrackingArea *ta = [[NSTrackingArea alloc] initWithRect:NSMakeRect(0, 0, qwidget->width(), qwidget->height()) @@ -643,62 +681,6 @@ extern "C" { qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); } -- (NSView *)viewUnderTransparentForMouseView:(NSView *)mouseView widget:(QWidget *)widgetToGetMouse - withWindowPoint:(NSPoint)windowPoint -{ - NSMutableArray *viewsToLookAt = [NSMutableArray arrayWithCapacity:5]; - [viewsToLookAt addObject:mouseView]; - QWidget *parentWidget = widgetToGetMouse->parentWidget(); - while (parentWidget) { - [viewsToLookAt addObject:qt_mac_nativeview_for(parentWidget)]; - parentWidget = parentWidget->parentWidget(); - } - - // Now walk through the subviews of each view and determine which subview should - // get the event. We look through all the subviews at a given level with - // the assumption that the last item to be found the candidate has a higher z-order. - // Unfortunately, fast enumeration doesn't go backwards in 10.5, so assume go fast - // forward is quicker than the slow normal way backwards. - NSView *candidateView = nil; - for (NSView *lookView in viewsToLookAt) { - NSPoint tmpPoint = [lookView convertPoint:windowPoint fromView:nil]; - for (NSView *view in [lookView subviews]) { - if (view == mouseView || [view isHidden]) - continue; - NSRect frameRect = [view frame]; - if (NSMouseInRect(tmpPoint, [view frame], [view isFlipped])) - candidateView = view; - } - if (candidateView) - break; - } - - - if (candidateView != nil) { - // Now that we've got a candidate, we have to dig into it's tree and see where it is. - NSView *lowerView = nil; - NSView *viewForDescent = candidateView; - while (viewForDescent) { - NSPoint tmpPoint = [viewForDescent convertPoint:windowPoint fromView:nil]; - // Apply same rule as above wrt z-order. - for (NSView *view in [viewForDescent subviews]) { - if (![view isHidden] && NSMouseInRect(tmpPoint, [view frame], [view isFlipped])) - lowerView = view; - } - if (!lowerView) // Low as we can be at this point. - candidateView = viewForDescent; - - // Try to go deeper, will also exit out of the loop, if we found the point. - viewForDescent = lowerView; - lowerView = nil; - } - } - // I am transparent, so I can't be a candidate. - if (candidateView == mouseView) - candidateView = nil; - return candidateView; -} - - (void)mouseDown:(NSEvent *)theEvent { qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, Qt::LeftButton); diff --git a/src/gui/kernel/qcocoaview_mac_p.h b/src/gui/kernel/qcocoaview_mac_p.h index 6583139..4762b17 100644 --- a/src/gui/kernel/qcocoaview_mac_p.h +++ b/src/gui/kernel/qcocoaview_mac_p.h @@ -68,6 +68,7 @@ struct DnDParams NSEvent *theEvent; NSPoint localPoint; NSDragOperation performedAction; + NSPoint activeDragEnterPos; }; QT_END_NAMESPACE @@ -86,6 +87,7 @@ Q_GUI_EXPORT bool sendKeyEvents; QString *composingText; QStringList *currentCustomTypes; + NSInteger dragEnterSequence; } - (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate; - (void) finishInitWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate; @@ -104,8 +106,6 @@ Q_GUI_EXPORT - (QWidget *)qt_qwidget; - (BOOL)qt_leftButtonIsRightButton; - (void)qt_setLeftButtonIsRightButton:(BOOL)isSwapped; -- (NSView *)viewUnderTransparentForMouseView:(NSView *)mouseView widget:(QWidget *)widgetToGetMouse - withWindowPoint:(NSPoint)windowPoint; + (DnDParams*)currentMouseEvent; @end diff --git a/src/gui/kernel/qcocoawindow_mac.mm b/src/gui/kernel/qcocoawindow_mac.mm index 7084416..eb08982 100644 --- a/src/gui/kernel/qcocoawindow_mac.mm +++ b/src/gui/kernel/qcocoawindow_mac.mm @@ -104,6 +104,12 @@ QT_USE_NAMESPACE last resort (i.e., this is code that can potentially be removed). */ +- (void)toggleToolbarShown:(id)sender +{ + macSendToolbarChangeEvent([self QT_MANGLE_NAMESPACE(qt_qwidget)]); + [super toggleToolbarShown:sender]; +} + - (void)keyDown:(NSEvent *)theEvent { bool keyOK = qt_dispatchKeyEvent(theEvent, [self QT_MANGLE_NAMESPACE(qt_qwidget)]); diff --git a/src/gui/kernel/qmultitouch_mac.mm b/src/gui/kernel/qmultitouch_mac.mm index 3fe85a9..3d2eae6 100644 --- a/src/gui/kernel/qmultitouch_mac.mm +++ b/src/gui/kernel/qmultitouch_mac.mm @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE #ifdef QT_MAC_USE_COCOA -QHash<int, QCocoaTouch*> QCocoaTouch::_currentTouches; +QHash<qint64, QCocoaTouch*> QCocoaTouch::_currentTouches; QPointF QCocoaTouch::_screenReferencePos; QPointF QCocoaTouch::_trackpadReferencePos; int QCocoaTouch::_idAssignmentCount = 0; @@ -62,7 +62,7 @@ QCocoaTouch::QCocoaTouch(NSTouch *nstouch) _touchPoint.setId(_idAssignmentCount++); _touchPoint.setPressure(1.0); - _identity = int([nstouch identity]); + _identity = qint64([nstouch identity]); _currentTouches.insert(_identity, this); updateTouchData(nstouch, NSTouchPhaseBegan); } @@ -100,7 +100,7 @@ void QCocoaTouch::updateTouchData(NSTouch *nstouch, NSTouchPhase phase) QCocoaTouch *QCocoaTouch::findQCocoaTouch(NSTouch *nstouch) { - int identity = int([nstouch identity]); + qint64 identity = qint64([nstouch identity]); if (_currentTouches.contains(identity)) return _currentTouches.value(identity); return 0; diff --git a/src/gui/kernel/qmultitouch_mac_p.h b/src/gui/kernel/qmultitouch_mac_p.h index 3fa8f6c..618e9ca 100644 --- a/src/gui/kernel/qmultitouch_mac_p.h +++ b/src/gui/kernel/qmultitouch_mac_p.h @@ -74,7 +74,7 @@ class QCocoaTouch static void setMouseInDraggingState(bool inDraggingState); private: - static QHash<int, QCocoaTouch*> _currentTouches; + static QHash<qint64, QCocoaTouch*> _currentTouches; static QPointF _screenReferencePos; static QPointF _trackpadReferencePos; static int _idAssignmentCount; @@ -82,7 +82,7 @@ class QCocoaTouch static bool _updateInternalStateOnly; QTouchEvent::TouchPoint _touchPoint; - int _identity; + qint64 _identity; QCocoaTouch(NSTouch *nstouch); ~QCocoaTouch(); diff --git a/src/gui/kernel/qt_cocoa_helpers_mac.mm b/src/gui/kernel/qt_cocoa_helpers_mac.mm index 13b0e50..a98a7f8 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac.mm +++ b/src/gui/kernel/qt_cocoa_helpers_mac.mm @@ -177,11 +177,10 @@ void macWindowToolbarShow(const QWidget *widget, bool show ) { OSWindowRef wnd = qt_mac_window_for(widget); #if QT_MAC_USE_COCOA - NSToolbar *toolbar = [wnd toolbar]; - if (toolbar) { + if (NSToolbar *toolbar = [wnd toolbar]) { QMacCocoaAutoReleasePool pool; if (show != [toolbar isVisible]) { - [wnd toggleToolbarShown:wnd]; + [toolbar setVisible:show]; } else { // The toolbar may be in sync, but we are not, update our framestrut. qt_widget_private(const_cast<QWidget *>(widget))->updateFrameStrut(); @@ -197,22 +196,21 @@ void macWindowToolbarSet( void * /*OSWindowRef*/ window, void *toolbarRef ) { OSWindowRef wnd = static_cast<OSWindowRef>(window); #if QT_MAC_USE_COCOA - [wnd setToolbar:static_cast<NSToolbar *>(toolbarRef)]; + [wnd setToolbar:static_cast<NSToolbar *>(toolbarRef)]; #else SetWindowToolbar(wnd, static_cast<HIToolbarRef>(toolbarRef)); #endif } -bool macWindowToolbarVisible( void * /*OSWindowRef*/ window ) +bool macWindowToolbarIsVisible( void * /*OSWindowRef*/ window ) { OSWindowRef wnd = static_cast<OSWindowRef>(window); #if QT_MAC_USE_COCOA - NSToolbar *toolbar = [wnd toolbar]; - if (toolbar) + if (NSToolbar *toolbar = [wnd toolbar]) return [toolbar isVisible]; return false; #else - return IsWindowToolbarVisible(wnd); + return IsWindowToolbarVisible(wnd); #endif } @@ -220,12 +218,12 @@ void macWindowSetHasShadow( void * /*OSWindowRef*/ window, bool hasShadow ) { OSWindowRef wnd = static_cast<OSWindowRef>(window); #if QT_MAC_USE_COCOA - [wnd setHasShadow:BOOL(hasShadow)]; + [wnd setHasShadow:BOOL(hasShadow)]; #else - if (hasShadow) - ChangeWindowAttributes(wnd, 0, kWindowNoShadowAttribute); - else - ChangeWindowAttributes(wnd, kWindowNoShadowAttribute, 0); + if (hasShadow) + ChangeWindowAttributes(wnd, 0, kWindowNoShadowAttribute); + else + ChangeWindowAttributes(wnd, kWindowNoShadowAttribute, 0); #endif } @@ -233,9 +231,9 @@ void macWindowFlush(void * /*OSWindowRef*/ window) { OSWindowRef wnd = static_cast<OSWindowRef>(window); #if QT_MAC_USE_COCOA - [wnd flushWindowIfNeeded]; + [wnd flushWindowIfNeeded]; #else - HIWindowFlush(wnd); + HIWindowFlush(wnd); #endif } @@ -352,6 +350,12 @@ Qt::MouseButton qt_mac_get_button(EventMouseButton button) return Qt::NoButton; } +void macSendToolbarChangeEvent(QWidget *widget) +{ + QToolBarChangeEvent ev(!(GetCurrentKeyModifiers() & cmdKey)); + qt_sendSpontaneousEvent(widget, &ev); +} + Q_GLOBAL_STATIC(QMacTabletHash, tablet_hash) QMacTabletHash *qt_mac_tablet_hash() { diff --git a/src/gui/kernel/qt_cocoa_helpers_mac_p.h b/src/gui/kernel/qt_cocoa_helpers_mac_p.h index 3881ccd..5f6204f 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac_p.h +++ b/src/gui/kernel/qt_cocoa_helpers_mac_p.h @@ -118,9 +118,10 @@ void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds = 0); bool macWindowIsTextured(void * /*OSWindowRef*/ window); void macWindowToolbarShow(const QWidget *widget, bool show ); void macWindowToolbarSet( void * /*OSWindowRef*/ window, void* toolbarRef ); -bool macWindowToolbarVisible( void * /*OSWindowRef*/ window ); +bool macWindowToolbarIsVisible( void * /*OSWindowRef*/ window ); void macWindowSetHasShadow( void * /*OSWindowRef*/ window, bool hasShadow ); void macWindowFlush(void * /*OSWindowRef*/ window); +void macSendToolbarChangeEvent(QWidget *widget); struct HIContentBorderMetrics; void qt_mac_updateContentBorderMetricts(void * /*OSWindowRef */window, const ::HIContentBorderMetrics &metrics); void * /*NSImage */qt_mac_create_nsimage(const QPixmap &pm); diff --git a/src/gui/kernel/qt_mac.cpp b/src/gui/kernel/qt_mac.cpp index 27df5d1..0c3b707 100644 --- a/src/gui/kernel/qt_mac.cpp +++ b/src/gui/kernel/qt_mac.cpp @@ -134,7 +134,7 @@ QColor qcolorForThemeTextColor(ThemeTextColor themeColor) #ifdef Q_OS_MAC32 RGBColor c; GetThemeTextColor(themeColor, 32, true, &c); - QColor color = QColor(c.red / 265, c.green / 256, c.blue / 256); + QColor color = QColor(c.red / 256, c.green / 256, c.blue / 256); return color; #else // There is no equivalent to GetThemeTextColor in 64-bit and it was rather bad that @@ -156,13 +156,13 @@ QColor qcolorForThemeTextColor(ThemeTextColor themeColor) case kThemeTextColorAlertInactive: case kThemeTextColorDialogInactive: case kThemeTextColorPlacardInactive: - return QColor(67, 69, 69, 255); + return QColor(69, 69, 69, 255); case kThemeTextColorPopupButtonInactive: case kThemeTextColorPopupLabelInactive: case kThemeTextColorPushButtonInactive: case kThemeTextColorTabFrontInactive: case kThemeTextColorBevelButtonInactive: - return QColor(123, 127, 127, 255); + return QColor(127, 127, 127, 255); default: { QNativeImage nativeImage(16,16, QNativeImage::systemFormat()); CGRect cgrect = CGRectMake(0, 0, 16, 16); diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index d1ab45a..c21ebda 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -4541,6 +4541,11 @@ void QWidget::unsetLayoutDirection() By default, this property contains a cursor with the Qt::ArrowCursor shape. + Some underlying window implementations will reset the cursor if it + leaves a widget even if the mouse is grabbed. If you want to have + a cursor set for all widgets, even when outside the window, consider + QApplication::setOverrideCursor(). + \sa QApplication::setOverrideCursor() */ @@ -9894,11 +9899,8 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) break; case Qt::WA_InputMethodEnabled: { QInputContext *ic = d->ic; - if (!ic) { - // implicitly create input context only if we have a focus - if (hasFocus()) - ic = d->inputContext(); - } + if (!ic && (!on || hasFocus())) + ic = d->inputContext(); if (ic) { if (on && hasFocus() && ic->focusWidget() != this && isEnabled()) { ic->setFocusWidget(this); diff --git a/src/gui/kernel/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm index ad16485..5e2dfb6 100644 --- a/src/gui/kernel/qwidget_mac.mm +++ b/src/gui/kernel/qwidget_mac.mm @@ -843,8 +843,7 @@ OSStatus QWidgetPrivate::qt_window_event(EventHandlerCallRef er, EventRef event, extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp qt_button_down = 0; } else if(ekind == kEventWindowToolbarSwitchMode) { - QToolBarChangeEvent ev(!(GetCurrentKeyModifiers() & cmdKey)); - QApplication::sendSpontaneousEvent(widget, &ev); + macSendToolbarChangeEvent(widget); HIToolbarRef toolbar; if (GetWindowToolbar(wid, &toolbar) == noErr) { if (toolbar) { @@ -1521,12 +1520,16 @@ void QWidgetPrivate::toggleDrawers(bool visible) *****************************************************************************/ bool QWidgetPrivate::qt_mac_update_sizer(QWidget *w, int up) { + // I'm not sure what "up" is if(!w || !w->isWindow()) return false; QTLWExtra *topData = w->d_func()->topData(); QWExtra *extraData = w->d_func()->extraData(); - topData->resizer += up; + // topData->resizer is only 4 bits, so subtracting -1 from zero causes bad stuff + // to happen, prevent that here (you really want the thing hidden). + if (up >= 0 || topData->resizer != 0) + topData->resizer += up; OSWindowRef windowRef = qt_mac_window_for(OSViewRef(w->winId())); { #ifndef QT_MAC_USE_COCOA @@ -1539,7 +1542,6 @@ bool QWidgetPrivate::qt_mac_update_sizer(QWidget *w, int up) bool remove_grip = (topData->resizer || (w->windowFlags() & Qt::FramelessWindowHint) || (extraData->maxw && extraData->maxh && extraData->maxw == extraData->minw && extraData->maxh == extraData->minh)); - #ifndef QT_MAC_USE_COCOA WindowAttributes attr; GetWindowAttributes(windowRef, &attr); diff --git a/src/gui/math3d/qgenericmatrix.h b/src/gui/math3d/qgenericmatrix.h index 1131f9b..7bdf70a 100644 --- a/src/gui/math3d/qgenericmatrix.h +++ b/src/gui/math3d/qgenericmatrix.h @@ -119,7 +119,9 @@ Q_INLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT>::QGenericMatrix() template <int N, int M, typename T, typename InnerT> Q_INLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT>::QGenericMatrix(const QGenericMatrix<N, M, T, InnerT>& other) { - qMemCopy(m, other.m, sizeof(m)); + for (int col = 0; col < N; ++col) + for (int row = 0; row < M; ++row) + m[col][row] = other.m[col][row]; } template <int N, int M, typename T, typename InnerT> diff --git a/src/gui/math3d/qmatrix4x4.cpp b/src/gui/math3d/qmatrix4x4.cpp index b998353..88f58c8 100644 --- a/src/gui/math3d/qmatrix4x4.cpp +++ b/src/gui/math3d/qmatrix4x4.cpp @@ -53,10 +53,6 @@ QT_BEGIN_NAMESPACE \brief The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space. \since 4.6 - The matrix elements are stored internally using the most efficient - numeric representation for the underlying hardware: floating-point - or fixed-point. - \sa QVector3D, QGenericMatrix */ @@ -308,8 +304,7 @@ QMatrix4x4::QMatrix4x4(const QTransform& transform) // The 4x4 matrix inverse algorithm is based on that described at: // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q24 // Some optimization has been done to avoid making copies of 3x3 -// sub-matrices, to do calculations in fixed-point where required, -// and to unroll the loops. +// sub-matrices and to unroll the loops. // Calculate the determinant of a 3x3 sub-matrix. // | A B C | diff --git a/src/gui/math3d/qquaternion.cpp b/src/gui/math3d/qquaternion.cpp index 9988e2b..d9d4160 100644 --- a/src/gui/math3d/qquaternion.cpp +++ b/src/gui/math3d/qquaternion.cpp @@ -55,10 +55,6 @@ QT_BEGIN_NAMESPACE Quaternions are used to represent rotations in 3D space, and consist of a 3D rotation axis specified by the x, y, and z coordinates, and a scalar representing the rotation angle. - - The components of a quaternion are stored internally using the most - efficient representation for the GL rendering engine, which will be - either floating-point or fixed-point. */ /*! diff --git a/src/gui/math3d/qvector2d.cpp b/src/gui/math3d/qvector2d.cpp index 9b5d123..b492aa8 100644 --- a/src/gui/math3d/qvector2d.cpp +++ b/src/gui/math3d/qvector2d.cpp @@ -57,9 +57,7 @@ QT_BEGIN_NAMESPACE The QVector2D class can also be used to represent vertices in 2D space. We therefore do not need to provide a separate vertex class. - The coordinates are stored internally using the most efficient - representation for the GL rendering engine, which will be either - floating-point or fixed-point. + \sa QVector3D, QVector4D, QQuaternion */ /*! diff --git a/src/gui/math3d/qvector3d.cpp b/src/gui/math3d/qvector3d.cpp index 977152a..95550cd 100644 --- a/src/gui/math3d/qvector3d.cpp +++ b/src/gui/math3d/qvector3d.cpp @@ -61,9 +61,7 @@ QT_BEGIN_NAMESPACE The QVector3D class can also be used to represent vertices in 3D space. We therefore do not need to provide a separate vertex class. - The coordinates are stored internally using the most efficient - representation for the GL rendering engine, which will be either - floating-point or fixed-point. + \sa QVector2D, QVector4D, QQuaternion */ /*! diff --git a/src/gui/math3d/qvector4d.cpp b/src/gui/math3d/qvector4d.cpp index a28d2a1..1f7d921 100644 --- a/src/gui/math3d/qvector4d.cpp +++ b/src/gui/math3d/qvector4d.cpp @@ -57,10 +57,6 @@ QT_BEGIN_NAMESPACE The QVector4D class can also be used to represent vertices in 4D space. We therefore do not need to provide a separate vertex class. - The coordinates are stored internally using the most efficient - representation for the GL rendering engine, which will be either - floating-point or fixed-point. - \sa QQuaternion, QVector2D, QVector3D */ diff --git a/src/gui/text/qsyntaxhighlighter.cpp b/src/gui/text/qsyntaxhighlighter.cpp index db1a38e..f69562d 100644 --- a/src/gui/text/qsyntaxhighlighter.cpp +++ b/src/gui/text/qsyntaxhighlighter.cpp @@ -65,6 +65,18 @@ public: void _q_reformatBlocks(int from, int charsRemoved, int charsAdded); void reformatBlock(QTextBlock block); + + inline void rehighlight(QTextCursor &cursor, QTextCursor::MoveOperation operation) { + QObject::disconnect(doc, SIGNAL(contentsChange(int,int,int)), + q_func(), SLOT(_q_reformatBlocks(int,int,int))); + cursor.beginEditBlock(); + int from = cursor.position(); + cursor.movePosition(operation); + _q_reformatBlocks(from, 0, cursor.position() - from); + cursor.endEditBlock(); + QObject::connect(doc, SIGNAL(contentsChange(int,int,int)), + q_func(), SLOT(_q_reformatBlocks(int,int,int))); + } inline void _q_delayedRehighlight() { if (!rehighlightPending) @@ -356,6 +368,8 @@ QTextDocument *QSyntaxHighlighter::document() const \since 4.2 Redoes the highlighting of the whole document. + + \sa rehighlightBlock() */ void QSyntaxHighlighter::rehighlight() { @@ -363,15 +377,25 @@ void QSyntaxHighlighter::rehighlight() if (!d->doc) return; - disconnect(d->doc, SIGNAL(contentsChange(int,int,int)), - this, SLOT(_q_reformatBlocks(int,int,int))); QTextCursor cursor(d->doc); - cursor.beginEditBlock(); - cursor.movePosition(QTextCursor::End); - d->_q_reformatBlocks(0, 0, cursor.position()); - cursor.endEditBlock(); - connect(d->doc, SIGNAL(contentsChange(int,int,int)), - this, SLOT(_q_reformatBlocks(int,int,int))); + d->rehighlight(cursor, QTextCursor::End); +} + +/*! + \since 4.6 + + Redoes the highlighting of the given QTextBlock \a block. + + \sa rehighlight() +*/ +void QSyntaxHighlighter::rehighlightBlock(const QTextBlock &block) +{ + Q_D(QSyntaxHighlighter); + if (!d->doc) + return; + + QTextCursor cursor(block); + d->rehighlight(cursor, QTextCursor::EndOfBlock); } /*! diff --git a/src/gui/text/qsyntaxhighlighter.h b/src/gui/text/qsyntaxhighlighter.h index 4e5271b..ee249b8 100644 --- a/src/gui/text/qsyntaxhighlighter.h +++ b/src/gui/text/qsyntaxhighlighter.h @@ -78,6 +78,7 @@ public: public Q_SLOTS: void rehighlight(); + void rehighlightBlock(const QTextBlock &block); protected: virtual void highlightBlock(const QString &text) = 0; diff --git a/src/gui/widgets/qabstractscrollarea_p.h b/src/gui/widgets/qabstractscrollarea_p.h index 7e0f444..aef8ac5 100644 --- a/src/gui/widgets/qabstractscrollarea_p.h +++ b/src/gui/widgets/qabstractscrollarea_p.h @@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE class QScrollBar; class QAbstractScrollAreaScrollBarContainer; -class QAbstractScrollAreaPrivate: public QFramePrivate +class Q_AUTOTEST_EXPORT QAbstractScrollAreaPrivate: public QFramePrivate { Q_DECLARE_PUBLIC(QAbstractScrollArea) diff --git a/src/gui/widgets/qabstractspinbox.cpp b/src/gui/widgets/qabstractspinbox.cpp index da18d13..433406c 100644 --- a/src/gui/widgets/qabstractspinbox.cpp +++ b/src/gui/widgets/qabstractspinbox.cpp @@ -1577,7 +1577,7 @@ void QAbstractSpinBox::initStyleOption(QStyleOptionSpinBox *option) const option->initFrom(this); option->activeSubControls = QStyle::SC_None; option->buttonSymbols = d->buttonSymbols; - option->subControls = QStyle::SC_SpinBoxFrame; + option->subControls = QStyle::SC_SpinBoxFrame | QStyle::SC_SpinBoxEditField; if (d->buttonSymbols != QAbstractSpinBox::NoButtons) { option->subControls |= QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown; if (d->buttonState & Up) { diff --git a/src/gui/widgets/qdockarealayout.cpp b/src/gui/widgets/qdockarealayout.cpp index b905ccd..ee29b55 100644 --- a/src/gui/widgets/qdockarealayout.cpp +++ b/src/gui/widgets/qdockarealayout.cpp @@ -1707,7 +1707,7 @@ QDockAreaLayoutItem &QDockAreaLayoutInfo::item(const QList<int> &path) Q_ASSERT(!path.isEmpty()); const int index = path.first(); if (path.count() > 1) { - const QDockAreaLayoutItem &item = item_list.at(index); + const QDockAreaLayoutItem &item = item_list[index]; Q_ASSERT(item.subinfo != 0); return item.subinfo->item(path.mid(1)); } diff --git a/src/gui/widgets/qeffects.cpp b/src/gui/widgets/qeffects.cpp index d6d0a16..f3b1b76 100644 --- a/src/gui/widgets/qeffects.cpp +++ b/src/gui/widgets/qeffects.cpp @@ -128,7 +128,8 @@ QAlphaWidget::~QAlphaWidget() { #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE) // Restore user-defined opacity value - widget->setWindowOpacity(windowOpacity); + if (widget) + widget->setWindowOpacity(windowOpacity); #endif } diff --git a/src/gui/widgets/qgroupbox.cpp b/src/gui/widgets/qgroupbox.cpp index 2380e78..5758b6a 100644 --- a/src/gui/widgets/qgroupbox.cpp +++ b/src/gui/widgets/qgroupbox.cpp @@ -478,11 +478,7 @@ void QGroupBox::focusInEvent(QFocusEvent *fe) if (focusPolicy() == Qt::NoFocus) { d->_q_fixFocus(fe->reason()); } else { - QStyleOptionGroupBox box; - initStyleOption(&box); - QRect rect = style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxCheckBox, this) - | style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxLabel, this); - update(rect); + QWidget::focusInEvent(fe); } } diff --git a/src/gui/widgets/qmainwindow.cpp b/src/gui/widgets/qmainwindow.cpp index 0c841eb..c51bed9 100644 --- a/src/gui/widgets/qmainwindow.cpp +++ b/src/gui/widgets/qmainwindow.cpp @@ -1369,20 +1369,25 @@ bool QMainWindow::event(QEvent *event) #ifdef Q_WS_MAC case QEvent::Show: if (unifiedTitleAndToolBarOnMac()) - macWindowToolbarShow(this, true); + d->layout->syncUnifiedToolbarVisibility(); + d->layout->blockVisiblityCheck = false; break; -# ifdef QT_MAC_USE_COCOA case QEvent::WindowStateChange: { + if (isHidden()) { + // We are coming out of a minimize, leave things as is. + d->layout->blockVisiblityCheck = true; + } +# ifdef QT_MAC_USE_COCOA // We need to update the HIToolbar status when we go out of or into fullscreen. QWindowStateChangeEvent *wce = static_cast<QWindowStateChangeEvent *>(event); if ((windowState() & Qt::WindowFullScreen) || (wce->oldState() & Qt::WindowFullScreen)) { d->layout->updateHIToolBarStatus(); } +# endif // Cocoa } break; -# endif // Cocoa -#endif +#endif // Q_WS_MAC #if !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_CURSOR) case QEvent::CursorChange: if (d->cursorAdjusted) { diff --git a/src/gui/widgets/qmainwindowlayout.cpp b/src/gui/widgets/qmainwindowlayout.cpp index aba9120..3936a67 100644 --- a/src/gui/widgets/qmainwindowlayout.cpp +++ b/src/gui/widgets/qmainwindowlayout.cpp @@ -939,15 +939,70 @@ void QMainWindowLayout::getStyleOptionInfo(QStyleOptionToolBar *option, QToolBar void QMainWindowLayout::toggleToolBarsVisible() { - layoutState.toolBarAreaLayout.visible = !layoutState.toolBarAreaLayout.visible; - if (!layoutState.mainWindow->isMaximized()){ - QPoint topLeft = parentWidget()->geometry().topLeft(); - QRect r = parentWidget()->geometry(); - r = layoutState.toolBarAreaLayout.rectHint(r); - r.moveTo(topLeft); - parentWidget()->setGeometry(r); - } else{ - update(); + bool updateNonUnifiedParts = true; +#ifdef Q_WS_MAC + if (layoutState.mainWindow->unifiedTitleAndToolBarOnMac()) { + // If we hit this case, someone has pressed the "toolbar button" which will + // toggle the unified toolbar visiblity, because that's what the user wants. + // We might be in a situation where someone has hidden all the toolbars + // beforehand (maybe in construction), but now they've hit this button and + // and are expecting the items to show. What do we do? + // 1) Check the visibility of all the toolbars, if one is visible, do nothing, this + // preserves what people would expect (these toolbars were visible when I clicked last time). + // 2) If NONE are visible, then show them all. Again, this preserves the user expectation + // of, "I want to see the toolbars." The user may get more toolbars than expected, but this + // is better seeing nothing. + // Don't worry about any of this if we are going invisible. This does mean we may get + // into issues when switching into and out of fullscreen mode, but this is probably minor. + // If we ever need to do hiding, that would have to be taken care of after the unified toolbar + // has finished hiding. + // People can of course handle the QEvent::ToolBarChange event themselves and do + // WHATEVER they want if they don't like what we are doing (though the unified toolbar + // will fire regardless). + + // Check if we REALLY need to update the geometry below. If we only have items in the + // unified toolbar, all the docks will be empty, so there's very little point + // in doing the geometry as Apple will do it (we also avoid flicker in Cocoa as well). + // FWIW, layoutState.toolBarAreaLayout.visible and the state of the unified toolbar + // visibility can get out of sync. I really don't think it's a big issue. It is kept + // to a minimum because we only change the visibility if we absolutely must. + // update the "non unified parts." + updateNonUnifiedParts = !layoutState.toolBarAreaLayout.isEmpty(); + + // We get this function before the unified toolbar does its thing. + // So, the value will be opposite of what we expect. + bool goingVisible = !macWindowToolbarIsVisible(qt_mac_window_for(layoutState.mainWindow)); + if (goingVisible) { + const int ToolBarCount = qtoolbarsInUnifiedToolbarList.size(); + bool needAllVisible = true; + for (int i = 0; i < ToolBarCount; ++i) { + if (!qtoolbarsInUnifiedToolbarList.at(i)->isHidden()) { + needAllVisible = false; + break; + } + } + if (needAllVisible) { + QBoolBlocker blocker(blockVisiblityCheck); // Disable the visibilty check because + // the toggle has already happened. + for (int i = 0; i < ToolBarCount; ++i) + qtoolbarsInUnifiedToolbarList.at(i)->setVisible(true); + } + } + if (!updateNonUnifiedParts) + layoutState.toolBarAreaLayout.visible = goingVisible; + } +#endif + if (updateNonUnifiedParts) { + layoutState.toolBarAreaLayout.visible = !layoutState.toolBarAreaLayout.visible; + if (!layoutState.mainWindow->isMaximized()) { + QPoint topLeft = parentWidget()->geometry().topLeft(); + QRect r = parentWidget()->geometry(); + r = layoutState.toolBarAreaLayout.rectHint(r); + r.moveTo(topLeft); + parentWidget()->setGeometry(r); + } else { + update(); + } } } @@ -1574,9 +1629,6 @@ void QMainWindowLayout::animationFinished(QWidget *widget) #ifndef QT_NO_DOCKWIDGET #ifndef QT_NO_TABBAR - //it is important to set the current tab before applying the layout - //so that applyState will not try to counter the result of the animation - //by putting the item in negative space if (qobject_cast<QDockWidget*>(widget) != 0) { // info() might return null if the widget is destroyed while // animating but before the animationFinished signal is received. @@ -1586,8 +1638,6 @@ void QMainWindowLayout::animationFinished(QWidget *widget) #endif #endif - applyState(layoutState, false); - savedState.clear(); currentGapPos.clear(); pluggingWidget = 0; @@ -1641,6 +1691,9 @@ QMainWindowLayout::QMainWindowLayout(QMainWindow *mainwindow) #ifndef QT_NO_RUBBERBAND , gapIndicator(new QRubberBand(QRubberBand::Rectangle, mainwindow)) #endif //QT_NO_RUBBERBAND +#ifdef Q_WS_MAC + , blockVisiblityCheck(false) +#endif { #ifndef QT_NO_DOCKWIDGET #ifndef QT_NO_TABBAR diff --git a/src/gui/widgets/qmainwindowlayout_mac.mm b/src/gui/widgets/qmainwindowlayout_mac.mm index 6632be7..61719c2 100644 --- a/src/gui/widgets/qmainwindowlayout_mac.mm +++ b/src/gui/widgets/qmainwindowlayout_mac.mm @@ -338,18 +338,16 @@ void QMainWindowLayout::updateHIToolBarStatus() 0, kWindowUnifiedTitleAndToolbarAttribute); } #endif - macWindowToolbarShow(layoutState.mainWindow, useMacToolbar); layoutState.mainWindow->setUpdatesEnabled(false); // reduces a little bit of flicker, not all though if (!useMacToolbar) { - OSWindowRef windowRef = qt_mac_window_for(parentWidget()); - macWindowToolbarShow(parentWidget(), false); + macWindowToolbarShow(layoutState.mainWindow, false); // Move everything out of the HIToolbar into the main toolbar. while (!qtoolbarsInUnifiedToolbarList.isEmpty()) { // Should shrink the list by one every time. layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, qtoolbarsInUnifiedToolbarList.first()); } - macWindowToolbarSet(windowRef, NULL); + macWindowToolbarSet(qt_mac_window_for(layoutState.mainWindow), 0); } else { QList<QToolBar *> toolbars = layoutState.mainWindow->findChildren<QToolBar *>(); for (int i = 0; i < toolbars.size(); ++i) { @@ -359,6 +357,7 @@ void QMainWindowLayout::updateHIToolBarStatus() layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, toolbar); } } + syncUnifiedToolbarVisibility(); } layoutState.mainWindow->setUpdatesEnabled(true); } @@ -439,7 +438,7 @@ void QMainWindowLayout::insertIntoMacToolbar(QToolBar *before, QToolBar *toolbar #else NSString *toolbarID = kQToolBarNSToolbarIdentifier; toolbarID = [toolbarID stringByAppendingFormat:@"%p", toolbar]; - cocoaItemIDToToolbarHash.insert(QCFString::toQString(CFStringRef(toolbarID)), toolbar); + cocoaItemIDToToolbarHash.insert(qt_mac_NSStringToQString(toolbarID), toolbar); [macToolbar insertItemWithItemIdentifier:toolbarID atIndex:beforeIndex]; #endif } @@ -487,6 +486,7 @@ void QMainWindowLayout::cleanUpMacToolbarItems() void QMainWindowLayout::fixSizeInUnifiedToolbar(QToolBar *tb) const { +#ifdef QT_MAC_USE_COCOA QHash<void *, QToolBar *>::const_iterator it = unifiedToolbarHash.constBegin(); NSToolbarItem *item = nil; while (it != unifiedToolbarHash.constEnd()) { @@ -507,5 +507,26 @@ void QMainWindowLayout::fixSizeInUnifiedToolbar(QToolBar *tb) const nssize.height = size.height() - 2; [item setMinSize:nssize]; } +#else + Q_UNUSED(tb); +#endif } + +void QMainWindowLayout::syncUnifiedToolbarVisibility() +{ + if (blockVisiblityCheck) + return; + + Q_ASSERT(layoutState.mainWindow->unifiedTitleAndToolBarOnMac()); + bool show = false; + const int ToolBarCount = qtoolbarsInUnifiedToolbarList.count(); + for (int i = 0; i < ToolBarCount; ++i) { + if (qtoolbarsInUnifiedToolbarList.at(i)->isVisible()) { + show = true; + break; + } + } + macWindowToolbarShow(layoutState.mainWindow, show); +} + QT_END_NAMESPACE diff --git a/src/gui/widgets/qmainwindowlayout_p.h b/src/gui/widgets/qmainwindowlayout_p.h index 45f62cd..524fdbf 100644 --- a/src/gui/widgets/qmainwindowlayout_p.h +++ b/src/gui/widgets/qmainwindowlayout_p.h @@ -335,6 +335,8 @@ public: void cleanUpMacToolbarItems(); void fixSizeInUnifiedToolbar(QToolBar *tb) const; bool useHIToolBar; + void syncUnifiedToolbarVisibility(); + bool blockVisiblityCheck; #endif }; QT_END_NAMESPACE diff --git a/src/gui/widgets/qprogressbar.cpp b/src/gui/widgets/qprogressbar.cpp index ac3338b..d168028 100644 --- a/src/gui/widgets/qprogressbar.cpp +++ b/src/gui/widgets/qprogressbar.cpp @@ -204,7 +204,7 @@ bool QProgressBarPrivate::repaintRequired() const \o A progress bar shown in the Plastique widget style. \endtable - \sa QTimeLine, QProgressDialog, {fowler}{GUI Design Handbook: Progress Indicator} + \sa QProgressDialog, {fowler}{GUI Design Handbook: Progress Indicator} */ /*! diff --git a/src/gui/widgets/qspinbox.cpp b/src/gui/widgets/qspinbox.cpp index e069a21..3933272 100644 --- a/src/gui/widgets/qspinbox.cpp +++ b/src/gui/widgets/qspinbox.cpp @@ -50,6 +50,7 @@ #include <qdebug.h> #include <math.h> +#include <float.h> QT_BEGIN_NAMESPACE @@ -823,8 +824,8 @@ void QDoubleSpinBox::setRange(double minimum, double maximum) Sets how many decimals the spinbox will use for displaying and interpreting doubles. - \warning The results might not be reliable with very high values - for \a decimals. + \warning The maximum value for \a decimals is DBL_MAX_10_EXP + + DBL_DIG (ie. 323) because of the limitations of the double type. Note: The maximum, minimum and value might change as a result of changing this property. @@ -840,7 +841,7 @@ int QDoubleSpinBox::decimals() const void QDoubleSpinBox::setDecimals(int decimals) { Q_D(QDoubleSpinBox); - d->decimals = qMax(0, decimals); + d->decimals = qBound(0, decimals, DBL_MAX_10_EXP + DBL_DIG); setRange(minimum(), maximum()); // make sure values are rounded setValue(value()); diff --git a/src/gui/widgets/qtabbar.cpp b/src/gui/widgets/qtabbar.cpp index 11cb6a1..690e624 100644 --- a/src/gui/widgets/qtabbar.cpp +++ b/src/gui/widgets/qtabbar.cpp @@ -663,7 +663,7 @@ void QTabBarPrivate::refresh() if (pressedIndex != -1 && movable && QApplication::mouseButtons() == Qt::NoButton) { - _q_moveTabFinished(pressedIndex); + moveTabFinished(pressedIndex); if (!validIndex(pressedIndex)) pressedIndex = -1; } @@ -1662,26 +1662,17 @@ void QTabBarPrivate::slide(int from, int to) q->setUpdatesEnabled(true); int postLocation = vertical ? q->tabRect(to).y() : q->tabRect(to).x(); int length = postLocation - preLocation; - tabList[to].makeTimeLine(q); - tabList[to].dragOffset += -1 * length; - tabList[to].timeLine->setFrameRange(tabList[to].dragOffset, 0); - animations[tabList[to].timeLine] = to; - tabList[to].timeLine->setDuration(ANIMATION_DURATION); - if (tabList[to].timeLine->state() != QTimeLine::Running) - tabList[to].timeLine->start(); + tabList[to].dragOffset -= length; + tabList[to].startAnimation(this, ANIMATION_DURATION); } -void QTabBarPrivate::_q_moveTab(int offset) +void QTabBarPrivate::moveTab(int index, int offset) { - Q_Q(QTabBar); - if (QTimeLine *timeLine = qobject_cast<QTimeLine *>(q->sender())) { - int index = animations[timeLine]; - if (!validIndex(index)) - return; - tabList[index].dragOffset = offset; - layoutTab(index); // Make buttons follow tab - q->update(); - } + if (!validIndex(index)) + return; + tabList[index].dragOffset = offset; + layoutTab(index); // Make buttons follow tab + q_func()->update(); } /*!\reimp @@ -1695,7 +1686,7 @@ void QTabBar::mousePressEvent(QMouseEvent *event) } // Be safe! if (d->pressedIndex != -1 && d->movable) - d->_q_moveTabFinished(d->pressedIndex); + d->moveTabFinished(d->pressedIndex); d->pressedIndex = d->indexAtPos(event->pos()); if (d->validIndex(d->pressedIndex)) { @@ -1721,7 +1712,7 @@ void QTabBar::mouseMoveEvent(QMouseEvent *event) // Be safe! if (d->pressedIndex != -1 && event->buttons() == Qt::NoButton) - d->_q_moveTabFinished(d->pressedIndex); + d->moveTabFinished(d->pressedIndex); // Start drag if (!d->dragInProgress && d->pressedIndex != -1) { @@ -1789,16 +1780,6 @@ void QTabBar::mouseMoveEvent(QMouseEvent *event) optTabBase.documentMode = d->documentMode; } -void QTabBarPrivate::_q_moveTabFinished() -{ - Q_Q(QTabBar); - if (QTimeLine *timeLine = qobject_cast<QTimeLine *>(q->sender())) { - int index = animations[timeLine]; - animations.remove(timeLine); - _q_moveTabFinished(index); - } -} - void QTabBarPrivate::setupMovableTab() { Q_Q(QTabBar); @@ -1838,11 +1819,19 @@ void QTabBarPrivate::setupMovableTab() movingTab->setVisible(true); } -void QTabBarPrivate::_q_moveTabFinished(int index) +void QTabBarPrivate::moveTabFinished(int index) { Q_Q(QTabBar); bool cleanup = (pressedIndex == index) || (pressedIndex == -1) || !validIndex(index); - if (animations.isEmpty() && cleanup) { + bool allAnimationsFinished = true; +#ifndef QT_NO_ANIMATION + for(int i = 0; allAnimationsFinished && i < tabList.count(); ++i) { + const Tab &t = tabList.at(i); + if (t.animation && t.animation->state() == QAbstractAnimation::Running) + allAnimationsFinished = false; + } +#endif //QT_NO_ANIMATION + if (allAnimationsFinished && cleanup) { movingTab->setVisible(false); // We might not get a mouse release for (int i = 0; i < tabList.count(); ++i) { tabList[i].dragOffset = 0; @@ -1877,17 +1866,8 @@ void QTabBar::mouseReleaseEvent(QMouseEvent *event) ? tabRect(d->pressedIndex).height() : tabRect(d->pressedIndex).width(); int duration = qMin(ANIMATION_DURATION, - ((length < 0 ? (-1 * length) : length) * ANIMATION_DURATION) / width); - if (duration > 0) { - d->tabList[d->pressedIndex].makeTimeLine(this); - d->tabList[d->pressedIndex].timeLine->setFrameRange(length, 0); - d->animations[d->tabList[d->pressedIndex].timeLine] = d->pressedIndex; - d->tabList[d->pressedIndex].timeLine->setDuration(duration); - if (d->tabList[d->pressedIndex].timeLine->state() != QTimeLine::Running) - d->tabList[d->pressedIndex].timeLine->start(); - } else { - d->_q_moveTabFinished(d->pressedIndex); - } + (qAbs(length) * ANIMATION_DURATION) / width); + d->tabList[d->pressedIndex].startAnimation(d, duration); d->dragInProgress = false; d->movingTab->setVisible(false); d->dragStartPosition = QPoint(); diff --git a/src/gui/widgets/qtabbar.h b/src/gui/widgets/qtabbar.h index 7514486..402f54b 100644 --- a/src/gui/widgets/qtabbar.h +++ b/src/gui/widgets/qtabbar.h @@ -215,8 +215,6 @@ private: Q_DECLARE_PRIVATE(QTabBar) Q_PRIVATE_SLOT(d_func(), void _q_scrollTabs()) Q_PRIVATE_SLOT(d_func(), void _q_closeTab()) - Q_PRIVATE_SLOT(d_func(), void _q_moveTab(int)) - Q_PRIVATE_SLOT(d_func(), void _q_moveTabFinished()) }; #endif // QT_NO_TABBAR diff --git a/src/gui/widgets/qtabbar_p.h b/src/gui/widgets/qtabbar_p.h index dbae055..b9b9fba 100644 --- a/src/gui/widgets/qtabbar_p.h +++ b/src/gui/widgets/qtabbar_p.h @@ -58,9 +58,8 @@ #include <qicon.h> #include <qtoolbutton.h> -#include <qtimeline.h> -#include <qhash.h> #include <qdebug.h> +#include <qvariantanimation.h> #ifndef QT_NO_TABBAR @@ -75,9 +74,10 @@ class QTabBarPrivate : public QWidgetPrivate Q_DECLARE_PUBLIC(QTabBar) public: QTabBarPrivate() - :currentIndex(-1), pressedIndex(-1), - shape(QTabBar::RoundedNorth), - layoutDirty(false), drawBase(true), scrollOffset(0), expanding(true), closeButtonOnTabs(false), selectionBehaviorOnRemove(QTabBar::SelectRightTab), paintWithOffsets(true), movable(false), dragInProgress(false), documentMode(false), movingTab(0) {} + :currentIndex(-1), pressedIndex(-1), shape(QTabBar::RoundedNorth), layoutDirty(false), + drawBase(true), scrollOffset(0), expanding(true), closeButtonOnTabs(false), + selectionBehaviorOnRemove(QTabBar::SelectRightTab), paintWithOffsets(true), movable(false), + dragInProgress(false), documentMode(false), movingTab(0) {} int currentIndex; int pressedIndex; @@ -88,16 +88,13 @@ public: struct Tab { inline Tab(const QIcon &ico, const QString &txt) - : enabled(true) - , shortcutId(0) - , text(txt) - , icon(ico) - , leftWidget(0) - , rightWidget(0) - , lastTab(-1) - , timeLine(0) - , dragOffset(0) + : enabled(true) , shortcutId(0), text(txt), icon(ico), + leftWidget(0), rightWidget(0), lastTab(-1), dragOffset(0) +#ifndef QT_NO_ANIMATION + , animation(0) +#endif //QT_NO_ANIMATION {} + bool operator==(const Tab &other) const { return &other == this; } bool enabled; int shortcutId; QString text; @@ -117,21 +114,39 @@ public: QWidget *leftWidget; QWidget *rightWidget; int lastTab; - - QTimeLine *timeLine; int dragOffset; - void makeTimeLine(QWidget *q) { - if (timeLine) - return; - timeLine = new QTimeLine(ANIMATION_DURATION, q); - q->connect(timeLine, SIGNAL(frameChanged(int)), q, SLOT(_q_moveTab(int))); - q->connect(timeLine, SIGNAL(finished()), q, SLOT(_q_moveTabFinished())); +#ifndef QT_NO_ANIMATION + ~Tab() { delete animation; } + struct TabBarAnimation : public QVariantAnimation { + TabBarAnimation(Tab *t, QTabBarPrivate *_priv) : tab(t), priv(_priv) + { setEasingCurve(QEasingCurve::InOutQuad); } + + void updateCurrentValue(const QVariant ¤t) + { priv->moveTab(priv->tabList.indexOf(*tab), current.toInt()); } + + void updateState(State, State newState) + { if (newState == Stopped) priv->moveTabFinished(priv->tabList.indexOf(*tab)); } + private: + //these are needed for the callbacks + Tab *tab; + QTabBarPrivate *priv; + } *animation; + + void startAnimation(QTabBarPrivate *priv, int duration) { + if (!animation) + animation = new TabBarAnimation(this, priv); + animation->setStartValue(dragOffset); + animation->setEndValue(0); + animation->setDuration(duration); + animation->start(); } - +#else + void startAnimation(QTabBarPrivate *priv, int duration) + { Q_UNUSED(duration); priv->moveTabFinished(priv->tabList.indexOf(*this)); } +#endif //QT_NO_ANIMATION }; QList<Tab> tabList; - QHash<QTimeLine*, int> animations; int calculateNewPosition(int from, int to, int index) const; void slide(int from, int to); @@ -152,9 +167,8 @@ public: void _q_scrollTabs(); void _q_closeTab(); - void _q_moveTab(int); - void _q_moveTabFinished(); - void _q_moveTabFinished(int offset); + void moveTab(int index, int offset); + void moveTabFinished(int index); QRect hoverRect; void refresh(); diff --git a/src/gui/widgets/qtoolbar.cpp b/src/gui/widgets/qtoolbar.cpp index b249915..b65f1ba 100644 --- a/src/gui/widgets/qtoolbar.cpp +++ b/src/gui/widgets/qtoolbar.cpp @@ -1074,6 +1074,15 @@ static bool waitForPopup(QToolBar *tb, QWidget *popup) return false; } +#if defined(Q_WS_MAC) +static bool toolbarInUnifiedToolBar(QToolBar *toolbar) +{ + const QMainWindow *mainWindow = qobject_cast<const QMainWindow *>(toolbar->parentWidget()); + return mainWindow && mainWindow->unifiedTitleAndToolBarOnMac() + && mainWindow->toolBarArea(toolbar) == Qt::TopToolBarArea; +} +#endif + /*! \reimp */ bool QToolBar::event(QEvent *event) { @@ -1096,7 +1105,15 @@ bool QToolBar::event(QEvent *event) // fallthrough intended case QEvent::Show: d->toggleViewAction->setChecked(event->type() == QEvent::Show); -#if defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA) +#if defined(Q_WS_MAC) + if (toolbarInUnifiedToolBar(this)) { + // I can static_cast because I did the qobject_cast in the if above, therefore + // we must have a QMainWindowLayout here. + QMainWindowLayout *mwLayout = static_cast<QMainWindowLayout *>(parentWidget()->layout()); + mwLayout->fixSizeInUnifiedToolbar(this); + mwLayout->syncUnifiedToolbarVisibility(); + } +# if !defined(QT_MAC_USE_COCOA) // Fall through case QEvent::LayoutRequest: { // There's currently no way to invalidate the size and let @@ -1111,10 +1128,9 @@ bool QToolBar::event(QEvent *event) } if (needUpdate) { - OSWindowRef windowRef = qt_mac_window_for(this); - if (mainWindow->unifiedTitleAndToolBarOnMac() - && mainWindow->toolBarArea(this) == Qt::TopToolBarArea - && macWindowToolbarVisible(windowRef)) { + OSWindowRef windowRef = qt_mac_window_for(mainWindow); + if (toolbarInUnifiedToolBar(this) + && macWindowToolbarIsVisible(windowRef)) { DisableScreenUpdates(); macWindowToolbarShow(this, false); macWindowToolbarShow(this, true); @@ -1126,7 +1142,8 @@ bool QToolBar::event(QEvent *event) return earlyResult; } } -#endif +# endif // !QT_MAC_USE_COCOA +#endif // Q_WS_MAC break; case QEvent::ParentChange: d->layout->checkUsePopupMenu(); diff --git a/src/gui/widgets/qwidgetanimator.cpp b/src/gui/widgets/qwidgetanimator.cpp index 26cf905..1a93b51 100644 --- a/src/gui/widgets/qwidgetanimator.cpp +++ b/src/gui/widgets/qwidgetanimator.cpp @@ -100,7 +100,6 @@ void QWidgetAnimator::animate(QWidget *widget, const QRect &_final_geometry, boo m_animation_map[widget] = anim; connect(anim, SIGNAL(finished()), SLOT(animationFinished())); anim->start(QPropertyAnimation::DeleteWhenStopped); - Q_ASSERT(animate || widget->geometry() == final_geometry); #else //we do it in one shot widget->setGeometry(final_geometry); diff --git a/src/gui/widgets/qwidgetanimator_p.h b/src/gui/widgets/qwidgetanimator_p.h index 64697a9..5a3e39d 100644 --- a/src/gui/widgets/qwidgetanimator_p.h +++ b/src/gui/widgets/qwidgetanimator_p.h @@ -61,6 +61,7 @@ QT_BEGIN_NAMESPACE class QWidget; class QMainWindowLayout; class QPropertyAnimation; +class QRect; class QWidgetAnimator : public QObject { diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index 483589b..7a616aa 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -201,6 +201,7 @@ bool QHttpNetworkReply::isFinished() const QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl) : QHttpNetworkHeaderPrivate(newUrl), state(NothingDoneState), statusCode(100), majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0), + chunkedTransferEncoding(0), currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false), autoDecompress(false), responseData(0), requestIsPrepared(false) { @@ -506,6 +507,9 @@ qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket) state = ReadingDataState; fragment.clear(); // next fragment bodyLength = contentLength(); // cache the length + + // cache isChunked() since it is called often + chunkedTransferEncoding = headerField("transfer-encoding").toLower().contains("chunked"); } return bytes; } @@ -546,7 +550,7 @@ void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header) bool QHttpNetworkReplyPrivate::isChunked() { - return headerField("transfer-encoding").toLower().contains("chunked"); + return chunkedTransferEncoding; } bool QHttpNetworkReplyPrivate::connectionCloseEnabled() diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h index b86cfaa..5eb70ce 100644 --- a/src/network/access/qhttpnetworkreply_p.h +++ b/src/network/access/qhttpnetworkreply_p.h @@ -198,6 +198,7 @@ public: qint64 contentRead; qint64 totalProgress; QByteArray fragment; // used for header, status, chunk header etc, not for reply data + bool chunkedTransferEncoding; qint64 currentChunkSize; qint64 currentChunkRead; QPointer<QHttpNetworkConnection> connection; diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp index de39970..28319bb 100644 --- a/src/network/access/qnetworkreplyimpl.cpp +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -461,8 +461,8 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data) void QNetworkReplyImplPrivate::finished() { Q_Q(QNetworkReplyImpl); - Q_ASSERT_X(state != Finished, "QNetworkReplyImpl", - "Backend called finished/finishedWithError more than once"); + if (state == Finished || state == Aborted) + return; state = Finished; pendingNotifications.clear(); diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp index 1d968c2..608db65 100644 --- a/src/network/kernel/qnetworkproxy.cpp +++ b/src/network/kernel/qnetworkproxy.cpp @@ -366,7 +366,7 @@ static QNetworkProxy::Capabilities defaultCapabilitiesForType(QNetworkProxy::Pro int(QNetworkProxy::HostNameLookupCapability)), }; - if (int(type) < 0 && int(type) > int(QNetworkProxy::FtpCachingProxy)) + if (int(type) < 0 || int(type) > int(QNetworkProxy::FtpCachingProxy)) type = QNetworkProxy::DefaultProxy; return QNetworkProxy::Capabilities(defaults[int(type)]); } diff --git a/src/network/socket/qlocalsocket_win.cpp b/src/network/socket/qlocalsocket_win.cpp index 1a971f0..794b2b7 100644 --- a/src/network/socket/qlocalsocket_win.cpp +++ b/src/network/socket/qlocalsocket_win.cpp @@ -168,7 +168,7 @@ void QLocalSocket::connectToServer(const QString &name, OpenMode openMode) // we have a valid handle d->serverName = name; - if (setSocketDescriptor((quintptr)localSocket), openMode) { + if (setSocketDescriptor((quintptr)localSocket, ConnectedState, openMode)) { d->handle = localSocket; emit connected(); } @@ -299,8 +299,6 @@ void QLocalSocket::abort() DWORD QLocalSocketPrivate::bytesAvailable() { Q_Q(QLocalSocket); - if (q->state() != QLocalSocket::ConnectedState) - return 0; DWORD bytes; if (PeekNamedPipe(handle, NULL, 0, NULL, &bytes, NULL)) { return bytes; @@ -410,7 +408,7 @@ bool QLocalSocket::setSocketDescriptor(quintptr socketDescriptor, d->handle = (int*)socketDescriptor; d->state = socketState; emit stateChanged(d->state); - if (d->state == ConnectedState) { + if (d->state == ConnectedState && openMode.testFlag(QIODevice::ReadOnly)) { d->startAsyncRead(); d->checkReadyRead(); } @@ -471,6 +469,10 @@ bool QLocalSocket::waitForDisconnected(int msecs) Q_D(QLocalSocket); if (state() == UnconnectedState) return false; + if (!openMode().testFlag(QIODevice::ReadOnly)) { + qWarning("QLocalSocket::waitForDisconnected isn't supported for write only pipes."); + return false; + } QIncrementalSleepTimer timer(msecs); forever { d->bytesAvailable(); // to check if PeekNamedPipe fails diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp index 096bf40..4928b67 100644 --- a/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp @@ -81,7 +81,7 @@ template <> inline const bool* ptr<bool>(const bool &) { return 0; } template <typename device, typename T1, typename T2, typename T3> static void rasterFallbackWarn(const char *msg, const char *func, const device *dev, int scale, bool matrixRotShear, bool simplePen, - bool dfbHandledClip, + bool dfbHandledClip, bool unsupportedCompositionMode, const char *nameOne, const T1 &one, const char *nameTwo, const T2 &two, const char *nameThree, const T3 &three) @@ -98,7 +98,8 @@ static void rasterFallbackWarn(const char *msg, const char *func, const device * dbg << "scale" << scale << "matrixRotShear" << matrixRotShear << "simplePen" << simplePen - << "dfbHandledClip" << dfbHandledClip; + << "dfbHandledClip" << dfbHandledClip + << "unsupportedCompositionMode" << unsupportedCompositionMode; const T1 *t1 = ptr(one); const T2 *t2 = ptr(two); @@ -124,6 +125,7 @@ static void rasterFallbackWarn(const char *msg, const char *func, const device * __FUNCTION__, state()->painter->device(), \ d_func()->scale, d_func()->matrixRotShear, \ d_func()->simplePen, d_func()->dfbCanHandleClip(), \ + d_func()->unsupportedCompositionMode, \ #one, one, #two, two, #three, three); \ if (op & (QT_DIRECTFB_DISABLE_RASTERFALLBACKS)) \ return; @@ -138,6 +140,7 @@ static void rasterFallbackWarn(const char *msg, const char *func, const device * __FUNCTION__, state()->painter->device(), \ d_func()->scale, d_func()->matrixRotShear, \ d_func()->simplePen, d_func()->dfbCanHandleClip(), \ + d_func()->unsupportedCompositionMode, \ #one, one, #two, two, #three, three); #else #define RASTERFALLBACK(op, one, two, three) @@ -238,7 +241,7 @@ public: void fillRects(const QRectF *rects, int count); void drawRects(const QRectF *rects, int count); - void drawTiledPixmap(const QRectF &dest, const QPixmap &pixmap); + void drawTiledPixmap(const QRectF &dest, const QPixmap &pixmap, const QPointF &pos); void blit(const QRectF &dest, IDirectFBSurface *surface, const QRectF &src); inline void updateClip(); @@ -281,6 +284,7 @@ private: bool unsupportedCompositionMode; QDirectFBPaintEngine *q; + QRect currentClip; friend class QDirectFBPaintEngine; }; @@ -340,6 +344,7 @@ bool QDirectFBPaintEngine::end() #if (Q_DIRECTFB_VERSION >= 0x010000) d->surface->ReleaseSource(d->surface); #endif + d->currentClip = QRect(); d->surface->SetClip(d->surface, NULL); d->surface = 0; return QRasterPaintEngine::end(); @@ -610,30 +615,26 @@ void QDirectFBPaintEngine::drawPixmap(const QPointF &p, const QPixmap &pm) void QDirectFBPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, - const QPointF &sp) + const QPointF &offset) { Q_D(QDirectFBPaintEngine); d->updateClip(); if (pixmap.pixmapData()->classId() != QPixmapData::DirectFBClass) { - RASTERFALLBACK(DRAW_TILED_PIXMAP, r, pixmap.size(), sp); + RASTERFALLBACK(DRAW_TILED_PIXMAP, r, pixmap.size(), offset); d->lock(); - QRasterPaintEngine::drawTiledPixmap(r, pixmap, sp); - } else if (d->unsupportedCompositionMode || !d->dfbCanHandleClip(r) || d->matrixRotShear || !sp.isNull() + QRasterPaintEngine::drawTiledPixmap(r, pixmap, offset); + } else if (d->unsupportedCompositionMode || !d->dfbCanHandleClip(r) || d->matrixRotShear || d->scale == QDirectFBPaintEnginePrivate::NegativeScale) { - RASTERFALLBACK(DRAW_TILED_PIXMAP, r, pixmap.size(), sp); + RASTERFALLBACK(DRAW_TILED_PIXMAP, r, pixmap.size(), offset); const QImage *img = static_cast<QDirectFBPixmapData*>(pixmap.pixmapData())->buffer(DSLF_READ); d->lock(); QRasterPixmapData *data = new QRasterPixmapData(QPixmapData::PixmapType); data->fromImage(*img, Qt::AutoColor); const QPixmap pix(data); - QRasterPaintEngine::drawTiledPixmap(r, pix, sp); + QRasterPaintEngine::drawTiledPixmap(r, pix, offset); } else { d->unlock(); - QPixmapData *data = pixmap.pixmapData(); - Q_ASSERT(data->classId() == QPixmapData::DirectFBClass); - QDirectFBPixmapData *dfbData = static_cast<QDirectFBPixmapData*>(data); - dfbData->unlockDirectFB(); - d->drawTiledPixmap(r, pixmap); + d->drawTiledPixmap(r, pixmap, offset); } } @@ -730,14 +731,17 @@ void QDirectFBPaintEngine::fillRect(const QRectF &rect, const QBrush &brush) d->surface->FillRectangle(d->surface, r.x(), r.y(), r.width(), r.height()); return; } - case Qt::TexturePattern: - if (state()->brushOrigin == QPointF() && brush.transform().isIdentity()) { - //could handle certain types of brush.transform() E.g. scale - d->unlock(); - d->drawTiledPixmap(rect, brush.texture()); - return; - } - break; + case Qt::TexturePattern: { + if (d->scale == QDirectFBPaintEnginePrivate::NegativeScale) + break; + + const QPixmap texture = brush.texture(); + if (texture.pixmapData()->classId() != QPixmapData::DirectFBClass) + break; + + d->unlock(); + d->drawTiledPixmap(rect, texture, rect.topLeft() - state()->brushOrigin); + return; } default: break; } @@ -1059,6 +1063,8 @@ void QDirectFBPaintEnginePrivate::blit(const QRectF &dest, IDirectFBSurface *s, { const QRect sr = src.toRect(); const QRect dr = transform.mapRect(dest).toRect(); + if (dr.isEmpty()) + return; const DFBRectangle sRect = { sr.x(), sr.y(), sr.width(), sr.height() }; DFBResult result; @@ -1072,55 +1078,105 @@ void QDirectFBPaintEnginePrivate::blit(const QRectF &dest, IDirectFBSurface *s, DirectFBError("QDirectFBPaintEngine::drawPixmap()", result); } -void QDirectFBPaintEnginePrivate::drawTiledPixmap(const QRectF &dest, - const QPixmap &pixmap) +static inline qreal fixCoord(qreal rect_pos, qreal pixmapSize, qreal offset) { + qreal pos = rect_pos - offset; + while (pos > rect_pos) + pos -= pixmapSize; + while (pos + pixmapSize < rect_pos) + pos += pixmapSize; + return pos; +} + +void QDirectFBPaintEnginePrivate::drawTiledPixmap(const QRectF &dest, const QPixmap &pixmap, const QPointF &off) +{ + Q_ASSERT(!dirtyClip); + const QRect destinationRect = transform.mapRect(dest).toRect().normalized(); + QRect newClip = destinationRect; + if (!currentClip.isEmpty()) + newClip &= currentClip; + + if (newClip.isNull()) + return; + + const DFBRegion clip = { + newClip.x(), + newClip.y(), + newClip.x() + newClip.width() - 1, + newClip.y() + newClip.height() - 1 + }; + surface->SetClip(surface, &clip); + + QPointF offset = off; + Q_ASSERT(transform.type() <= QTransform::TxScale); prepareForBlit(pixmap.hasAlphaChannel()); QPixmapData *data = pixmap.pixmapData(); Q_ASSERT(data->classId() == QPixmapData::DirectFBClass); QDirectFBPixmapData *dfbData = static_cast<QDirectFBPixmapData*>(data); - IDirectFBSurface *s = dfbData->directFBSurface(); - const QRect dr = transform.mapRect(dest).toRect(); - DFBResult result = DFB_OK; - - if (scale == NoScale && dr == QRect(0, 0, fbWidth, fbHeight)) { - result = surface->TileBlit(surface, s, 0, 0, 0); - } else if (scale == NoScale) { - const int dx = pixmap.width(); - const int dy = pixmap.height(); - const DFBRectangle rect = { 0, 0, dx, dy }; - QVarLengthArray<DFBRectangle> rects; - QVarLengthArray<DFBPoint> points; - - for (int y = dr.y(); y <= dr.bottom(); y += dy) { - for (int x = dr.x(); x <= dr.right(); x += dx) { - rects.append(rect); - const DFBPoint point = { x, y }; - points.append(point); + dfbData->unlockDirectFB(); + const QSize pixmapSize = dfbData->size(); + IDirectFBSurface *sourceSurface = dfbData->directFBSurface(); + if (transform.isScaling()) { + Q_ASSERT(qMin(transform.m11(), transform.m22()) >= 0); + offset.rx() *= transform.m11(); + offset.ry() *= transform.m22(); + + const QSizeF mappedSize(pixmapSize.width() * transform.m11(), pixmapSize.height() * transform.m22()); + qreal y = ::fixCoord(destinationRect.y(), mappedSize.height(), offset.y()); + const qreal startX = ::fixCoord(destinationRect.x(), mappedSize.width(), offset.x()); + while (y < destinationRect.bottom()) { + qreal x = startX; + while (x < destinationRect.right()) { + const DFBRectangle destination = { qRound(x), qRound(y), mappedSize.width(), mappedSize.height() }; + surface->StretchBlit(surface, sourceSurface, 0, &destination); + x += mappedSize.width(); } + y += mappedSize.height(); } - result = surface->BatchBlit(surface, s, rects.constData(), - points.constData(), points.size()); } else { - const QRect sr = transform.mapRect(QRect(0, 0, pixmap.width(), pixmap.height())); - const int dx = sr.width(); - const int dy = sr.height(); - const DFBRectangle sRect = { 0, 0, dx, dy }; - - for (int y = dr.y(); y <= dr.bottom(); y += dy) { - for (int x = dr.x(); x <= dr.right(); x += dx) { - const DFBRectangle dRect = { x, y, dx, dy }; - result = surface->StretchBlit(surface, s, &sRect, &dRect); - if (result != DFB_OK) { - y = dr.bottom() + 1; - break; - } + qreal y = ::fixCoord(destinationRect.y(), pixmapSize.height(), offset.y()); + const qreal startX = ::fixCoord(destinationRect.x(), pixmapSize.width(), offset.x()); + int horizontal = qMax(1, destinationRect.width() / pixmapSize.width()) + 1; + if (startX != destinationRect.x()) + ++horizontal; + int vertical = qMax(1, destinationRect.height() / pixmapSize.height()) + 1; + if (y != destinationRect.y()) + ++vertical; + + const int maxCount = (vertical * horizontal); + QVarLengthArray<DFBRectangle, 16> sourceRects(maxCount); + QVarLengthArray<DFBPoint, 16> points(maxCount); + + int i = 0; + while (y < destinationRect.bottom()) { + Q_ASSERT(i < maxCount); + qreal x = startX; + while (x < destinationRect.right()) { + points[i].x = qRound(x); + points[i].y = qRound(y); + sourceRects[i].x = 0; + sourceRects[i].y = 0; + sourceRects[i].w = int(pixmapSize.width()); + sourceRects[i].h = int(pixmapSize.height()); + x += pixmapSize.width(); + ++i; } + y += pixmapSize.height(); } + surface->BatchBlit(surface, sourceSurface, sourceRects.constData(), points.constData(), i); } - if (result != DFB_OK) - DirectFBError("QDirectFBPaintEngine::drawTiledPixmap()", result); + if (currentClip.isEmpty()) { + surface->SetClip(surface, 0); + } else { + const DFBRegion clip = { + currentClip.x(), + currentClip.y(), + currentClip.x() + currentClip.width(), + currentClip.y() + currentClip.height() + }; + surface->SetClip(surface, &clip); + } } void QDirectFBPaintEnginePrivate::updateClip() @@ -1128,6 +1184,7 @@ void QDirectFBPaintEnginePrivate::updateClip() if (!dirtyClip) return; + currentClip = QRect(); const QClipData *clipData = clip(); if (!clipData || !clipData->enabled) { surface->SetClip(surface, NULL); @@ -1140,6 +1197,8 @@ void QDirectFBPaintEnginePrivate::updateClip() clipData->clipRect.y() + clipData->clipRect.height() }; surface->SetClip(surface, &r); + currentClip = clipData->clipRect.normalized(); + // ### is this guaranteed to always be normalized? dfbHandledClip = true; } else if (clipData->hasRegionClip && ignoreSystemClip && clipData->clipRegion == systemClip) { dfbHandledClip = true; diff --git a/src/plugins/kbddrivers/sl5000/main.cpp b/src/plugins/kbddrivers/sl5000/main.cpp index 4d61266..cc68747 100644 --- a/src/plugins/kbddrivers/sl5000/main.cpp +++ b/src/plugins/kbddrivers/sl5000/main.cpp @@ -66,10 +66,9 @@ QStringList QSL5000KbdDriver::keys() const QWSKeyboardHandler* QSL5000KbdDriver::create(const QString &driver, const QString &device) { - Q_UNUSED(device); if (driver.compare(QLatin1String("SL5000"), Qt::CaseInsensitive)) return 0; - return new QWSSL5000KeyboardHandler(driver); + return new QWSSL5000KeyboardHandler(device); } Q_EXPORT_PLUGIN2(qwssl5000kbddriver, QSL5000KbdDriver) diff --git a/src/plugins/kbddrivers/vr41xx/main.cpp b/src/plugins/kbddrivers/vr41xx/main.cpp index 2cba1f7..c9ba4d7 100644 --- a/src/plugins/kbddrivers/vr41xx/main.cpp +++ b/src/plugins/kbddrivers/vr41xx/main.cpp @@ -66,10 +66,9 @@ QStringList QVr41xxKbdDriver::keys() const QWSKeyboardHandler* QVr41xxKbdDriver::create(const QString &driver, const QString &device) { - Q_UNUSED(device); if (driver.compare(QLatin1String("VR41xx"), Qt::CaseInsensitive)) return 0; - return new QWSVr41xxKeyboardHandler(driver); + return new QWSVr41xxKeyboardHandler(device); } Q_EXPORT_PLUGIN2(qwsvr41xxkbddriver, QVr41xxKbdDriver) diff --git a/src/plugins/kbddrivers/yopy/main.cpp b/src/plugins/kbddrivers/yopy/main.cpp index bfddabe..7079d88 100644 --- a/src/plugins/kbddrivers/yopy/main.cpp +++ b/src/plugins/kbddrivers/yopy/main.cpp @@ -66,10 +66,9 @@ QStringList QYopyKbdDriver::keys() const QWSKeyboardHandler* QYopyKbdDriver::create(const QString &driver, const QString &device) { - Q_UNUSED(device); if (driver.compare(QLatin1String("Yopy"), Qt::CaseInsensitive)) return 0; - return new QWSYopyKeyboardHandler(driver); + return new QWSYopyKeyboardHandler(device); } Q_EXPORT_PLUGIN2(qwsyopykbddriver, QYopyKbdDriver) diff --git a/src/sql/drivers/oci/qsql_oci.cpp b/src/sql/drivers/oci/qsql_oci.cpp index 979eeec..617f116 100644 --- a/src/sql/drivers/oci/qsql_oci.cpp +++ b/src/sql/drivers/oci/qsql_oci.cpp @@ -609,7 +609,7 @@ static QSqlField qFromOraInf(const OraFieldInfo &ofi) QSqlField f(ofi.name, ofi.type); f.setRequired(ofi.oraIsNull == 0); - if (ofi.type == QVariant::String) + if (ofi.type == QVariant::String && ofi.oraType != SQLT_NUM && ofi.oraType != SQLT_VNU) f.setLength(ofi.oraFieldLength); else f.setLength(ofi.oraPrecision == 0 ? 38 : int(ofi.oraPrecision)); diff --git a/src/sql/drivers/odbc/qsql_odbc.cpp b/src/sql/drivers/odbc/qsql_odbc.cpp index 50defdf..fa9031a 100644 --- a/src/sql/drivers/odbc/qsql_odbc.cpp +++ b/src/sql/drivers/odbc/qsql_odbc.cpp @@ -89,7 +89,8 @@ public: enum DefaultCase{Lower, Mixed, Upper, Sensitive}; QODBCDriverPrivate() : hEnv(0), hDbc(0), useSchema(false), disconnectCount(0), isMySqlServer(false), - isMSSqlServer(false), hasSQLFetchScroll(true), hasMultiResultSets(false) + isMSSqlServer(false), hasSQLFetchScroll(true), hasMultiResultSets(false), + isQuoteInitialized(false), quote(QLatin1Char('"')) { unicode = false; } @@ -116,7 +117,10 @@ public: QString &schema, QString &table); DefaultCase defaultCase() const; QString adjustCase(const QString&) const; - QChar quoteChar() const; + QChar quoteChar(); +private: + bool isQuoteInitialized; + QChar quote; }; class QODBCPrivate @@ -566,10 +570,8 @@ static int qGetODBCVersion(const QString &connOpts) return SQL_OV_ODBC2; } -QChar QODBCDriverPrivate::quoteChar() const +QChar QODBCDriverPrivate::quoteChar() { - static bool isQuoteInitialized = false; - static QChar quote = QChar::fromLatin1('"'); if (!isQuoteInitialized) { char driverResponse[4]; SQLSMALLINT length; @@ -579,9 +581,9 @@ QChar QODBCDriverPrivate::quoteChar() const sizeof(driverResponse), &length); if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) { - quote = QChar::fromLatin1(driverResponse[0]); + quote = QLatin1Char(driverResponse[0]); } else { - quote = QChar::fromLatin1('"'); + quote = QLatin1Char('"'); } isQuoteInitialized = true; } diff --git a/src/sql/drivers/psql/qsql_psql.cpp b/src/sql/drivers/psql/qsql_psql.cpp index 0c92013..c61c526 100644 --- a/src/sql/drivers/psql/qsql_psql.cpp +++ b/src/sql/drivers/psql/qsql_psql.cpp @@ -54,11 +54,33 @@ #include <qstringlist.h> #include <qmutex.h> + #include <libpq-fe.h> #include <pg_config.h> #include <stdlib.h> #include <math.h> +// below code taken from an example at http://www.gnu.org/software/hello/manual/autoconf/Function-Portability.html +#ifndef isnan + # define isnan(x) \ + (sizeof (x) == sizeof (long double) ? isnan_ld (x) \ + : sizeof (x) == sizeof (double) ? isnan_d (x) \ + : isnan_f (x)) + static inline int isnan_f (float x) { return x != x; } + static inline int isnan_d (double x) { return x != x; } + static inline int isnan_ld (long double x) { return x != x; } +#endif + +#ifndef isinf + # define isinf(x) \ + (sizeof (x) == sizeof (long double) ? isinf_ld (x) \ + : sizeof (x) == sizeof (double) ? isinf_d (x) \ + : isinf_f (x)) + static inline int isinf_f (float x) { return isnan (x - x); } + static inline int isinf_d (double x) { return isnan (x - x); } + static inline int isinf_ld (long double x) { return isnan (x - x); } +#endif + // workaround for postgres defining their OIDs in a private header file #define QBOOLOID 16 @@ -601,10 +623,9 @@ static QPSQLDriver::Protocol getPSQLVersion(PGconn* connection) { QPSQLDriver::Protocol serverVersion = QPSQLDriver::Version6; PGresult* result = PQexec(connection, "select version()"); - int status = PQresultStatus(result); + int status = PQresultStatus(result); if (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK) { QString val = QString::fromAscii(PQgetvalue(result, 0, 0)); - PQclear(result); QRegExp rx(QLatin1String("(\\d+)\\.(\\d+)")); rx.setMinimal(true); // enforce non-greedy RegExp if (rx.indexIn(val) != -1) { @@ -645,6 +666,7 @@ static QPSQLDriver::Protocol getPSQLVersion(PGconn* connection) } } } + PQclear(result); if (serverVersion < QPSQLDriver::Version71) qWarning("This version of PostgreSQL is not supported and may not work."); @@ -1161,6 +1183,21 @@ QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const qPQfreemem(data); break; } + case QVariant::Double: { + double val = field.value().toDouble(); + if (isnan(val)) + r = QLatin1String("'NaN'"); + else { + int res = isinf(val); + if (res == 1) + r = QLatin1String("'Infinity'"); + else if (res == -1) + r = QLatin1String("'-Infinity'"); + else + r = QSqlDriver::formatValue(field, trimStrings); + } + break; + } default: r = QSqlDriver::formatValue(field, trimStrings); break; @@ -1265,15 +1302,15 @@ QStringList QPSQLDriver::subscribedToNotificationsImplementation() const void QPSQLDriver::_q_handleNotification(int) { PQconsumeInput(d->connection); - PGnotify *notify = PQnotifies(d->connection); - if (notify) { - QString name(QLatin1String(notify->relname)); + PGnotify *notify = 0; + while((notify = PQnotifies(d->connection)) != 0) { + QString name(QLatin1String(notify->relname)); if (d->seid.contains(name)) emit notification(name); else qWarning("QPSQLDriver: received notification for '%s' which isn't subscribed to.", - qPrintable(name)); + qPrintable(name)); qPQfreemem(notify); } diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index 156af26..4315a8c 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -205,7 +205,7 @@ bool QSqlTableModelPrivate::exec(const QString &stmt, bool prepStatement, editQuery.addBindValue(rec.value(i)); } for (i = 0; i < whereValues.count(); ++i) { - if (whereValues.isGenerated(i)) + if (whereValues.isGenerated(i) && !whereValues.isNull(i)) editQuery.addBindValue(whereValues.value(i)); } @@ -538,6 +538,7 @@ bool QSqlTableModel::setData(const QModelIndex &index, const QVariant &value, in isOk = updateRowInTable(index.row(), d->editBuffer); if (isOk) select(); + emit dataChanged(index, index); break; } case OnRowChange: if (index.row() == d->insertIndex) { diff --git a/src/svg/qsvghandler.cpp b/src/svg/qsvghandler.cpp index 5950fac..5f9d1dd 100644 --- a/src/svg/qsvghandler.cpp +++ b/src/svg/qsvghandler.cpp @@ -68,6 +68,7 @@ QT_BEGIN_NAMESPACE + double qstrtod(const char *s00, char const **se, bool *ok); static bool parsePathDataFast(const QStringRef &data, QPainterPath &path); @@ -320,6 +321,7 @@ static qreal toDouble(const QChar *&str) ++str; } } + temp[pos] = '\0'; qreal val; @@ -365,16 +367,24 @@ static qreal toDouble(const QChar *&str) return val; } -static qreal toDouble(const QString &str) +static qreal toDouble(const QString &str, bool *ok = NULL) { const QChar *c = str.constData(); - return toDouble(c); + qreal res = toDouble(c); + if (ok) { + *ok = ((*c) == QLatin1Char('\0')); + } + return res; } -static qreal toDouble(const QStringRef &str) +static qreal toDouble(const QStringRef &str, bool *ok = NULL) { const QChar *c = str.constData(); - return toDouble(c); + qreal res = toDouble(c); + if (ok) { + *ok = (c == (str.constData() + str.length())); + } + return res; } static QVector<qreal> parseNumbersList(const QChar *&str) @@ -497,14 +507,17 @@ static bool constructColor(const QString &colorStr, const QString &opacity, if (!resolveColor(colorStr, color, handler)) return false; if (!opacity.isEmpty()) { - qreal op = qMin(qreal(1.0), qMax(qreal(0.0), toDouble(opacity))); + bool ok = true; + qreal op = qMin(qreal(1.0), qMax(qreal(0.0), toDouble(opacity, &ok))); + if (!ok) + op = 1.0; color.setAlphaF(op); } return true; } static qreal parseLength(const QString &str, QSvgHandler::LengthType &type, - QSvgHandler *handler) + QSvgHandler *handler, bool *ok = NULL) { QString numStr = str.trimmed(); @@ -533,15 +546,15 @@ static qreal parseLength(const QString &str, QSvgHandler::LengthType &type, type = handler->defaultCoordinateSystem(); //type = QSvgHandler::LT_OTHER; } - qreal len = toDouble(numStr); + qreal len = toDouble(numStr, ok); //qDebug()<<"len is "<<len<<", from '"<<numStr << "'"; return len; } -static inline qreal convertToNumber(const QString &str, QSvgHandler *handler) +static inline qreal convertToNumber(const QString &str, QSvgHandler *handler, bool *ok = NULL) { QSvgHandler::LengthType type; - qreal num = parseLength(str, type, handler); + qreal num = parseLength(str, type, handler, ok); if (type == QSvgHandler::LT_PERCENT) { num = num/100.0; } @@ -631,15 +644,32 @@ static void parseBrush(QSvgNode *node, QString opacity = attributes.value(QLatin1String("fill-opacity")).toString(); QString myId = someId(attributes); - value = value.trimmed(); - fillRule = fillRule.trimmed(); - if (!value.isEmpty() || !fillRule.isEmpty()) { - Qt::FillRule f = Qt::WindingFill; + QSvgFillStyle *inherited = + static_cast<QSvgFillStyle*>(node->parent()->styleProperty( + QSvgStyleProperty::FILL)); + QSvgFillStyle *prop = new QSvgFillStyle(QColor(Qt::black)); + + //fill-rule attribute handling + Qt::FillRule f = Qt::WindingFill; + if (!fillRule.isEmpty() && fillRule != QLatin1String("inherit")) { if (fillRule == QLatin1String("evenodd")) f = Qt::OddEvenFill; + } else if (inherited) { + f = inherited->fillRule(); + } + + //fill-opacity atttribute handling + qreal fillOpacity = 1.0; + if (!opacity.isEmpty() && opacity != QLatin1String("inherit")) { + fillOpacity = qMin(qreal(1.0), qMax(qreal(0.0), toDouble(opacity))); + } else if (inherited) { + fillOpacity = inherited->fillOpacity(); + } + + //fill attribute handling + if ((!value.isEmpty()) && (value != QLatin1String("inherit")) ) { if (value.startsWith(QLatin1String("url"))) { value = value.remove(0, 3); - QSvgFillStyle *prop = new QSvgFillStyle(0); QSvgStyleProperty *style = styleFromUrl(node, value); if (style) { prop->setFillStyle(style); @@ -648,30 +678,26 @@ static void parseBrush(QSvgNode *node, prop->setGradientId(id); prop->setGradientResolved(false); } - if (!opacity.isEmpty()) { - qreal clampedOpacity = qMin(qreal(1.0), qMax(qreal(0.0), toDouble(opacity))); - prop->setFillOpacity(clampedOpacity); - } - if (!fillRule.isEmpty()) - prop->setFillRule(f); - node->appendStyleProperty(prop,myId); } else if (value != QLatin1String("none")) { QColor color; - if (constructColor(value, opacity, color, handler)) { - QSvgFillStyle *prop = new QSvgFillStyle(QBrush(color)); - if (!fillRule.isEmpty()) - prop->setFillRule(f); - node->appendStyleProperty(prop, myId); - } + if (resolveColor(value, color, handler)) + prop->setBrush(QBrush(color)); } else { - QSvgFillStyle *prop = new QSvgFillStyle(QBrush(Qt::NoBrush)); - if (!fillRule.isEmpty()) - prop->setFillRule(f); - node->appendStyleProperty(prop, myId); + prop->setBrush(QBrush(Qt::NoBrush)); + } + } else if (inherited) { + if (inherited->style()) { + prop->setFillStyle(inherited->style()); + } else { + prop->setBrush(inherited->qbrush()); } } + prop->setFillOpacity(fillOpacity); + prop->setFillRule(f); + node->appendStyleProperty(prop,myId); } + static void parseQPen(QPen &pen, QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler) @@ -3006,7 +3032,11 @@ static bool parseStopNode(QSvgStyleProperty *parent, QString colorStr = attrs.value(QString(), QLatin1String("stop-color")).toString(); QString opacityStr = attrs.value(QString(), QLatin1String("stop-opacity")).toString(); QColor color; - qreal offset = convertToNumber(offsetStr, handler); + + bool ok = true; + qreal offset = convertToNumber(offsetStr, handler, &ok); + if (!ok) + offset = 0.0; if (colorStr.isEmpty()) { colorStr = QLatin1String("#000000"); } @@ -3095,12 +3125,16 @@ static QSvgNode *createSvgNode(QSvgNode *parent, QStringList lst = viewBoxStr.split(QLatin1Char(' '), QString::SkipEmptyParts); if (lst.count() != 4) lst = viewBoxStr.split(QLatin1Char(','), QString::SkipEmptyParts); + int count = lst.count(); + while (count < 4) { + lst.append(QLatin1String("")); + count++; + } QString xStr = lst.at(0).trimmed(); QString yStr = lst.at(1).trimmed(); QString widthStr = lst.at(2).trimmed(); QString heightStr = lst.at(3).trimmed(); - QSvgHandler::LengthType lt; qreal x = parseLength(xStr, lt, handler); qreal y = parseLength(yStr, lt, handler); @@ -3108,15 +3142,14 @@ static QSvgNode *createSvgNode(QSvgNode *parent, qreal h = parseLength(heightStr, lt, handler); node->setViewBox(QRectF(x, y, w, h)); - } else if (width && height){ + + } else if (width && height) { if (type == QSvgHandler::LT_PT) { width = convertToPixels(width, false, type); height = convertToPixels(height, false, type); } - node->setViewBox(QRectF(0, 0, width, height)); } - handler->setDefaultCoordinateSystem(QSvgHandler::LT_PX); return node; diff --git a/src/svg/qsvgstyle.cpp b/src/svg/qsvgstyle.cpp index 556201b..4c8247b 100644 --- a/src/svg/qsvgstyle.cpp +++ b/src/svg/qsvgstyle.cpp @@ -81,12 +81,12 @@ void QSvgQualityStyle::revert(QPainter *, QSvgExtraStates &) } QSvgFillStyle::QSvgFillStyle(const QBrush &brush) - : m_fill(brush), m_style(0), m_fillRuleSet(false), m_fillOpacitySet(false), m_gradientResolved (true) + : m_fill(brush), m_style(0), m_fillRuleSet(false), m_fillOpacitySet(false), m_fillRule(Qt::WindingFill), m_fillOpacity(1.0), m_gradientResolved (true) { } QSvgFillStyle::QSvgFillStyle(QSvgStyleProperty *style) - : m_style(style), m_fillRuleSet(false), m_fillOpacitySet(false), m_gradientResolved (true) + : m_style(style), m_fillRuleSet(false), m_fillOpacitySet(false), m_fillRule(Qt::WindingFill), m_fillOpacity(1.0), m_gradientResolved (true) { } @@ -102,6 +102,16 @@ void QSvgFillStyle::setFillOpacity(qreal opacity) m_fillOpacity = opacity; } +void QSvgFillStyle::setFillStyle(QSvgStyleProperty* style) +{ + m_style = style; +} + +void QSvgFillStyle::setBrush(QBrush brush) +{ + m_fill = brush; +} + static void recursivelySetFill(QSvgNode *node, Qt::FillRule f) { if (node->type() == QSvgNode::PATH) { diff --git a/src/svg/qsvgstyle_p.h b/src/svg/qsvgstyle_p.h index f1d0811..ac5e109 100644 --- a/src/svg/qsvgstyle_p.h +++ b/src/svg/qsvgstyle_p.h @@ -224,12 +224,29 @@ public: void setFillRule(Qt::FillRule f); void setFillOpacity(qreal opacity); + void setFillStyle(QSvgStyleProperty* style); + void setBrush(QBrush brush); const QBrush & qbrush() const { return m_fill; } + qreal fillOpacity() const + { + return m_fillOpacity; + } + + Qt::FillRule fillRule() const + { + return m_fillRule; + } + + QSvgStyleProperty* style() const + { + return m_style; + } + void setGradientId(const QString &Id) { m_gradientId = Id; @@ -240,7 +257,6 @@ public: return m_gradientId; } - void setGradientResolved(bool resolved) { m_gradientResolved = resolved; @@ -251,16 +267,6 @@ public: return m_gradientResolved; } - void setFillStyle(QSvgStyleProperty* style) - { - m_style = style; - } - - void setBrush(QBrush brush) - { - m_fill = brush; - } - private: // fill v v 'inherit' | <Paint.datatype> // fill-opacity v v 'inherit' | <OpacityValue.datatype> |