diff options
author | Kent Hansen <khansen@trolltech.com> | 2009-08-20 18:45:43 (GMT) |
---|---|---|
committer | Kent Hansen <khansen@trolltech.com> | 2009-08-20 18:45:43 (GMT) |
commit | d63d0c10c0eeef0d2f93f3d0b197e09399087b00 (patch) | |
tree | 746a1856103d6367f9821a81b7eee25aeb10a7ed | |
parent | 435c87186ff54ebfd481e41e07b9a136fbc87a20 (diff) | |
parent | e90a5e819478b656963de0492a1b234c8863a12b (diff) | |
download | Qt-d63d0c10c0eeef0d2f93f3d0b197e09399087b00.zip Qt-d63d0c10c0eeef0d2f93f3d0b197e09399087b00.tar.gz Qt-d63d0c10c0eeef0d2f93f3d0b197e09399087b00.tar.bz2 |
Merge branch 'master' of git@scm.dev.nokia.troll.no:qt/qt into qtscript-jsc-backend
Conflicts:
src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pri
82 files changed, 2134 insertions, 1393 deletions
diff --git a/demos/browser/networkaccessmanager.cpp b/demos/browser/networkaccessmanager.cpp index 3781652..3ce9c08 100644 --- a/demos/browser/networkaccessmanager.cpp +++ b/demos/browser/networkaccessmanager.cpp @@ -57,16 +57,21 @@ #include <QtNetwork/QAuthenticator> #include <QtNetwork/QNetworkDiskCache> #include <QtNetwork/QNetworkProxy> +#include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkReply> #include <QtNetwork/QSslError> NetworkAccessManager::NetworkAccessManager(QObject *parent) - : QNetworkAccessManager(parent) + : QNetworkAccessManager(parent), + requestFinishedCount(0), requestFinishedFromCacheCount(0), requestFinishedPipelinedCount(0), + requestFinishedSecureCount(0) { connect(this, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); connect(this, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), SLOT(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*))); + connect(this, SIGNAL(finished(QNetworkReply *)), + SLOT(requestFinished(QNetworkReply *))); #ifndef QT_NO_OPENSSL connect(this, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)), SLOT(sslErrors(QNetworkReply*, const QList<QSslError>&))); @@ -79,6 +84,35 @@ NetworkAccessManager::NetworkAccessManager(QObject *parent) setCache(diskCache); } +QNetworkReply* NetworkAccessManager::createRequest(Operation op, const QNetworkRequest & req, QIODevice * outgoingData) +{ + QNetworkRequest request = req; // copy so we can modify + // this is a temporary hack until we properly use the pipelining flags from QtWebkit + // pipeline everything! :) + request.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + return QNetworkAccessManager::createRequest(op, request, outgoingData); +} + +void NetworkAccessManager::requestFinished(QNetworkReply *reply) +{ + requestFinishedCount++; + + if (reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool() == true) + requestFinishedFromCacheCount++; + + if (reply->attribute(QNetworkRequest::HttpPipeliningWasUsedAttribute).toBool() == true) + requestFinishedPipelinedCount++; + + if (reply->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool() == true) + requestFinishedSecureCount++; + + double pctCached = (double(requestFinishedFromCacheCount) * 100.0/ double(requestFinishedCount)); + double pctPipelined = (double(requestFinishedPipelinedCount) * 100.0/ double(requestFinishedCount)); + double pctSecure = (double(requestFinishedSecureCount) * 100.0/ double(requestFinishedCount)); + qDebug("%lli requests [%3.2f%% from cache] [%3.2f%% pipelined] [%3.2f%% SSL/TLS]", requestFinishedCount, pctCached, pctPipelined, pctSecure); + +} + void NetworkAccessManager::loadSettings() { QSettings settings; diff --git a/demos/browser/networkaccessmanager.h b/demos/browser/networkaccessmanager.h index 381cb50..189d6a0 100644 --- a/demos/browser/networkaccessmanager.h +++ b/demos/browser/networkaccessmanager.h @@ -43,6 +43,7 @@ #define NETWORKACCESSMANAGER_H #include <QtNetwork/QNetworkAccessManager> +#include <QtNetwork/QNetworkRequest> class NetworkAccessManager : public QNetworkAccessManager { @@ -51,11 +52,18 @@ class NetworkAccessManager : public QNetworkAccessManager public: NetworkAccessManager(QObject *parent = 0); + virtual QNetworkReply* createRequest ( Operation op, const QNetworkRequest & req, QIODevice * outgoingData = 0 ); + private: QList<QString> sslTrustedHostList; + qint64 requestFinishedCount; + qint64 requestFinishedFromCacheCount; + qint64 requestFinishedPipelinedCount; + qint64 requestFinishedSecureCount; public slots: void loadSettings(); + void requestFinished(QNetworkReply *reply); private slots: void authenticationRequired(QNetworkReply *reply, QAuthenticator *auth); diff --git a/examples/painting/svgviewer/files/bubbles.svg b/examples/painting/svgviewer/files/bubbles.svg index 65867da..9fae8cc 100644 --- a/examples/painting/svgviewer/files/bubbles.svg +++ b/examples/painting/svgviewer/files/bubbles.svg @@ -91,56 +91,56 @@ <g transform="translate(200,700)"> <use xlink:href="#bubble" fill="url(#blueBubble)" /> - <animateTransform attributeName="transform" type="translate" + <animateTransform attributeName="transform" type="translate" additive="sum" values="0,0; 0,-800" begin="1s" dur="10s" fill="freeze" repeatCount="indefinite" /> </g> <g transform="translate(315,700)"> <g transform="scale(0.5,0.5)"> <use xlink:href="#bubble" fill="url(#redBubble)" /> </g> - <animateTransform attributeName="transform" type="translate" + <animateTransform attributeName="transform" type="translate" additive="sum" values="0,0; 0,-800" begin="3s" dur="7s" fill="freeze" repeatCount="indefinite" /> </g> <g transform="translate(80,700)"> <g transform="scale(0.65,0.65)"> <use xlink:href="#bubble" fill="url(#greenBubble)" /> </g> - <animateTransform attributeName="transform" type="translate" + <animateTransform attributeName="transform" type="translate" additive="sum" values="0,0; 0,-800" begin="5s" dur="9s" fill="freeze" repeatCount="indefinite" /> </g> <g transform="translate(255,700)"> <g transform="scale(0.3,0.3)"> <use xlink:href="#bubble" fill="url(#yellowBubble)" /> </g> - <animateTransform attributeName="transform" type="translate" + <animateTransform attributeName="transform" type="translate" additive="sum" values="0,0; 0,-800" begin="2s" dur="6s" fill="freeze" repeatCount="indefinite" /> </g> <g transform="translate(565,700)"> <g transform="scale(0.4,0.4)"> <use xlink:href="#bubble" fill="url(#blueBubble)" /> </g> - <animateTransform attributeName="transform" type="translate" + <animateTransform attributeName="transform" type="translate" additive="sum" values="0,0; 0,-800" begin="4s" dur="8s" fill="freeze" repeatCount="indefinite" /> </g> <g transform="translate(715,700)"> <g transform="scale(0.6,0.6)"> <use xlink:href="#bubble" fill="url(#redBubble)" /> </g> - <animateTransform attributeName="transform" type="translate" + <animateTransform attributeName="transform" type="translate" additive="sum" values="0,0; 0,-800" begin="1s" dur="4s" fill="freeze" repeatCount="indefinite" /> </g> <g transform="translate(645,700)"> <g transform="scale(0.375,0.375)"> <use xlink:href="#bubble" fill="url(#greenBubble)" /> </g> - <animateTransform attributeName="transform" type="translate" + <animateTransform attributeName="transform" type="translate" additive="sum" values="0,0; 0,-800" begin="0s" dur="11s" fill="freeze" repeatCount="indefinite" /> </g> <g transform="translate(555,700)"> <g transform="scale(0.9,0.9)"> <use xlink:href="#bubble" fill="url(#yellowBubble)" /> </g> - <animateTransform attributeName="transform" type="translate" + <animateTransform attributeName="transform" type="translate" additive="sum" values="0,0; 0,-800" begin="3s" dur="7.5s" fill="freeze" repeatCount="indefinite" /> </g> @@ -148,28 +148,28 @@ <g transform="scale(0.5,0.5)"> <use xlink:href="#bubble" fill="url(#blueBubble)" /> </g> - <animateTransform attributeName="transform" type="translate" + <animateTransform attributeName="transform" type="translate" additive="sum" values="0,0; 0,-800" begin="3s" dur="6s" fill="freeze" repeatCount="indefinite" /> </g> <g transform="translate(215,700)"> <g transform="scale(0.45,0.45)"> <use xlink:href="#bubble" fill="url(#redBubble)" /> </g> - <animateTransform attributeName="transform" type="translate" + <animateTransform attributeName="transform" type="translate" additive="sum" values="0,0; 0,-800" begin="5.5s" dur="7s" fill="freeze" repeatCount="indefinite" /> </g> <g transform="translate(420,700)"> <g transform="scale(0.75,0.75)"> <use xlink:href="#bubble" fill="url(#greenBubble)" /> </g> - <animateTransform attributeName="transform" type="translate" + <animateTransform attributeName="transform" type="translate" additive="sum" values="0,0; 0,-800" begin="1s" dur="9s" fill="freeze" repeatCount="indefinite" /> </g> <g transform="translate(815,700)"> <g transform="scale(0.6,0.6)"> <use xlink:href="#bubble" fill="url(#yellowBubble)" /> </g> - <animateTransform attributeName="transform" type="translate" + <animateTransform attributeName="transform" type="translate" additive="sum" values="0,0; 0,-800" begin="2s" dur="9.5s" fill="freeze" repeatCount="indefinite" /> </g> @@ -186,7 +186,7 @@ <circle fill="#a6ce39" cx="0" cy="0" r="33" /> <path fill="black" d="M 37,50 L 50,37 L 12,-1 L 22,-11 L 10,-24 L -24,10 L -11,22 L -1,12 Z" /> - <animateTransform attributeName="transform" type="rotate" values="0; 360" + <animateTransform attributeName="transform" type="rotate" additive="sum" values="0; 360" begin="0s" dur="10s" fill="freeze" repeatCount="indefinite" /> </g> <g transform="translate(200,375)"> diff --git a/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pri b/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pri index 154e40b..85645be 100644 --- a/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pri +++ b/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pri @@ -128,7 +128,7 @@ SOURCES += \ yarr/RegexJIT.cpp \ interpreter/RegisterFile.cpp -win*: SOURCES += jit/ExecutableAllocatorWin.cpp +win32-*|wince*: SOURCES += jit/ExecutableAllocatorWin.cpp else: SOURCES += jit/ExecutableAllocatorPosix.cpp # AllInOneFile.cpp helps gcc analize and optimize code diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp index f42a2ff..20e7845 100644 --- a/src/corelib/global/qlibraryinfo.cpp +++ b/src/corelib/global/qlibraryinfo.cpp @@ -487,7 +487,7 @@ QT_END_NAMESPACE extern const char qt_core_interpreter[] __attribute__((section(".interp"))) = ELF_INTERPRETER; -extern "C" +extern "C" void qt_core_boilerplate(); void qt_core_boilerplate() { printf("This is the QtCore library version " QT_VERSION_STR "\n" diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index ca178ce..fd1e367 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -2445,7 +2445,7 @@ QDebug operator<<(QDebug debug, QDir::Filters filters) return debug; } -QDebug operator<<(QDebug debug, QDir::SortFlags sorting) +static QDebug operator<<(QDebug debug, QDir::SortFlags sorting) { if (sorting == QDir::NoSort) { debug << "QDir::SortFlags(NoSort)"; diff --git a/src/corelib/kernel/qabstracteventdispatcher_p.h b/src/corelib/kernel/qabstracteventdispatcher_p.h index 86c9d79..dc69265 100644 --- a/src/corelib/kernel/qabstracteventdispatcher_p.h +++ b/src/corelib/kernel/qabstracteventdispatcher_p.h @@ -58,6 +58,8 @@ QT_BEGIN_NAMESPACE +Q_CORE_EXPORT uint qGlobalPostedEventsCount(); + class Q_CORE_EXPORT QAbstractEventDispatcherPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QAbstractEventDispatcher) diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 022ae13..e874c90 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -3219,12 +3219,12 @@ static void blocking_activate(QObject *sender, int signal, QObjectPrivate::Conne /*!\internal \obsolete. - Used to be called from QMetaObject::activate(QObject *, QMetaObject *, int, int, void **) + Used to be called from QMetaObject::activate(QObject *, QMetaObject *, int, int, void **) before Qt 4.6 */ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv) { - int offset = sender->metaObject()->methodOffset(); - activate(sender, from_signal_index - offset, to_signal_index - offset, argv); + Q_UNUSED(to_signal_index); + activate(sender, from_signal_index, argv); } /*!\internal @@ -3371,7 +3371,7 @@ void QMetaObject::activate(QObject *sender, int signal_index, void **argv) const QMetaObject *mo = sender->metaObject(); while (mo && mo->methodOffset() > signal_index) mo = mo->superClass(); - activate(sender, signal_index - mo->methodOffset(), argv); + activate(sender, mo, signal_index - mo->methodOffset(), argv); } /*!\internal diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 54d6073..533ccd7 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -2467,7 +2467,7 @@ double QVariant::toDouble(bool *ok) const */ float QVariant::toFloat(bool *ok) const { - return qNumVariantToHelper<float>(d, handler, ok, d.data.d); + return qNumVariantToHelper<float>(d, handler, ok, d.data.f); } /*! @@ -2484,7 +2484,7 @@ float QVariant::toFloat(bool *ok) const */ qreal QVariant::toReal(bool *ok) const { - return qNumVariantToHelper<qreal>(d, handler, ok, d.data.d); + return qNumVariantToHelper<qreal>(d, handler, ok, d.data.real); } /*! diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 79bd5b8..d6a704e 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -358,6 +358,7 @@ class Q_CORE_EXPORT QVariant bool b; double d; float f; + qreal real; qlonglong ll; qulonglong ull; QObject *o; diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h index 993c43b..c36899d 100644 --- a/src/corelib/kernel/qvariant_p.h +++ b/src/corelib/kernel/qvariant_p.h @@ -57,6 +57,7 @@ // to a pointer of the input type #include <QtCore/qglobal.h> +#include <QtCore/qvariant.h> QT_BEGIN_NAMESPACE @@ -145,6 +146,8 @@ inline void v_clear(QVariant::Private *d, T* = 0) } +Q_CORE_EXPORT const QVariant::Handler *qcoreVariantHandler(); + QT_END_NAMESPACE #endif // QVARIANT_P_H diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h index 2853b1a..defa7af 100644 --- a/src/corelib/statemachine/qstatemachine_p.h +++ b/src/corelib/statemachine/qstatemachine_p.h @@ -217,6 +217,8 @@ public: static const Handler *handler; }; +Q_CORE_EXPORT const QStateMachinePrivate::Handler *qcoreStateMachineHandler(); + QT_END_NAMESPACE #endif diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index 635c799..e952ea2 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -310,6 +310,7 @@ public: // stl compatibility typedef const char & const_reference; typedef char & reference; + typedef char value_type; void push_back(char c); void push_back(const char *c); void push_back(const QByteArray &a); diff --git a/src/corelib/tools/qregexp.cpp b/src/corelib/tools/qregexp.cpp index 0e2f8f2..f54a938 100644 --- a/src/corelib/tools/qregexp.cpp +++ b/src/corelib/tools/qregexp.cpp @@ -832,7 +832,7 @@ struct QRegExpEngineKey } }; -bool operator==(const QRegExpEngineKey &key1, const QRegExpEngineKey &key2) +static bool operator==(const QRegExpEngineKey &key1, const QRegExpEngineKey &key2) { return key1.pattern == key2.pattern && key1.patternSyntax == key2.patternSyntax && key1.cs == key2.cs; diff --git a/src/dbus/qdbusmessage.cpp b/src/dbus/qdbusmessage.cpp index a676a01..851eaf0 100644 --- a/src/dbus/qdbusmessage.cpp +++ b/src/dbus/qdbusmessage.cpp @@ -707,7 +707,7 @@ QDBusMessage::MessageType QDBusMessage::type() const \sa QDBusConnection::send() */ #ifndef QT_NO_DEBUG_STREAM -QDebug operator<<(QDebug dbg, QDBusMessage::MessageType t) +static QDebug operator<<(QDebug dbg, QDBusMessage::MessageType t) { switch (t) { diff --git a/src/gui/dialogs/qfileinfogatherer_p.h b/src/gui/dialogs/qfileinfogatherer_p.h index cb980d2..bab4a29 100644 --- a/src/gui/dialogs/qfileinfogatherer_p.h +++ b/src/gui/dialogs/qfileinfogatherer_p.h @@ -184,7 +184,7 @@ private: QMutex mutex; QWaitCondition condition; - bool abort; + volatile bool abort; QStack<QString> path; QStack<QStringList> files; diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 708ba09..1d52182 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -1010,7 +1010,7 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) } // Resolve depth. - resolveDepth(parent ? parent->d_ptr->depth : -1); + invalidateDepthRecursively(); dirtySceneTransform = 1; // Restore the sub focus chain. @@ -4401,14 +4401,42 @@ bool QGraphicsItemPrivate::discardUpdateRequest(bool ignoreClipping, bool ignore /*! \internal +*/ +int QGraphicsItemPrivate::depth() const +{ + if (itemDepth == -1) + const_cast<QGraphicsItemPrivate *>(this)->resolveDepth(); + + return itemDepth; +} - Resolves the stacking depth of this object and all its children. +/*! + \internal */ -void QGraphicsItemPrivate::resolveDepth(int parentDepth) +void QGraphicsItemPrivate::invalidateDepthRecursively() { - depth = parentDepth + 1; + if (itemDepth == -1) + return; + + itemDepth = -1; for (int i = 0; i < children.size(); ++i) - children.at(i)->d_ptr->resolveDepth(depth); + children.at(i)->d_ptr->invalidateDepthRecursively(); +} + +/*! + \internal + + Resolves the stacking depth of this object and all its ancestors. +*/ +void QGraphicsItemPrivate::resolveDepth() +{ + if (!parent) + itemDepth = 0; + else { + if (parent->d_ptr->itemDepth == -1) + parent->d_ptr->resolveDepth(); + itemDepth = parent->d_ptr->itemDepth + 1; + } } /*! @@ -5578,8 +5606,8 @@ QGraphicsItem *QGraphicsItem::commonAncestorItem(const QGraphicsItem *other) con return const_cast<QGraphicsItem *>(this); const QGraphicsItem *thisw = this; const QGraphicsItem *otherw = other; - int thisDepth = d_ptr->depth; - int otherDepth = other->d_ptr->depth; + int thisDepth = d_ptr->depth(); + int otherDepth = other->d_ptr->depth(); while (thisDepth > otherDepth) { thisw = thisw->d_ptr->parent; --thisDepth; diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 43d690f..ae7feb2 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -123,7 +123,7 @@ public: transformData(0), index(-1), siblingIndex(-1), - depth(0), + itemDepth(-1), focusProxy(0), subFocusItem(0), imHints(Qt::ImhNone), @@ -209,7 +209,9 @@ public: void setEnabledHelper(bool newEnabled, bool explicitly, bool update = true); bool discardUpdateRequest(bool ignoreClipping = false, bool ignoreVisibleBit = false, bool ignoreDirtyBit = false, bool ignoreOpacity = false) const; - void resolveDepth(int parentDepth); + int depth() const; + void invalidateDepthRecursively(); + void resolveDepth(); void addChild(QGraphicsItem *child); void removeChild(QGraphicsItem *child); void setParentItemHelper(QGraphicsItem *parent); @@ -419,7 +421,7 @@ public: QTransform sceneTransform; int index; int siblingIndex; - int depth; + int itemDepth; // Lazily calculated when calling depth(). QGraphicsItem *focusProxy; QList<QGraphicsItem **> focusProxyRefs; QGraphicsItem *subFocusItem; diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp index d68183c..a80cdca 100644 --- a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp +++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp @@ -419,8 +419,8 @@ bool QGraphicsSceneBspTreeIndexPrivate::closestItemFirst_withoutCache(const QGra // Find common ancestor, and each item's ancestor closest to the common // ancestor. - int item1Depth = d1->depth; - int item2Depth = d2->depth; + int item1Depth = d1->depth(); + int item2Depth = d2->depth(); const QGraphicsItem *p = item1; const QGraphicsItem *t1 = item1; while (item1Depth > item2Depth && (p = p->d_ptr->parent)) { diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp index ccf8457..19701b4 100644 --- a/src/gui/image/qbmphandler.cpp +++ b/src/gui/image/qbmphandler.cpp @@ -81,14 +81,14 @@ static void swapPixel01(QImage *image) // 1-bpp: swap 0 and 1 pixels const int BMP_FILEHDR_SIZE = 14; // size of BMP_FILEHDR data -QDataStream &operator>>(QDataStream &s, BMP_FILEHDR &bf) +static QDataStream &operator>>(QDataStream &s, BMP_FILEHDR &bf) { // read file header s.readRawData(bf.bfType, 2); s >> bf.bfSize >> bf.bfReserved1 >> bf.bfReserved2 >> bf.bfOffBits; return s; } -QDataStream &operator<<(QDataStream &s, const BMP_FILEHDR &bf) +static QDataStream &operator<<(QDataStream &s, const BMP_FILEHDR &bf) { // write file header s.writeRawData(bf.bfType, 2); s << bf.bfSize << bf.bfReserved1 << bf.bfReserved2 << bf.bfOffBits; @@ -106,7 +106,7 @@ const int BMP_RLE4 = 2; // run-length encoded, 4 const int BMP_BITFIELDS = 3; // RGB values encoded in data as bit-fields -QDataStream &operator>>(QDataStream &s, BMP_INFOHDR &bi) +static QDataStream &operator>>(QDataStream &s, BMP_INFOHDR &bi) { s >> bi.biSize; if (bi.biSize == BMP_WIN || bi.biSize == BMP_OS2) { @@ -128,7 +128,7 @@ QDataStream &operator>>(QDataStream &s, BMP_INFOHDR &bi) return s; } -QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi) +static QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi) { s << bi.biSize; s << bi.biWidth << bi.biHeight; diff --git a/src/gui/image/qiconloader_p.h b/src/gui/image/qiconloader_p.h index b2944ef..5d5c211 100644 --- a/src/gui/image/qiconloader_p.h +++ b/src/gui/image/qiconloader_p.h @@ -85,6 +85,7 @@ struct QIconDirInfo class QIconLoaderEngineEntry { public: + virtual ~QIconLoaderEngineEntry() {} virtual QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) = 0; diff --git a/src/gui/itemviews/qabstractitemview.cpp b/src/gui/itemviews/qabstractitemview.cpp index 9eb71b7..d79a378 100644 --- a/src/gui/itemviews/qabstractitemview.cpp +++ b/src/gui/itemviews/qabstractitemview.cpp @@ -3378,11 +3378,7 @@ QPoint QAbstractItemView::dirtyRegionOffset() const */ void QAbstractItemView::startAutoScroll() { - Q_D(QAbstractItemView); - // ### it would be nice to make this into a style hint one day - int scrollInterval = (verticalScrollMode() == QAbstractItemView::ScrollPerItem) ? 150 : 50; - d->autoScrollTimer.start(scrollInterval, this); - d->autoScrollCount = 0; + d_func()->startAutoScroll(); } /*! @@ -3390,9 +3386,7 @@ void QAbstractItemView::startAutoScroll() */ void QAbstractItemView::stopAutoScroll() { - Q_D(QAbstractItemView); - d->autoScrollTimer.stop(); - d->autoScrollCount = 0; + d_func()->stopAutoScroll(); } /*! diff --git a/src/gui/itemviews/qabstractitemview_p.h b/src/gui/itemviews/qabstractitemview_p.h index 2ba027d..e7991d2 100644 --- a/src/gui/itemviews/qabstractitemview_p.h +++ b/src/gui/itemviews/qabstractitemview_p.h @@ -126,6 +126,15 @@ public: void doDelayedItemsLayout(int delay = 0); void interruptDelayedItemsLayout() const; + void startAutoScroll() + { // ### it would be nice to make this into a style hint one day + int scrollInterval = (verticalScrollMode == QAbstractItemView::ScrollPerItem) ? 150 : 50; + autoScrollTimer.start(scrollInterval, q_func()); + autoScrollCount = 0; + } + void stopAutoScroll() { autoScrollTimer.stop(); autoScrollCount = 0;} + + bool dropOn(QDropEvent *event, int *row, int *col, QModelIndex *index); bool droppingOnItself(QDropEvent *event, const QModelIndex &index); diff --git a/src/gui/itemviews/qlistview.cpp b/src/gui/itemviews/qlistview.cpp index cc9d643..3e9db3b 100644 --- a/src/gui/itemviews/qlistview.cpp +++ b/src/gui/itemviews/qlistview.cpp @@ -458,15 +458,13 @@ QSize QListView::gridSize() const void QListView::setViewMode(ViewMode mode) { Q_D(QListView); - if (d->viewMode == mode) + if (d->commonListView && d->viewMode == mode) return; d->viewMode = mode; + delete d->commonListView; if (mode == ListMode) { - delete d->dynamicListView; - d->dynamicListView = 0; - if (!d->staticListView) - d->staticListView = new QStaticListViewBase(this, d); + d->commonListView = new QListModeViewBase(this, d); if (!(d->modeProperties & QListViewPrivate::Wrap)) d->setWrapping(false); if (!(d->modeProperties & QListViewPrivate::Spacing)) @@ -482,10 +480,7 @@ void QListView::setViewMode(ViewMode mode) if (!(d->modeProperties & QListViewPrivate::SelectionRectVisible)) d->showElasticBand = false; } else { - delete d->staticListView; - d->staticListView = 0; - if (!d->dynamicListView) - d->dynamicListView = new QDynamicListViewBase(this, d); + d->commonListView = new QIconModeViewBase(this, d); if (!(d->modeProperties & QListViewPrivate::Wrap)) d->setWrapping(true); if (!(d->modeProperties & QListViewPrivate::Spacing)) @@ -549,24 +544,12 @@ void QListView::setRowHidden(int row, bool hide) { Q_D(QListView); const bool hidden = d->isHidden(row); - if (d->viewMode == ListMode) { - if (hide && !hidden) - d->hiddenRows.append(d->model->index(row, 0)); - else if (!hide && hidden) - d->hiddenRows.remove(d->hiddenRows.indexOf(d->model->index(row, 0))); - d->doDelayedItemsLayout(); - } else { - if (hide && !hidden) { - d->dynamicListView->removeItem(row); - d->hiddenRows.append(d->model->index(row, 0)); - } else if (!hide && hidden) { - d->hiddenRows.remove(d->hiddenRows.indexOf(d->model->index(row, 0))); - d->dynamicListView->insertItem(row); - } - if (d->resizeMode == Adjust) - d->doDelayedItemsLayout(); - d->viewport->update(); - } + if (hide && !hidden) + d->commonListView->appendHiddenRow(row); + else if (!hide && hidden) + d->commonListView->removeHiddenRow(row); + d->doDelayedItemsLayout(); + d->viewport->update(); } /*! @@ -575,7 +558,7 @@ void QListView::setRowHidden(int row, bool hide) QRect QListView::visualRect(const QModelIndex &index) const { Q_D(const QListView); - return d->mapToViewport(rectForIndex(index), d->viewMode == QListView::ListMode); + return d->mapToViewport(rectForIndex(index)); } /*! @@ -612,69 +595,17 @@ int QListViewPrivate::horizontalScrollToValue(const QModelIndex &index, const QR const bool rightOf = q->isRightToLeft() ? rect.right() > area.right() : (rect.right() > area.right()) && (rect.left() > area.left()); - int horizontalValue = hbar->value(); - - // ScrollPerItem - if (q->horizontalScrollMode() == QAbstractItemView::ScrollPerItem && viewMode == QListView::ListMode) { - const QListViewItem item = indexToListViewItem(index); - const QRect rect = q->visualRect(index); - horizontalValue = staticListView->horizontalPerItemValue(itemIndex(item), - horizontalValue, area.width(), - leftOf, rightOf, isWrapping(), hint, rect.width()); - } else { // ScrollPerPixel - if (q->isRightToLeft()) { - if (hint == QListView::PositionAtCenter) { - horizontalValue += ((area.width() - rect.width()) / 2) - rect.left(); - } else { - if (leftOf) - horizontalValue -= rect.left(); - else if (rightOf) - horizontalValue += qMin(rect.left(), area.width() - rect.right()); - } - } else { - if (hint == QListView::PositionAtCenter) { - horizontalValue += rect.left() - ((area.width()- rect.width()) / 2); - } else { - if (leftOf) - horizontalValue += rect.left(); - else if (rightOf) - horizontalValue += qMin(rect.left(), rect.right() - area.width()); - } - } - } - return horizontalValue; + return commonListView->horizontalScrollToValue(q->visualIndex(index), hint, leftOf, rightOf, area, rect); } int QListViewPrivate::verticalScrollToValue(const QModelIndex &index, const QRect &rect, QListView::ScrollHint hint) const { Q_Q(const QListView); - const QRect area = viewport->rect(); const bool above = (hint == QListView::EnsureVisible && rect.top() < area.top()); const bool below = (hint == QListView::EnsureVisible && rect.bottom() > area.bottom()); - - int verticalValue = vbar->value(); - - // ScrollPerItem - if (verticalScrollMode == QAbstractItemView::ScrollPerItem && viewMode == QListView::ListMode) { - const QListViewItem item = indexToListViewItem(index); - const QRect rect = q->visualRect(index); - verticalValue = staticListView->verticalPerItemValue(itemIndex(item), - verticalValue, area.height(), - above, below, isWrapping(), hint, rect.height()); - - } else { // ScrollPerPixel - QRect adjusted = rect.adjusted(-spacing(), -spacing(), spacing(), spacing()); - if (hint == QListView::PositionAtTop || above) - verticalValue += adjusted.top(); - else if (hint == QListView::PositionAtBottom || below) - verticalValue += qMin(adjusted.top(), adjusted.bottom() - area.height() + 1); - else if (hint == QListView::PositionAtCenter) - verticalValue += adjusted.top() - ((area.height() - adjusted.height()) / 2); - } - - return verticalValue; + return commonListView->verticalScrollToValue(q->visualIndex(index), hint, above, below, area, rect); } void QListViewPrivate::selectAll(QItemSelectionModel::SelectionFlags command) @@ -766,23 +697,12 @@ void QListView::setRootIndex(const QModelIndex &index) Scroll the view contents by \a dx and \a dy. */ + void QListView::scrollContentsBy(int dx, int dy) { Q_D(QListView); - d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling - - if (d->viewMode == ListMode) - d->staticListView->scrollContentsBy(dx, dy); - else if (state() == DragSelectingState) - d->scrollElasticBandBy(isRightToLeft() ? -dx : dx, dy); - - d->scrollContentsBy(isRightToLeft() ? -dx : dx, dy); - - // update the dragged items - if (d->viewMode == IconMode) // ### move to dynamic class - if (!d->dynamicListView->draggedItems.isEmpty()) - d->viewport->update(d->dynamicListView->draggedItemsRect().translated(dx, dy)); + d->commonListView->scrollContentsBy(dx, dy, d->state == QListView::DragSelectingState); } /*! @@ -811,9 +731,7 @@ QSize QListView::contentsSize() const */ void QListView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { - Q_D(QListView); - if (d->viewMode == IconMode) - d->dynamicListView->dataChanged(topLeft, bottomRight); + d_func()->commonListView->dataChanged(topLeft, bottomRight); QAbstractItemView::dataChanged(topLeft, bottomRight); } @@ -864,7 +782,7 @@ void QListView::mouseMoveEvent(QMouseEvent *e) && d->selectionMode != NoSelection) { QRect rect(d->pressedPosition, e->pos() + QPoint(horizontalOffset(), verticalOffset())); rect = rect.normalized(); - d->viewport->update(d->mapToViewport(rect.united(d->elasticBand), d->viewMode == QListView::ListMode)); + d->viewport->update(d->mapToViewport(rect.united(d->elasticBand))); d->elasticBand = rect; } } @@ -878,7 +796,7 @@ void QListView::mouseReleaseEvent(QMouseEvent *e) QAbstractItemView::mouseReleaseEvent(e); // #### move this implementation into a dynamic class if (d->showElasticBand && d->elasticBand.isValid()) { - d->viewport->update(d->mapToViewport(d->elasticBand, d->viewMode == QListView::ListMode)); + d->viewport->update(d->mapToViewport(d->elasticBand)); d->elasticBand = QRect(); } } @@ -935,69 +853,27 @@ void QListView::resizeEvent(QResizeEvent *e) */ void QListView::dragMoveEvent(QDragMoveEvent *e) { - // ### move implementation to dynamic - Q_D(QListView); - if (e->source() == this && d->viewMode == IconMode) { - // the ignore by default - e->ignore(); - if (d->canDecode(e)) { - // get old dragged items rect - QRect itemsRect = d->dynamicListView->itemsRect(d->dynamicListView->draggedItems); - d->viewport->update(itemsRect.translated(d->dynamicListView->draggedItemsDelta())); - // update position - d->dynamicListView->draggedItemsPos = e->pos(); - // get new items rect - d->viewport->update(itemsRect.translated(d->dynamicListView->draggedItemsDelta())); - // set the item under the cursor to current - QModelIndex index; - if (d->movement == Snap) { - QRect rect(d->dynamicListView->snapToGrid(e->pos() + d->offset()), d->gridSize()); - const QVector<QModelIndex> intersectVector = d->intersectingSet(rect); - index = intersectVector.count() > 0 - ? intersectVector.last() : QModelIndex(); - } else { - index = indexAt(e->pos()); - } - // check if we allow drops here - if (e->source() == this && d->dynamicListView->draggedItems.contains(index)) - e->accept(); // allow changing item position - else if (d->model->flags(index) & Qt::ItemIsDropEnabled) - e->accept(); // allow dropping on dropenabled items - else if (!index.isValid()) - e->accept(); // allow dropping in empty areas - } - // do autoscrolling - if (d->shouldAutoScroll(e->pos())) - startAutoScroll(); - } else { // not internal + if (!d_func()->commonListView->filterDragMoveEvent(e)) QAbstractItemView::dragMoveEvent(e); - } } + /*! \reimp */ void QListView::dragLeaveEvent(QDragLeaveEvent *e) { - // ### move implementation to dynamic - Q_D(QListView); - if (d->viewMode == IconMode) { - d->viewport->update(d->dynamicListView->draggedItemsRect()); // erase the area - d->dynamicListView->draggedItemsPos = QPoint(-1, -1); // don't draw the dragged items - } - QAbstractItemView::dragLeaveEvent(e); + if (!d_func()->commonListView->filterDragLeaveEvent(e)) + QAbstractItemView::dragLeaveEvent(e); } /*! \reimp */ -void QListView::dropEvent(QDropEvent *event) +void QListView::dropEvent(QDropEvent *e) { - Q_D(QListView); - if (event->source() == this && d->viewMode == IconMode) - internalDrop(event); // ### move to dynamic - else - QAbstractItemView::dropEvent(event); + if (!d_func()->commonListView->filterDropEvent(e)) + QAbstractItemView::dropEvent(e); } /*! @@ -1005,10 +881,7 @@ void QListView::dropEvent(QDropEvent *event) */ void QListView::startDrag(Qt::DropActions supportedActions) { - Q_D(QListView); - if (d->viewMode == IconMode) // ### move to dynamic - internalDrag(supportedActions); - else + if (!d_func()->commonListView->filterStartDrag(supportedActions)) QAbstractItemView::startDrag(supportedActions); } @@ -1020,41 +893,8 @@ void QListView::startDrag(Qt::DropActions supportedActions) */ void QListView::internalDrop(QDropEvent *event) { - Q_D(QListView); - if (d->viewMode == QListView::ListMode) - return; - - // ### move to dynamic class - QPoint offset(horizontalOffset(), verticalOffset()); - QPoint end = event->pos() + offset; - QPoint start = d->pressedPosition; - QPoint delta = (d->movement == Snap ? - d->dynamicListView->snapToGrid(end) - - d->dynamicListView->snapToGrid(start) : end - start); - QSize contents = d->contentsSize(); - QList<QModelIndex> indexes = d->selectionModel->selectedIndexes(); - for (int i = 0; i < indexes.count(); ++i) { - QModelIndex index = indexes.at(i); - QRect rect = rectForIndex(index); - d->viewport->update(d->mapToViewport(rect, d->viewMode == QListView::ListMode)); - QPoint dest = rect.topLeft() + delta; - if (isRightToLeft()) - dest.setX(d->flipX(dest.x()) - rect.width()); - d->dynamicListView->moveItem(index.row(), dest); - update(index); - } - stopAutoScroll(); - d->dynamicListView->draggedItems.clear(); - emit indexesMoved(indexes); - event->accept(); // we have handled the event - // if the size has not grown, we need to check if it has shrinked - if (d->dynamicListView - && (d->contentsSize().width() <= contents.width() - || d->contentsSize().height() <= contents.height())) { - d->dynamicListView->updateContentsSize(); - } - if (d->contentsSize() != contents) - updateGeometries(); + // ### Qt5: remove that function + Q_UNUSED(event); } /*! @@ -1065,31 +905,8 @@ void QListView::internalDrop(QDropEvent *event) */ void QListView::internalDrag(Qt::DropActions supportedActions) { - Q_D(QListView); - if (d->viewMode == QListView::ListMode) - return; - - // #### move to dynamic class - - // This function does the same thing as in QAbstractItemView::startDrag(), - // plus adding viewitems to the draggedItems list. - // We need these items to draw the drag items - QModelIndexList indexes = d->selectionModel->selectedIndexes(); - if (indexes.count() > 0 ) { - if (d->viewport->acceptDrops()) { - QModelIndexList::ConstIterator it = indexes.constBegin(); - for (; it != indexes.constEnd(); ++it) - if (d->model->flags(*it) & Qt::ItemIsDragEnabled - && (*it).column() == d->column) - d->dynamicListView->draggedItems.push_back(*it); - } - QDrag *drag = new QDrag(this); - drag->setMimeData(d->model->mimeData(indexes)); - Qt::DropAction action = drag->exec(supportedActions, Qt::CopyAction); - d->dynamicListView->draggedItems.clear(); - if (action == Qt::MoveAction) - d->clearOrRemove(); - } + // ### Qt5: remove that function + Q_UNUSED(supportedActions); } #endif // QT_NO_DRAGANDDROP @@ -1117,6 +934,7 @@ QStyleOptionViewItem QListView::viewOptions() const return option; } + /*! \reimp */ @@ -1213,18 +1031,7 @@ void QListView::paintEvent(QPaintEvent *e) } #ifndef QT_NO_DRAGANDDROP - // #### move this implementation into a dynamic class - if (d->viewMode == IconMode) - if (!d->dynamicListView->draggedItems.isEmpty() - && d->viewport->rect().contains(d->dynamicListView->draggedItemsPos)) { - QPoint delta = d->dynamicListView->draggedItemsDelta(); - painter.translate(delta.x(), delta.y()); - d->dynamicListView->drawItems(&painter, d->dynamicListView->draggedItems); - } - // FIXME: Until the we can provide a proper drop indicator - // in IconMode, it makes no sense to show it - if (d->viewMode == ListMode) - d->paintDropIndicator(&painter); + d->commonListView->paintDragDrop(&painter); #endif #ifndef QT_NO_RUBBERBAND @@ -1263,31 +1070,7 @@ QModelIndex QListView::indexAt(const QPoint &p) const */ int QListView::horizontalOffset() const { - Q_D(const QListView); - // ### split into static and dynamic - if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem && d->viewMode == ListMode) { - if (d->isWrapping()) { - if (d->flow == TopToBottom && !d->staticListView->segmentPositions.isEmpty()) { - const int max = d->staticListView->segmentPositions.count() - 1; - int currentValue = qBound(0, horizontalScrollBar()->value(), max); - int position = d->staticListView->segmentPositions.at(currentValue); - int maximumValue = qBound(0, horizontalScrollBar()->maximum(), max); - int maximum = d->staticListView->segmentPositions.at(maximumValue); - return (isRightToLeft() ? maximum - position : position); - } - //return 0; - } else { - if (d->flow == LeftToRight && !d->staticListView->flowPositions.isEmpty()) { - int position = d->staticListView->flowPositions.at(horizontalScrollBar()->value()); - int maximum = d->staticListView->flowPositions.at(horizontalScrollBar()->maximum()); - return (isRightToLeft() ? maximum - position : position); - } - //return 0; - } - } - return (isRightToLeft() - ? horizontalScrollBar()->maximum() - horizontalScrollBar()->value() - : horizontalScrollBar()->value()); + return d_func()->commonListView->horizontalOffset(); } /*! @@ -1295,30 +1078,7 @@ int QListView::horizontalOffset() const */ int QListView::verticalOffset() const { - // ## split into static and dynamic - Q_D(const QListView); - if (verticalScrollMode() == QAbstractItemView::ScrollPerItem && d->viewMode == ListMode) { - if (d->isWrapping()) { - if (d->flow == LeftToRight && !d->staticListView->segmentPositions.isEmpty()) { - int value = verticalScrollBar()->value(); - if (value >= d->staticListView->segmentPositions.count()) { - //qWarning("QListView: Vertical scroll bar is out of bounds"); - return 0; - } - return d->staticListView->segmentPositions.at(value); - } - } else { - if (d->flow == TopToBottom && !d->staticListView->flowPositions.isEmpty()) { - int value = verticalScrollBar()->value(); - if (value > d->staticListView->flowPositions.count()) { - //qWarning("QListView: Vertical scroll bar is out of bounds"); - return 0; - } - return d->staticListView->flowPositions.at(value) - d->spacing(); - } - } - } - return verticalScrollBar()->value(); + return d_func()->commonListView->verticalOffset(); } /*! @@ -1444,15 +1204,7 @@ QModelIndex QListView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifie */ QRect QListView::rectForIndex(const QModelIndex &index) const { - Q_D(const QListView); - if (!d->isIndexValid(index) - || index.parent() != d->root - || index.column() != d->column - || isIndexHidden(index)) - return QRect(); - d->executePostedLayout(); - QListViewItem item = d->indexToListViewItem(index); - return d->viewItemRect(item); + return d_func()->rectForIndex(index); } /*! @@ -1460,8 +1212,8 @@ QRect QListView::rectForIndex(const QModelIndex &index) const Sets the contents position of the item at \a index in the model to the given \a position. - If the list view's movement mode is Static, this function will have no - effect. + If the list view's movement mode is Static or its view mode is ListView, + this function will have no effect. */ void QListView::setPositionForIndex(const QPoint &position, const QModelIndex &index) { @@ -1473,15 +1225,7 @@ void QListView::setPositionForIndex(const QPoint &position, const QModelIndex &i return; d->executePostedLayout(); - if (index.row() >= d->dynamicListView->items.count()) - return; - const QSize oldContents = d->contentsSize(); - update(index); // update old position - d->dynamicListView->moveItem(index.row(), position); - update(index); // update new position - - if (d->contentsSize() != oldContents) - updateGeometries(); // update the scroll bars + d->commonListView->setPositionForIndex(position, index); } /*! @@ -1717,99 +1461,8 @@ void QListView::updateGeometries() QModelIndex index = d->model->index(0, d->column, d->root); QStyleOptionViewItemV4 option = d->viewOptionsV4(); QSize step = d->itemSize(option, index); - - QSize csize = d->contentsSize(); - QSize vsize = d->viewport->size(); - QSize max = maximumViewportSize(); - if (max.width() >= d->contentsSize().width() && max.height() >= d->contentsSize().height()) - vsize = max; - - // ### reorder the logic - - // ### split into static and dynamic - - const bool vertical = verticalScrollMode() == QAbstractItemView::ScrollPerItem; - const bool horizontal = horizontalScrollMode() == QAbstractItemView::ScrollPerItem; - - if (d->flow == TopToBottom) { - if (horizontal && d->isWrapping() && d->viewMode == ListMode) { - const QVector<int> segmentPositions = d->staticListView->segmentPositions; - const int steps = segmentPositions.count() - 1; - if (steps > 0) { - int pageSteps = d->staticListView->perItemScrollingPageSteps(vsize.width(), - csize.width(), - isWrapping()); - horizontalScrollBar()->setSingleStep(1); - horizontalScrollBar()->setPageStep(pageSteps); - horizontalScrollBar()->setRange(0, steps - pageSteps); - } else { - horizontalScrollBar()->setRange(0, 0); - } - } else { - horizontalScrollBar()->setSingleStep(step.width() + d->spacing()); - horizontalScrollBar()->setPageStep(vsize.width()); - horizontalScrollBar()->setRange(0, d->contentsSize().width() - vsize.width()); - } - if (vertical && !d->isWrapping() && d->viewMode == ListMode) { - const QVector<int> flowPositions = d->staticListView->flowPositions; - const int steps = flowPositions.count() - 1; - if (steps > 0) { - int pageSteps = d->staticListView->perItemScrollingPageSteps(vsize.height(), - csize.height(), - isWrapping()); - verticalScrollBar()->setSingleStep(1); - verticalScrollBar()->setPageStep(pageSteps); - verticalScrollBar()->setRange(0, steps - pageSteps); - } else { - verticalScrollBar()->setRange(0, 0); - } - // } else if (vertical && d->isWrapping() && d->movement == Static) { - // ### wrapped scrolling in flow direction - } else { - verticalScrollBar()->setSingleStep(step.height() + d->spacing()); - verticalScrollBar()->setPageStep(vsize.height()); - verticalScrollBar()->setRange(0, d->contentsSize().height() - vsize.height()); - } - } else { // LeftToRight - if (horizontal && !d->isWrapping() && d->viewMode == ListMode) { - const QVector<int> flowPositions = d->staticListView->flowPositions; - int steps = flowPositions.count() - 1; - if (steps > 0) { - int pageSteps = d->staticListView->perItemScrollingPageSteps(vsize.width(), - csize.width(), - isWrapping()); - horizontalScrollBar()->setSingleStep(1); - horizontalScrollBar()->setPageStep(pageSteps); - horizontalScrollBar()->setRange(0, steps - pageSteps); - } else { - horizontalScrollBar()->setRange(0, 0); - } - // } else if (horizontal && d->isWrapping() && d->movement == Static) { - // ### wrapped scrolling in flow direction - } else { - horizontalScrollBar()->setSingleStep(step.width() + d->spacing()); - horizontalScrollBar()->setPageStep(vsize.width()); - horizontalScrollBar()->setRange(0, d->contentsSize().width() - vsize.width()); - } - if (vertical && d->isWrapping() && d->viewMode == ListMode) { - const QVector<int> segmentPositions = d->staticListView->segmentPositions; - int steps = segmentPositions.count() - 1; - if (steps > 0) { - int pageSteps = d->staticListView->perItemScrollingPageSteps(vsize.height(), - csize.height(), - isWrapping()); - verticalScrollBar()->setSingleStep(1); - verticalScrollBar()->setPageStep(pageSteps); - verticalScrollBar()->setRange(0, steps - pageSteps); - } else { - verticalScrollBar()->setRange(0, 0); - } - } else { - verticalScrollBar()->setSingleStep(step.height() + d->spacing()); - verticalScrollBar()->setPageStep(vsize.height()); - verticalScrollBar()->setRange(0, d->contentsSize().height() - vsize.height()); - } - } + d->commonListView->updateHorizontalScrollBar(step); + d->commonListView->updateVerticalScrollBar(step); } QAbstractItemView::updateGeometries(); @@ -1958,14 +1611,14 @@ bool QListView::event(QEvent *e) QListViewPrivate::QListViewPrivate() : QAbstractItemViewPrivate(), - dynamicListView(0), + commonListView(0), wrap(false), space(0), flow(QListView::TopToBottom), movement(QListView::Static), resizeMode(QListView::Fixed), layoutMode(QListView::SinglePass), - viewMode(QListView::IconMode), //this will ensure the first initialization to ListView + viewMode(QListView::ListMode), modeProperties(0), column(0), uniformItemSizes(false), @@ -1976,21 +1629,14 @@ QListViewPrivate::QListViewPrivate() QListViewPrivate::~QListViewPrivate() { - if (viewMode == QListView::ListMode) - delete staticListView; - else - delete dynamicListView; + delete commonListView; } void QListViewPrivate::clear() { - // ### split into dynamic and static // initialization of data structs cachedItemSize = QSize(); - if (viewMode == QListView::ListMode) - staticListView->clear(); - else - dynamicListView->clear(); + commonListView->clear(); } void QListViewPrivate::prepareItemsLayout() @@ -1999,7 +1645,7 @@ void QListViewPrivate::prepareItemsLayout() clear(); //take the size as if there were scrollbar in order to prevent scrollbar to blink - layoutBounds = QRect(QPoint(0,0), q->maximumViewportSize()); + layoutBounds = QRect(QPoint(), q->maximumViewportSize()); int frameAroundContents = 0; if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) @@ -2017,15 +1663,8 @@ void QListViewPrivate::prepareItemsLayout() layoutBounds.adjust(0, 0, -verticalMargin, -horizontalMargin); - int rowCount = model->rowCount(root); - int colCount = model->columnCount(root); - if (colCount <= 0) - rowCount = 0; // no contents - if (viewMode == QListView::ListMode) { - staticListView->flowPositions.resize(rowCount); - } else { - dynamicListView->tree.create(qMax(rowCount - hiddenRows.count(), 0)); - } + int rowCount = model->columnCount(root) <= 0 ? 0 : model->rowCount(root); + commonListView->setRowCount(rowCount); } /*! @@ -2033,7 +1672,6 @@ void QListViewPrivate::prepareItemsLayout() */ bool QListViewPrivate::doItemsLayout(int delta) { - // ### split into static and dynamic int max = model->rowCount(root) - 1; int first = batchStartRow(); int last = qMin(first + delta - 1, max); @@ -2057,9 +1695,7 @@ bool QListViewPrivate::doItemsLayout(int delta) info.flow = flow; info.max = max; - if (viewMode == QListView::ListMode) - return staticListView->doBatchedItemLayout(info, max); - return dynamicListView->doBatchedItemLayout(info, max); + return commonListView->doBatchedItemLayout(info, max); } QListViewItem QListViewPrivate::indexToListViewItem(const QModelIndex &index) const @@ -2067,29 +1703,16 @@ QListViewItem QListViewPrivate::indexToListViewItem(const QModelIndex &index) co if (!index.isValid() || isHidden(index.row())) return QListViewItem(); - if (viewMode == QListView::ListMode) - return staticListView->indexToListViewItem(index); - return dynamicListView->indexToListViewItem(index); + return commonListView->indexToListViewItem(index); } - -int QListViewPrivate::itemIndex(const QListViewItem &item) const -{ - if (viewMode == QListView::ListMode) - return staticListView->itemIndex(item); - return dynamicListView->itemIndex(item); -} - -QRect QListViewPrivate::mapToViewport(const QRect &rect, bool greedy) const +QRect QListViewPrivate::mapToViewport(const QRect &rect, bool extend) const { Q_Q(const QListView); if (!rect.isValid()) return rect; - QRect result = rect; - if (greedy) - result = staticListView->mapToViewport(rect); - + QRect result = extend ? commonListView->mapToViewport(rect) : rect; int dx = -q->horizontalOffset(); int dy = -q->verticalOffset(); result.adjust(dx, dy, dx, dy); @@ -2177,46 +1800,217 @@ QItemSelection QListViewPrivate::selection(const QRect &rect) const } /* - * Static ListView Implementation + * Common ListView Implementation */ -int QStaticListViewBase::verticalPerItemValue(int itemIndex, int verticalValue, int areaHeight, - bool above, bool below, bool wrap, - QListView::ScrollHint hint, int itemHeight) const +void QCommonListViewBase::appendHiddenRow(int row) +{ + dd->hiddenRows.append(dd->model->index(row, 0)); +} + +void QCommonListViewBase::removeHiddenRow(int row) { - int value = qBound(0, verticalValue, flowPositions.count() - 1); - if (above) - return perItemScrollToValue(itemIndex, value, areaHeight, QListView::PositionAtTop, - Qt::Vertical,wrap, itemHeight); - else if (below) - return perItemScrollToValue(itemIndex, value, areaHeight, QListView::PositionAtBottom, - Qt::Vertical, wrap, itemHeight); - else if (hint != QListView::EnsureVisible) - return perItemScrollToValue(itemIndex, value, areaHeight, hint, Qt::Vertical, wrap, itemHeight); - return value; + dd->hiddenRows.remove(dd->hiddenRows.indexOf(dd->model->index(row, 0))); } -int QStaticListViewBase::horizontalPerItemValue(int itemIndex, int horizontalValue, int areaWidth, - bool leftOf, bool rightOf, bool wrap, - QListView::ScrollHint hint, int itemWidth) const +void QCommonListViewBase::updateHorizontalScrollBar(const QSize &step) { - int value = qBound(0, horizontalValue, flowPositions.count() - 1); + horizontalScrollBar()->setSingleStep(step.width() + spacing()); + horizontalScrollBar()->setPageStep(viewport()->width()); + horizontalScrollBar()->setRange(0, contentsSize.width() - viewport()->width()); +} + +void QCommonListViewBase::updateVerticalScrollBar(const QSize &step) +{ + verticalScrollBar()->setSingleStep(step.height() + spacing()); + verticalScrollBar()->setPageStep(viewport()->height()); + verticalScrollBar()->setRange(0, contentsSize.height() - viewport()->height()); +} + +void QCommonListViewBase::scrollContentsBy(int dx, int dy, bool /*scrollElasticBand*/) +{ + dd->scrollContentsBy(isRightToLeft() ? -dx : dx, dy); +} + +int QCommonListViewBase::verticalScrollToValue(int /*index*/, QListView::ScrollHint hint, + bool above, bool below, const QRect &area, const QRect &rect) const +{ + int verticalValue = verticalScrollBar()->value(); + QRect adjusted = rect.adjusted(-spacing(), -spacing(), spacing(), spacing()); + if (hint == QListView::PositionAtTop || above) + verticalValue += adjusted.top(); + else if (hint == QListView::PositionAtBottom || below) + verticalValue += qMin(adjusted.top(), adjusted.bottom() - area.height() + 1); + else if (hint == QListView::PositionAtCenter) + verticalValue += adjusted.top() - ((area.height() - adjusted.height()) / 2); + return verticalValue; +} + +int QCommonListViewBase::horizontalOffset() const +{ + return (isRightToLeft() ? horizontalScrollBar()->maximum() - horizontalScrollBar()->value() : horizontalScrollBar()->value()); +} + +int QCommonListViewBase::horizontalScrollToValue(const int /*index*/, QListView::ScrollHint hint, + bool leftOf, bool rightOf, const QRect &area, const QRect &rect) const +{ + int horizontalValue = horizontalScrollBar()->value(); + if (isRightToLeft()) { + if (hint == QListView::PositionAtCenter) { + horizontalValue += ((area.width() - rect.width()) / 2) - rect.left(); + } else { + if (leftOf) + horizontalValue -= rect.left(); + else if (rightOf) + horizontalValue += qMin(rect.left(), area.width() - rect.right()); + } + } else { + if (hint == QListView::PositionAtCenter) { + horizontalValue += rect.left() - ((area.width()- rect.width()) / 2); + } else { + if (leftOf) + horizontalValue += rect.left(); + else if (rightOf) + horizontalValue += qMin(rect.left(), rect.right() - area.width()); + } + } + return horizontalValue; +} + +/* + * ListMode ListView Implementation +*/ + +#ifndef QT_NO_DRAGANDDROP +void QListModeViewBase::paintDragDrop(QPainter *painter) +{ + // FIXME: Until the we can provide a proper drop indicator + // in IconMode, it makes no sense to show it + dd->paintDropIndicator(painter); +} +#endif //QT_NO_DRAGANDDROP + +void QListModeViewBase::updateVerticalScrollBar(const QSize &step) +{ + if (verticalScrollMode() == QAbstractItemView::ScrollPerItem + && ((flow() == QListView::TopToBottom && !isWrapping()) + || (flow() == QListView::LeftToRight && isWrapping()))) { + const int steps = (flow() == QListView::TopToBottom ? flowPositions : segmentPositions).count() - 1; + if (steps > 0) { + const int pageSteps = perItemScrollingPageSteps(viewport()->height(), contentsSize.height(), isWrapping()); + verticalScrollBar()->setSingleStep(1); + verticalScrollBar()->setPageStep(pageSteps); + verticalScrollBar()->setRange(0, steps - pageSteps); + } else { + verticalScrollBar()->setRange(0, 0); + } + // } else if (vertical && d->isWrapping() && d->movement == Static) { + // ### wrapped scrolling in flow direction + } else { + QCommonListViewBase::updateVerticalScrollBar(step); + } +} + +void QListModeViewBase::updateHorizontalScrollBar(const QSize &step) +{ + if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem + && ((flow() == QListView::TopToBottom && isWrapping()) + || (flow() == QListView::LeftToRight && !isWrapping()))) { + int steps = (flow() == QListView::TopToBottom ? segmentPositions : flowPositions).count() - 1; + if (steps > 0) { + const int pageSteps = perItemScrollingPageSteps(viewport()->width(), contentsSize.width(), isWrapping()); + horizontalScrollBar()->setSingleStep(1); + horizontalScrollBar()->setPageStep(pageSteps); + horizontalScrollBar()->setRange(0, steps - pageSteps); + } else { + horizontalScrollBar()->setRange(0, 0); + } + } else { + QCommonListViewBase::updateHorizontalScrollBar(step); + } +} + +int QListModeViewBase::verticalScrollToValue(int index, QListView::ScrollHint hint, + bool above, bool below, const QRect &area, const QRect &rect) const +{ + if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) { + int value = qBound(0, verticalScrollBar()->value(), flowPositions.count() - 1); + if (above) + hint = QListView::PositionAtTop; + else if (below) + hint = QListView::PositionAtBottom; + if (hint == QListView::EnsureVisible) + return value; + + return perItemScrollToValue(index, value, area.height(), hint, Qt::Vertical, isWrapping(), rect.height()); + } + + return QCommonListViewBase::verticalScrollToValue(index, hint, above, below, area, rect); +} + +int QListModeViewBase::horizontalOffset() const +{ + if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) { + if (isWrapping()) { + if (flow() == QListView::TopToBottom && !segmentPositions.isEmpty()) { + const int max = segmentPositions.count() - 1; + int currentValue = qBound(0, horizontalScrollBar()->value(), max); + int position = segmentPositions.at(currentValue); + int maximumValue = qBound(0, horizontalScrollBar()->maximum(), max); + int maximum = segmentPositions.at(maximumValue); + return (isRightToLeft() ? maximum - position : position); + } + } else if (flow() == QListView::LeftToRight && !flowPositions.isEmpty()) { + int position = flowPositions.at(horizontalScrollBar()->value()); + int maximum = flowPositions.at(horizontalScrollBar()->maximum()); + return (isRightToLeft() ? maximum - position : position); + } + } + return QCommonListViewBase::horizontalOffset(); +} + +int QListModeViewBase::verticalOffset() const +{ + if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) { + if (isWrapping()) { + if (flow() == QListView::LeftToRight && !segmentPositions.isEmpty()) { + int value = verticalScrollBar()->value(); + if (value >= segmentPositions.count()) + return 0; + return segmentPositions.at(value); + } + } else if (flow() == QListView::TopToBottom && !flowPositions.isEmpty()) { + int value = verticalScrollBar()->value(); + if (value > flowPositions.count()) + return 0; + return flowPositions.at(value) - spacing(); + } + } + return QCommonListViewBase::verticalOffset(); +} + +int QListModeViewBase::horizontalScrollToValue(int index, QListView::ScrollHint hint, + bool leftOf, bool rightOf, const QRect &area, const QRect &rect) const +{ + if (horizontalScrollMode() != QAbstractItemView::ScrollPerItem) + return QCommonListViewBase::horizontalScrollToValue(index, hint, leftOf, rightOf, area, rect); + + int value = qBound(0, horizontalScrollBar()->value(), flowPositions.count() - 1); if (leftOf) - return perItemScrollToValue(itemIndex, value, areaWidth, QListView::PositionAtTop, - Qt::Horizontal, wrap, itemWidth); + hint = QListView::PositionAtTop; else if (rightOf) - return perItemScrollToValue(itemIndex, value, areaWidth, QListView::PositionAtBottom, - Qt::Horizontal, wrap, itemWidth); - else if (hint != QListView::EnsureVisible) - return perItemScrollToValue(itemIndex, value, areaWidth, hint, Qt::Horizontal, wrap, itemWidth); - return value; + hint = QListView::PositionAtBottom; + if (hint == QListView::EnsureVisible) + return value; + + return perItemScrollToValue(index, value, area.width(), hint, Qt::Horizontal, isWrapping(), rect.width()); } -void QStaticListViewBase::scrollContentsBy(int &dx, int &dy) +void QListModeViewBase::scrollContentsBy(int dx, int dy, bool scrollElasticBand) { // ### reorder this logic - const int verticalValue = verticalScrollBarValue(); - const int horizontalValue = horizontalScrollBarValue(); + const int verticalValue = verticalScrollBar()->value(); + const int horizontalValue = horizontalScrollBar()->value(); const bool vertical = (verticalScrollMode() == QAbstractItemView::ScrollPerItem); const bool horizontal = (horizontalScrollMode() == QAbstractItemView::ScrollPerItem); @@ -2255,9 +2049,10 @@ void QStaticListViewBase::scrollContentsBy(int &dx, int &dy) dx = previousCoordinate - currentCoordinate; } } + QCommonListViewBase::scrollContentsBy(dx, dy, scrollElasticBand); } -bool QStaticListViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, int max) +bool QListModeViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, int max) { doStaticLayout(info); if (batchStartRow > max) { // stop items layout @@ -2269,7 +2064,7 @@ bool QStaticListViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, i return false; // not done } -QListViewItem QStaticListViewBase::indexToListViewItem(const QModelIndex &index) const +QListViewItem QListModeViewBase::indexToListViewItem(const QModelIndex &index) const { if (flowPositions.isEmpty() || segmentPositions.isEmpty() @@ -2305,7 +2100,7 @@ QListViewItem QStaticListViewBase::indexToListViewItem(const QModelIndex &index) return QListViewItem(QRect(pos, size), index.row()); } -QPoint QStaticListViewBase::initStaticLayout(const QListViewLayoutInfo &info) +QPoint QListModeViewBase::initStaticLayout(const QListViewLayoutInfo &info) { int x, y; if (info.first == 0) { @@ -2340,7 +2135,7 @@ QPoint QStaticListViewBase::initStaticLayout(const QListViewLayoutInfo &info) /*! \internal */ -void QStaticListViewBase::doStaticLayout(const QListViewLayoutInfo &info) +void QListModeViewBase::doStaticLayout(const QListViewLayoutInfo &info) { const bool useItemSize = !info.grid.isValid(); const QPoint topLeft = initStaticLayout(info); @@ -2443,7 +2238,7 @@ 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. */ -QVector<QModelIndex> QStaticListViewBase::intersectingStaticSet(const QRect &area) const +QVector<QModelIndex> QListModeViewBase::intersectingSet(const QRect &area) const { QVector<QModelIndex> ret; int segStartPosition; @@ -2480,19 +2275,14 @@ QVector<QModelIndex> QStaticListViewBase::intersectingStaticSet(const QRect &are ret += index; #if 0 // for debugging else - qWarning("intersectingStaticSet: row %d was invalid", row); + qWarning("intersectingSet: row %d was invalid", row); #endif } } return ret; } -int QStaticListViewBase::itemIndex(const QListViewItem &item) const -{ - return item.indexHint; -} - -QRect QStaticListViewBase::mapToViewport(const QRect &rect) const +QRect QListModeViewBase::mapToViewport(const QRect &rect) const { if (isWrapping()) return rect; @@ -2510,7 +2300,7 @@ QRect QStaticListViewBase::mapToViewport(const QRect &rect) const return result; } -int QStaticListViewBase::perItemScrollingPageSteps(int length, int bounds, bool wrap) const +int QListModeViewBase::perItemScrollingPageSteps(int length, int bounds, bool wrap) const { const QVector<int> positions = (wrap ? segmentPositions : flowPositions); if (positions.isEmpty() || bounds <= length) @@ -2538,7 +2328,7 @@ int QStaticListViewBase::perItemScrollingPageSteps(int length, int bounds, bool return qMax(pageSteps, 1); } -int QStaticListViewBase::perItemScrollToValue(int index, int scrollValue, int viewportSize, +int QListModeViewBase::perItemScrollToValue(int index, int scrollValue, int viewportSize, QAbstractItemView::ScrollHint hint, Qt::Orientation orientation, bool wrap, int itemExtent) const { @@ -2598,7 +2388,7 @@ int QStaticListViewBase::perItemScrollToValue(int index, int scrollValue, int vi return scrollValue; } -void QStaticListViewBase::clear() +void QListModeViewBase::clear() { flowPositions.clear(); segmentPositions.clear(); @@ -2610,10 +2400,175 @@ void QStaticListViewBase::clear() } /* - * Dynamic ListView Implementation + * IconMode ListView Implementation */ -void QDynamicListViewBase::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +void QIconModeViewBase::setPositionForIndex(const QPoint &position, const QModelIndex &index) +{ + if (index.row() >= items.count()) + return; + const QSize oldContents = contentsSize; + qq->update(index); // update old position + moveItem(index.row(), position); + qq->update(index); // update new position + + if (contentsSize != oldContents) + dd->viewUpdateGeometries(); // update the scroll bars +} + +void QIconModeViewBase::appendHiddenRow(int row) +{ + if (row >= 0 && row < items.count()) //remove item + tree.removeLeaf(items.at(row).rect(), row); + QCommonListViewBase::appendHiddenRow(row); +} + +void QIconModeViewBase::removeHiddenRow(int row) +{ + QCommonListViewBase::appendHiddenRow(row); + if (row >= 0 && row < items.count()) //insert item + tree.insertLeaf(items.at(row).rect(), row); +} + +#ifndef QT_NO_DRAGANDDROP +void QIconModeViewBase::paintDragDrop(QPainter *painter) +{ + if (!draggedItems.isEmpty() && viewport()->rect().contains(draggedItemsPos)) { + //we need to draw the items that arre dragged + painter->translate(draggedItemsDelta()); + QStyleOptionViewItemV4 option = viewOptions(); + option.state &= ~QStyle::State_MouseOver; + QVector<QModelIndex>::const_iterator it = draggedItems.begin(); + QListViewItem item = indexToListViewItem(*it); + for (; it != draggedItems.end(); ++it) { + item = indexToListViewItem(*it); + option.rect = viewItemRect(item); + delegate(*it)->paint(painter, option, *it); + } + } +} + +bool QIconModeViewBase::filterStartDrag(Qt::DropActions supportedActions) +{ + // This function does the same thing as in QAbstractItemView::startDrag(), + // plus adding viewitems to the draggedItems list. + // We need these items to draw the drag items + QModelIndexList indexes = dd->selectionModel->selectedIndexes(); + if (indexes.count() > 0 ) { + if (viewport()->acceptDrops()) { + QModelIndexList::ConstIterator it = indexes.constBegin(); + for (; it != indexes.constEnd(); ++it) + if (dd->model->flags(*it) & Qt::ItemIsDragEnabled + && (*it).column() == dd->column) + draggedItems.push_back(*it); + } + QDrag *drag = new QDrag(qq); + drag->setMimeData(dd->model->mimeData(indexes)); + Qt::DropAction action = drag->exec(supportedActions, Qt::CopyAction); + draggedItems.clear(); + if (action == Qt::MoveAction) + dd->clearOrRemove(); + } + return true; +} + +bool QIconModeViewBase::filterDropEvent(QDropEvent *e) +{ + if (e->source() != qq) + return false; + + const QSize contents = contentsSize; + QPoint offset(horizontalOffset(), verticalOffset()); + QPoint end = e->pos() + offset; + QPoint start = dd->pressedPosition; + QPoint delta = (dd->movement == QListView::Snap ? snapToGrid(end) - snapToGrid(start) : end - start); + QList<QModelIndex> indexes = dd->selectionModel->selectedIndexes(); + for (int i = 0; i < indexes.count(); ++i) { + QModelIndex index = indexes.at(i); + QRect rect = dd->rectForIndex(index); + viewport()->update(mapToViewport(rect)); + QPoint dest = rect.topLeft() + delta; + if (qq->isRightToLeft()) + dest.setX(dd->flipX(dest.x()) - rect.width()); + moveItem(index.row(), dest); + qq->update(index); + } + dd->stopAutoScroll(); + draggedItems.clear(); + dd->emitIndexesMoved(indexes); + e->accept(); // we have handled the event + // if the size has not grown, we need to check if it has shrinked + if (contentsSize != contents) { + if ((contentsSize.width() <= contents.width() + || contentsSize.height() <= contents.height())) { + updateContentsSize(); + } + dd->viewUpdateGeometries(); + } + return true; +} + +bool QIconModeViewBase::filterDragLeaveEvent(QDragLeaveEvent *e) +{ + viewport()->update(draggedItemsRect()); // erase the area + draggedItemsPos = QPoint(-1, -1); // don't draw the dragged items + return QCommonListViewBase::filterDragLeaveEvent(e); +} + +bool QIconModeViewBase::filterDragMoveEvent(QDragMoveEvent *e) +{ + if (e->source() != qq || !dd->canDecode(e)) + return false; + + // ignore by default + e->ignore(); + // get old dragged items rect + QRect itemsRect = this->itemsRect(draggedItems); + viewport()->update(itemsRect.translated(draggedItemsDelta())); + // update position + draggedItemsPos = e->pos(); + // get new items rect + viewport()->update(itemsRect.translated(draggedItemsDelta())); + // set the item under the cursor to current + QModelIndex index; + if (movement() == QListView::Snap) { + QRect rect(snapToGrid(e->pos() + offset()), gridSize()); + const QVector<QModelIndex> intersectVector = intersectingSet(rect); + index = intersectVector.count() > 0 ? intersectVector.last() : QModelIndex(); + } else { + index = qq->indexAt(e->pos()); + } + // check if we allow drops here + if (draggedItems.contains(index)) + e->accept(); // allow changing item position + else if (dd->model->flags(index) & Qt::ItemIsDropEnabled) + e->accept(); // allow dropping on dropenabled items + else if (!index.isValid()) + e->accept(); // allow dropping in empty areas + + // the event was treated. do autoscrolling + if (dd->shouldAutoScroll(e->pos())) + dd->startAutoScroll(); + return true; +} +#endif // QT_NO_DRAGANDDROP + +void QIconModeViewBase::setRowCount(int rowCount) +{ + tree.create(qMax(rowCount - hiddenCount(), 0)); +} + +void QIconModeViewBase::scrollContentsBy(int dx, int dy, bool scrollElasticBand) +{ + if (scrollElasticBand) + dd->scrollElasticBandBy(isRightToLeft() ? -dx : dx, dy); + + QCommonListViewBase::scrollContentsBy(dx, dy, scrollElasticBand); + if (!draggedItems.isEmpty()) + viewport()->update(draggedItemsRect().translated(dx, dy)); +} + +void QIconModeViewBase::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { if (column() >= topLeft.column() && column() <= bottomRight.column()) { QStyleOptionViewItemV4 option = viewOptions(); @@ -2623,23 +2578,29 @@ void QDynamicListViewBase::dataChanged(const QModelIndex &topLeft, const QModelI } } -bool QDynamicListViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, int max) +bool QIconModeViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, int max) { if (info.last >= items.count()) { - createItems(info.last + 1); + //first we create the items + QStyleOptionViewItemV4 option = viewOptions(); + for (int row = items.count(); row <= info.last; ++row) { + QSize size = itemSize(option, modelIndex(row)); + QListViewItem item(QRect(0, 0, size.width(), size.height()), row); // default pos + items.append(item); + } doDynamicLayout(info); } return (batchStartRow > max); // done } -QListViewItem QDynamicListViewBase::indexToListViewItem(const QModelIndex &index) const +QListViewItem QIconModeViewBase::indexToListViewItem(const QModelIndex &index) const { if (index.isValid() && index.row() < items.count()) return items.at(index.row()); return QListViewItem(); } -void QDynamicListViewBase::initBspTree(const QSize &contents) +void QIconModeViewBase::initBspTree(const QSize &contents) { // remove all items from the tree int leafCount = tree.leafCount(); @@ -2656,7 +2617,7 @@ void QDynamicListViewBase::initBspTree(const QSize &contents) tree.init(QRect(0, 0, contents.width(), contents.height()), type); } -QPoint QDynamicListViewBase::initDynamicLayout(const QListViewLayoutInfo &info) +QPoint QIconModeViewBase::initDynamicLayout(const QListViewLayoutInfo &info) { int x, y; if (info.first == 0) { @@ -2678,7 +2639,7 @@ QPoint QDynamicListViewBase::initDynamicLayout(const QListViewLayoutInfo &info) /*! \internal */ -void QDynamicListViewBase::doDynamicLayout(const QListViewLayoutInfo &info) +void QIconModeViewBase::doDynamicLayout(const QListViewLayoutInfo &info) { const bool useItemSize = !info.grid.isValid(); const QPoint topLeft = initDynamicLayout(info); @@ -2712,7 +2673,7 @@ void QDynamicListViewBase::doDynamicLayout(const QListViewLayoutInfo &info) if (moved.count() != items.count()) moved.resize(items.count()); - QRect rect(QPoint(0, 0), topLeft); + QRect rect(QPoint(), topLeft); QListViewItem *item = 0; for (int row = info.first; row <= info.last; ++row) { item = &items[row]; @@ -2805,43 +2766,18 @@ void QDynamicListViewBase::doDynamicLayout(const QListViewLayoutInfo &info) viewport()->update(); } -QVector<QModelIndex> QDynamicListViewBase::intersectingDynamicSet(const QRect &area) const +QVector<QModelIndex> QIconModeViewBase::intersectingSet(const QRect &area) const { - QDynamicListViewBase *that = const_cast<QDynamicListViewBase*>(this); + QIconModeViewBase *that = const_cast<QIconModeViewBase*>(this); QBspTree::Data data(static_cast<void*>(that)); QVector<QModelIndex> res; that->interSectingVector = &res; - that->tree.climbTree(area, &QDynamicListViewBase::addLeaf, data); + that->tree.climbTree(area, &QIconModeViewBase::addLeaf, data); that->interSectingVector = 0; return res; } -void QDynamicListViewBase::createItems(int to) -{ - int count = items.count(); - QSize size; - QStyleOptionViewItemV4 option = viewOptions(); - for (int row = count; row < to; ++row) { - size = itemSize(option, modelIndex(row)); - QListViewItem item(QRect(0, 0, size.width(), size.height()), row); // default pos - items.append(item); - } -} - -void QDynamicListViewBase::drawItems(QPainter *painter, const QVector<QModelIndex> &indexes) const -{ - QStyleOptionViewItemV4 option = viewOptions(); - option.state &= ~QStyle::State_MouseOver; - QVector<QModelIndex>::const_iterator it = indexes.begin(); - QListViewItem item = indexToListViewItem(*it); - for (; it != indexes.end(); ++it) { - item = indexToListViewItem(*it); - option.rect = viewItemRect(item); - delegate(*it)->paint(painter, option, *it); - } -} - -QRect QDynamicListViewBase::itemsRect(const QVector<QModelIndex> &indexes) const +QRect QIconModeViewBase::itemsRect(const QVector<QModelIndex> &indexes) const { QVector<QModelIndex>::const_iterator it = indexes.begin(); QListViewItem item = indexToListViewItem(*it); @@ -2853,7 +2789,7 @@ QRect QDynamicListViewBase::itemsRect(const QVector<QModelIndex> &indexes) const return rect; } -int QDynamicListViewBase::itemIndex(const QListViewItem &item) const +int QIconModeViewBase::itemIndex(const QListViewItem &item) const { if (!item.isValid()) return -1; @@ -2889,11 +2825,11 @@ int QDynamicListViewBase::itemIndex(const QListViewItem &item) const return -1; } -void QDynamicListViewBase::addLeaf(QVector<int> &leaf, const QRect &area, +void QIconModeViewBase::addLeaf(QVector<int> &leaf, const QRect &area, uint visited, QBspTree::Data data) { QListViewItem *vi; - QDynamicListViewBase *_this = static_cast<QDynamicListViewBase *>(data.ptr); + QIconModeViewBase *_this = static_cast<QIconModeViewBase *>(data.ptr); for (int i = 0; i < leaf.count(); ++i) { int idx = leaf.at(i); if (idx < 0 || idx >= _this->items.count()) @@ -2909,19 +2845,7 @@ void QDynamicListViewBase::addLeaf(QVector<int> &leaf, const QRect &area, } } -void QDynamicListViewBase::insertItem(int index) -{ - if (index >= 0 && index < items.count()) - tree.insertLeaf(items.at(index).rect(), index); -} - -void QDynamicListViewBase::removeItem(int index) -{ - if (index >= 0 && index < items.count()) - tree.removeLeaf(items.at(index).rect(), index); -} - -void QDynamicListViewBase::moveItem(int index, const QPoint &dest) +void QIconModeViewBase::moveItem(int index, const QPoint &dest) { // does not impact on the bintree itself or the contents rect QListViewItem *item = &items[index]; @@ -2941,14 +2865,14 @@ void QDynamicListViewBase::moveItem(int index, const QPoint &dest) moved.setBit(index, true); } -QPoint QDynamicListViewBase::snapToGrid(const QPoint &pos) const +QPoint QIconModeViewBase::snapToGrid(const QPoint &pos) const { int x = pos.x() - (pos.x() % gridSize().width()); int y = pos.y() - (pos.y() % gridSize().height()); return QPoint(x, y); } -QPoint QDynamicListViewBase::draggedItemsDelta() const +QPoint QIconModeViewBase::draggedItemsDelta() const { if (movement() == QListView::Snap) { QPoint snapdelta = QPoint((offset().x() % gridSize().width()), @@ -2958,7 +2882,7 @@ QPoint QDynamicListViewBase::draggedItemsDelta() const return draggedItemsPos - pressedPosition(); } -QRect QDynamicListViewBase::draggedItemsRect() const +QRect QIconModeViewBase::draggedItemsRect() const { QRect rect = itemsRect(draggedItems); rect.translate(draggedItemsDelta()); @@ -2977,7 +2901,7 @@ void QListViewPrivate::scrollElasticBandBy(int dx, int dy) elasticBand.moveTop(elasticBand.top() - dy); } -void QDynamicListViewBase::clear() +void QIconModeViewBase::clear() { tree.destroy(); items.clear(); @@ -2986,7 +2910,7 @@ void QDynamicListViewBase::clear() batchSavedDeltaSeg = 0; } -void QDynamicListViewBase::updateContentsSize() +void QIconModeViewBase::updateContentsSize() { QRect bounding; for (int i = 0; i < items.count(); ++i) @@ -3039,7 +2963,7 @@ int QListView::visualIndex(const QModelIndex &index) const Q_D(const QListView); d->executePostedLayout(); QListViewItem itm = d->indexToListViewItem(index); - return d->itemIndex(itm); + return d->commonListView->itemIndex(itm); } QT_END_NAMESPACE diff --git a/src/gui/itemviews/qlistview_p.h b/src/gui/itemviews/qlistview_p.h index 6c8d324..db9eb59 100644 --- a/src/gui/itemviews/qlistview_p.h +++ b/src/gui/itemviews/qlistview_p.h @@ -67,8 +67,8 @@ QT_BEGIN_NAMESPACE class QListViewItem { friend class QListViewPrivate; - friend class QStaticListViewBase; - friend class QDynamicListViewBase; + friend class QListModeViewBase; + friend class QIconModeViewBase; public: inline QListViewItem() : x(-1), y(-1), w(0), h(0), indexHint(-1), visited(0xffff) {} @@ -120,8 +120,42 @@ class QListViewPrivate; class QCommonListViewBase { public: - inline QCommonListViewBase(QListView *q, QListViewPrivate *d) : dd(d), qq(q) {} - + inline QCommonListViewBase(QListView *q, QListViewPrivate *d) : dd(d), qq(q), batchStartRow(0), batchSavedDeltaSeg(0) {} + virtual ~QCommonListViewBase() {} + + //common interface + virtual int itemIndex(const QListViewItem &item) const = 0; + virtual QListViewItem indexToListViewItem(const QModelIndex &index) const = 0; + virtual bool doBatchedItemLayout(const QListViewLayoutInfo &info, int max) = 0; + virtual void clear() = 0; + virtual void setRowCount(int) = 0; + virtual QVector<QModelIndex> intersectingSet(const QRect &area) const = 0; + + virtual int horizontalScrollToValue(int index, QListView::ScrollHint hint, + bool leftOf, bool rightOf, const QRect &area, const QRect &rect) const; + virtual int verticalScrollToValue(int index, QListView::ScrollHint hint, + bool above, bool below, const QRect &area, const QRect &rect) const; + virtual void scrollContentsBy(int dx, int dy, bool scrollElasticBand); + virtual QRect mapToViewport(const QRect &rect) const {return rect;} + virtual int horizontalOffset() const; + virtual int verticalOffset() const { return verticalScrollBar()->value(); } + virtual void updateHorizontalScrollBar(const QSize &step); + virtual void updateVerticalScrollBar(const QSize &step); + virtual void dataChanged(const QModelIndex &, const QModelIndex &) { } + virtual void appendHiddenRow(int row); + virtual void removeHiddenRow(int row); + virtual void setPositionForIndex(const QPoint &, const QModelIndex &) { } + +#ifndef QT_NO_DRAGANDDROP + virtual void paintDragDrop(QPainter *painter) = 0; + virtual bool filterDragMoveEvent(QDragMoveEvent *) { return false; } + virtual bool filterDragLeaveEvent(QDragLeaveEvent *) { return false; } + virtual bool filterDropEvent(QDropEvent *) { return false; } + virtual bool filterStartDrag(Qt::DropActions) { return false; } +#endif + + + //other inline members inline int spacing() const; inline bool isWrapping() const; inline QSize gridSize() const; @@ -133,8 +167,8 @@ public: inline bool uniformItemSizes() const; inline int column() const; - inline int verticalScrollBarValue() const; - inline int horizontalScrollBarValue() const; + inline QScrollBar *verticalScrollBar() const; + inline QScrollBar *horizontalScrollBar() const; inline QListView::ScrollMode verticalScrollMode() const; inline QListView::ScrollMode horizontalScrollMode() const; @@ -157,110 +191,107 @@ public: QListViewPrivate *dd; QListView *qq; + QSize contentsSize; + int batchStartRow; + int batchSavedDeltaSeg; }; -// ### rename to QListModeViewBase -class QStaticListViewBase : public QCommonListViewBase +class QListModeViewBase : public QCommonListViewBase { - friend class QListViewPrivate; public: - QStaticListViewBase(QListView *q, QListViewPrivate *d) : QCommonListViewBase(q, d), - batchStartRow(0), batchSavedDeltaSeg(0), batchSavedPosition(0) {} + QListModeViewBase(QListView *q, QListViewPrivate *d) : QCommonListViewBase(q, d) {} QVector<int> flowPositions; QVector<int> segmentPositions; QVector<int> segmentStartRows; QVector<int> segmentExtents; - QSize contentsSize; - // used when laying out in batches - int batchStartRow; - int batchSavedDeltaSeg; int batchSavedPosition; + //reimplementations + int itemIndex(const QListViewItem &item) const { return item.indexHint; } + QListViewItem indexToListViewItem(const QModelIndex &index) const; bool doBatchedItemLayout(const QListViewLayoutInfo &info, int max); + void clear(); + void setRowCount(int rowCount) { flowPositions.resize(rowCount); } + QVector<QModelIndex> intersectingSet(const QRect &area) const; + + int horizontalScrollToValue(int index, QListView::ScrollHint hint, + bool leftOf, bool rightOf,const QRect &area, const QRect &rect) const; + int verticalScrollToValue(int index, QListView::ScrollHint hint, + bool above, bool below, const QRect &area, const QRect &rect) const; + void scrollContentsBy(int dx, int dy, bool scrollElasticBand); + QRect mapToViewport(const QRect &rect) const; + int horizontalOffset() const; + int verticalOffset() const; + void updateHorizontalScrollBar(const QSize &step); + void updateVerticalScrollBar(const QSize &step); + +#ifndef QT_NO_DRAGANDDROP + void paintDragDrop(QPainter *painter); +#endif +private: QPoint initStaticLayout(const QListViewLayoutInfo &info); void doStaticLayout(const QListViewLayoutInfo &info); - QVector<QModelIndex> intersectingStaticSet(const QRect &area) const; - - int itemIndex(const QListViewItem &item) const; - - int perItemScrollingPageSteps(int length, int bounds, bool wrap) const; - int perItemScrollToValue(int index, int value, int height, QAbstractItemView::ScrollHint hint, Qt::Orientation orientation, bool wrap, int extent) const; - - QRect mapToViewport(const QRect &rect) const; - - QListViewItem indexToListViewItem(const QModelIndex &index) const; - - void scrollContentsBy(int &dx, int &dy); - - int verticalPerItemValue(int itemIndex, int verticalValue, int areaHeight, - bool above, bool below, bool wrap, QListView::ScrollHint hint, int itemHeight) const; - int horizontalPerItemValue(int itemIndex, int horizontalValue, int areaWidth, - bool leftOf, bool rightOf, bool wrap, QListView::ScrollHint hint, int itemWidth) const; - - void clear(); + int perItemScrollingPageSteps(int length, int bounds, bool wrap) const; }; -// ### rename to QIconModeViewBase -class QDynamicListViewBase : public QCommonListViewBase +class QIconModeViewBase : public QCommonListViewBase { - friend class QListViewPrivate; public: - QDynamicListViewBase(QListView *q, QListViewPrivate *d) : QCommonListViewBase(q, d), - batchStartRow(0), batchSavedDeltaSeg(0), interSectingVector(0) {} + QIconModeViewBase(QListView *q, QListViewPrivate *d) : QCommonListViewBase(q, d), interSectingVector(0) {} QBspTree tree; QVector<QListViewItem> items; QBitArray moved; - QSize contentsSize; - QVector<QModelIndex> draggedItems; // indices to the tree.itemVector mutable QPoint draggedItemsPos; // used when laying out in batches - int batchStartRow; - int batchSavedDeltaSeg; - QVector<QModelIndex> *interSectingVector; //used from within intersectingDynamicSet + QVector<QModelIndex> *interSectingVector; //used from within intersectingSet - void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + //reimplementations + int itemIndex(const QListViewItem &item) const; + QListViewItem indexToListViewItem(const QModelIndex &index) const; bool doBatchedItemLayout(const QListViewLayoutInfo &info, int max); + void clear(); + void setRowCount(int rowCount); + QVector<QModelIndex> intersectingSet(const QRect &area) const; + + void scrollContentsBy(int dx, int dy, bool scrollElasticBand); + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void appendHiddenRow(int row); + void removeHiddenRow(int row); + void setPositionForIndex(const QPoint &position, const QModelIndex &index); + +#ifndef QT_NO_DRAGANDDROP + void paintDragDrop(QPainter *painter); + bool filterDragMoveEvent(QDragMoveEvent *); + bool filterDragLeaveEvent(QDragLeaveEvent *); + bool filterDropEvent(QDropEvent *e); + bool filterStartDrag(Qt::DropActions); +#endif +private: void initBspTree(const QSize &contents); QPoint initDynamicLayout(const QListViewLayoutInfo &info); void doDynamicLayout(const QListViewLayoutInfo &info); - QVector<QModelIndex> intersectingDynamicSet(const QRect &area) const; - static void addLeaf(QVector<int> &leaf, const QRect &area, uint visited, QBspTree::Data data); - - void insertItem(int index); - void removeItem(int index); - void moveItem(int index, const QPoint &dest); - - int itemIndex(const QListViewItem &item) const; - - void createItems(int to); - void drawItems(QPainter *painter, const QVector<QModelIndex> &indexes) const; QRect itemsRect(const QVector<QModelIndex> &indexes) const; - - QPoint draggedItemsDelta() const; QRect draggedItemsRect() const; - QPoint snapToGrid(const QPoint &pos) const; - - void scrollElasticBandBy(int dx, int dy); - - QListViewItem indexToListViewItem(const QModelIndex &index) const; - - void clear(); void updateContentsSize(); + QPoint draggedItemsDelta() const; + void drawItems(QPainter *painter, const QVector<QModelIndex> &indexes) const; + void moveItem(int index, const QPoint &dest); + }; class QListViewPrivate: public QAbstractItemViewPrivate @@ -278,23 +309,13 @@ public: inline QVector<QModelIndex> intersectingSet(const QRect &area, bool doLayout = true) const { if (doLayout) executePostedLayout(); QRect a = (q_func()->isRightToLeft() ? flipX(area.normalized()) : area.normalized()); - return (viewMode == QListView::ListMode) ? staticListView->intersectingStaticSet(a) - : dynamicListView->intersectingDynamicSet(a); + return commonListView->intersectingSet(a); } - // ### FIXME: - inline void resetBatchStartRow() - { if (viewMode == QListView::ListMode) staticListView->batchStartRow = 0; - else dynamicListView->batchStartRow = 0; } - inline int batchStartRow() const - { return (viewMode == QListView::ListMode - ? staticListView->batchStartRow : dynamicListView->batchStartRow); } - inline QSize contentsSize() const - { return (viewMode == QListView::ListMode - ? staticListView->contentsSize : dynamicListView->contentsSize); } - inline void setContentsSize(int w, int h) - { if (viewMode == QListView::ListMode) staticListView->contentsSize = QSize(w, h); - else dynamicListView->contentsSize = QSize(w, h); } + inline void resetBatchStartRow() { commonListView->batchStartRow = 0; } + inline int batchStartRow() const { return commonListView->batchStartRow; } + inline QSize contentsSize() const { return commonListView->contentsSize; } + inline void setContentsSize(int w, int h) { commonListView->contentsSize = QSize(w, h); } inline int flipX(int x) const { return qMax(viewport->width(), contentsSize().width()) - x; } @@ -305,12 +326,22 @@ public: inline QRect viewItemRect(const QListViewItem &item) const { if (q_func()->isRightToLeft()) return flipX(item.rect()); return item.rect(); } - int itemIndex(const QListViewItem &item) const; QListViewItem indexToListViewItem(const QModelIndex &index) const; inline QModelIndex listViewItemToIndex(const QListViewItem &item) const - { return model->index(itemIndex(item), column, root); } + { return model->index(commonListView->itemIndex(item), column, root); } + + QRect rectForIndex(const QModelIndex &index) const + { + if (!isIndexValid(index) || index.parent() != root || index.column() != column || isHidden(index.row())) + return QRect(); + executePostedLayout(); + return viewItemRect(indexToListViewItem(index)); + } + + void viewUpdateGeometries() { q_func()->updateGeometries(); } - QRect mapToViewport(const QRect &rect, bool greedy = false) const; + + QRect mapToViewport(const QRect &rect, bool extend = true) const; QModelIndex closestIndex(const QRect &target, const QVector<QModelIndex> &candidates) const; QSize itemSize(const QStyleOptionViewItem &option, const QModelIndex &index) const; @@ -351,10 +382,10 @@ public: QItemViewPaintPairs draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const; - union { - QDynamicListViewBase *dynamicListView; - QStaticListViewBase *staticListView; - }; + void emitIndexesMoved(const QModelIndexList &indexes) { emit q_func()->indexesMoved(indexes); } + + + QCommonListViewBase *commonListView; // ### FIXME: see if we can move the members into the dynamic/static classes @@ -412,8 +443,8 @@ inline QPoint QCommonListViewBase::pressedPosition() const { return dd->pressedP inline bool QCommonListViewBase::uniformItemSizes() const { return dd->uniformItemSizes; } inline int QCommonListViewBase::column() const { return dd->column; } -inline int QCommonListViewBase::verticalScrollBarValue() const { return qq->verticalScrollBar()->value(); } -inline int QCommonListViewBase::horizontalScrollBarValue() const { return qq->horizontalScrollBar()->value(); } +inline QScrollBar *QCommonListViewBase::verticalScrollBar() const { return qq->verticalScrollBar(); } +inline QScrollBar *QCommonListViewBase::horizontalScrollBar() const { return qq->horizontalScrollBar(); } inline QListView::ScrollMode QCommonListViewBase::verticalScrollMode() const { return qq->verticalScrollMode(); } inline QListView::ScrollMode QCommonListViewBase::horizontalScrollMode() const { return qq->horizontalScrollMode(); } diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 574d845..ccf6ac9 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -101,6 +101,10 @@ extern bool qt_wince_is_pocket_pc(); //qguifunctions_wince.cpp #include "qdatetime.h" +#ifdef QT_MAC_USE_COCOA +#include <private/qt_cocoa_helpers_mac_p.h> +#endif + //#define ALIEN_DEBUG static void initResources() @@ -3477,6 +3481,15 @@ void QApplication::changeOverrideCursor(const QCursor &cursor) if (qApp->d_func()->cursor_list.isEmpty()) return; qApp->d_func()->cursor_list.removeFirst(); +#ifdef QT_MAC_USE_COCOA + // We use native NSCursor stacks in Cocoa. The currentCursor is the + // top of this stack. So to avoid flickering of cursor, we have to + // change the cusor instead of pop-ing the existing OverrideCursor + // and pushing the new one. + qApp->d_func()->cursor_list.prepend(cursor); + qt_cocoaChangeOverrideCursor(cursor); + return; +#endif setOverrideCursor(cursor); } #endif diff --git a/src/gui/kernel/qcocoaapplicationdelegate_mac.mm b/src/gui/kernel/qcocoaapplicationdelegate_mac.mm index 572df70..172d07b 100644 --- a/src/gui/kernel/qcocoaapplicationdelegate_mac.mm +++ b/src/gui/kernel/qcocoaapplicationdelegate_mac.mm @@ -197,11 +197,19 @@ static void cleanupCocoaApplicationDelegate() } } - // Prevent Cocoa from terminating the application, since this simply - // exits the program whithout allowing QApplication::exec() to return. - // The call to QApplication::quit() above will instead quit the - // application from the Qt side. - return NSTerminateCancel; + if (qtPrivate->threadData->eventLoops.size() == 0) { + // INVARIANT: No event loop is executing. This probably + // means that Qt is used as a plugin, or as a part of a native + // Cocoa application. In any case it should be fine to + // terminate now: + return NSTerminateNow; + } else { + // Prevent Cocoa from terminating the application, since this simply + // exits the program whithout allowing QApplication::exec() to return. + // The call to QApplication::quit() above will instead quit the + // application from the Qt side. + return NSTerminateCancel; + } } - (void)applicationDidFinishLaunching:(NSNotification *)aNotification diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm index 7ac0d89..5a0209d 100644 --- a/src/gui/kernel/qcocoaview_mac.mm +++ b/src/gui/kernel/qcocoaview_mac.mm @@ -667,7 +667,15 @@ extern "C" { - (void)mouseMoved:(NSEvent *)theEvent { - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); + // We always enable mouse tracking for all QCocoaView-s. In cases where we have + // child views, we will receive mouseMoved for both parent & the child (if + // mouse is over the child). We need to ignore the parent mouseMoved in such + // cases. + NSPoint windowPoint = [theEvent locationInWindow]; + NSView *candidateView = [[[self window] contentView] hitTest:windowPoint]; + if (candidateView && candidateView == self) { + qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); + } } - (void)mouseDown:(NSEvent *)theEvent diff --git a/src/gui/kernel/qshortcutmap.cpp b/src/gui/kernel/qshortcutmap.cpp index c965bac..0027deb 100644 --- a/src/gui/kernel/qshortcutmap.cpp +++ b/src/gui/kernel/qshortcutmap.cpp @@ -99,11 +99,11 @@ struct QShortcutEntry QObject *owner; }; -#ifndef QT_NO_DEBUG_STREAM +#if 0 //ndef QT_NO_DEBUG_STREAM /*! \internal QDebug operator<< for easy debug output of the shortcut entries. */ -QDebug &operator<<(QDebug &dbg, const QShortcutEntry *se) { +static QDebug &operator<<(QDebug &dbg, const QShortcutEntry *se) { if (!se) return dbg << "QShortcutEntry(0x0)"; dbg.nospace() diff --git a/src/gui/kernel/qt_cocoa_helpers_mac.mm b/src/gui/kernel/qt_cocoa_helpers_mac.mm index 7596802..a9b8970 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac.mm +++ b/src/gui/kernel/qt_cocoa_helpers_mac.mm @@ -1238,4 +1238,12 @@ void qt_mac_menu_collapseSeparators(void */*NSMenu **/ theMenu, bool collapse) } } +#ifdef QT_MAC_USE_COCOA +void qt_cocoaChangeOverrideCursor(const QCursor &cursor) +{ + QMacCocoaAutoReleasePool pool; + [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursor)) set]; +} +#endif + QT_END_NAMESPACE diff --git a/src/gui/kernel/qt_cocoa_helpers_mac_p.h b/src/gui/kernel/qt_cocoa_helpers_mac_p.h index 24d7096..932abef 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac_p.h +++ b/src/gui/kernel/qt_cocoa_helpers_mac_p.h @@ -133,6 +133,7 @@ bool qt_mac_checkForNativeSizeGrip(const QWidget *widget); void qt_dispatchTabletProximityEvent(void * /*NSEvent * */ tabletEvent); #ifdef QT_MAC_USE_COCOA bool qt_dispatchKeyEventWithCocoa(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent); +void qt_cocoaChangeOverrideCursor(const QCursor &cursor); #endif void qt_mac_menu_collapseSeparators(void * /*NSMenu */ menu, bool collapse); bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent); diff --git a/src/gui/kernel/qwidget_win.cpp b/src/gui/kernel/qwidget_win.cpp index 77ab590..ea334b6 100644 --- a/src/gui/kernel/qwidget_win.cpp +++ b/src/gui/kernel/qwidget_win.cpp @@ -2060,8 +2060,6 @@ void QWidgetPrivate::winSetupGestures() Q_Q(QWidget); if (!q) return; - q->setAttribute(Qt::WA_DontCreateNativeAncestors); - q->setAttribute(Qt::WA_NativeWindow); if (!q->isVisible()) return; QApplicationPrivate *qAppPriv = QApplicationPrivate::instance(); diff --git a/src/gui/styles/gtksymbols.cpp b/src/gui/styles/gtksymbols.cpp index 51f40e3..6a5ea49 100644 --- a/src/gui/styles/gtksymbols.cpp +++ b/src/gui/styles/gtksymbols.cpp @@ -642,7 +642,7 @@ GtkStyle* QGtk::gtkStyle(const QString &path) return 0; } -static void update_toolbar_style(GtkWidget *gtkToolBar, GParamSpec *pspec, gpointer user_data) +static void update_toolbar_style(GtkWidget *gtkToolBar, GParamSpec *, gpointer) { GtkToolbarStyle toolbar_style = GTK_TOOLBAR_ICONS; g_object_get(gtkToolBar, "toolbar-style", &toolbar_style, NULL); diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp index 533ef46..e7aeed0 100644 --- a/src/gui/text/qtextdocument_p.cpp +++ b/src/gui/text/qtextdocument_p.cpp @@ -63,10 +63,10 @@ QT_BEGIN_NAMESPACE // The VxWorks DIAB compiler crashes when initializing the anonymouse union with { a7 } #if !defined(Q_CC_DIAB) # define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \ - QTextUndoCommand c = { a1, a2, a3, a4, a5, a6, { a7 }, a8 } + QTextUndoCommand c = { a1, a2, 0, 0, a3, a4, a5, a6, { a7 }, a8 } #else # define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \ - QTextUndoCommand c = { a1, a2, a3, a4, a5, a6 }; c.blockFormat = a7; c.revision = a8 + QTextUndoCommand c = { a1, a2, 0, 0, a3, a4, a5, a6 }; c.blockFormat = a7; c.revision = a8 #endif /* @@ -961,14 +961,18 @@ int QTextDocumentPrivate::undoRedo(bool undo) B->revision = c.revision; } - if (undo) { - if (undoState == 0 || !undoStack[undoState-1].block) - break; - } else { + if (!undo) ++undoState; - if (undoState == undoStack.size() || !undoStack[undoState-1].block) - break; - } + + bool inBlock = ( + undoState > 0 + && undoState < undoStack.size() + && undoStack[undoState].block_part + && undoStack[undoState-1].block_part + && !undoStack[undoState-1].block_end + ); + if (!inBlock) + break; } undoEnabled = true; int editPos = -1; @@ -993,7 +997,8 @@ void QTextDocumentPrivate::appendUndoItem(QAbstractUndoItem *item) QTextUndoCommand c; c.command = QTextUndoCommand::Custom; - c.block = editBlock != 0; + c.block_part = editBlock != 0; + c.block_end = 0; c.operation = QTextUndoCommand::MoveCursor; c.format = 0; c.strPos = 0; @@ -1014,9 +1019,10 @@ void QTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c) if (!undoStack.isEmpty() && modified) { QTextUndoCommand &last = undoStack[undoState - 1]; - if ( (last.block && c.block) // part of the same block => can merge - || (!c.block && !last.block // two single undo items => can merge - && (undoState < 2 || !undoStack[undoState-2].block))) { + + if ( (last.block_part && c.block_part && !last.block_end) // part of the same block => can merge + || (!c.block_part && !last.block_part)) { // two single undo items => can merge + if (last.tryMerge(c)) return; } @@ -1028,7 +1034,7 @@ void QTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c) emitUndoAvailable(true); emitRedoAvailable(false); - if (!c.block) + if (!c.block_part) emit document()->undoCommandAdded(); } @@ -1094,7 +1100,7 @@ void QTextDocumentPrivate::joinPreviousEditBlock() beginEditBlock(); if (undoEnabled && undoState) - undoStack[undoState - 1].block = true; + undoStack[undoState - 1].block_end = false; } void QTextDocumentPrivate::endEditBlock() @@ -1103,10 +1109,10 @@ void QTextDocumentPrivate::endEditBlock() return; if (undoEnabled && undoState > 0) { - const bool wasBlocking = undoStack[undoState - 1].block; - undoStack[undoState - 1].block = false; - if (wasBlocking) + if (undoStack[undoState - 1].block_part) { + undoStack[undoState - 1].block_end = true; emit document()->undoCommandAdded(); + } } finishEdit(); diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h index 55aa17e..363309c 100644 --- a/src/gui/text/qtextdocument_p.h +++ b/src/gui/text/qtextdocument_p.h @@ -139,7 +139,9 @@ public: MoveCursor = 1 }; quint16 command; - quint8 block; ///< All undo commands that have this set to true are combined with the preceding command on undo/redo. + uint block_part : 1; // all commands that are part of an undo block (including the first and the last one) have this set to 1 + uint block_end : 1; // the last command in an undo block has this set to 1. + uint block_padding : 6; // padding since block used to be a quint8 quint8 operation; int format; quint32 strPos; diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index bf2d58a..397e0b5 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -65,19 +65,37 @@ QT_BEGIN_NAMESPACE -const int QHttpNetworkConnectionPrivate::channelCount = 6; +const int QHttpNetworkConnectionPrivate::defaultChannelCount = 6; + +// the maximum amount of requests that might be pipelined into a socket +// from what was suggested, 3 seems to be OK +const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3; + QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt) : hostName(hostName), port(port), encrypt(encrypt), + channelCount(defaultChannelCount), pendingAuthSignal(false), pendingProxyAuthSignal(false) #ifndef QT_NO_NETWORKPROXY , networkProxy(QNetworkProxy::NoProxy) #endif +{ + channels = new QHttpNetworkConnectionChannel[channelCount]; +} +QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt) +: hostName(hostName), port(port), encrypt(encrypt), + channelCount(channelCount), + pendingAuthSignal(false), pendingProxyAuthSignal(false) +#ifndef QT_NO_NETWORKPROXY + , networkProxy(QNetworkProxy::NoProxy) +#endif { channels = new QHttpNetworkConnectionChannel[channelCount]; } + + QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate() { for (int i = 0; i < channelCount; ++i) { @@ -139,17 +157,6 @@ qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailableNextBlock(const return reply.d_func()->responseData.sizeNextBlock(); } -qint64 QHttpNetworkConnectionPrivate::compressedBytesAvailable(const QHttpNetworkReply &reply) const -{ - return reply.d_func()->compressedData.size(); -} - -void QHttpNetworkConnectionPrivate::eraseData(QHttpNetworkReply *reply) -{ - reply->d_func()->compressedData.clear(); - reply->d_func()->responseData.clear(); -} - void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair) { QHttpNetworkRequest &request = messagePair.first; @@ -223,231 +230,8 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair) reply->d_func()->requestIsPrepared = true; } -bool QHttpNetworkConnectionPrivate::ensureConnection(QAbstractSocket *socket) -{ - // make sure that this socket is in a connected state, if not initiate - // connection to the host. - if (socket->state() != QAbstractSocket::ConnectedState) { - // connect to the host if not already connected. - int index = indexOf(socket); - // resend this request after we receive the disconnected signal - if (socket->state() == QAbstractSocket::ClosingState) { - channels[index].resendCurrent = true; - return false; - } - channels[index].state = QHttpNetworkConnectionChannel::ConnectingState; - channels[index].pendingEncrypt = encrypt; - - // This workaround is needed since we use QAuthenticator for NTLM authentication. The "phase == Done" - // is the usual criteria for emitting authentication signals. The "phase" is set to "Done" when the - // last header for Authorization is generated by the QAuthenticator. Basic & Digest logic does not - // check the "phase" for generating the Authorization header. NTLM authentication is a two stage - // process & needs the "phase". To make sure the QAuthenticator uses the current username/password - // the phase is reset to Start. - QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[index].authenticator); - if (priv && priv->phase == QAuthenticatorPrivate::Done) - priv->phase = QAuthenticatorPrivate::Start; - priv = QAuthenticatorPrivate::getPrivate(channels[index].proxyAuthenticator); - if (priv && priv->phase == QAuthenticatorPrivate::Done) - priv->phase = QAuthenticatorPrivate::Start; - - QString connectHost = hostName; - qint16 connectPort = port; - -#ifndef QT_NO_NETWORKPROXY - // HTTPS always use transparent proxy. - if (networkProxy.type() != QNetworkProxy::NoProxy && !encrypt) { - connectHost = networkProxy.hostName(); - connectPort = networkProxy.port(); - } -#endif - if (encrypt) { -#ifndef QT_NO_OPENSSL - QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); - sslSocket->connectToHostEncrypted(connectHost, connectPort); - if (channels[index].ignoreAllSslErrors) - sslSocket->ignoreSslErrors(); - sslSocket->ignoreSslErrors(channels[index].ignoreSslErrorsList); -#else - emitReplyError(socket, channels[index].reply, QNetworkReply::ProtocolUnknownError); -#endif - } else { - socket->connectToHost(connectHost, connectPort); - } - return false; - } - return true; -} - -bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket) -{ - Q_Q(QHttpNetworkConnection); - int i = indexOf(socket); - switch (channels[i].state) { - case QHttpNetworkConnectionChannel::IdleState: { // write the header - if (!ensureConnection(socket)) { - // wait for the connection (and encryption) to be done - // sendRequest will be called again from either - // _q_connected or _q_encrypted - return false; - } - channels[i].written = 0; // excluding the header - channels[i].bytesTotal = 0; - if (channels[i].reply) { - channels[i].reply->d_func()->clear(); - channels[i].reply->d_func()->connection = q; - channels[i].reply->d_func()->autoDecompress = channels[i].request.d->autoDecompress; - } - channels[i].state = QHttpNetworkConnectionChannel::WritingState; - channels[i].pendingEncrypt = false; - // if the url contains authentication parameters, use the new ones - // both channels will use the new authentication parameters - if (!channels[i].request.url().userInfo().isEmpty()) { - QUrl url = channels[i].request.url(); - QAuthenticator &auth = channels[i].authenticator; - if (url.userName() != auth.user() - || (!url.password().isEmpty() && url.password() != auth.password())) { - auth.setUser(url.userName()); - auth.setPassword(url.password()); - copyCredentials(i, &auth, false); - } - // clear the userinfo, since we use the same request for resending - // userinfo in url can conflict with the one in the authenticator - url.setUserInfo(QString()); - channels[i].request.setUrl(url); - } - createAuthorization(socket, channels[i].request); -#ifndef QT_NO_NETWORKPROXY - QByteArray header = QHttpNetworkRequestPrivate::header(channels[i].request, - (networkProxy.type() != QNetworkProxy::NoProxy)); -#else - QByteArray header = QHttpNetworkRequestPrivate::header(channels[i].request, - false); -#endif - socket->write(header); - QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice(); - if (uploadByteDevice) { - // connect the signals so this function gets called again - QObject::connect(uploadByteDevice, SIGNAL(readyRead()), &channels[i], SLOT(_q_uploadDataReadyRead())); - - channels[i].bytesTotal = channels[i].request.contentLength(); - } else { - socket->flush(); // ### Remove this when pipelining is implemented. We want less TCP packets! - channels[i].state = QHttpNetworkConnectionChannel::WaitingState; - break; - } - // write the initial chunk together with the headers - // fall through - } - case QHttpNetworkConnectionChannel::WritingState: - { - // write the data - QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice(); - if (!uploadByteDevice || channels[i].bytesTotal == channels[i].written) { - if (uploadByteDevice) - emit channels[i].reply->dataSendProgress(channels[i].written, channels[i].bytesTotal); - channels[i].state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response - sendRequest(socket); - break; - } - - // only feed the QTcpSocket buffer when there is less than 32 kB in it - const qint64 socketBufferFill = 32*1024; - const qint64 socketWriteMaxSize = 16*1024; - - -#ifndef QT_NO_OPENSSL - QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); - // if it is really an ssl socket, check more than just bytesToWrite() - while ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0)) - <= socketBufferFill && channels[i].bytesTotal != channels[i].written) -#else - while (socket->bytesToWrite() <= socketBufferFill - && channels[i].bytesTotal != channels[i].written) -#endif - { - // get pointer to upload data - qint64 currentReadSize; - qint64 desiredReadSize = qMin(socketWriteMaxSize, channels[i].bytesTotal - channels[i].written); - const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize); - - if (currentReadSize == -1) { - // premature eof happened - emitReplyError(socket, channels[i].reply, QNetworkReply::UnknownNetworkError); - return false; - break; - } else if (readPointer == 0 || currentReadSize == 0) { - // nothing to read currently, break the loop - break; - } else { - qint64 currentWriteSize = socket->write(readPointer, currentReadSize); - if (currentWriteSize == -1 || currentWriteSize != currentReadSize) { - // socket broke down - emitReplyError(socket, channels[i].reply, QNetworkReply::UnknownNetworkError); - return false; - } else { - channels[i].written += currentWriteSize; - uploadByteDevice->advanceReadPointer(currentWriteSize); - - emit channels[i].reply->dataSendProgress(channels[i].written, channels[i].bytesTotal); - - if (channels[i].written == channels[i].bytesTotal) { - // make sure this function is called once again - channels[i].state = QHttpNetworkConnectionChannel::WaitingState; - sendRequest(socket); - break; - } - } - } - } - break; - } - - case QHttpNetworkConnectionChannel::WaitingState: - { - QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice(); - if (uploadByteDevice) { - QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), &channels[i], SLOT(_q_uploadDataReadyRead())); - } - // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called - // this is needed if the sends an reply before we have finished sending the request. In that - // case receiveReply had been called before but ignored the server reply - receiveReply(socket, channels[i].reply); - break; - } - case QHttpNetworkConnectionChannel::ReadingState: - case QHttpNetworkConnectionChannel::Wait4AuthState: - // ignore _q_bytesWritten in these states - // fall through - default: - break; - } - return true; -} - -bool QHttpNetworkConnectionPrivate::shouldEmitSignals(QHttpNetworkReply *reply) -{ - // for 401 & 407 don't emit the data signals. Content along with these - // responses are send only if the authentication fails. - return (reply && reply->d_func()->statusCode != 401 && reply->d_func()->statusCode != 407); -} - -bool QHttpNetworkConnectionPrivate::expectContent(QHttpNetworkReply *reply) -{ - // check whether we can expect content after the headers (rfc 2616, sec4.4) - if (!reply) - return false; - if ((reply->d_func()->statusCode >= 100 && reply->d_func()->statusCode < 200) - || reply->d_func()->statusCode == 204 || reply->d_func()->statusCode == 304) - return false; - if (reply->d_func()->request.operation() == QHttpNetworkRequest::Head) - return !shouldEmitSignals(reply); - if (reply->d_func()->contentLength() == 0) - return false; - return true; -} void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, @@ -460,271 +244,13 @@ void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket, emit reply->finishedWithError(errorCode, reply->d_func()->errorString); int i = indexOf(socket); // remove the corrupt data if any - eraseData(channels[i].reply); - closeChannel(i); + reply->d_func()->eraseData(); + channels[i].close(); // send the next request QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); } } -#ifndef QT_NO_COMPRESS -bool QHttpNetworkConnectionPrivate::expand(QAbstractSocket *socket, QHttpNetworkReply *reply, bool dataComplete) -{ - Q_ASSERT(socket); - Q_ASSERT(reply); - - qint64 total = compressedBytesAvailable(*reply); - if (total >= CHUNK || dataComplete) { - int i = indexOf(socket); - // uncompress the data - QByteArray content, inflated; - content = reply->d_func()->compressedData; - reply->d_func()->compressedData.clear(); - - int ret = Z_OK; - if (content.size()) - ret = reply->d_func()->gunzipBodyPartially(content, inflated); - int retCheck = (dataComplete) ? Z_STREAM_END : Z_OK; - if (ret >= retCheck) { - if (inflated.size()) { - reply->d_func()->totalProgress += inflated.size(); - reply->d_func()->appendUncompressedReplyData(inflated); - if (shouldEmitSignals(reply)) { - // important: At the point of this readyRead(), inflated must be cleared, - // else implicit sharing will trigger memcpy when the user is reading data! - emit reply->readyRead(); - // make sure that the reply is valid - if (channels[i].reply != reply) - return true; - emit reply->dataReadProgress(reply->d_func()->totalProgress, 0); - // make sure that the reply is valid - if (channels[i].reply != reply) - return true; - - } - } - } else { - emitReplyError(socket, reply, QNetworkReply::ProtocolFailure); - return false; - } - } - return true; -} -#endif - -void QHttpNetworkConnectionPrivate::receiveReply(QAbstractSocket *socket, QHttpNetworkReply *reply) -{ - Q_ASSERT(socket); - - Q_Q(QHttpNetworkConnection); - qint64 bytes = 0; - QAbstractSocket::SocketState state = socket->state(); - int i = indexOf(socket); - - // connection might be closed to signal the end of data - if (state == QAbstractSocket::UnconnectedState) { - if (!socket->bytesAvailable()) { - if (reply && reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) { - reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState; - channels[i].state = QHttpNetworkConnectionChannel::IdleState; - allDone(socket, reply); - } else { - // try to reconnect/resend before sending an error. - if (channels[i].reconnectAttempts-- > 0) { - resendCurrentRequest(socket); - } else if (reply) { - reply->d_func()->errorString = errorDetail(QNetworkReply::RemoteHostClosedError, socket); - emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString); - QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); - } - } - } - } - - // read loop for the response - while (socket->bytesAvailable()) { - QHttpNetworkReplyPrivate::ReplyState state = reply ? reply->d_func()->state : QHttpNetworkReplyPrivate::AllDoneState; - switch (state) { - case QHttpNetworkReplyPrivate::NothingDoneState: - case QHttpNetworkReplyPrivate::ReadingStatusState: { - qint64 statusBytes = reply->d_func()->readStatus(socket); - if (statusBytes == -1) { - // error reading the status, close the socket and emit error - socket->close(); - reply->d_func()->errorString = errorDetail(QNetworkReply::ProtocolFailure, socket); - emit reply->finishedWithError(QNetworkReply::ProtocolFailure, reply->d_func()->errorString); - QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); - break; - } - bytes += statusBytes; - channels[i].lastStatus = reply->d_func()->statusCode; - break; - } - case QHttpNetworkReplyPrivate::ReadingHeaderState: - bytes += reply->d_func()->readHeader(socket); - if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) { - if (reply->d_func()->isGzipped() && reply->d_func()->autoDecompress) { - // remove the Content-Length from header - reply->d_func()->removeAutoDecompressHeader(); - } else { - reply->d_func()->autoDecompress = false; - } - if (reply && reply->d_func()->statusCode == 100) { - reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState; - break; // ignore - } - if (shouldEmitSignals(reply)) - emit reply->headerChanged(); - if (!expectContent(reply)) { - reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState; - channels[i].state = QHttpNetworkConnectionChannel::IdleState; - allDone(socket, reply); - return; - } - } - break; - case QHttpNetworkReplyPrivate::ReadingDataState: { - if (!reply->d_func()->isChunked() && !reply->d_func()->autoDecompress - && reply->d_func()->bodyLength > 0) { - // bulk files like images should fulfill these properties and - // we can therefore save on memory copying - bytes = reply->d_func()->readBodyFast(socket, &reply->d_func()->responseData); - reply->d_func()->totalProgress += bytes; - if (shouldEmitSignals(reply)) { - emit reply->readyRead(); - // make sure that the reply is valid - if (channels[i].reply != reply) - return; - emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength); - // make sure that the reply is valid - if (channels[i].reply != reply) - return; - } - } - else - { - // use the traditional slower reading (for compressed encoding, chunked encoding, - // no content-length etc) - QByteDataBuffer byteDatas; - bytes = reply->d_func()->readBody(socket, &byteDatas); - if (bytes) { - if (reply->d_func()->autoDecompress) - reply->d_func()->appendCompressedReplyData(byteDatas); - else - reply->d_func()->appendUncompressedReplyData(byteDatas); - - if (!reply->d_func()->autoDecompress) { - reply->d_func()->totalProgress += bytes; - if (shouldEmitSignals(reply)) { - // important: At the point of this readyRead(), the byteDatas list must be empty, - // else implicit sharing will trigger memcpy when the user is reading data! - emit reply->readyRead(); - // make sure that the reply is valid - if (channels[i].reply != reply) - return; - emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength); - // make sure that the reply is valid - if (channels[i].reply != reply) - return; - } - } -#ifndef QT_NO_COMPRESS - else if (!expand(socket, reply, false)) { // expand a chunk if possible - return; // ### expand failed - } -#endif - } - } - if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) - break; - // everything done, fall through - } - case QHttpNetworkReplyPrivate::AllDoneState: - channels[i].state = QHttpNetworkConnectionChannel::IdleState; - allDone(socket, reply); - break; - default: - break; - } - } -} - -void QHttpNetworkConnectionPrivate::allDone(QAbstractSocket *socket, QHttpNetworkReply *reply) -{ -#ifndef QT_NO_COMPRESS - // expand the whole data. - if (expectContent(reply) && reply->d_func()->autoDecompress && !reply->d_func()->streamEnd) - expand(socket, reply, true); // ### if expand returns false, its an error -#endif - // while handling 401 & 407, we might reset the status code, so save this. - bool emitFinished = shouldEmitSignals(reply); - handleStatus(socket, reply); - // ### at this point there should be no more data on the socket - // close if server requested - int i = indexOf(socket); - if (reply->d_func()->connectionCloseEnabled()) - closeChannel(i); - // queue the finished signal, this is required since we might send new requests from - // slot connected to it. The socket will not fire readyRead signal, if we are already - // in the slot connected to readyRead - if (emitFinished) - QMetaObject::invokeMethod(reply, "finished", Qt::QueuedConnection); - // reset the reconnection attempts after we receive a complete reply. - // in case of failures, each channel will attempt two reconnects before emitting error. - channels[i].reconnectAttempts = 2; -} - -void QHttpNetworkConnectionPrivate::handleStatus(QAbstractSocket *socket, QHttpNetworkReply *reply) -{ - Q_ASSERT(socket); - Q_ASSERT(reply); - - Q_Q(QHttpNetworkConnection); - - int statusCode = reply->statusCode(); - bool resend = false; - - switch (statusCode) { - case 401: - case 407: - if (handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) { - if (resend) { - int i = indexOf(socket); - - QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice(); - if (uploadByteDevice) { - if (uploadByteDevice->reset()) { - channels[i].written = 0; - } else { - emitReplyError(socket, reply, QNetworkReply::ContentReSendError); - break; - } - } - - eraseData(reply); - - // also use async _q_startNextRequest so we dont break with closed - // proxy or server connections.. - channels[i].resendCurrent = true; - QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); - } - } else { - int i = indexOf(socket); - emit channels[i].reply->headerChanged(); - emit channels[i].reply->readyRead(); - QNetworkReply::NetworkError errorCode = (statusCode == 407) - ? QNetworkReply::ProxyAuthenticationRequiredError - : QNetworkReply::AuthenticationRequiredError; - reply->d_func()->errorString = errorDetail(errorCode, socket); - emit q->error(errorCode, reply->d_func()->errorString); - emit channels[i].reply->finished(); - } - break; - default: - QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); - } -} - void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy) { Q_ASSERT(auth); @@ -747,6 +273,7 @@ void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthentica } +// handles the authentication for one channel and eventually re-starts the other channels bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend) { @@ -786,8 +313,8 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket if (priv->phase == QAuthenticatorPrivate::Done) { if ((isProxy && pendingProxyAuthSignal) ||(!isProxy && pendingAuthSignal)) { // drop the request - eraseData(channels[i].reply); - closeChannel(i); + reply->d_func()->eraseData(); + channels[i].close(); channels[i].lastStatus = 0; channels[i].state = QHttpNetworkConnectionChannel::Wait4AuthState; return false; @@ -892,7 +419,24 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor return reply; } -void QHttpNetworkConnectionPrivate::unqueueAndSendRequest(QAbstractSocket *socket) +void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair) +{ + Q_Q(QHttpNetworkConnection); + + QHttpNetworkRequest request = pair.first; + switch (request.priority()) { + case QHttpNetworkRequest::HighPriority: + highPriorityQueue.prepend(pair); + break; + case QHttpNetworkRequest::NormalPriority: + case QHttpNetworkRequest::LowPriority: + lowPriorityQueue.prepend(pair); + break; + } + QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); +} + +void QHttpNetworkConnectionPrivate::dequeueAndSendRequest(QAbstractSocket *socket) { Q_ASSERT(socket); @@ -906,8 +450,9 @@ void QHttpNetworkConnectionPrivate::unqueueAndSendRequest(QAbstractSocket *socke channels[i].request = messagePair.first; channels[i].reply = messagePair.second; - sendRequest(socket); + // remove before sendRequest! else we might pipeline the same request again highPriorityQueue.removeAt(j); + channels[i].sendRequest(); return; } } @@ -919,28 +464,113 @@ void QHttpNetworkConnectionPrivate::unqueueAndSendRequest(QAbstractSocket *socke prepareRequest(messagePair); channels[i].request = messagePair.first; channels[i].reply = messagePair.second; - sendRequest(socket); + // remove before sendRequest! else we might pipeline the same request again lowPriorityQueue.removeAt(j); + channels[i].sendRequest(); return; } } } -void QHttpNetworkConnectionPrivate::closeChannel(int channel) +// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel +void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket) { - channels[channel].close(); + // return fast if there is nothing to pipeline + if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty()) + return; + + int i = indexOf(socket); + + bool highPriorityQueueProcessingDone = false; + bool lowPriorityQueueProcessingDone = false; + + while (!highPriorityQueueProcessingDone && !lowPriorityQueueProcessingDone) { + // this loop runs once per request we intend to pipeline in. + + if (channels[i].pipeliningSupported != QHttpNetworkConnectionChannel::PipeliningProbablySupported) + return; + + // the current request that is in must already support pipelining + if (!channels[i].request.isPipeliningAllowed()) + return; + + // the current request must be a idempotent (right now we only check GET) + if (channels[i].request.operation() != QHttpNetworkRequest::Get) + return; + + // check if socket is connected + if (socket->state() != QAbstractSocket::ConnectedState) + return; + + // check for resendCurrent + if (channels[i].resendCurrent) + return; + + // we do not like authentication stuff + // ### make sure to be OK with this in later releases + if (!channels[i].authenticator.isNull() || !channels[i].authenticator.user().isEmpty()) + return; + if (!channels[i].proxyAuthenticator.isNull() || !channels[i].proxyAuthenticator.user().isEmpty()) + return; + + // check for pipeline length + if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) + return; + + // must be in ReadingState or WaitingState + if (! (channels[i].state == QHttpNetworkConnectionChannel::WaitingState + || channels[i].state == QHttpNetworkConnectionChannel::ReadingState)) + return; + + highPriorityQueueProcessingDone = fillPipeline(highPriorityQueue, channels[i]); + // not finished with highPriorityQueue? then loop again + if (!highPriorityQueueProcessingDone) + continue; + // highPriorityQueue was processed, now deal with the lowPriorityQueue + lowPriorityQueueProcessingDone = fillPipeline(lowPriorityQueue, channels[i]); + } } -void QHttpNetworkConnectionPrivate::resendCurrentRequest(QAbstractSocket *socket) +// returns true when the processing of a queue has been done +bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel) { - Q_Q(QHttpNetworkConnection); - Q_ASSERT(socket); - int i = indexOf(socket); - closeChannel(i); - channels[i].resendCurrent = true; - QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); + if (queue.isEmpty()) + return true; + + for (int i = 0; i < queue.length(); i++) { + HttpMessagePair messagePair = queue.at(i); + const QHttpNetworkRequest &request = messagePair.first; + + // we currently do not support pipelining if HTTP authentication is used + if (!request.url().userInfo().isEmpty()) + continue; + + // take only GET requests + if (request.operation() != QHttpNetworkRequest::Get) + continue; + + if (!request.isPipeliningAllowed()) + continue; + + // remove it from the queue + queue.takeAt(i); + // we modify the queue we iterate over here, but since we return from the function + // afterwards this is fine. + + // actually send it + if (!messagePair.second->d_func()->requestIsPrepared) + prepareRequest(messagePair); + channel.pipelineInto(messagePair); + + // return false because we processed something and need to process again + return false; + } + + // return true, the queue has been processed and not changed + return true; } + QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket* socket) { Q_ASSERT(socket); @@ -992,7 +622,7 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply) if (channels[i].reply == reply) { channels[i].reply = 0; if (reply->d_func()->connectionCloseEnabled()) - closeChannel(i); + channels[i].close(); QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); return; } @@ -1031,7 +661,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest() channels[i].resendCurrent = false; channels[i].state = QHttpNetworkConnectionChannel::IdleState; if (channels[i].reply) - sendRequest(channels[i].socket); + channels[i].sendRequest(); } } QAbstractSocket *socket = 0; @@ -1043,20 +673,33 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest() break; } } - if (!socket) - return; // this will be called after finishing current request. - unqueueAndSendRequest(socket); + + // this socket is free, + if (socket) + dequeueAndSendRequest(socket); + + // try to push more into all sockets + // ### FIXME we should move this to the beginning of the function + // as soon as QtWebkit is properly using the pipelining + // (e.g. not for XMLHttpRequest or the first page load) + // ### FIXME we should also divide the requests more even + // on the connected sockets + //tryToFillPipeline(socket); + // return fast if there is nothing to pipeline + if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty()) + return; + for (int j = 0; j < channelCount; j++) + fillPipeline(channels[j].socket); } void QHttpNetworkConnectionPrivate::_q_restartAuthPendingRequests() { // send the request using the idle socket for (int i = 0 ; i < channelCount; ++i) { - QAbstractSocket *socket = channels[i].socket; if (channels[i].state == QHttpNetworkConnectionChannel::Wait4AuthState) { channels[i].state = QHttpNetworkConnectionChannel::IdleState; if (channels[i].reply) - sendRequest(socket); + channels[i].sendRequest(); } } } @@ -1069,6 +712,13 @@ QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 d->init(); } +QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent) + : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent) +{ + Q_D(QHttpNetworkConnection); + d->init(); +} + QHttpNetworkConnection::~QHttpNetworkConnection() { } @@ -1215,6 +865,19 @@ void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int #endif //QT_NO_OPENSSL +#ifndef QT_NO_NETWORKPROXY +// only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not +// from QHttpNetworkConnectionChannel::handleAuthenticationChallenge +// e.g. it is for SOCKS proxies which require authentication. +void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth) +{ + Q_Q(QHttpNetworkConnection); + emit q->proxyAuthenticationRequired(proxy, auth, q); + int i = indexOf(chan->socket); + copyCredentials(i, auth, true); +} +#endif + QT_END_NAMESPACE diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index 99dda2a..af764ed 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -89,6 +89,7 @@ class Q_AUTOTEST_EXPORT QHttpNetworkConnection : public QObject public: QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0); + QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0); ~QHttpNetworkConnection(); //The hostname to which this is connected to. @@ -153,7 +154,11 @@ class QHttpNetworkConnectionPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QHttpNetworkConnection) public: + static const int defaultChannelCount; + static const int defaultPipelineLength; + QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt); + QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt); ~QHttpNetworkConnectionPrivate(); void init(); @@ -166,12 +171,13 @@ public: bool isSocketReading(QAbstractSocket *socket) const; QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request); - void unqueueAndSendRequest(QAbstractSocket *socket); + void requeueRequest(const HttpMessagePair &pair); // e.g. after pipeline broke + void dequeueAndSendRequest(QAbstractSocket *socket); void prepareRequest(HttpMessagePair &request); - bool sendRequest(QAbstractSocket *socket); - void receiveReply(QAbstractSocket *socket, QHttpNetworkReply *reply); - void resendCurrentRequest(QAbstractSocket *socket); - void closeChannel(int channel); + + void fillPipeline(QAbstractSocket *socket); + bool fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel); + void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy); // private slots @@ -179,9 +185,9 @@ public: void _q_restartAuthPendingRequests(); // send the currently blocked request void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request); - bool ensureConnection(QAbstractSocket *socket); + QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket); - void eraseData(QHttpNetworkReply *reply); + #ifndef QT_NO_COMPRESS bool expand(QAbstractSocket *socket, QHttpNetworkReply *reply, bool dataComplete); #endif @@ -191,7 +197,7 @@ public: quint16 port; bool encrypt; - static const int channelCount; + const int channelCount; QHttpNetworkConnectionChannel *channels; // parallel connections to the server bool pendingAuthSignal; // there is an incomplete authentication signal @@ -199,14 +205,11 @@ public: qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const; qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const; - qint64 compressedBytesAvailable(const QHttpNetworkReply &reply) const; + void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode); bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend); - void allDone(QAbstractSocket *socket, QHttpNetworkReply *reply); - void handleStatus(QAbstractSocket *socket, QHttpNetworkReply *reply); - inline bool shouldEmitSignals(QHttpNetworkReply *reply); - inline bool expectContent(QHttpNetworkReply *reply); + #ifndef QT_NO_OPENSSL QSslConfiguration sslConfiguration(const QHttpNetworkReply &reply) const; @@ -214,6 +217,7 @@ public: #ifndef QT_NO_NETWORKPROXY QNetworkProxy networkProxy; + void emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth); #endif //The request queues diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 5ffab76..9d78c55 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -41,6 +41,7 @@ #include "qhttpnetworkconnection_p.h" #include "qhttpnetworkconnectionchannel_p.h" +#include "private/qnoncontiguousbytedevice_p.h" #include <qpair.h> #include <qdebug.h> @@ -113,61 +114,646 @@ void QHttpNetworkConnectionChannel::close() } +bool QHttpNetworkConnectionChannel::sendRequest() +{ + switch (state) { + case QHttpNetworkConnectionChannel::IdleState: { // write the header + if (!ensureConnection()) { + // wait for the connection (and encryption) to be done + // sendRequest will be called again from either + // _q_connected or _q_encrypted + return false; + } + written = 0; // excluding the header + bytesTotal = 0; + if (reply) { + reply->d_func()->clear(); + reply->d_func()->connection = connection; + reply->d_func()->autoDecompress = request.d->autoDecompress; + reply->d_func()->pipeliningUsed = false; + } + state = QHttpNetworkConnectionChannel::WritingState; + pendingEncrypt = false; + // if the url contains authentication parameters, use the new ones + // both channels will use the new authentication parameters + if (!request.url().userInfo().isEmpty()) { + QUrl url = request.url(); + QAuthenticator &auth = authenticator; + if (url.userName() != auth.user() + || (!url.password().isEmpty() && url.password() != auth.password())) { + auth.setUser(url.userName()); + auth.setPassword(url.password()); + connection->d_func()->copyCredentials(connection->d_func()->indexOf(socket), &auth, false); + } + // clear the userinfo, since we use the same request for resending + // userinfo in url can conflict with the one in the authenticator + url.setUserInfo(QString()); + request.setUrl(url); + } + connection->d_func()->createAuthorization(socket, request); +#ifndef QT_NO_NETWORKPROXY + QByteArray header = QHttpNetworkRequestPrivate::header(request, + (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)); +#else + QByteArray header = QHttpNetworkRequestPrivate::header(request, false); +#endif + socket->write(header); + QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); + if (uploadByteDevice) { + // connect the signals so this function gets called again + QObject::connect(uploadByteDevice, SIGNAL(readyRead()),this, SLOT(_q_uploadDataReadyRead())); + + bytesTotal = request.contentLength(); + } else { + state = QHttpNetworkConnectionChannel::WaitingState; + sendRequest(); + break; + } + // write the initial chunk together with the headers + // fall through + } + case QHttpNetworkConnectionChannel::WritingState: + { + // write the data + QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); + if (!uploadByteDevice || bytesTotal == written) { + if (uploadByteDevice) + emit reply->dataSendProgress(written, bytesTotal); + state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response + sendRequest(); + break; + } + + // only feed the QTcpSocket buffer when there is less than 32 kB in it + const qint64 socketBufferFill = 32*1024; + const qint64 socketWriteMaxSize = 16*1024; + + +#ifndef QT_NO_OPENSSL + QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); + // if it is really an ssl socket, check more than just bytesToWrite() + while ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0)) + <= socketBufferFill && bytesTotal != written) +#else + while (socket->bytesToWrite() <= socketBufferFill + && bytesTotal != written) +#endif + { + // get pointer to upload data + qint64 currentReadSize; + qint64 desiredReadSize = qMin(socketWriteMaxSize, bytesTotal - written); + const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize); + + if (currentReadSize == -1) { + // premature eof happened + connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError); + return false; + break; + } else if (readPointer == 0 || currentReadSize == 0) { + // nothing to read currently, break the loop + break; + } else { + qint64 currentWriteSize = socket->write(readPointer, currentReadSize); + if (currentWriteSize == -1 || currentWriteSize != currentReadSize) { + // socket broke down + connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError); + return false; + } else { + written += currentWriteSize; + uploadByteDevice->advanceReadPointer(currentWriteSize); + + emit reply->dataSendProgress(written, bytesTotal); + + if (written == bytesTotal) { + // make sure this function is called once again + state = QHttpNetworkConnectionChannel::WaitingState; + sendRequest(); + break; + } + } + } + } + break; + } + + case QHttpNetworkConnectionChannel::WaitingState: + { + QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); + if (uploadByteDevice) { + QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(_q_uploadDataReadyRead())); + } + + // HTTP pipelining + connection->d_func()->fillPipeline(socket); + socket->flush(); + + // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called + // this is needed if the sends an reply before we have finished sending the request. In that + // case receiveReply had been called before but ignored the server reply + receiveReply(); + break; + } + case QHttpNetworkConnectionChannel::ReadingState: + case QHttpNetworkConnectionChannel::Wait4AuthState: + // ignore _q_bytesWritten in these states + // fall through + default: + break; + } + return true; +} + + +void QHttpNetworkConnectionChannel::receiveReply() +{ + Q_ASSERT(socket); + + qint64 bytes = 0; + QAbstractSocket::SocketState socketState = socket->state(); + + // connection might be closed to signal the end of data + if (socketState == QAbstractSocket::UnconnectedState) { + if (!socket->bytesAvailable()) { + if (reply && reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) { + reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState; + this->state = QHttpNetworkConnectionChannel::IdleState; + allDone(); + } else { + // try to reconnect/resend before sending an error. + if (reconnectAttempts-- > 0) { + closeAndResendCurrentRequest(); + } else if (reply) { + reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket); + emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString); + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + } + } + } + } + + // read loop for the response + while (socket->bytesAvailable()) { + QHttpNetworkReplyPrivate::ReplyState state = reply ? reply->d_func()->state : QHttpNetworkReplyPrivate::AllDoneState; + switch (state) { + case QHttpNetworkReplyPrivate::NothingDoneState: + case QHttpNetworkReplyPrivate::ReadingStatusState: { + eatWhitespace(); + qint64 statusBytes = reply->d_func()->readStatus(socket); + if (statusBytes == -1 && reconnectAttempts <= 0) { + // too many errors reading/receiving/parsing the status, close the socket and emit error + close(); + reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::ProtocolFailure, socket); + emit reply->finishedWithError(QNetworkReply::ProtocolFailure, reply->d_func()->errorString); + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + break; + } else if (statusBytes == -1) { + reconnectAttempts--; + reply->d_func()->clear(); + closeAndResendCurrentRequest(); + break; + } + bytes += statusBytes; + lastStatus = reply->d_func()->statusCode; + break; + } + case QHttpNetworkReplyPrivate::ReadingHeaderState: + bytes += reply->d_func()->readHeader(socket); + if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) { + if (reply->d_func()->isGzipped() && reply->d_func()->autoDecompress) { + // remove the Content-Length from header + reply->d_func()->removeAutoDecompressHeader(); + } else { + reply->d_func()->autoDecompress = false; + } + if (reply && reply->d_func()->statusCode == 100) { + reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState; + break; // ignore + } + if (reply->d_func()->shouldEmitSignals()) + emit reply->headerChanged(); + if (!reply->d_func()->expectContent()) { + reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState; + this->state = QHttpNetworkConnectionChannel::IdleState; + allDone(); + return; + } + } + break; + case QHttpNetworkReplyPrivate::ReadingDataState: { + if (!reply->d_func()->isChunked() && !reply->d_func()->autoDecompress + && reply->d_func()->bodyLength > 0) { + // bulk files like images should fulfill these properties and + // we can therefore save on memory copying + bytes = reply->d_func()->readBodyFast(socket, &reply->d_func()->responseData); + reply->d_func()->totalProgress += bytes; + if (reply->d_func()->shouldEmitSignals()) { + QPointer<QHttpNetworkReply> replyPointer = reply; + emit reply->readyRead(); + // make sure that the reply is valid + if (replyPointer.isNull()) + return; + emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength); + // make sure that the reply is valid + if (replyPointer.isNull()) + return; + } + } + else + { + // use the traditional slower reading (for compressed encoding, chunked encoding, + // no content-length etc) + QByteDataBuffer byteDatas; + bytes = reply->d_func()->readBody(socket, &byteDatas); + if (bytes) { + if (reply->d_func()->autoDecompress) + reply->d_func()->appendCompressedReplyData(byteDatas); + else + reply->d_func()->appendUncompressedReplyData(byteDatas); + + if (!reply->d_func()->autoDecompress) { + reply->d_func()->totalProgress += bytes; + if (reply->d_func()->shouldEmitSignals()) { + QPointer<QHttpNetworkReply> replyPointer = reply; + // important: At the point of this readyRead(), the byteDatas list must be empty, + // else implicit sharing will trigger memcpy when the user is reading data! + emit reply->readyRead(); + // make sure that the reply is valid + if (replyPointer.isNull()) + return; + emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength); + // make sure that the reply is valid + if (replyPointer.isNull()) + return; + } + } +#ifndef QT_NO_COMPRESS + else if (!expand(false)) { // expand a chunk if possible + return; // ### expand failed + } +#endif + } + } + if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) + break; + // everything done, fall through + } + case QHttpNetworkReplyPrivate::AllDoneState: + this->state = QHttpNetworkConnectionChannel::IdleState; + allDone(); + break; + default: + break; + } + } +} + +bool QHttpNetworkConnectionChannel::ensureConnection() +{ + // make sure that this socket is in a connected state, if not initiate + // connection to the host. + if (socket->state() != QAbstractSocket::ConnectedState) { + // connect to the host if not already connected. + // resend this request after we receive the disconnected signal + if (socket->state() == QAbstractSocket::ClosingState) { + resendCurrent = true; + return false; + } + state = QHttpNetworkConnectionChannel::ConnectingState; + pendingEncrypt = connection->d_func()->encrypt; + + // reset state + pipeliningSupported = PipeliningSupportUnknown; + + // This workaround is needed since we use QAuthenticator for NTLM authentication. The "phase == Done" + // is the usual criteria for emitting authentication signals. The "phase" is set to "Done" when the + // last header for Authorization is generated by the QAuthenticator. Basic & Digest logic does not + // check the "phase" for generating the Authorization header. NTLM authentication is a two stage + // process & needs the "phase". To make sure the QAuthenticator uses the current username/password + // the phase is reset to Start. + QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(authenticator); + if (priv && priv->phase == QAuthenticatorPrivate::Done) + priv->phase = QAuthenticatorPrivate::Start; + priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator); + if (priv && priv->phase == QAuthenticatorPrivate::Done) + priv->phase = QAuthenticatorPrivate::Start; + + QString connectHost = connection->d_func()->hostName; + qint16 connectPort = connection->d_func()->port; + +#ifndef QT_NO_NETWORKPROXY + // HTTPS always use transparent proxy. + if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !connection->d_func()->encrypt) { + connectHost = connection->d_func()->networkProxy.hostName(); + connectPort = connection->d_func()->networkProxy.port(); + } +#endif + if (connection->d_func()->encrypt) { +#ifndef QT_NO_OPENSSL + QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); + sslSocket->connectToHostEncrypted(connectHost, connectPort); + if (ignoreAllSslErrors) + sslSocket->ignoreSslErrors(); + sslSocket->ignoreSslErrors(ignoreSslErrorsList); +#else + connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError); +#endif + } else { + socket->connectToHost(connectHost, connectPort); + } + return false; + } + return true; +} + + +#ifndef QT_NO_COMPRESS +bool QHttpNetworkConnectionChannel::expand(bool dataComplete) +{ + Q_ASSERT(socket); + Q_ASSERT(reply); + + qint64 total = reply->d_func()->compressedData.size(); + if (total >= CHUNK || dataComplete) { + // uncompress the data + QByteArray content, inflated; + content = reply->d_func()->compressedData; + reply->d_func()->compressedData.clear(); + + int ret = Z_OK; + if (content.size()) + ret = reply->d_func()->gunzipBodyPartially(content, inflated); + int retCheck = (dataComplete) ? Z_STREAM_END : Z_OK; + if (ret >= retCheck) { + if (inflated.size()) { + reply->d_func()->totalProgress += inflated.size(); + reply->d_func()->appendUncompressedReplyData(inflated); + if (reply->d_func()->shouldEmitSignals()) { + QPointer<QHttpNetworkReply> replyPointer = reply; + // important: At the point of this readyRead(), inflated must be cleared, + // else implicit sharing will trigger memcpy when the user is reading data! + emit reply->readyRead(); + // make sure that the reply is valid + if (replyPointer.isNull()) + return true; + emit reply->dataReadProgress(reply->d_func()->totalProgress, 0); + // make sure that the reply is valid + if (replyPointer.isNull()) + return true; + + } + } + } else { + connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolFailure); + return false; + } + } + return true; +} +#endif + + +void QHttpNetworkConnectionChannel::allDone() +{ +#ifndef QT_NO_COMPRESS + // expand the whole data. + if (reply->d_func()->expectContent() && reply->d_func()->autoDecompress && !reply->d_func()->streamEnd) + expand(true); // ### if expand returns false, its an error +#endif + // while handling 401 & 407, we might reset the status code, so save this. + bool emitFinished = reply->d_func()->shouldEmitSignals(); + handleStatus(); + // ### at this point there should be no more data on the socket + // close if server requested + if (reply->d_func()->connectionCloseEnabled()) + close(); + // queue the finished signal, this is required since we might send new requests from + // slot connected to it. The socket will not fire readyRead signal, if we are already + // in the slot connected to readyRead + if (emitFinished) + QMetaObject::invokeMethod(reply, "finished", Qt::QueuedConnection); + // reset the reconnection attempts after we receive a complete reply. + // in case of failures, each channel will attempt two reconnects before emitting error. + reconnectAttempts = 2; + + detectPipeliningSupport(); + + // move next from pipeline to current request + if (!alreadyPipelinedRequests.isEmpty()) { + if (resendCurrent || reply->d_func()->connectionCloseEnabled() || socket->state() != QAbstractSocket::ConnectedState) { + // move the pipelined ones back to the main queue + requeueCurrentlyPipelinedRequests(); + } else { + // there were requests pipelined in and we can continue + HttpMessagePair messagePair = alreadyPipelinedRequests.takeFirst(); + + request = messagePair.first; + reply = messagePair.second; + state = QHttpNetworkConnectionChannel::ReadingState; + resendCurrent = false; + + written = 0; // message body, excluding the header, irrelevant here + bytesTotal = 0; // message body total, excluding the header, irrelevant here + + // pipeline even more + connection->d_func()->fillPipeline(socket); + + // continue reading + receiveReply(); + } + } else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) { + eatWhitespace(); + // this is weird. we had nothing pipelined but still bytes available. better close it. + if (socket->bytesAvailable() > 0) + close(); + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + } else if (alreadyPipelinedRequests.isEmpty()) { + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + } +} + +void QHttpNetworkConnectionChannel::detectPipeliningSupport() +{ + // detect HTTP Pipelining support + QByteArray serverHeaderField = reply->headerField("Server"); + if ( + // check for broken servers in server reply header + // this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining + (!serverHeaderField.contains("Microsoft-IIS/4.")) + && (!serverHeaderField.contains("Microsoft-IIS/5.")) + && (!serverHeaderField.contains("Netscape-Enterprise/3.")) + // check for HTTP/1.1 + && (reply->d_func()->majorVersion == 1 && reply->d_func()->minorVersion == 1) + // check for not having connection close + && (!reply->d_func()->connectionCloseEnabled()) + // check if it is still connected + && (socket->state() == QAbstractSocket::ConnectedState) + ) { + pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningProbablySupported; + } else { + pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown; + } +} + +// called when the connection broke and we need to queue some pipelined requests again +void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests() +{ + for (int i = 0; i < alreadyPipelinedRequests.length(); i++) + connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i)); + alreadyPipelinedRequests.clear(); + + close(); + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); +} + +void QHttpNetworkConnectionChannel::eatWhitespace() +{ + char c; + while (socket->bytesAvailable()) { + if (socket->peek(&c, 1) != 1) + return; + + // read all whitespace and line endings + if (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31) { + socket->read(&c, 1); + continue; + } else { + break; + } + } +} + +void QHttpNetworkConnectionChannel::handleStatus() +{ + Q_ASSERT(socket); + Q_ASSERT(reply); + + int statusCode = reply->statusCode(); + bool resend = false; + + switch (statusCode) { + case 401: // auth required + case 407: // proxy auth required + if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) { + if (resend) { + QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); + if (uploadByteDevice) { + if (uploadByteDevice->reset()) { + written = 0; + } else { + connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError); + break; + } + } + + reply->d_func()->eraseData(); + + if (alreadyPipelinedRequests.isEmpty()) { + // this does a re-send without closing the connection + resendCurrent = true; + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + } else { + // we had requests pipelined.. better close the connection in closeAndResendCurrentRequest + closeAndResendCurrentRequest(); + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + } + } + } else { + emit reply->headerChanged(); + emit reply->readyRead(); + QNetworkReply::NetworkError errorCode = (statusCode == 407) + ? QNetworkReply::ProxyAuthenticationRequiredError + : QNetworkReply::AuthenticationRequiredError; + reply->d_func()->errorString = connection->d_func()->errorDetail(errorCode, socket); + emit connection->error(errorCode, reply->d_func()->errorString); + emit reply->finished(); + } + break; + default: + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + } +} + +void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair) +{ + // this is only called for simple GET + + QHttpNetworkRequest &request = pair.first; + QHttpNetworkReply *reply = pair.second; + if (reply) { + reply->d_func()->clear(); + reply->d_func()->connection = connection; + reply->d_func()->autoDecompress = request.d->autoDecompress; + reply->d_func()->pipeliningUsed = true; + } + +#ifndef QT_NO_NETWORKPROXY + QByteArray header = QHttpNetworkRequestPrivate::header(request, + (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)); +#else + QByteArray header = QHttpNetworkRequestPrivate::header(request, false); +#endif + socket->write(header); + + alreadyPipelinedRequests.append(pair); +} + +void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest() +{ + requeueCurrentlyPipelinedRequests(); + close(); + resendCurrent = true; + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); +} + //private slots void QHttpNetworkConnectionChannel::_q_readyRead() { - if (!socket) - return; // ### error if (connection->d_func()->isSocketWaiting(socket) || connection->d_func()->isSocketReading(socket)) { state = QHttpNetworkConnectionChannel::ReadingState; if (reply) - connection->d_func()->receiveReply(socket, reply); + receiveReply(); } - // ### error } void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes) { Q_UNUSED(bytes); - if (!socket) - return; // ### error // bytes have been written to the socket. write even more of them :) if (connection->d_func()->isSocketWriting(socket)) - connection->d_func()->sendRequest(socket); + sendRequest(); // otherwise we do nothing } void QHttpNetworkConnectionChannel::_q_disconnected() { - if (!socket) - return; // ### error // read the available data before closing if (connection->d_func()->isSocketWaiting(socket) || connection->d_func()->isSocketReading(socket)) { state = QHttpNetworkConnectionChannel::ReadingState; if (reply) - connection->d_func()->receiveReply(socket, reply); + receiveReply(); } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) { // re-sending request because the socket was in ClosingState QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); } state = QHttpNetworkConnectionChannel::IdleState; + + requeueCurrentlyPipelinedRequests(); } void QHttpNetworkConnectionChannel::_q_connected() { - if (!socket) - return; // ### error - // improve performance since we get the request sent by the kernel ASAP socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); + pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown; + // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again! //channels[i].reconnectAttempts = 2; if (!pendingEncrypt) { state = QHttpNetworkConnectionChannel::IdleState; if (reply) - connection->d_func()->sendRequest(socket); + sendRequest(); else close(); } @@ -193,7 +779,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket // while "Reading" the _q_disconnected() will handle this. if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) { if (reconnectAttempts-- > 0) { - connection->d_func()->resendCurrentRequest(socket); + closeAndResendCurrentRequest(); return; } else { send2Reply = true; @@ -206,7 +792,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket case QAbstractSocket::SocketTimeoutError: // try to reconnect/resend before sending an error. if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) { - connection->d_func()->resendCurrentRequest(socket); + closeAndResendCurrentRequest(); return; } send2Reply = true; @@ -244,13 +830,13 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket #ifndef QT_NO_NETWORKPROXY void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth) { - emit connection->proxyAuthenticationRequired(proxy, auth, connection); + connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth); } #endif void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead() { - connection->d_func()->sendRequest(socket); + sendRequest(); } #ifndef QT_NO_OPENSSL @@ -259,7 +845,7 @@ void QHttpNetworkConnectionChannel::_q_encrypted() if (!socket) return; // ### error state = QHttpNetworkConnectionChannel::IdleState; - connection->d_func()->sendRequest(socket); + sendRequest(); } void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors) diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h index f937669..220b72c 100644 --- a/src/network/access/qhttpnetworkconnectionchannel_p.h +++ b/src/network/access/qhttpnetworkconnectionchannel_p.h @@ -82,10 +82,14 @@ class QHttpNetworkReply; class QByteArray; class QHttpNetworkConnection; +#ifndef HttpMessagePair +typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair; +#endif + class QHttpNetworkConnectionChannel : public QObject { Q_OBJECT public: - enum ChannelState { + enum ChannelState { IdleState = 0, // ready to send request ConnectingState = 1, // connecting to host WritingState = 2, // writing the data @@ -112,12 +116,24 @@ public: bool ignoreAllSslErrors; QList<QSslError> ignoreSslErrorsList; #endif + + // HTTP pipelining -> http://en.wikipedia.org/wiki/Http_pipelining + enum PipeliningSupport { + PipeliningSupportUnknown, // default for a new connection + PipeliningProbablySupported, // after having received a server response that indicates support + PipeliningNotSupported // currently not used + }; + PipeliningSupport pipeliningSupported; + QList<HttpMessagePair> alreadyPipelinedRequests; + + QHttpNetworkConnectionChannel() : socket(0), state(IdleState), reply(0), written(0), bytesTotal(0), resendCurrent(false), lastStatus(0), pendingEncrypt(false), reconnectAttempts(2), authMehtod(QAuthenticatorPrivate::None), proxyAuthMehtod(QAuthenticatorPrivate::None) #ifndef QT_NO_OPENSSL , ignoreAllSslErrors(false) #endif + , pipeliningSupported(PipeliningSupportUnknown) , connection(0) {} @@ -127,6 +143,23 @@ public: void init(); void close(); + bool sendRequest(); + void receiveReply(); + + bool ensureConnection(); + + bool expand(bool dataComplete); + void allDone(); // reply header + body have been read + void handleStatus(); // called from allDone() + + void pipelineInto(HttpMessagePair &pair); + void requeueCurrentlyPipelinedRequests(); + void detectPipeliningSupport(); + + void closeAndResendCurrentRequest(); + + void eatWhitespace(); + protected slots: void _q_bytesWritten(qint64 bytes); // proceed sending void _q_readyRead(); // pending data to read diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index c31c59c..4a315e4 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -187,6 +187,10 @@ bool QHttpNetworkReply::isFinished() const return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState; } +bool QHttpNetworkReply::isPipeliningUsed() const +{ + return d_func()->pipeliningUsed; +} QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl) @@ -195,6 +199,7 @@ QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl) chunkedTransferEncoding(0), currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false), autoDecompress(false), responseData(), requestIsPrepared(false) + ,pipeliningUsed(false) { } @@ -418,20 +423,26 @@ qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket) } bool ok = parseStatus(fragment); state = ReadingHeaderState; - fragment.clear(); // next fragment - - if (!ok) + fragment.clear(); + if (!ok) { return -1; + } break; } else { c = 0; - bytes += socket->read(&c, 1); + int haveRead = socket->read(&c, 1); + if (haveRead == -1) + return -1; + bytes += haveRead; fragment.append(c); } // is this a valid reply? if (fragment.length() >= 5 && !fragment.startsWith("HTTP/")) + { + fragment.clear(); return -1; + } } @@ -568,7 +579,6 @@ qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteData if (contentRead + haveRead == bodyLength) { state = AllDoneState; - socket->readAll(); // Read the rest to clean (CRLF) ### will break pipelining } contentRead += haveRead; @@ -588,8 +598,6 @@ qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuff } else { bytes += readReplyBodyRaw(socket, out, socket->bytesAvailable()); } - if (state == AllDoneState) - socket->readAll(); // Read the rest to clean (CRLF) ### will break pipelining contentRead += bytes; return bytes; } @@ -717,6 +725,33 @@ void QHttpNetworkReplyPrivate::appendCompressedReplyData(QByteDataBuffer &data) } +bool QHttpNetworkReplyPrivate::shouldEmitSignals() +{ + // for 401 & 407 don't emit the data signals. Content along with these + // responses are send only if the authentication fails. + return (statusCode != 401 && statusCode != 407); +} + +bool QHttpNetworkReplyPrivate::expectContent() +{ + // check whether we can expect content after the headers (rfc 2616, sec4.4) + if ((statusCode >= 100 && statusCode < 200) + || statusCode == 204 || statusCode == 304) + return false; + if (request.operation() == QHttpNetworkRequest::Head) + return !shouldEmitSignals(); + if (contentLength() == 0) + return false; + return true; +} + +void QHttpNetworkReplyPrivate::eraseData() +{ + compressedData.clear(); + responseData.clear(); +} + + // SSL support below #ifndef QT_NO_OPENSSL diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h index 5aee067..8d4d724 100644 --- a/src/network/access/qhttpnetworkreply_p.h +++ b/src/network/access/qhttpnetworkreply_p.h @@ -128,6 +128,8 @@ public: bool isFinished() const; + bool isPipeliningUsed() const; + #ifndef QT_NO_OPENSSL QSslConfiguration sslConfiguration() const; void setSslConfiguration(const QSslConfiguration &config); @@ -177,6 +179,10 @@ public: void appendUncompressedReplyData(QByteDataBuffer &data); void appendCompressedReplyData(QByteDataBuffer &data); + bool shouldEmitSignals(); + bool expectContent(); + void eraseData(); + qint64 bytesAvailable() const; bool isChunked(); bool connectionCloseEnabled(); @@ -219,6 +225,8 @@ public: QByteDataBuffer responseData; // uncompressed body QByteArray compressedData; // compressed body (temporary) bool requestIsPrepared; + + bool pipeliningUsed; }; diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp index 693e1f4..8b2c4e9 100644 --- a/src/network/access/qhttpnetworkrequest.cpp +++ b/src/network/access/qhttpnetworkrequest.cpp @@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op, QHttpNetworkRequest::Priority pri, const QUrl &newUrl) : QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), uploadByteDevice(0), - autoDecompress(false) + autoDecompress(false), pipeliningAllowed(false) { } @@ -60,6 +60,7 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest priority = other.priority; uploadByteDevice = other.uploadByteDevice; autoDecompress = other.autoDecompress; + pipeliningAllowed = other.pipeliningAllowed; } QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate() @@ -239,6 +240,16 @@ void QHttpNetworkRequest::setPriority(Priority priority) d->priority = priority; } +bool QHttpNetworkRequest::isPipeliningAllowed() const +{ + return d->pipeliningAllowed; +} + +void QHttpNetworkRequest::setPipeliningAllowed(bool b) +{ + d->pipeliningAllowed = b; +} + void QHttpNetworkRequest::setUploadByteDevice(QNonContiguousByteDevice *bd) { d->uploadByteDevice = bd; diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h index ffacb14..2468be9 100644 --- a/src/network/access/qhttpnetworkrequest_p.h +++ b/src/network/access/qhttpnetworkrequest_p.h @@ -106,6 +106,9 @@ public: Priority priority() const; void setPriority(Priority priority); + bool isPipeliningAllowed() const; + void setPipeliningAllowed(bool b); + void setUploadByteDevice(QNonContiguousByteDevice *bd); QNonContiguousByteDevice* uploadByteDevice() const; @@ -113,6 +116,7 @@ private: QSharedDataPointer<QHttpNetworkRequestPrivate> d; friend class QHttpNetworkRequestPrivate; friend class QHttpNetworkConnectionPrivate; + friend class QHttpNetworkConnectionChannel; }; class QHttpNetworkRequestPrivate : public QHttpNetworkHeaderPrivate @@ -132,6 +136,7 @@ public: QHttpNetworkRequest::Priority priority; mutable QNonContiguousByteDevice* uploadByteDevice; bool autoDecompress; + bool pipeliningAllowed; }; diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp index 479990a..3532de8 100644 --- a/src/network/access/qnetworkaccesshttpbackend.cpp +++ b/src/network/access/qnetworkaccesshttpbackend.cpp @@ -516,6 +516,9 @@ void QNetworkAccessHttpBackend::postRequest() return; // no need to send the request! :) } + if (request().attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true) + httpRequest.setPipeliningAllowed(true); + httpReply = http->sendRequest(httpRequest); httpReply->setParent(this); #ifndef QT_NO_OPENSSL @@ -713,6 +716,8 @@ void QNetworkAccessHttpBackend::checkForRedirect(const int statusCode) void QNetworkAccessHttpBackend::replyHeaderChanged() { + setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, httpReply->isPipeliningUsed()); + // reconstruct the HTTP header QList<QPair<QByteArray, QByteArray> > headerMap = httpReply->header(); QList<QPair<QByteArray, QByteArray> >::ConstIterator it = headerMap.constBegin(), diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index ce5f6c7..839bf31 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -804,7 +804,13 @@ void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBac QAuthenticator *authenticator) { Q_Q(QNetworkAccessManager); - + // ### FIXME Tracking of successful authentications + // This code is a bit broken right now for SOCKS authentication + // first request: proxyAuthenticationRequired gets emitted, credentials gets saved + // second request: (proxy != backend->reply->lastProxyAuthentication) does not evaluate to true, + // proxyAuthenticationRequired gets emitted again + // possible solution: some tracking inside the authenticator + // or a new function proxyAuthenticationSucceeded(true|false) if (proxy != backend->reply->lastProxyAuthentication) { QNetworkAuthenticationCredential *cred = fetchCachedCredentials(proxy); if (cred) { diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 721f8c4..c6cbddd 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -167,6 +167,16 @@ QT_BEGIN_NAMESPACE When using this flag with sequential upload data, the ContentLengthHeader header must be set. + \value HttpPipeliningAllowedAttribute + Requests only, type: QVariant::Bool (default: false) + Indicates whether the QNetworkAccessManager code is + allowed to use HTTP pipelining with this request. + + \value HttpPipeliningWasUsedAttribute + Replies only, type: QVariant::Bool + Indicates whether the HTTP pipelining was used for receiving + this reply. + \value User Special type. Additional information can be passed in QVariants with types ranging from User to UserMax. The default diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index aaeed48..2c26038 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -76,6 +76,8 @@ public: CacheSaveControlAttribute, SourceIsFromCacheAttribute, DoNotBufferUploadDataAttribute, + HttpPipeliningAllowedAttribute, + HttpPipeliningWasUsedAttribute, User = 1000, UserMax = 32767 diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp index ee1369d..77b2a7e 100644 --- a/src/network/kernel/qhostinfo.cpp +++ b/src/network/kernel/qhostinfo.cpp @@ -162,16 +162,13 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, QWindowsSockInit bust; // makes sure WSAStartup was callled #endif - // Support for IDNA - QString lookup = QString::fromLatin1(QUrl::toAce(name)); - QHostInfoResult *result = new QHostInfoResult; result->autoDelete = false; QObject::connect(result, SIGNAL(resultsReady(QHostInfo)), receiver, member); int id = result->lookupId = theIdCounter.fetchAndAddRelaxed(1); - if (lookup.isEmpty()) { + if (name.isEmpty()) { QHostInfo info(id); info.setError(QHostInfo::HostNotFound); info.setErrorString(QObject::tr("No host name given")); @@ -182,7 +179,7 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, } QHostInfoAgent *agent = theAgent(); - agent->addHostName(lookup, result); + agent->addHostName(name, result); #if !defined QT_NO_THREAD if (!agent->isRunning()) @@ -226,13 +223,7 @@ QHostInfo QHostInfo::fromName(const QString &name) qDebug("QHostInfo::fromName(\"%s\")",name.toLatin1().constData()); #endif - if (!name.isEmpty()) - return QHostInfoAgent::fromName(QLatin1String(QUrl::toAce(name))); - - QHostInfo retval; - retval.d->err = HostNotFound; - retval.d->errorStr = QObject::tr("No host name given"); - return retval; + return QHostInfoAgent::fromName(name); } /*! diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp index 4a23399..e3d51e7 100644 --- a/src/network/kernel/qhostinfo_unix.cpp +++ b/src/network/kernel/qhostinfo_unix.cpp @@ -124,7 +124,6 @@ static void resolveLibrary() QHostInfo QHostInfoAgent::fromName(const QString &hostName) { QHostInfo results; - results.setHostName(hostName); #if defined(QHOSTINFO_DEBUG) qDebug("QHostInfoAgent::fromName(%s) looking up...", @@ -194,6 +193,22 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) #endif } + // IDN support + QByteArray aceHostname; + if (results.hostName().isEmpty()) { + // it's a hostname resolution + aceHostname = QUrl::toAce(hostName); + results.setHostName(hostName); + if (aceHostname.isEmpty()) { + results.setError(QHostInfo::HostNotFound); + results.setErrorString(hostName.isEmpty() ? QObject::tr("No host name given") : QObject::tr("Invalid hostname")); + return results; + } + } else { + // it's an IP reverse resolution + aceHostname = results.hostName().toLatin1(); + } + #if !defined (QT_NO_GETADDRINFO) // Call getaddrinfo, and place all IPv4 addresses at the start and // the IPv6 addresses at the end of the address list in results. @@ -205,12 +220,12 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) hints.ai_flags = Q_ADDRCONFIG; #endif - int result = getaddrinfo(hostName.toLatin1().constData(), 0, &hints, &res); + int result = getaddrinfo(aceHostname.constData(), 0, &hints, &res); # ifdef Q_ADDRCONFIG if (result == EAI_BADFLAGS) { // if the lookup failed with AI_ADDRCONFIG set, try again without it hints.ai_flags = 0; - result = getaddrinfo(hostName.toLatin1().constData(), 0, &hints, &res); + result = getaddrinfo(aceHostname.constData(), 0, &hints, &res); } # endif @@ -264,7 +279,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) // use one QHostInfoAgent, but if more agents are introduced, locking // must be provided. QMutexLocker locker(::getHostByNameMutex()); - hostent *result = gethostbyname(hostName.toLatin1().constData()); + hostent *result = gethostbyname(aceHostname.constData()); if (result) { if (result->h_addrtype == AF_INET) { QList<QHostAddress> addresses; diff --git a/src/network/kernel/qhostinfo_win.cpp b/src/network/kernel/qhostinfo_win.cpp index 93dc720..c5dc2d2 100644 --- a/src/network/kernel/qhostinfo_win.cpp +++ b/src/network/kernel/qhostinfo_win.cpp @@ -52,6 +52,7 @@ #include <qlibrary.h> #include <qtimer.h> #include <qmutex.h> +#include <qurl.h> #include <private/qmutexpool_p.h> QT_BEGIN_NAMESPACE @@ -129,7 +130,6 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) } QHostInfo results; - results.setHostName(hostName); #if defined(QHOSTINFO_DEBUG) qDebug("QHostInfoAgent::fromName(%p): looking up \"%s\" (IPv6 support is %s)", @@ -178,12 +178,28 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) } } + // IDN support + QByteArray aceHostname; + if (results.hostName().isEmpty()) { + // it's a hostname resolution + aceHostname = QUrl::toAce(hostName); + results.setHostName(hostName); + if (aceHostname.isEmpty()) { + results.setError(QHostInfo::HostNotFound); + results.setErrorString(hostName.isEmpty() ? QObject::tr("No host name given") : QObject::tr("Invalid hostname")); + return results; + } + } else { + // it's an IP reverse resolution + aceHostname = results.hostName().toLatin1(); + } + if (local_getaddrinfo && local_freeaddrinfo) { // Call getaddrinfo, and place all IPv4 addresses at the start // and the IPv6 addresses at the end of the address list in // results. qt_addrinfo *res; - int err = local_getaddrinfo(hostName.toLatin1().constData(), 0, 0, &res); + int err = local_getaddrinfo(aceHostname.constData(), 0, 0, &res); if (err == 0) { QList<QHostAddress> addresses; for (qt_addrinfo *p = res; p != 0; p = p->ai_next) { @@ -218,7 +234,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) } } else { // Fall back to gethostbyname, which only supports IPv4. - hostent *ent = gethostbyname(hostName.toLatin1().constData()); + hostent *ent = gethostbyname(aceHostname.constData()); if (ent) { char **p; QList<QHostAddress> addresses; diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 34f7e7a..5b6a714 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -246,6 +246,7 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height) #ifdef QT_OPENGL_ES_2 QDataBuffer<uchar> buffer(4*oldWidth*oldHeight); + buffer.resize(4*oldWidth*oldHeight); glReadPixels(0, 0, oldWidth, oldHeight, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data()); // do an in-place conversion from GL_RGBA to GL_ALPHA diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp index ba715c3..26a2374 100644 --- a/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp @@ -559,7 +559,9 @@ void QDirectFBPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, QRasterPaintEngine::drawPixmap(r, pixmap, sr); } else if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_SupportedBlits) || (d->transformationType & QDirectFBPaintEnginePrivate::Matrix_BlitsUnsupported) - || d->clipType == QDirectFBPaintEnginePrivate::ComplexClip) { + || d->clipType == QDirectFBPaintEnginePrivate::ComplexClip + || (state()->renderHints & QPainter::SmoothPixmapTransform + && state()->matrix.mapRect(r).size() != sr.size())) { RASTERFALLBACK(DRAW_PIXMAP, r, pixmap.size(), sr); const QImage *img = static_cast<QDirectFBPixmapData*>(pixmap.pixmapData())->buffer(DSLF_READ); d->lock(); @@ -593,7 +595,8 @@ void QDirectFBPaintEngine::drawTiledPixmap(const QRectF &r, QRasterPaintEngine::drawTiledPixmap(r, pixmap, offset); } else if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_SupportedBlits) || (d->transformationType & QDirectFBPaintEnginePrivate::Matrix_BlitsUnsupported) - || d->clipType == QDirectFBPaintEnginePrivate::ComplexClip) { + || d->clipType == QDirectFBPaintEnginePrivate::ComplexClip + || (state()->renderHints & QPainter::SmoothPixmapTransform && state()->matrix.isScaling())) { RASTERFALLBACK(DRAW_TILED_PIXMAP, r, pixmap.size(), offset); const QImage *img = static_cast<QDirectFBPixmapData*>(pixmap.pixmapData())->buffer(DSLF_READ); d->lock(); @@ -709,7 +712,8 @@ void QDirectFBPaintEngine::fillRect(const QRectF &rect, const QBrush &brush) case Qt::TexturePattern: { if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_SupportedBlits) - || (d->transformationType & QDirectFBPaintEnginePrivate::Matrix_BlitsUnsupported)) { + || (d->transformationType & QDirectFBPaintEnginePrivate::Matrix_BlitsUnsupported) + || (state()->renderHints & QPainter::SmoothPixmapTransform && state()->matrix.isScaling())) { break; } diff --git a/src/svg/qsvggenerator.cpp b/src/svg/qsvggenerator.cpp index ace4cb8..ef2dd5a 100644 --- a/src/svg/qsvggenerator.cpp +++ b/src/svg/qsvggenerator.cpp @@ -921,7 +921,8 @@ void QSvgPaintEngine::drawImage(const QRectF &r, const QImage &image, stream() << "x=\""<<r.x()<<"\" " "y=\""<<r.y()<<"\" " "width=\""<<r.width()<<"\" " - "height=\""<<r.height()<<"\" "; + "height=\""<<r.height()<<"\" " + "preserveAspectRatio=\"none\" "; QByteArray data; QBuffer buffer(&data); diff --git a/src/svg/qsvghandler.cpp b/src/svg/qsvghandler.cpp index a79e4a0..adfe468 100644 --- a/src/svg/qsvghandler.cpp +++ b/src/svg/qsvghandler.cpp @@ -2012,6 +2012,14 @@ static bool parseAimateMotionNode(QSvgNode *parent, return true; } +static void parseNumberTriplet(QVector<qreal> &values, const QChar *&s) +{ + QVector<qreal> list = parseNumbersList(s); + values << list; + for (int i = 3 - list.size(); i > 0; --i) + values.append(0.0); +} + static bool parseAnimateTransformNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler) @@ -2025,28 +2033,47 @@ static bool parseAnimateTransformNode(QSvgNode *parent, QString fillStr = attributes.value(QLatin1String("fill")).toString(); QString fromStr = attributes.value(QLatin1String("from")).toString(); QString toStr = attributes.value(QLatin1String("to")).toString(); + QString byStr = attributes.value(QLatin1String("by")).toString(); + QString addtv = attributes.value(QLatin1String("additive")).toString(); + + QSvgAnimateTransform::Additive additive = QSvgAnimateTransform::Replace; + if (addtv == QLatin1String("sum")) + additive = QSvgAnimateTransform::Sum; QVector<qreal> vals; if (values.isEmpty()) { - const QChar *s = fromStr.constData(); - QVector<qreal> lst = parseNumbersList(s); - while (lst.count() < 3) - lst.append(0.0); - vals << lst; - - s = toStr.constData(); - lst = parseNumbersList(s); - while (lst.count() < 3) - lst.append(0.0); - vals << lst; + const QChar *s; + if (fromStr.isEmpty()) { + if (!byStr.isEmpty()) { + // By-animation. + additive = QSvgAnimateTransform::Sum; + vals.append(0.0); + vals.append(0.0); + vals.append(0.0); + parseNumberTriplet(vals, s = byStr.constData()); + } else { + // To-animation not defined. + return false; + } + } else { + if (!toStr.isEmpty()) { + // From-to-animation. + parseNumberTriplet(vals, s = fromStr.constData()); + parseNumberTriplet(vals, s = toStr.constData()); + } else if (!byStr.isEmpty()) { + // From-by-animation. + parseNumberTriplet(vals, s = fromStr.constData()); + parseNumberTriplet(vals, s = byStr.constData()); + for (int i = vals.size() - 3; i < vals.size(); ++i) + vals[i] += vals[i - 3]; + } else { + return false; + } + } } else { const QChar *s = values.constData(); while (s && *s != QLatin1Char(0)) { - QVector<qreal> tmpVals = parseNumbersList(s); - while (tmpVals.count() < 3) - tmpVals.append(0.0); - - vals << tmpVals; + parseNumberTriplet(vals, s); if (*s == QLatin1Char(0)) break; ++s; @@ -2088,7 +2115,7 @@ static bool parseAnimateTransformNode(QSvgNode *parent, } QSvgAnimateTransform *anim = new QSvgAnimateTransform(begin, end, 0); - anim->setArgs(type, vals); + anim->setArgs(type, additive, vals); anim->setFreeze(fillStr == QLatin1String("freeze")); anim->setRepeatCount( (repeatStr == QLatin1String("indefinite"))? -1 : diff --git a/src/svg/qsvgstyle.cpp b/src/svg/qsvgstyle.cpp index 1ecf870..e1a7049 100644 --- a/src/svg/qsvgstyle.cpp +++ b/src/svg/qsvgstyle.cpp @@ -509,10 +509,25 @@ void QSvgStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *node, QSvgExtra //animated transforms have to be applied //_after_ the original object transformations if (!animateTransforms.isEmpty()) { - QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr; - for (itr = animateTransforms.constBegin(); itr != animateTransforms.constEnd(); - ++itr) { - (*itr)->apply(p, rect, node, states); + qreal totalTimeElapsed = node->document()->currentElapsed(); + // Find the last animateTransform with additive="replace", since this will override all + // previous animateTransforms. + QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr = animateTransforms.constEnd(); + do { + --itr; + if ((*itr)->animActive(totalTimeElapsed) + && (*itr)->additiveType() == QSvgAnimateTransform::Replace) { + // An animateTransform with additive="replace" will replace the transform attribute. + if (transform) + transform->revert(p, states); + break; + } + } while (itr != animateTransforms.constBegin()); + + // Apply the animateTransforms after and including the last one with additive="replace". + for (; itr != animateTransforms.constEnd(); ++itr) { + if ((*itr)->animActive(totalTimeElapsed)) + (*itr)->apply(p, rect, node, states); } } @@ -558,13 +573,15 @@ void QSvgStyle::revert(QPainter *p, QSvgExtraStates &states) //animated transforms need to be reverted _before_ //the native transforms if (!animateTransforms.isEmpty()) { - QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr; - itr = animateTransforms.constBegin(); - //only need to rever the first one because that - //one has the original world matrix for the primitve - if (itr != animateTransforms.constEnd()) { - (*itr)->revert(p, states); + QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr = animateTransforms.constBegin(); + for (; itr != animateTransforms.constEnd(); ++itr) { + if ((*itr)->transformApplied()) { + (*itr)->revert(p, states); + break; + } } + for (; itr != animateTransforms.constEnd(); ++itr) + (*itr)->clearTransformApplied(); } if (transform) { @@ -587,15 +604,16 @@ void QSvgStyle::revert(QPainter *p, QSvgExtraStates &states) QSvgAnimateTransform::QSvgAnimateTransform(int startMs, int endMs, int byMs ) : QSvgStyleProperty(), m_from(startMs), m_to(endMs), m_by(byMs), - m_type(Empty), m_count(0), m_finished(false) + m_type(Empty), m_count(0), m_finished(false), m_additive(Replace), m_transformApplied(false) { m_totalRunningTime = m_to - m_from; } -void QSvgAnimateTransform::setArgs(TransformType type, const QVector<qreal> &args) +void QSvgAnimateTransform::setArgs(TransformType type, Additive additive, const QVector<qreal> &args) { m_type = type; m_args = args; + m_additive = additive; Q_ASSERT(!(args.count()%3)); m_count = args.count() / 3; } @@ -604,15 +622,14 @@ void QSvgAnimateTransform::apply(QPainter *p, const QRectF &, QSvgNode *node, QS { m_oldWorldTransform = p->worldTransform(); resolveMatrix(node); - if (!m_finished || m_freeze) - p->setWorldTransform(m_transform, true); + p->setWorldTransform(m_transform, true); + m_transformApplied = true; } void QSvgAnimateTransform::revert(QPainter *p, QSvgExtraStates &) { - if (!m_finished || m_freeze) { - p->setWorldTransform(m_oldWorldTransform, false /* don't combine */); - } + p->setWorldTransform(m_oldWorldTransform, false /* don't combine */); + m_transformApplied = false; } void QSvgAnimateTransform::resolveMatrix(QSvgNode *node) @@ -622,11 +639,14 @@ void QSvgAnimateTransform::resolveMatrix(QSvgNode *node) if (totalTimeElapsed < m_from || m_finished) return; - qreal animationFrame = (totalTimeElapsed - m_from) / m_to; + qreal animationFrame = 0; + if (m_totalRunningTime != 0) { + animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime; - if (m_repeatCount >= 0 && m_repeatCount < animationFrame) { - m_finished = true; - animationFrame = m_repeatCount; + if (m_repeatCount >= 0 && m_repeatCount < animationFrame) { + m_finished = true; + animationFrame = m_repeatCount; + } } qreal percentOfAnimation = animationFrame; diff --git a/src/svg/qsvgstyle_p.h b/src/svg/qsvgstyle_p.h index c18a265..056b73b 100644 --- a/src/svg/qsvgstyle_p.h +++ b/src/svg/qsvgstyle_p.h @@ -250,7 +250,7 @@ public: { return m_style; } - + void setGradientId(const QString &Id) { m_gradientId = Id; @@ -529,20 +529,56 @@ public: SkewX, SkewY }; + enum Additive + { + Sum, + Replace + }; public: QSvgAnimateTransform(int startMs, int endMs, int by = 0); - void setArgs(TransformType type, const QVector<qreal> &args); + void setArgs(TransformType type, Additive additive, const QVector<qreal> &args); void setFreeze(bool freeze); void setRepeatCount(qreal repeatCount); virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states); virtual void revert(QPainter *p, QSvgExtraStates &states); virtual Type type() const; + QSvgAnimateTransform::Additive additiveType() const + { + return m_additive; + } + + bool animActive(qreal totalTimeElapsed) + { + if (totalTimeElapsed < m_from) + return false; + if (m_freeze || m_repeatCount < 0) // fill="freeze" or repeat="indefinite" + return true; + if (m_totalRunningTime == 0) + return false; + qreal animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime; + if (animationFrame > m_repeatCount) + return false; + return true; + } + + bool transformApplied() const + { + return m_transformApplied; + } + + // Call this instead of revert if you know that revert is unnecessary. + void clearTransformApplied() + { + m_transformApplied = false; + } + protected: void resolveMatrix(QSvgNode *node); private: qreal m_from, m_to, m_by; qreal m_totalRunningTime; TransformType m_type; + Additive m_additive; QVector<qreal> m_args; int m_count; QTransform m_transform; @@ -550,6 +586,7 @@ private: bool m_finished; bool m_freeze; qreal m_repeatCount; + bool m_transformApplied; }; diff --git a/tests/auto/qhostinfo/tst_qhostinfo.cpp b/tests/auto/qhostinfo/tst_qhostinfo.cpp index 4b60aca..abe49df 100644 --- a/tests/auto/qhostinfo/tst_qhostinfo.cpp +++ b/tests/auto/qhostinfo/tst_qhostinfo.cpp @@ -303,7 +303,7 @@ void tst_QHostInfo::reverseLookup_data() // ### Use internal DNS instead. Discussed with Andreas. //QTest::newRow("classical.hexago.com") << QString("2001:5c0:0:2::24") << QStringList(QString("classical.hexago.com")) << 0; QTest::newRow("www.cisco.com") << QString("198.133.219.25") << QStringList(QString("origin-www.cisco.com")) << 0; - QTest::newRow("bogusexample.doenstexist.org") << QString("1::2::3::4") << QStringList() << 1; + QTest::newRow("bogus-name") << QString("1::2::3::4") << QStringList() << 1; } void tst_QHostInfo::reverseLookup() diff --git a/tests/auto/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp b/tests/auto/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp index 4f24721..c4c33d5 100644 --- a/tests/auto/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp +++ b/tests/auto/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp @@ -99,6 +99,9 @@ private Q_SLOTS: void get401_data(); void get401(); + void getMultiple_data(); + void getMultiple(); + void getMultipleWithPipeliningAndMultiplePriorities(); }; @@ -763,5 +766,123 @@ void tst_QHttpNetworkConnection::nossl() } #endif + +void tst_QHttpNetworkConnection::getMultiple_data() +{ + QTest::addColumn<quint16>("connectionCount"); + QTest::addColumn<bool>("pipeliningAllowed"); + // send 100 requests. apache will usually force-close after 100 requests in a single tcp connection + QTest::addColumn<int>("requestCount"); + + QTest::newRow("6 connections, no pipelining, 100 requests") << quint16(6) << false << 100; + QTest::newRow("1 connection, no pipelining, 100 requests") << quint16(1) << false << 100; + QTest::newRow("6 connections, pipelining allowed, 100 requests") << quint16(2) << true << 100; + QTest::newRow("1 connection, pipelining allowed, 100 requests") << quint16(1) << true << 100; +} + +void tst_QHttpNetworkConnection::getMultiple() +{ + QFETCH(quint16, connectionCount); + QFETCH(bool, pipeliningAllowed); + QFETCH(int, requestCount); + + QHttpNetworkConnection connection(connectionCount, QtNetworkSettings::serverName()); + + QList<QHttpNetworkRequest*> requests; + QList<QHttpNetworkReply*> replies; + + for (int i = 0; i < requestCount; i++) { + // depending on what you use the results will vary. + // for the "real" results, use a URL that has "internet latency" for you. Then (6 connections, pipelining) will win. + // for LAN latency, you will possibly get that (1 connection, no pipelining) is the fastest + QHttpNetworkRequest *request = new QHttpNetworkRequest("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"); + // located in Berlin: + //QHttpNetworkRequest *request = new QHttpNetworkRequest(QUrl("http://klinsmann.nokia.trolltech.de/~berlin/qtcreatorad.gif")); + if (pipeliningAllowed) + request->setPipeliningAllowed(true); + requests.append(request); + QHttpNetworkReply *reply = connection.sendRequest(*request); + replies.append(reply); + } + + QTime stopWatch; + stopWatch.start(); + int finishedCount = 0; + do { + QCoreApplication::instance()->processEvents(); + if (stopWatch.elapsed() >= 60000) + break; + + finishedCount = 0; + for (int i = 0; i < replies.length(); i++) + if (replies.at(i)->isFinished()) + finishedCount++; + + } while (finishedCount != replies.length()); + + // redundant + for (int i = 0; i < replies.length(); i++) + QVERIFY(replies.at(i)->isFinished()); + + qDebug() << "===" << stopWatch.elapsed() << "msec ==="; + + qDeleteAll(requests); + qDeleteAll(replies); +} + +void tst_QHttpNetworkConnection::getMultipleWithPipeliningAndMultiplePriorities() +{ + quint16 requestCount = 100; + + // use 2 connections. + QHttpNetworkConnection connection(2, QtNetworkSettings::serverName()); + + QList<QHttpNetworkRequest*> requests; + QList<QHttpNetworkReply*> replies; + + for (int i = 0; i < requestCount; i++) { + + QHttpNetworkRequest *request = new QHttpNetworkRequest("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"); + + if (i % 2 || i % 3) + request->setPipeliningAllowed(true); + + if (i % 3) + request->setPriority(QHttpNetworkRequest::HighPriority); + else if (i % 5) + request->setPriority(QHttpNetworkRequest::NormalPriority); + else if (i % 7) + request->setPriority(QHttpNetworkRequest::LowPriority); + + requests.append(request); + QHttpNetworkReply *reply = connection.sendRequest(*request); + replies.append(reply); + } + + QTime stopWatch; + stopWatch.start(); + int finishedCount = 0; + do { + QCoreApplication::instance()->processEvents(); + if (stopWatch.elapsed() >= 60000) + break; + + finishedCount = 0; + for (int i = 0; i < replies.length(); i++) + if (replies.at(i)->isFinished()) + finishedCount++; + + } while (finishedCount != replies.length()); + + // redundant + for (int i = 0; i < replies.length(); i++) + QVERIFY(replies.at(i)->isFinished()); + + qDebug() << "===" << stopWatch.elapsed() << "msec ==="; + + qDeleteAll(requests); + qDeleteAll(replies); +} + QTEST_MAIN(tst_QHttpNetworkConnection) #include "tst_qhttpnetworkconnection.moc" diff --git a/tests/auto/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/qnetworkreply/tst_qnetworkreply.cpp index d339803..7a9d016 100644 --- a/tests/auto/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/qnetworkreply/tst_qnetworkreply.cpp @@ -2254,8 +2254,11 @@ void tst_QNetworkReply::ioGetFromHttpBrokenServer_data() QTest::addColumn<bool>("doDisconnect"); QTest::newRow("no-newline") << QByteArray("Hello World") << false; - QTest::newRow("just-newline") << QByteArray("\r\n") << false; - QTest::newRow("just-2newline") << QByteArray("\r\n\r\n") << false; + + // these are OK now, we just eat the lonely newlines + //QTest::newRow("just-newline") << QByteArray("\r\n") << false; + //QTest::newRow("just-2newline") << QByteArray("\r\n\r\n") << false; + QTest::newRow("with-newlines") << QByteArray("Long first line\r\nLong second line") << false; QTest::newRow("with-newlines2") << QByteArray("\r\nSecond line") << false; QTest::newRow("with-newlines3") << QByteArray("ICY\r\nSecond line") << false; @@ -2931,7 +2934,7 @@ void tst_QNetworkReply::ioPostToHttpFromSocket() QSignalSpy authenticationRequiredSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*))); QSignalSpy proxyAuthenticationRequiredSpy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); - QTestEventLoop::instance().enterLoop(1); + QTestEventLoop::instance().enterLoop(3); disconnect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); diff --git a/tests/auto/qsharedpointer/tst_qsharedpointer.cpp b/tests/auto/qsharedpointer/tst_qsharedpointer.cpp index c9b2325..b2afb9f 100644 --- a/tests/auto/qsharedpointer/tst_qsharedpointer.cpp +++ b/tests/auto/qsharedpointer/tst_qsharedpointer.cpp @@ -799,6 +799,7 @@ void tst_QSharedPointer::differentPointers() QVERIFY(baseptr.data() == aData); QVERIFY(aData == baseptr.data()); + QVERIFY(bool(operator==<Data,DiffPtrDerivedData>(baseptr, aData))); QVERIFY(baseptr == aData); QVERIFY(aData == baseptr); } @@ -1654,21 +1655,6 @@ void tst_QSharedPointer::invalidConstructs_data() << "QObject *ptr = new QObject;\n" "QWeakPointer<QObject> weak = ptr;\n" // this makes the object unmanaged "QSharedPointer<QObject> shared(ptr);\n"; - -#ifndef QT_NO_DEBUG - // this tests a Q_ASSERT, so it is only valid in debug mode - // the DerivedFromQObject destructor below creates a QWeakPointer from parent(). - // parent() is not 0 in the current Qt implementation, but has started destruction, - // so the code should detect that issue - QTest::newRow("shared-pointer-from-qobject-in-destruction") - << &QTest::QExternalTest::tryRunFail - << "class DerivedFromQObject: public QObject { public:\n" - " DerivedFromQObject(QObject *parent): QObject(parent) {}\n" - " ~DerivedFromQObject() { QWeakPointer<QObject> weak = parent(); }\n" - "};\n" - "QObject obj;\n" - "new DerivedFromQObject(&obj);"; -#endif } void tst_QSharedPointer::invalidConstructs() diff --git a/tests/auto/qstringbuilder/qstringbuilder.pro b/tests/auto/qstringbuilder/qstringbuilder.pro deleted file mode 100644 index c5a26d3..0000000 --- a/tests/auto/qstringbuilder/qstringbuilder.pro +++ /dev/null @@ -1,5 +0,0 @@ - -TEMPLATE = subdirs -SUBDIRS = scenario1.pro scenario2.pro scenario3.pro scenario4.pro - - diff --git a/tests/auto/qstringbuilder/scenario1.pro b/tests/auto/qstringbuilder/scenario1.pro deleted file mode 100644 index d72451c..0000000 --- a/tests/auto/qstringbuilder/scenario1.pro +++ /dev/null @@ -1,9 +0,0 @@ -load(qttest_p4) - -QT = core - -SOURCES += scenario1.cpp -HEADERS += tst_qstringbuilder.h - -DEFINES += SCENARIO=1 - diff --git a/tests/auto/qstringbuilder/scenario2.pro b/tests/auto/qstringbuilder/scenario2.pro deleted file mode 100644 index 78e0c68..0000000 --- a/tests/auto/qstringbuilder/scenario2.pro +++ /dev/null @@ -1,8 +0,0 @@ -load(qttest_p4) - -QT = core - -SOURCES += scenario2.cpp -HEADERS += tst_qstringbuilder.h - -DEFINES += SCENARIO=2 diff --git a/tests/auto/qstringbuilder/scenario3.pro b/tests/auto/qstringbuilder/scenario3.pro deleted file mode 100644 index 7b9e5af..0000000 --- a/tests/auto/qstringbuilder/scenario3.pro +++ /dev/null @@ -1,8 +0,0 @@ -load(qttest_p4) - -QT = core - -SOURCES += scenario3.cpp -HEADERS += tst_qstringbuilder.h - -DEFINES += SCENARIO=3 diff --git a/tests/auto/qstringbuilder/scenario4.pro b/tests/auto/qstringbuilder/scenario4.pro deleted file mode 100644 index 1b62b25..0000000 --- a/tests/auto/qstringbuilder/scenario4.pro +++ /dev/null @@ -1,8 +0,0 @@ -load(qttest_p4) - -QT = core - -SOURCES += scenario4.cpp -HEADERS += tst_qstringbuilder.h - -DEFINES += SCENARIO=4 diff --git a/tests/auto/qstringbuilder1/qstringbuilder1.pro b/tests/auto/qstringbuilder1/qstringbuilder1.pro new file mode 100644 index 0000000..1ca9d45 --- /dev/null +++ b/tests/auto/qstringbuilder1/qstringbuilder1.pro @@ -0,0 +1,9 @@ +load(qttest_p4) + +QT = core + +SOURCES += tst_qstringbuilder1.cpp +HEADERS += ../qstringbuilder1/stringbuilder.h + +DEFINES += SCENARIO=1 + diff --git a/tests/auto/qstringbuilder/tst_qstringbuilder.cpp b/tests/auto/qstringbuilder1/stringbuilder.cpp index ffe7739..c26099a 100644 --- a/tests/auto/qstringbuilder/tst_qstringbuilder.cpp +++ b/tests/auto/qstringbuilder1/stringbuilder.cpp @@ -86,7 +86,7 @@ #endif #include <QtTest/QtTest> -#include "tst_qstringbuilder.h" +#include "stringbuilder.h" //TESTED_CLASS=QStringBuilder //TESTED_FILES=qstringbuilder.cpp diff --git a/tests/auto/qstringbuilder/tst_qstringbuilder.h b/tests/auto/qstringbuilder1/stringbuilder.h index b7e7f5a..b7e7f5a 100644 --- a/tests/auto/qstringbuilder/tst_qstringbuilder.h +++ b/tests/auto/qstringbuilder1/stringbuilder.h diff --git a/tests/auto/qstringbuilder/scenario3.cpp b/tests/auto/qstringbuilder1/tst_qstringbuilder1.cpp index 9159649..f6fa7c0 100644 --- a/tests/auto/qstringbuilder/scenario3.cpp +++ b/tests/auto/qstringbuilder1/tst_qstringbuilder1.cpp @@ -39,4 +39,4 @@ ** ****************************************************************************/ -#include "tst_qstringbuilder.cpp" +#include "../qstringbuilder1/stringbuilder.cpp" diff --git a/tests/auto/qstringbuilder2/qstringbuilder2.pro b/tests/auto/qstringbuilder2/qstringbuilder2.pro new file mode 100644 index 0000000..c0b3ebc --- /dev/null +++ b/tests/auto/qstringbuilder2/qstringbuilder2.pro @@ -0,0 +1,8 @@ +load(qttest_p4) + +QT = core + +SOURCES += tst_qstringbuilder2.cpp +HEADERS += ../qstringbuilder1/stringbuilder.h + +DEFINES += SCENARIO=2 diff --git a/tests/auto/qstringbuilder/scenario1.cpp b/tests/auto/qstringbuilder2/tst_qstringbuilder2.cpp index 9159649..f6fa7c0 100644 --- a/tests/auto/qstringbuilder/scenario1.cpp +++ b/tests/auto/qstringbuilder2/tst_qstringbuilder2.cpp @@ -39,4 +39,4 @@ ** ****************************************************************************/ -#include "tst_qstringbuilder.cpp" +#include "../qstringbuilder1/stringbuilder.cpp" diff --git a/tests/auto/qstringbuilder3/qstringbuilder3.pro b/tests/auto/qstringbuilder3/qstringbuilder3.pro new file mode 100644 index 0000000..93d1a39 --- /dev/null +++ b/tests/auto/qstringbuilder3/qstringbuilder3.pro @@ -0,0 +1,8 @@ +load(qttest_p4) + +QT = core + +SOURCES += tst_qstringbuilder3.cpp +HEADERS += ../qstringbuilder1/stringbuilder.h + +DEFINES += SCENARIO=3 diff --git a/tests/auto/qstringbuilder/scenario2.cpp b/tests/auto/qstringbuilder3/tst_qstringbuilder3.cpp index 9159649..f6fa7c0 100644 --- a/tests/auto/qstringbuilder/scenario2.cpp +++ b/tests/auto/qstringbuilder3/tst_qstringbuilder3.cpp @@ -39,4 +39,4 @@ ** ****************************************************************************/ -#include "tst_qstringbuilder.cpp" +#include "../qstringbuilder1/stringbuilder.cpp" diff --git a/tests/auto/qstringbuilder4/qstringbuilder4.pro b/tests/auto/qstringbuilder4/qstringbuilder4.pro new file mode 100644 index 0000000..eeec447 --- /dev/null +++ b/tests/auto/qstringbuilder4/qstringbuilder4.pro @@ -0,0 +1,8 @@ +load(qttest_p4) + +QT = core + +SOURCES += tst_qstringbuilder4.cpp +HEADERS += ../qstringbuilder1/stringbuilder.h + +DEFINES += SCENARIO=4 diff --git a/tests/auto/qstringbuilder/scenario4.cpp b/tests/auto/qstringbuilder4/tst_qstringbuilder4.cpp index 25f7932..480db66 100644 --- a/tests/auto/qstringbuilder/scenario4.cpp +++ b/tests/auto/qstringbuilder4/tst_qstringbuilder4.cpp @@ -39,4 +39,4 @@ ** ****************************************************************************/ -#include "tst_qstringbuilder.cpp" +#include "../qstringbuilder1/stringbuilder.cpp" diff --git a/tests/auto/qtcpsocket/tst_qtcpsocket.cpp b/tests/auto/qtcpsocket/tst_qtcpsocket.cpp index ebb62e1..6d0dc21 100644 --- a/tests/auto/qtcpsocket/tst_qtcpsocket.cpp +++ b/tests/auto/qtcpsocket/tst_qtcpsocket.cpp @@ -45,6 +45,8 @@ #else #include <sys/types.h> #include <sys/socket.h> +#include <fcntl.h> +#include <unistd.h> #define SOCKET int #define INVALID_SOCKET -1 #endif @@ -415,6 +417,11 @@ void tst_QTcpSocket::setSocketDescriptor() } #else SOCKET sock = ::socket(AF_INET, SOCK_STREAM, 0); + + // artificially increase the value of sock + SOCKET sock2 = ::fcntl(sock, F_DUPFD, sock + 50); + ::close(sock); + sock = sock2; #endif QVERIFY(sock != INVALID_SOCKET); diff --git a/tests/auto/qtextdocument/tst_qtextdocument.cpp b/tests/auto/qtextdocument/tst_qtextdocument.cpp index 4643df0..cffa437 100644 --- a/tests/auto/qtextdocument/tst_qtextdocument.cpp +++ b/tests/auto/qtextdocument/tst_qtextdocument.cpp @@ -2490,6 +2490,25 @@ void tst_QTextDocument::testUndoBlocks() QCOMPARE(doc->toPlainText(), QString("Hello WorldOne\nTwo\nThree")); doc->undo(); QCOMPARE(doc->toPlainText(), QString("Hello World")); + cursor.insertText("One\nTwo\nThree"); + cursor.insertText("Trailing text"); + doc->undo(); + QCOMPARE(doc->toPlainText(), QString("Hello WorldOne\nTwo\nThree")); + doc->undo(); + QCOMPARE(doc->toPlainText(), QString("Hello World")); + doc->undo(); + QCOMPARE(doc->toPlainText(), QString("")); + + cursor.insertText("quod"); + cursor.beginEditBlock(); + cursor.insertText(" erat"); + cursor.endEditBlock(); + cursor.insertText(" demonstrandum"); + QCOMPARE(doc->toPlainText(), QString("quod erat demonstrandum")); + doc->undo(); + QCOMPARE(doc->toPlainText(), QString("quod erat")); + doc->undo(); + QCOMPARE(doc->toPlainText(), QString("quod")); doc->undo(); QCOMPARE(doc->toPlainText(), QString("")); } diff --git a/tests/auto/qvariant/tst_qvariant.cpp b/tests/auto/qvariant/tst_qvariant.cpp index 9ad4482..709cba9 100644 --- a/tests/auto/qvariant/tst_qvariant.cpp +++ b/tests/auto/qvariant/tst_qvariant.cpp @@ -267,6 +267,8 @@ private slots: void convertByteArrayToBool_data() const; void toIntFromQString() const; void task256984_setValue(); + + void numericalConvert(); }; Q_DECLARE_METATYPE(QDate) @@ -3060,11 +3062,45 @@ void tst_QVariant::task256984_setValue() QVERIFY( !v2.isDetached() ); qVariantSetValue(v2, 3); //set an integer value - + QVERIFY( v1.isDetached() ); QVERIFY( v2.isDetached() ); } +void tst_QVariant::numericalConvert() +{ + QVariant vfloat(float(5.3)); + QVariant vdouble(double(5.3)); + QVariant vreal(qreal(5.3)); + QVariant vint(int(5)); + QVariant vuint(uint(5)); + QVariant vshort(short(5)); + QVariant vlonglong(quint64(5)); + QVariant vstringint(QString::fromLatin1("5")); + QVariant vstring(QString::fromLatin1("5.3")); + + QVector<QVariant *> vect; + vect << &vfloat << &vdouble << &vreal << &vint << &vuint << &vshort<< &vlonglong << &vstringint << &vstring; + + for(int i = 0; i < vect.size(); i++) { + double num = 5.3; + if (i >= 3 && i <= 7) + num = 5; + QVariant *v = vect.at(i); + QCOMPARE(v->toFloat() , float(num)); + QCOMPARE(float(v->toReal()) , float(num)); + QCOMPARE(float(v->toDouble()) , float(num)); + if(i != 8) { + QCOMPARE(v->toInt() , int(num)); + QCOMPARE(v->toUInt() , uint(num)); + QCOMPARE(v->toULongLong() , quint64(num)); + } + QCOMPARE(v->toString() , QString::number(num)); + } +} + + + QTEST_MAIN(tst_QVariant) #include "tst_qvariant.moc" diff --git a/tests/benchmarks/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/benchmarks/qgraphicsitem/tst_qgraphicsitem.cpp index 923838a..91dd28b 100644 --- a/tests/benchmarks/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/benchmarks/qgraphicsitem/tst_qgraphicsitem.cpp @@ -61,6 +61,7 @@ public slots: private slots: void setParentItem(); void setParentItem_deep(); + void setParentItem_deep_reversed(); void deleteItemWithManyChildren(); void setPos_data(); void setPos(); @@ -113,6 +114,19 @@ void tst_QGraphicsItem::setParentItem_deep() } } +void tst_QGraphicsItem::setParentItem_deep_reversed() +{ + QBENCHMARK { + QGraphicsRectItem *lastRect = new QGraphicsRectItem; + for (int i = 0; i < 100; ++i) { + QGraphicsRectItem *parentRect = new QGraphicsRectItem; + lastRect->setParentItem(parentRect); + lastRect = parentRect; + } + delete lastRect; + } +} + void tst_QGraphicsItem::deleteItemWithManyChildren() { QBENCHMARK { diff --git a/tools/designer/src/components/formeditor/formwindow.cpp b/tools/designer/src/components/formeditor/formwindow.cpp index 11fc06b..8a7bb79 100644 --- a/tools/designer/src/components/formeditor/formwindow.cpp +++ b/tools/designer/src/components/formeditor/formwindow.cpp @@ -2238,12 +2238,12 @@ QMenu *FormWindow::createPopupMenu(QWidget *w) layoutMenu->addAction(manager->actionAdjustSize()); layoutMenu->addAction(manager->actionHorizontalLayout()); layoutMenu->addAction(manager->actionVerticalLayout()); - layoutMenu->addAction(manager->actionGridLayout()); - layoutMenu->addAction(manager->actionFormLayout()); if (!isFormWindow) { layoutMenu->addAction(manager->actionSplitHorizontal()); layoutMenu->addAction(manager->actionSplitVertical()); } + layoutMenu->addAction(manager->actionGridLayout()); + layoutMenu->addAction(manager->actionFormLayout()); layoutMenu->addAction(manager->actionBreakLayout()); layoutMenu->addAction(manager->actionSimplifyLayout()); diff --git a/tools/designer/src/components/formeditor/qtbrushmanager.h b/tools/designer/src/components/formeditor/qtbrushmanager.h index 416a364..6a25180 100644 --- a/tools/designer/src/components/formeditor/qtbrushmanager.h +++ b/tools/designer/src/components/formeditor/qtbrushmanager.h @@ -71,10 +71,6 @@ public: void setCurrentBrush(const QString &name); QPixmap brushPixmap(const QBrush &brush) const; -signals: - void brushAdded(const QString &name, const QBrush &brush); - void brushRemoved(const QString &name); - void currentBrushChanged(const QString &name, const QBrush &brush); private: QtBrushManagerPrivate *d_ptr; diff --git a/tools/designer/src/lib/shared/qdesigner_command.cpp b/tools/designer/src/lib/shared/qdesigner_command.cpp index 7483c67..3badd30 100644 --- a/tools/designer/src/lib/shared/qdesigner_command.cpp +++ b/tools/designer/src/lib/shared/qdesigner_command.cpp @@ -1113,7 +1113,7 @@ void AddToolBoxPageCommand::init(QToolBox *toolBox, InsertionMode mode) m_widget = new QDesignerWidget(formWindow(), m_toolBox); m_itemText = QApplication::translate("Command", "Page"); m_itemIcon = QIcon(); - m_widget->setObjectName(QApplication::translate("Command", "page")); + m_widget->setObjectName(QLatin1String("page")); formWindow()->ensureUniqueObjectName(m_widget); setText(QApplication::translate("Command", "Insert Page")); @@ -1236,7 +1236,7 @@ void AddTabPageCommand::init(QTabWidget *tabWidget, InsertionMode mode) m_widget = new QDesignerWidget(formWindow(), m_tabWidget); m_itemText = QApplication::translate("Command", "Page"); m_itemIcon = QIcon(); - m_widget->setObjectName(QApplication::translate("Command", "tab")); + m_widget->setObjectName(QLatin1String("tab")); formWindow()->ensureUniqueObjectName(m_widget); setText(QApplication::translate("Command", "Insert Page")); @@ -1422,7 +1422,7 @@ void AddStackedWidgetPageCommand::init(QStackedWidget *stackedWidget, InsertionM if (mode == InsertAfter) m_index++; m_widget = new QDesignerWidget(formWindow(), m_stackedWidget); - m_widget->setObjectName(QApplication::translate("Command", "page")); + m_widget->setObjectName(QLatin1String("page")); formWindow()->ensureUniqueObjectName(m_widget); setText(QApplication::translate("Command", "Insert Page")); |