diff options
author | Yann Bodson <yann.bodson@nokia.com> | 2010-01-29 03:01:49 (GMT) |
---|---|---|
committer | Yann Bodson <yann.bodson@nokia.com> | 2010-01-29 03:01:49 (GMT) |
commit | 5cf8677758e6fbfa5bf360c73519c14630db808c (patch) | |
tree | b92c5b97561ef875c08397bb8b4b925007f557a6 /src | |
parent | b91871a108638a5a136ac2a4310f5a3743320f72 (diff) | |
parent | 430f3ba46d3db10f7b4534f74e65d2ca13b23747 (diff) | |
download | Qt-5cf8677758e6fbfa5bf360c73519c14630db808c.zip Qt-5cf8677758e6fbfa5bf360c73519c14630db808c.tar.gz Qt-5cf8677758e6fbfa5bf360c73519c14630db808c.tar.bz2 |
Merge branch 'kinetic-declarativeui' of scm.dev.nokia.troll.no:qt/kinetic into kinetic-declarativeui
Diffstat (limited to 'src')
-rw-r--r-- | src/declarative/qml/qml.pri | 11 | ||||
-rw-r--r-- | src/declarative/qml/qmlengine.cpp | 62 | ||||
-rw-r--r-- | src/declarative/qml/qmlengine.h | 8 | ||||
-rw-r--r-- | src/declarative/qml/qmlengine_p.h | 3 | ||||
-rw-r--r-- | src/declarative/qml/qmlnetworkaccessmanagerfactory.cpp | 93 | ||||
-rw-r--r-- | src/declarative/qml/qmlnetworkaccessmanagerfactory.h | 70 | ||||
-rw-r--r-- | src/declarative/util/qmlanimation.cpp | 6 | ||||
-rw-r--r-- | src/declarative/util/qmllistmodel.cpp | 10 | ||||
-rw-r--r-- | src/declarative/util/qmlpixmapcache.cpp | 321 | ||||
-rw-r--r-- | src/declarative/util/qmlpixmapcache_p.h | 10 |
10 files changed, 451 insertions, 143 deletions
diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index 6f2f57f..cd2fbff 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -1,5 +1,4 @@ INCLUDEPATH += $$PWD - SOURCES += \ $$PWD/qmlparser.cpp \ $$PWD/qmlinstruction.cpp \ @@ -50,8 +49,8 @@ SOURCES += \ $$PWD/qmlvaluetypescriptclass.cpp \ $$PWD/qmltypenamescriptclass.cpp \ $$PWD/qmllistscriptclass.cpp \ - $$PWD/qmlworkerscript.cpp - + $$PWD/qmlworkerscript.cpp \ + $$PWD/qmlnetworkaccessmanagerfactory.cpp HEADERS += \ $$PWD/qmlparser_p.h \ $$PWD/qmlglobal_p.h \ @@ -117,10 +116,8 @@ HEADERS += \ $$PWD/qmllistscriptclass_p.h \ $$PWD/qmlworkerscript_p.h \ $$PWD/qmlscriptclass_p.h \ - $$PWD/qmlguard_p.h - + $$PWD/qmlguard_p.h \ + $$PWD/qmlnetworkaccessmanagerfactory.h QT += sql - include(parser/parser.pri) include(rewriter/rewriter.pri) - diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index 5a40468..b3ac1bb 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -64,6 +64,7 @@ #include "qmlworkerscript_p.h" #include "qmlcomponent_p.h" #include "qmlscriptclass_p.h" +#include "qmlnetworkaccessmanagerfactory.h" #include <qfxperf_p_p.h> @@ -121,7 +122,8 @@ QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e) contextClass(0), sharedContext(0), sharedScope(0), objectClass(0), valueTypeClass(0), globalClass(0), cleanup(0), erroredBindings(0), inProgressCreations(0), scriptEngine(this), workerScriptEngine(0), componentAttacheds(0), inBeginCreate(false), - networkAccessManager(0), typeManager(e), uniqueId(1) + networkAccessManager(0), networkAccessManagerFactory(0), accessManagerValid(false), + typeManager(e), uniqueId(1) { globalClass = new QmlGlobalScriptClass(&scriptEngine); fileImportPath.append(QLibraryInfo::location(QLibraryInfo::DataPath)+QDir::separator()+QLatin1String("qml")); @@ -386,34 +388,64 @@ QmlContext *QmlEngine::rootContext() } /*! - Sets the common QNetworkAccessManager, \a network, used by all QML elements - instantiated by this engine. + Sets the \a factory to use for creating QNetworkAccessManager(s). + + QNetworkAccessManager is used for all network access by QML. + By implementing a factory it is possible to create custom + QNetworkAccessManager with specialized caching, proxy and + cookie support. +*/ +void QmlEngine::setNetworkAccessManagerFactory(QmlNetworkAccessManagerFactory *factory) +{ + Q_D(QmlEngine); + d->networkAccessManagerFactory = factory; +} - If the parent of \a network is this engine, the engine takes ownership and - will delete it as needed. Otherwise, ownership remains with the caller. +/*! + Returns the current QmlNetworkAccessManagerFactory. - This method should only be called before any QmlComponents are instantiated. + \sa setNetworkAccessManagerFactory() */ -void QmlEngine::setNetworkAccessManager(QNetworkAccessManager *network) +QmlNetworkAccessManagerFactory *QmlEngine::networkAccessManagerFactory() const +{ + Q_D(const QmlEngine); + return d->networkAccessManagerFactory; +} + +void QmlEngine::namInvalidated() { Q_D(QmlEngine); - if (d->networkAccessManager && d->networkAccessManager->parent() == this) - delete d->networkAccessManager; - d->networkAccessManager = network; + d->accessManagerValid = false; } /*! - Returns the common QNetworkAccessManager used by all QML elements + Returns a common QNetworkAccessManager which can be used by any QML element instantiated by this engine. - The default implements no caching, cookiejar, etc., just a default - QNetworkAccessManager. + If a QmlNetworkAccessManagerFactory has been set and a QNetworkAccessManager + has not yet been created, the QmlNetworkAccessManagerFactory will be used + to create the QNetworkAccessManager; otherwise the returned QNetworkAccessManager + will have no proxy or cache set. + + \sa setNetworkAccessManagerFactory() */ QNetworkAccessManager *QmlEngine::networkAccessManager() const { Q_D(const QmlEngine); - if (!d->networkAccessManager) - d->networkAccessManager = new QNetworkAccessManager(const_cast<QmlEngine*>(this)); + if (!d->accessManagerValid) { + delete d->networkAccessManagerFactory; + d->networkAccessManagerFactory = 0; + } + if (!d->networkAccessManager) { + if (d->networkAccessManagerFactory) { + connect(d->networkAccessManagerFactory, SIGNAL(invalidated()) + , this, SLOT(namInvalidated()), Qt::UniqueConnection); + d->networkAccessManager = d->networkAccessManagerFactory->create(const_cast<QmlEngine*>(this)); + } else { + d->networkAccessManager = new QNetworkAccessManager(const_cast<QmlEngine*>(this)); + } + d->accessManagerValid = true; + } return d->networkAccessManager; } diff --git a/src/declarative/qml/qmlengine.h b/src/declarative/qml/qmlengine.h index 7cbbcac..b9ec277 100644 --- a/src/declarative/qml/qmlengine.h +++ b/src/declarative/qml/qmlengine.h @@ -63,6 +63,7 @@ class QUrl; class QScriptEngine; class QScriptContext; class QNetworkAccessManager; +class QmlNetworkAccessManagerFactory; class Q_DECLARATIVE_EXPORT QmlEngine : public QObject { Q_PROPERTY(QString offlineStoragePath READ offlineStoragePath WRITE setOfflineStoragePath) @@ -77,7 +78,9 @@ public: void addImportPath(const QString& dir); - void setNetworkAccessManager(QNetworkAccessManager *); + void setNetworkAccessManagerFactory(QmlNetworkAccessManagerFactory *); + QmlNetworkAccessManagerFactory *networkAccessManagerFactory() const; + QNetworkAccessManager *networkAccessManager() const; void setOfflineStoragePath(const QString& dir); @@ -92,6 +95,9 @@ public: Q_SIGNALS: void quit (); +private Q_SLOTS: + void namInvalidated(); + private: Q_DECLARE_PRIVATE(QmlEngine) }; diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h index 85e8c80..6f62b40 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -92,6 +92,7 @@ class QmlValueTypeScriptClass; class QScriptEngineDebugger; class QNetworkReply; class QNetworkAccessManager; +class QmlNetworkAccessManagerFactory; class QmlAbstractBinding; class QScriptDeclarativeClass; class QmlTypeNameScriptClass; @@ -209,6 +210,8 @@ public: bool inBeginCreate; mutable QNetworkAccessManager *networkAccessManager; + mutable QmlNetworkAccessManagerFactory *networkAccessManagerFactory; + mutable bool accessManagerValid; QmlCompositeTypeManager typeManager; QStringList fileImportPath; diff --git a/src/declarative/qml/qmlnetworkaccessmanagerfactory.cpp b/src/declarative/qml/qmlnetworkaccessmanagerfactory.cpp new file mode 100644 index 0000000..6ae20de --- /dev/null +++ b/src/declarative/qml/qmlnetworkaccessmanagerfactory.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmlnetworkaccessmanagerfactory.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QmlNetworkAccessManagerFactory + \brief The QmlNetworkAccessManagerFactory class provides a factory for QNetworkAccessManager + + QNetworkAccessManager is used for all network access by QML. + By implementing a factory it is possible to create custom + QNetworkAccessManager with specialized caching, proxy and + cookie support. + + To implement a factory, subclass QmlNetworkAccessManagerFactory and implement + the create() method. + + If the created QNetworkAccessManager becomes invalid, due to a + change in proxy settings, for example, call the invalidate() method. + This will cause all QNetworkAccessManagers to be recreated. + + Note: the create() method may be called by multiple threads, so ensure the + implementation of this method is reentrant. +*/ +QmlNetworkAccessManagerFactory::~QmlNetworkAccessManagerFactory() +{ +} + +/*! + \fn QNetworkAccessManager *QmlNetworkAccessManagerFactory::create(QObject *parent) + + Implement this method to create a QNetworkAccessManager with \a parent. + This allows proxies, caching and cookie support to be setup appropriately. + + Note: this method may be called by multiple threads, so ensure the + implementation of this method is reentrant. +*/ + +/*! + Invalidates all currently created QNetworkAccessManager(s) which + will cause create() to be called for subsequent network access. +*/ +void QmlNetworkAccessManagerFactory::invalidate() +{ + emit invalidated(); +} + +/*! + \internal + \fn QmlNetworkAccessManagerFactory::invalidated() +*/ + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlnetworkaccessmanagerfactory.h b/src/declarative/qml/qmlnetworkaccessmanagerfactory.h new file mode 100644 index 0000000..f64918b --- /dev/null +++ b/src/declarative/qml/qmlnetworkaccessmanagerfactory.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLNETWORKACCESSMANAGERFACTORY_H +#define QMLNETWORKACCESSMANAGERFACTORY_H + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QNetworkAccessManager; +class Q_DECLARATIVE_EXPORT QmlNetworkAccessManagerFactory : public QObject +{ + Q_OBJECT +public: + virtual ~QmlNetworkAccessManagerFactory(); + void invalidate(); + virtual QNetworkAccessManager *create(QObject *parent) = 0; + +Q_SIGNALS: + void invalidated(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLNETWORKACCESSMANAGERFACTORY_H diff --git a/src/declarative/util/qmlanimation.cpp b/src/declarative/util/qmlanimation.cpp index f5333a6..efb4159 100644 --- a/src/declarative/util/qmlanimation.cpp +++ b/src/declarative/util/qmlanimation.cpp @@ -1660,10 +1660,12 @@ void QmlPropertyAnimationPrivate::convertVariant(QVariant &variant, int type) \inherits Animation \brief The PropertyAnimation element allows you to animate property changes. - Animate a size property over 200ms, from its current size to 20-by-20: + Animate theObject's size property over 200ms, from its current size to 20-by-20: \code - PropertyAnimation { property: "size"; to: "20x20"; duration: 200 } + PropertyAnimation { target: theObject; property: "size"; to: "20x20"; duration: 200 } \endcode + + For an introduction to animation in QML, see \l{QML Animation}. */ QmlPropertyAnimation::QmlPropertyAnimation(QObject *parent) diff --git a/src/declarative/util/qmllistmodel.cpp b/src/declarative/util/qmllistmodel.cpp index e975a5f..8c70539 100644 --- a/src/declarative/util/qmllistmodel.cpp +++ b/src/declarative/util/qmllistmodel.cpp @@ -405,7 +405,7 @@ QHash<int,QVariant> QmlListModel::data(int index, const QList<int> &roles) const { checkRoles(); QHash<int, QVariant> rv; - if (index >= count()) + if (index >= count() || index < 0) return rv; ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); @@ -430,7 +430,7 @@ QVariant QmlListModel::data(int index, int role) const { checkRoles(); QVariant rv; - if (index >= count()) + if (index >= count() || index < 0) return rv; ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); @@ -643,7 +643,7 @@ void QmlListModel::append(const QScriptValue& valuemap) */ QScriptValue QmlListModel::get(int index) const { - if (index >= count()) { + if (index >= count() || index < 0) { qmlInfo(this) << tr("get: index %1 out of range").arg(index); return 0; } @@ -680,7 +680,7 @@ void QmlListModel::set(int index, const QScriptValue& valuemap) qmlInfo(this) << tr("set: value is not an object"); return; } - if ( !_root || index > _root->values.count()) { + if ( !_root || index > _root->values.count() || index < 0) { qmlInfo(this) << tr("set: index %1 out of range").arg(index); return; } @@ -719,7 +719,7 @@ void QmlListModel::set(int index, const QScriptValue& valuemap) */ void QmlListModel::setProperty(int index, const QString& property, const QVariant& value) { - if ( !_root || index >= _root->values.count()) { + if ( !_root || index >= _root->values.count() || index < 0) { qmlInfo(this) << tr("set: index %1 out of range").arg(index); return; } diff --git a/src/declarative/util/qmlpixmapcache.cpp b/src/declarative/util/qmlpixmapcache.cpp index 4c1d448..c058408 100644 --- a/src/declarative/util/qmlpixmapcache.cpp +++ b/src/declarative/util/qmlpixmapcache.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qmlpixmapcache_p.h" +#include "qmlnetworkaccessmanagerfactory.h" #include "qfxperf_p_p.h" @@ -57,6 +58,15 @@ #include <QWaitCondition> #include <QtCore/qdebug.h> #include <private/qobject_p.h> +#include <QSslError> + +#ifdef Q_OS_LINUX +#include <pthread.h> +#include <linux/sched.h> +#endif + +// Maximum number of simultaneous image requests to send. +static const int maxImageRequestCount = 8; QT_BEGIN_NAMESPACE @@ -69,105 +79,231 @@ class QmlImageReader : public QThread { Q_OBJECT public: - QmlImageReader(QObject *parent=0); + QmlImageReader(QmlEngine *eng); ~QmlImageReader(); - void read(QmlPixmapReply *rep); + QmlPixmapReply *getImage(const QUrl &url); void cancel(QmlPixmapReply *rep); + static QmlImageReader *instance(QmlEngine *engine); + protected: void run(); + bool event(QEvent *event); -private: - struct Job { - Job() : reply(0), error(false) {} - QmlPixmapReply *reply; - QImage img; - bool error; - }; +private slots: + void networkRequestDone(); + void namInvalidated() { + accessManagerValid = false; + } - void loadImage(Job &job); +private: + QNetworkAccessManager *networkAccessManager() { + if (!accessManagerValid) { + delete accessManager; + accessManager = 0; + } + if (!accessManager) { + if (engine && engine->networkAccessManagerFactory()) { + connect(engine->networkAccessManagerFactory(), SIGNAL(invalidated()) + , this, SLOT(namInvalidated()), Qt::UniqueConnection); + accessManager = engine->networkAccessManagerFactory()->create(this); + } else { + accessManager = new QNetworkAccessManager(this); + } + accessManagerValid = true; + } + return accessManager; + } - QList<Job> jobs; + QList<QmlPixmapReply*> jobs; + QList<QmlPixmapReply*> cancelled; + QHash<QNetworkReply*,QmlPixmapReply*> replies; + QNetworkAccessManager *accessManager; + bool accessManagerValid; + QmlEngine *engine; QMutex mutex; - QWaitCondition haveJob; - bool quit; + static QHash<QmlEngine *,QmlImageReader*> readers; }; -class QmlImageDecodeEvent : public QEvent +QHash<QmlEngine *,QmlImageReader*> QmlImageReader::readers; + +class QmlImageReaderEvent : public QEvent { public: - QmlImageDecodeEvent(bool err, QImage &img) : QEvent(QEvent::User), error(err), image(img) {} + enum ReadError { NoError, Loading, Decoding }; - bool error; + QmlImageReaderEvent(QmlImageReaderEvent::ReadError err, QImage &img) + : QEvent(QEvent::User), error(err), image(img) {} + + ReadError error; QImage image; }; -Q_GLOBAL_STATIC(QmlImageReader, qmlImageReader) - -QmlImageReader::QmlImageReader(QObject *parent) : QThread(parent), quit(false) +QmlImageReader::QmlImageReader(QmlEngine *eng) + : QThread(eng), accessManager(0), accessManagerValid(false), engine(eng) { start(QThread::LowPriority); } QmlImageReader::~QmlImageReader() { - quit = true; - haveJob.wakeOne(); } -void QmlImageReader::read(QmlPixmapReply *reply) +QmlImageReader *QmlImageReader::instance(QmlEngine *engine) +{ + QmlImageReader *reader = readers.value(engine); + if (!reader) { + static QMutex rmutex; + rmutex.lock(); + reader = new QmlImageReader(engine); + readers.insert(engine, reader); + rmutex.unlock(); + } + + return reader; +} + +QmlPixmapReply *QmlImageReader::getImage(const QUrl &url) { mutex.lock(); - Job job; - job.reply = reply; - jobs.append(job); + QmlPixmapReply *reply = new QmlPixmapReply(engine, url); + jobs.append(reply); if (jobs.count() == 1) - haveJob.wakeOne(); + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); mutex.unlock(); + return reply; } void QmlImageReader::cancel(QmlPixmapReply *reply) { mutex.lock(); - QList<Job>::iterator it = jobs.begin(); - while (it != jobs.end()) { - if ((*it).reply == reply) { - jobs.erase(it); - break; + if (reply->isLoading()) { + // Already requested. Add to cancel list to be cancelled in reader thread. + cancelled.append(reply); + if (cancelled.count() == 1) + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); + } else { + // Not yet processed - just remove from waiting list + QList<QmlPixmapReply*>::iterator it = jobs.begin(); + while (it != jobs.end()) { + QmlPixmapReply *job = *it; + if (job == reply) { + jobs.erase(it); + break; + } + ++it; } - ++it; } mutex.unlock(); } -void QmlImageReader::loadImage(Job &job) +void QmlImageReader::run() { - QImageReader imgio(job.reply->device()); - if (imgio.read(&job.img)) { - job.error = false; - } else { - job.error = true; - qWarning() << imgio.errorString(); +#ifdef Q_OS_LINUX + struct sched_param param; + int policy; + + pthread_getschedparam(pthread_self(), &policy, ¶m); + pthread_setschedparam(pthread_self(), SCHED_IDLE, ¶m); +#endif + + exec(); +} + +bool QmlImageReader::event(QEvent *event) +{ + if (event->type() == QEvent::User) { + static int replyDownloadProgress = -1; + static int replyFinished = -1; + static int downloadProgress = -1; + static int thisNetworkRequestDone = -1; + + if (replyDownloadProgress == -1) { + replyDownloadProgress = QNetworkReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); + replyFinished = QNetworkReply::staticMetaObject.indexOfSignal("finished()"); + downloadProgress = QmlPixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); + thisNetworkRequestDone = QmlImageReader::staticMetaObject.indexOfSlot("networkRequestDone()"); + } + + while (1) { + mutex.lock(); + + if (cancelled.count()) { + for (int i = 0; i < cancelled.count(); ++i) { + QmlPixmapReply *job = cancelled.at(i); + QNetworkReply *reply = replies.key(job, 0); + if (reply && reply->isRunning()) { + replies.remove(reply); + reply->close(); + job->release(true); + } + } + cancelled.clear(); + } + + if (!accessManagerValid) { + // throw away existing requests and reschedule. + QHash<QNetworkReply*,QmlPixmapReply*>::iterator it = replies.begin(); + for (; it != replies.end(); ++it) { + delete it.key(); + jobs.prepend(*it); + } + replies.clear(); + } + + if (!jobs.count() || replies.count() > maxImageRequestCount) { + mutex.unlock(); + break; + } + + QmlPixmapReply *runningJob = jobs.takeFirst(); + runningJob->addRef(); + runningJob->setLoading(); + QUrl url = runningJob->url(); + mutex.unlock(); + + // fetch + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + QNetworkReply *reply = networkAccessManager()->get(req); + + QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress); + QMetaObject::connect(reply, replyFinished, this, thisNetworkRequestDone); + + replies.insert(reply, runningJob); + } + return true; } + + return QObject::event(event); } -void QmlImageReader::run() +void QmlImageReader::networkRequestDone() { - while (1) { - mutex.lock(); - if (!jobs.count()) - haveJob.wait(&mutex); - if (quit) - break; - Job runningJob = jobs.takeFirst(); - runningJob.reply->addRef(); - mutex.unlock(); - - loadImage(runningJob); - QCoreApplication::postEvent(runningJob.reply, new QmlImageDecodeEvent(runningJob.error, runningJob.img)); + QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); + QmlPixmapReply *job = replies.take(reply); + if (job) { + QImage image; + QmlImageReaderEvent::ReadError error; + if (reply->error()) { + error = QmlImageReaderEvent::Loading; + } else { + QImageReader imgio(reply); + if (imgio.read(&image)) { + error = QmlImageReaderEvent::NoError; + } else { + error = QmlImageReaderEvent::Decoding; + } + } + // send completion event to the QmlPixmapReply + QCoreApplication::postEvent(job, new QmlImageReaderEvent(error, image)); } + // kick off event loop again if we have dropped below max request count + if (replies.count() == maxImageRequestCount) + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); + reply->deleteLater(); } static bool readImage(QIODevice *dev, QPixmap *pixmap) @@ -228,46 +364,26 @@ class QmlPixmapReplyPrivate : public QObjectPrivate Q_DECLARE_PUBLIC(QmlPixmapReply) public: - QmlPixmapReplyPrivate(const QUrl &u, QNetworkReply *r) - : QObjectPrivate(), refCount(1), url(u), reply(r), status(QmlPixmapReply::Loading) { + QmlPixmapReplyPrivate(QmlEngine *e, const QUrl &u) + : QObjectPrivate(), refCount(1), url(u), status(QmlPixmapReply::Loading), loading(false), engine(e) { } int refCount; QUrl url; - QNetworkReply *reply; QPixmap pixmap; // ensure reference to pixmap so QPixmapCache does not discard - QImage image; QmlPixmapReply::Status status; + bool loading; + QmlEngine *engine; }; -QmlPixmapReply::QmlPixmapReply(const QUrl &url, QNetworkReply *reply) - : QObject(*new QmlPixmapReplyPrivate(url, reply), 0) +QmlPixmapReply::QmlPixmapReply(QmlEngine *engine, const QUrl &url) + : QObject(*new QmlPixmapReplyPrivate(engine, url), 0) { - Q_D(QmlPixmapReply); - - static int replyDownloadProgress = -1; - static int replyFinished = -1; - static int thisDownloadProgress = -1; - static int thisNetworkRequestDone = -1; - - if (replyDownloadProgress == -1) { - replyDownloadProgress = QNetworkReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); - replyFinished = QNetworkReply::staticMetaObject.indexOfSignal("finished()"); - thisDownloadProgress = QmlPixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); - thisNetworkRequestDone = QmlPixmapReply::staticMetaObject.indexOfSlot("networkRequestDone()"); - } - - QMetaObject::connect(d->reply, replyDownloadProgress, this, thisDownloadProgress, Qt::DirectConnection); - QMetaObject::connect(d->reply, replyFinished, this, thisNetworkRequestDone); } QmlPixmapReply::~QmlPixmapReply() { - Q_D(QmlPixmapReply); - if (d->status == Decoding) - qmlImageReader()->cancel(this); - delete d->reply; } const QUrl &QmlPixmapReply::url() const @@ -276,36 +392,16 @@ const QUrl &QmlPixmapReply::url() const return d->url; } -void QmlPixmapReply::networkRequestDone() -{ - Q_D(QmlPixmapReply); - if (d->reply->error()) { - d->pixmap = QPixmap(); - d->status = Error; - QByteArray key = d->url.toEncoded(QUrl::FormattingOption(0x100)); - QString strKey = QString::fromLatin1(key.constData(), key.count()); - QPixmapCache::insert(strKey, d->pixmap); - qWarning() << "Network error loading" << d->url << d->reply->errorString(); - emit finished(); - } else { - qmlImageReader()->read(this); - d->status = Decoding; - } -} - bool QmlPixmapReply::event(QEvent *event) { Q_D(QmlPixmapReply); if (event->type() == QEvent::User) { + d->loading = false; if (!release(true)) { - QmlImageDecodeEvent *de = static_cast<QmlImageDecodeEvent*>(event); - d->status = de->error ? Error : Ready; - if (d->status == Ready) { + QmlImageReaderEvent *de = static_cast<QmlImageReaderEvent*>(event); + d->status = (de->error == QmlImageReaderEvent::NoError) ? Ready : Error; + if (d->status == Ready) d->pixmap = QPixmap::fromImage(de->image); - d->image = QImage(); - } else { - qWarning() << "Error decoding" << d->url; - } QByteArray key = d->url.toEncoded(QUrl::FormattingOption(0x100)); QString strKey = QString::fromLatin1(key.constData(), key.count()); QPixmapCache::insert(strKey, d->pixmap); @@ -323,10 +419,16 @@ QmlPixmapReply::Status QmlPixmapReply::status() const return d->status; } -QIODevice *QmlPixmapReply::device() +bool QmlPixmapReply::isLoading() const +{ + Q_D(const QmlPixmapReply); + return d->loading; +} + +void QmlPixmapReply::setLoading() { Q_D(QmlPixmapReply); - return d->reply; + d->loading = true; } void QmlPixmapReply::addRef() @@ -342,11 +444,17 @@ bool QmlPixmapReply::release(bool defer) --d->refCount; if (d->refCount == 0) { qmlActivePixmapReplies()->remove(d->url); + if (d->status == Loading && !d->loading) + QmlImageReader::instance(d->engine)->cancel(this); if (defer) deleteLater(); else delete this; return true; + } else if (d->refCount == 1 && d->loading) { + // The only reference left is the reader thread. + qmlActivePixmapReplies()->remove(d->url); + QmlImageReader::instance(d->engine)->cancel(this); } return false; @@ -418,9 +526,8 @@ QmlPixmapReply *QmlPixmapCache::request(QmlEngine *engine, const QUrl &url) { QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies()->find(url); if (iter == qmlActivePixmapReplies()->end()) { - QNetworkRequest req(url); - req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); - QmlPixmapReply *item = new QmlPixmapReply(url, engine->networkAccessManager()->get(req)); + QmlImageReader *reader = QmlImageReader::instance(engine); + QmlPixmapReply *item = reader->getImage(url); iter = qmlActivePixmapReplies()->insert(url, item); } else { (*iter)->addRef(); diff --git a/src/declarative/util/qmlpixmapcache_p.h b/src/declarative/util/qmlpixmapcache_p.h index 711e902..0140352 100644 --- a/src/declarative/util/qmlpixmapcache_p.h +++ b/src/declarative/util/qmlpixmapcache_p.h @@ -59,10 +59,10 @@ class Q_DECLARATIVE_EXPORT QmlPixmapReply : public QObject { Q_OBJECT public: - QmlPixmapReply(const QUrl &url, QNetworkReply *reply); + QmlPixmapReply(QmlEngine *engine, const QUrl &url); ~QmlPixmapReply(); - enum Status { Ready, Error, Unrequested, Loading, Decoding }; + enum Status { Ready, Error, Unrequested, Loading }; Status status() const; const QUrl &url() const; @@ -75,12 +75,10 @@ protected: bool event(QEvent *event); private: - QIODevice *device(); void addRef(); bool release(bool defer=false); - -private Q_SLOTS: - void networkRequestDone(); + bool isLoading() const; + void setLoading(); private: Q_DISABLE_COPY(QmlPixmapReply) |