diff options
author | Warwick Allison <warwick.allison@nokia.com> | 2010-02-01 01:41:07 (GMT) |
---|---|---|
committer | Warwick Allison <warwick.allison@nokia.com> | 2010-02-01 01:41:07 (GMT) |
commit | 7e85c6dc7ba0aaa6dfcad40dc0e9df0e2adb3741 (patch) | |
tree | 07e839b8dd88c381471b96917ef5aafa484233b1 /src/declarative/util | |
parent | 6e8feab83fa9303b1345f4a27478ba3ee0316e86 (diff) | |
parent | 5cf8677758e6fbfa5bf360c73519c14630db808c (diff) | |
download | Qt-7e85c6dc7ba0aaa6dfcad40dc0e9df0e2adb3741.zip Qt-7e85c6dc7ba0aaa6dfcad40dc0e9df0e2adb3741.tar.gz Qt-7e85c6dc7ba0aaa6dfcad40dc0e9df0e2adb3741.tar.bz2 |
Merge branch 'kinetic-declarativeui' of git@scm.dev.nokia.troll.no:qt/kinetic into kinetic-declarativeui
Conflicts:
src/declarative/util/qmlpixmapcache.cpp
Diffstat (limited to 'src/declarative/util')
-rw-r--r-- | src/declarative/util/qmlanimation.cpp | 28 | ||||
-rw-r--r-- | src/declarative/util/qmlconnection.cpp | 2 | ||||
-rw-r--r-- | src/declarative/util/qmllistmodel.cpp | 92 | ||||
-rw-r--r-- | src/declarative/util/qmllistmodel_p.h | 2 | ||||
-rw-r--r-- | src/declarative/util/qmlpackage.cpp | 24 | ||||
-rw-r--r-- | src/declarative/util/qmlpixmapcache.cpp | 364 | ||||
-rw-r--r-- | src/declarative/util/qmlpixmapcache_p.h | 12 | ||||
-rw-r--r-- | src/declarative/util/qmlpropertymap.cpp | 5 | ||||
-rw-r--r-- | src/declarative/util/qmlstate.cpp | 3 | ||||
-rw-r--r-- | src/declarative/util/qmlstateoperations_p.h | 2 | ||||
-rw-r--r-- | src/declarative/util/qmlstyledtext.cpp | 157 | ||||
-rw-r--r-- | src/declarative/util/qmltransitionmanager.cpp | 3 |
12 files changed, 444 insertions, 250 deletions
diff --git a/src/declarative/util/qmlanimation.cpp b/src/declarative/util/qmlanimation.cpp index 4c5015d..efb4159 100644 --- a/src/declarative/util/qmlanimation.cpp +++ b/src/declarative/util/qmlanimation.cpp @@ -1032,15 +1032,26 @@ void QmlPropertyAction::transition(QmlStateActions &actions, QmlSetPropertyAnimationAction *data = new QmlSetPropertyAnimationAction; + bool hasExplicit = false; if (hasTarget && d->value.isValid()) { Action myAction; myAction.property = d->createProperty(target(), d->propertyName, this); if (myAction.property.isValid()) { myAction.toValue = d->value; data->actions << myAction; + hasExplicit = true; + for (int ii = 0; ii < actions.count(); ++ii) { + Action &action = actions[ii]; + if (action.property.object() == myAction.property.object() && + myAction.property.name() == action.property.name()) { + modified << action.property; + break; //### any chance there could be multiples? + } + } } } + if (!hasExplicit) for (int ii = 0; ii < actions.count(); ++ii) { Action &action = actions[ii]; @@ -1649,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) @@ -2219,6 +2232,7 @@ void QmlPropertyAnimation::transition(QmlStateActions &actions, data->interpolator = d->interpolator; data->reverse = direction == Backward ? true : false; + bool hasExplicit = false; //an explicit animation has been specified if (hasTarget && d->toIsDefined) { Action myAction; @@ -2231,9 +2245,19 @@ void QmlPropertyAnimation::transition(QmlStateActions &actions, d->convertVariant(d->to, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); myAction.toValue = d->to; data->actions << myAction; + hasExplicit = true; + for (int ii = 0; ii < actions.count(); ++ii) { + Action &action = actions[ii]; + if (action.property.object() == myAction.property.object() && + myAction.property.name() == action.property.name()) { + modified << action.property; + break; //### any chance there could be multiples? + } + } } } + if (!hasExplicit) for (int ii = 0; ii < actions.count(); ++ii) { Action &action = actions[ii]; diff --git a/src/declarative/util/qmlconnection.cpp b/src/declarative/util/qmlconnection.cpp index f5fbd3f..800fd6b 100644 --- a/src/declarative/util/qmlconnection.cpp +++ b/src/declarative/util/qmlconnection.cpp @@ -172,7 +172,7 @@ void QmlConnection::connectIfValid() if (lparen >= 0 && d->signal.length() > lparen+2) { QStringList l = d->signal.mid(lparen+1,d->signal.length()-lparen-2).split(QLatin1Char(',')); foreach (const QString &s, l) { - sigparams.append(s.toUtf8()); + sigparams.append(s.trimmed().toUtf8()); } } QString signalname = d->signal.left(lparen); diff --git a/src/declarative/util/qmllistmodel.cpp b/src/declarative/util/qmllistmodel.cpp index de6ee2e..8c70539 100644 --- a/src/declarative/util/qmllistmodel.cpp +++ b/src/declarative/util/qmllistmodel.cpp @@ -265,6 +265,7 @@ struct ModelNode QmlListModel *modelCache; ModelObject *objectCache; + bool isArray; }; QT_END_NAMESPACE @@ -280,6 +281,7 @@ void ModelNode::setObjectValue(const QScriptValue& valuemap) { ModelNode *value = new ModelNode; QScriptValue v = it.value(); if (v.isArray()) { + value->isArray = true; value->setListValue(v); } else { value->values << v.toVariant(); @@ -296,6 +298,7 @@ void ModelNode::setListValue(const QScriptValue& valuelist) { ModelNode *value = new ModelNode; QScriptValue v = it.value(); if (v.isArray()) { + value->isArray = true; value->setListValue(v); } else if (v.isObject()) { value->setObjectValue(v); @@ -367,27 +370,29 @@ QVariant QmlListModel::valueForNode(ModelNode *node) const { QObject *rv = 0; - if (!node->properties.isEmpty()) { - // Object - rv = node->object(this); - } else if (node->values.count() == 0) { - // Invalid - return QVariant(); - } else if (node->values.count() == 1) { - // Value - QVariant &var = node->values[0]; - ModelNode *valueNode = qvariant_cast<ModelNode *>(var); - if (valueNode) { - if (!valueNode->properties.isEmpty()) - rv = valueNode->object(this); - else - rv = valueNode->model(this); - } else { - return var; - } - } else if (node->values.count() > 1) { + if (node->isArray) { // List rv = node->model(this); + } else { + if (!node->properties.isEmpty()) { + // Object + rv = node->object(this); + } else if (node->values.count() == 0) { + // Invalid + return QVariant(); + } else if (node->values.count() == 1) { + // Value + QVariant &var = node->values[0]; + ModelNode *valueNode = qvariant_cast<ModelNode *>(var); + if (valueNode) { + if (!valueNode->properties.isEmpty()) + rv = valueNode->object(this); + else + rv = valueNode->model(this); + } else { + return var; + } + } } if (rv) @@ -400,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)); @@ -425,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)); @@ -638,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; } @@ -675,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; } @@ -700,7 +705,7 @@ void QmlListModel::set(int index, const QScriptValue& valuemap) } /*! - \qmlmethod ListModel::set(int index, string property, variant value) + \qmlmethod ListModel::setProperty(int index, string property, variant value) Changes the \a property of the item at \a index in the list model to \a value. @@ -712,9 +717,9 @@ void QmlListModel::set(int index, const QScriptValue& valuemap) \sa append() */ -void QmlListModel::set(int index, const QString& property, const QVariant& value) +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; } @@ -738,6 +743,9 @@ public: QByteArray compile(const QList<QmlCustomParserProperty> &); bool compileProperty(const QmlCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data); void setCustomData(QObject *, const QByteArray &); + +private: + bool definesEmptyList(const QString &); }; bool QmlListModelParser::compileProperty(const QmlCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data) @@ -857,6 +865,8 @@ void QmlListModelParser::setCustomData(QObject *obj, const QByteArray &d) QStack<ModelNode *> nodes; nodes << root; + bool processingSet = false; + const ListModelData *lmd = (const ListModelData *)d.constData(); const char *data = ((const char *)lmd) + lmd->dataOffset; @@ -870,6 +880,8 @@ void QmlListModelParser::setCustomData(QObject *obj, const QByteArray &d) ModelNode *n2 = new ModelNode; n->values << qVariantFromValue(n2); nodes.push(n2); + if (processingSet) + n->isArray = true; } break; @@ -880,7 +892,17 @@ void QmlListModelParser::setCustomData(QObject *obj, const QByteArray &d) case ListInstruction::Value: { ModelNode *n = nodes.top(); - n->values.append(QString::fromUtf8(QByteArray(data + instr.dataIdx))); + QString s = QString::fromUtf8(QByteArray(data + instr.dataIdx)); + + bool isEmptyList = false; + if (!n->isArray) + isEmptyList = definesEmptyList(s); + if (isEmptyList) + n->isArray = true; + else + n->values.append(s); + + processingSet = false; } break; @@ -890,12 +912,26 @@ void QmlListModelParser::setCustomData(QObject *obj, const QByteArray &d) ModelNode *n2 = new ModelNode; n->properties.insert(QString::fromUtf8(data + instr.dataIdx), n2); nodes.push(n2); + processingSet = true; } break; } } } +bool QmlListModelParser::definesEmptyList(const QString &s) +{ + if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) { + bool isEmptyList = true; + for (int i=1; i<s.length()-1; i++) { + if (!s[i].isSpace()) + return false; + } + return true; + } + return false; +} + QML_DEFINE_CUSTOM_TYPE(Qt, 4,6, ListModel, QmlListModel, QmlListModelParser) /*! @@ -933,7 +969,7 @@ static void dump(ModelNode *node, int ind) } ModelNode::ModelNode() -: modelCache(0), objectCache(0) +: modelCache(0), objectCache(0), isArray(false) { } diff --git a/src/declarative/util/qmllistmodel_p.h b/src/declarative/util/qmllistmodel_p.h index 47236d0..e18d3fd 100644 --- a/src/declarative/util/qmllistmodel_p.h +++ b/src/declarative/util/qmllistmodel_p.h @@ -80,7 +80,7 @@ public: Q_INVOKABLE void insert(int index, const QScriptValue&); Q_INVOKABLE QScriptValue get(int index) const; Q_INVOKABLE void set(int index, const QScriptValue&); - Q_INVOKABLE void set(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); Q_INVOKABLE void move(int from, int to, int count); Q_SIGNALS: diff --git a/src/declarative/util/qmlpackage.cpp b/src/declarative/util/qmlpackage.cpp index f9238ca..3214dc8 100644 --- a/src/declarative/util/qmlpackage.cpp +++ b/src/declarative/util/qmlpackage.cpp @@ -42,6 +42,7 @@ #include "qmlpackage_p.h" #include <private/qobject_p.h> +#include "private/qmlguard_p.h" QT_BEGIN_NAMESPACE @@ -50,7 +51,28 @@ class QmlPackagePrivate : public QObjectPrivate public: QmlPackagePrivate() {} - QmlConcreteList<QObject *> dataList; + class DataList; + struct DataGuard : public QmlGuard<QObject> + { + DataGuard(QObject *obj, DataList *l) : list(l) { (QmlGuard<QObject>&)*this = obj; } + DataList *list; + void objectDestroyed(QObject *) { + // we assume priv will always be destroyed after objectDestroyed calls + list->removeOne(*this); + } + }; + + class DataList : public QList<DataGuard>, public QmlList<QObject*> + { + public: + virtual void append(QObject* v) { QList<DataGuard>::append(DataGuard(v, this)); } + virtual void insert(int i, QObject* v) { QList<DataGuard>::insert(i, DataGuard(v, this)); } + virtual void clear() { QList<DataGuard>::clear(); } + virtual QObject* at(int i) const { return QList<DataGuard>::at(i); } + virtual void removeAt(int i) { QList<DataGuard>::removeAt(i); } + virtual int count() const { return QList<DataGuard>::count(); } + }; + DataList dataList; }; class QmlPackageAttached : public QObject diff --git a/src/declarative/util/qmlpixmapcache.cpp b/src/declarative/util/qmlpixmapcache.cpp index 1f68512..5189118 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,112 +58,252 @@ #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 +inline uint qHash(const QUrl &uri) +{ + return qHash(uri.toEncoded(QUrl::FormattingOption(0x100))); +} + 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 }; + + QmlImageReaderEvent(QmlImageReaderEvent::ReadError err, QImage &img) + : QEvent(QEvent::User), error(err), image(img) {} - bool error; + 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) @@ -215,85 +356,55 @@ static QString toLocalFileOrQrc(const QUrl& url) return r; } -typedef QHash<QString, QmlPixmapReply *> QmlPixmapReplyHash; -static QmlPixmapReplyHash qmlActivePixmapReplies; +typedef QHash<QUrl, QmlPixmapReply *> QmlPixmapReplyHash; +Q_GLOBAL_STATIC(QmlPixmapReplyHash, qmlActivePixmapReplies); class QmlPixmapReplyPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QmlPixmapReply) public: - QmlPixmapReplyPrivate(const QString &url, QNetworkReply *r) - : QObjectPrivate(), refCount(1), urlKey(url), 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; - QString urlKey; - QNetworkReply *reply; + QUrl url; QPixmap pixmap; // ensure reference to pixmap so QPixmapCache does not discard - QImage image; QmlPixmapReply::Status status; + bool loading; + QmlEngine *engine; }; -QmlPixmapReply::QmlPixmapReply(const QString &key, QNetworkReply *reply) - : QObject(*new QmlPixmapReplyPrivate(key, 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; } -void QmlPixmapReply::networkRequestDone() +const QUrl &QmlPixmapReply::url() const { - Q_D(QmlPixmapReply); - if (d->reply->error()) { - d->pixmap = QPixmap(); - d->status = Error; - QPixmapCache::insert(d->urlKey, d->pixmap); - qWarning() << "Network error loading" << d->urlKey << d->reply->errorString(); - emit finished(); - } else { - qmlImageReader()->read(this); - d->status = Decoding; - } + Q_D(const QmlPixmapReply); + return d->url; } 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->urlKey; - } - QPixmapCache::insert(d->urlKey, d->pixmap); // note: may fail (returns false) + QByteArray key = d->url.toEncoded(QUrl::FormattingOption(0x100)); + QString strKey = QString::fromLatin1(key.constData(), key.count()); + QPixmapCache::insert(strKey, d->pixmap); // note: may fail (returns false) emit finished(); } return true; @@ -308,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() @@ -326,12 +443,18 @@ bool QmlPixmapReply::release(bool defer) Q_ASSERT(d->refCount > 0); --d->refCount; if (d->refCount == 0) { - qmlActivePixmapReplies.remove(d->urlKey); + 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; @@ -373,22 +496,22 @@ QmlPixmapReply::Status QmlPixmapCache::get(const QUrl& url, QPixmap *pixmap) } #endif - QString key = url.toString(); - - QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies.find(key); - if (iter != qmlActivePixmapReplies.end() && (*iter)->status() == QmlPixmapReply::Ready) { + QByteArray key = url.toEncoded(QUrl::FormattingOption(0x100)); + QString strKey = QString::fromLatin1(key.constData(), key.count()); + QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies()->find(url); + if (iter != qmlActivePixmapReplies()->end() && (*iter)->status() == QmlPixmapReply::Ready) { // Must check this, since QPixmapCache::insert may have failed. *pixmap = (*iter)->d_func()->pixmap; status = (*iter)->status(); (*iter)->release(); - } else if (QPixmapCache::find(key, pixmap)) { - if (iter != qmlActivePixmapReplies.end()) { + } else if (QPixmapCache::find(strKey, pixmap)) { + if (iter != qmlActivePixmapReplies()->end()) { status = (*iter)->status(); (*iter)->release(); } else { status = pixmap->isNull() ? QmlPixmapReply::Error : QmlPixmapReply::Ready; } - } else if (iter != qmlActivePixmapReplies.end()) { + } else if (iter != qmlActivePixmapReplies()->end()) { status = QmlPixmapReply::Loading; } @@ -406,13 +529,11 @@ QmlPixmapReply::Status QmlPixmapCache::get(const QUrl& url, QPixmap *pixmap) */ QmlPixmapReply *QmlPixmapCache::request(QmlEngine *engine, const QUrl &url) { - QString key = url.toString(); - QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies.find(key); - if (iter == qmlActivePixmapReplies.end()) { - QNetworkRequest req(url); - req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); - QmlPixmapReply *item = new QmlPixmapReply(key, engine->networkAccessManager()->get(req)); - iter = qmlActivePixmapReplies.insert(key, item); + QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies()->find(url); + if (iter == qmlActivePixmapReplies()->end()) { + QmlImageReader *reader = QmlImageReader::instance(engine); + QmlPixmapReply *item = reader->getImage(url); + iter = qmlActivePixmapReplies()->insert(url, item); } else { (*iter)->addRef(); } @@ -430,9 +551,8 @@ QmlPixmapReply *QmlPixmapCache::request(QmlEngine *engine, const QUrl &url) */ void QmlPixmapCache::cancel(const QUrl& url, QObject *obj) { - QString key = url.toString(); - QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies.find(key); - if (iter == qmlActivePixmapReplies.end()) + QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies()->find(url); + if (iter == qmlActivePixmapReplies()->end()) return; QmlPixmapReply *reply = *iter; @@ -447,7 +567,7 @@ void QmlPixmapCache::cancel(const QUrl& url, QObject *obj) */ int QmlPixmapCache::pendingRequests() { - return qmlActivePixmapReplies.count(); + return qmlActivePixmapReplies()->count(); } #include <qmlpixmapcache.moc> diff --git a/src/declarative/util/qmlpixmapcache_p.h b/src/declarative/util/qmlpixmapcache_p.h index 4dcafcf..0140352 100644 --- a/src/declarative/util/qmlpixmapcache_p.h +++ b/src/declarative/util/qmlpixmapcache_p.h @@ -59,12 +59,14 @@ class Q_DECLARATIVE_EXPORT QmlPixmapReply : public QObject { Q_OBJECT public: - QmlPixmapReply(const QString &key, 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; + Q_SIGNALS: void finished(); void downloadProgress(qint64, qint64); @@ -73,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) diff --git a/src/declarative/util/qmlpropertymap.cpp b/src/declarative/util/qmlpropertymap.cpp index d0a0428..226f82e 100644 --- a/src/declarative/util/qmlpropertymap.cpp +++ b/src/declarative/util/qmlpropertymap.cpp @@ -118,7 +118,7 @@ void QmlPropertyMapMetaObject::propertyWrite(int index) key will be updated as well. To detect value changes made in the UI layer you can connect to the valueChanged() signal. - However, note that valueChanged() is \b NOT emitted when changes are made by calling insert() + However, note that valueChanged() is \bold NOT emitted when changes are made by calling insert() or clear() - it is only emitted when a value is updated from QML. \note It is not possible to remove keys from the map; once a key has been added, you can only @@ -270,6 +270,9 @@ const QVariant QmlPropertyMap::operator[](const QString &key) const \fn void QmlPropertyMap::valueChanged(const QString &key) This signal is emitted whenever one of the values in the map is changed. \a key is the key corresponding to the value that was changed. + + \note valueChanged() is \bold NOT emitted when changes are made by calling insert() + or clear() - it is only emitted when a value is updated from QML. */ QT_END_NAMESPACE diff --git a/src/declarative/util/qmlstate.cpp b/src/declarative/util/qmlstate.cpp index e41cbaf..6a5b82a 100644 --- a/src/declarative/util/qmlstate.cpp +++ b/src/declarative/util/qmlstate.cpp @@ -451,7 +451,8 @@ void QmlState::apply(QmlStateGroup *group, QmlTransition *trans, QmlState *rever qWarning() << " Action event:" << action.event->typeName(); else qWarning() << " Action:" << action.property.object() - << action.property.name() << action.fromValue << action.toValue; + << action.property.name() << "From:" << action.fromValue + << "To:" << action.toValue; } } diff --git a/src/declarative/util/qmlstateoperations_p.h b/src/declarative/util/qmlstateoperations_p.h index 87af7bb..75c17422 100644 --- a/src/declarative/util/qmlstateoperations_p.h +++ b/src/declarative/util/qmlstateoperations_p.h @@ -45,7 +45,7 @@ #include "qmlstate_p.h" #include <qmlgraphicsitem.h> -#include "../graphicsitems/qmlgraphicsanchors_p.h" +#include "private/qmlgraphicsanchors_p.h" #include <qmlscriptstring.h> QT_BEGIN_HEADER diff --git a/src/declarative/util/qmlstyledtext.cpp b/src/declarative/util/qmlstyledtext.cpp index ce2b2ea..36b5e49 100644 --- a/src/declarative/util/qmlstyledtext.cpp +++ b/src/declarative/util/qmlstyledtext.cpp @@ -39,7 +39,6 @@ ** ****************************************************************************/ -#include <QXmlStreamReader> #include <QStack> #include <QVector> #include <QPainter> @@ -52,20 +51,22 @@ QmlStyledText supports few tags: <b></b> - bold + <i></i> - italic <br> - new line - <font color="color_name"></font> + <font color="color_name" size="1-7"></font> - The opening and closing tags may be correctly nested. + The opening and closing tags must be correctly nested. */ class QmlStyledTextPrivate { public: - QmlStyledTextPrivate(const QString &t, QTextLayout &l) : text(t), layout(l) {} + QmlStyledTextPrivate(const QString &t, QTextLayout &l) : text(t), layout(l), baseFont(layout.font()) {} void parse(); bool parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format); bool parseCloseTag(const QChar *&ch, const QString &textIn); + void parseEntity(const QChar *&ch, const QString &textIn, QString &textOut); bool parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format); QPair<QStringRef,QStringRef> parseAttribute(const QChar *&ch, const QString &textIn); QStringRef parseValue(const QChar *&ch, const QString &textIn); @@ -77,6 +78,7 @@ public: QString text; QTextLayout &layout; + QFont baseFont; static const QChar lessThan; static const QChar greaterThan; @@ -84,6 +86,7 @@ public: static const QChar singleQuote; static const QChar doubleQuote; static const QChar slash; + static const QChar ampersand; }; const QChar QmlStyledTextPrivate::lessThan(QLatin1Char('<')); @@ -92,6 +95,7 @@ const QChar QmlStyledTextPrivate::equals(QLatin1Char('=')); const QChar QmlStyledTextPrivate::singleQuote(QLatin1Char('\'')); const QChar QmlStyledTextPrivate::doubleQuote(QLatin1Char('\"')); const QChar QmlStyledTextPrivate::slash(QLatin1Char('/')); +const QChar QmlStyledTextPrivate::ampersand(QLatin1Char('&')); QmlStyledText::QmlStyledText(const QString &string, QTextLayout &layout) : d(new QmlStyledTextPrivate(string, layout)) @@ -109,66 +113,6 @@ void QmlStyledText::parse(const QString &string, QTextLayout &layout) styledText.d->parse(); } -#define CUSTOM_PARSER - -#ifndef CUSTOM_PARSER -void QmlStyledTextPrivate::parse() -{ - QList<QTextLayout::FormatRange> ranges; - QStack<QTextCharFormat> formatStack; - - QString drawText; - drawText.reserve(text.count()); - - QXmlStreamReader xml("<html>" + text + "</html>"); - while (!xml.atEnd()) { - xml.readNext(); - if (xml.tokenType() == QXmlStreamReader::StartElement) { - QStringRef name = xml.name(); - if (name == "b") { - QTextCharFormat format; - if (formatStack.count()) - format = formatStack.top(); - else - format.setFont(layout.font()); - format.setFontWeight(QFont::Bold); - formatStack.push(format); - } else if (name == "br") { - drawText.append(QChar(QChar::LineSeparator)); - } else if (name == "font") { - QTextCharFormat format; - if (formatStack.count()) - format = formatStack.top(); - else - format.setFont(layout.font()); - QStringRef col = xml.attributes().value("color"); - if (!col.isEmpty()) { - format.setForeground(QColor(col.toString())); - formatStack.push(format); - } - } - } else if (xml.tokenType() == QXmlStreamReader::EndElement) { - if (formatStack.count() > 1) { - QStringRef name = xml.name(); - if (name == "b" || name == "font" || name == "br") - formatStack.pop(); - } - } else if (xml.tokenType() == QXmlStreamReader::Characters) { - if (formatStack.count() > 1) { - QTextLayout::FormatRange formatRange; - formatRange.format = formatStack.top(); - formatRange.start = drawText.length(); - formatRange.length = xml.text().length(); - ranges.append(formatRange); - } - drawText.append(xml.text()); - } - } - - layout.setText(drawText); - layout.setAdditionalFormats(ranges); -} -#else void QmlStyledTextPrivate::parse() { QList<QTextLayout::FormatRange> ranges; @@ -179,19 +123,20 @@ void QmlStyledTextPrivate::parse() int textStart = 0; int textLength = 0; + int rangeStart = 0; const QChar *ch = text.constData(); while (!ch->isNull()) { if (*ch == lessThan) { - if (textLength) { - if (formatStack.count()) { - QTextLayout::FormatRange formatRange; - formatRange.format = formatStack.top(); - formatRange.start = drawText.length(); - formatRange.length = textLength; - ranges.append(formatRange); - } + if (textLength) drawText.append(QStringRef(&text, textStart, textLength)); + if (rangeStart != drawText.length() && formatStack.count()) { + QTextLayout::FormatRange formatRange; + formatRange.format = formatStack.top(); + formatRange.start = rangeStart; + formatRange.length = drawText.length() - rangeStart; + ranges.append(formatRange); } + rangeStart = drawText.length(); ++ch; if (*ch == slash) { ++ch; @@ -202,33 +147,36 @@ void QmlStyledTextPrivate::parse() if (formatStack.count()) format = formatStack.top(); else - format.setFont(layout.font()); + format.setFont(baseFont); if (parseTag(ch, text, drawText, format)) formatStack.push(format); } textStart = ch - text.constData() + 1; textLength = 0; + } else if (*ch == ampersand) { + ++ch; + drawText.append(QStringRef(&text, textStart, textLength)); + parseEntity(ch, text, drawText); + textStart = ch - text.constData() + 1; + textLength = 0; } else { ++textLength; } ++ch; } - if (textLength) { - if (formatStack.count()) { - QTextLayout::FormatRange formatRange; - formatRange.format = formatStack.top(); - formatRange.start = drawText.length(); - formatRange.length = textLength; - ranges.append(formatRange); - } + if (textLength) drawText.append(QStringRef(&text, textStart, textLength)); + if (rangeStart != drawText.length() && formatStack.count()) { + QTextLayout::FormatRange formatRange; + formatRange.format = formatStack.top(); + formatRange.start = rangeStart; + formatRange.length = drawText.length() - rangeStart; + ranges.append(formatRange); } layout.setText(drawText); layout.setAdditionalFormats(ranges); } -#endif - bool QmlStyledTextPrivate::parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format) { @@ -239,7 +187,8 @@ bool QmlStyledTextPrivate::parseTag(const QChar *&ch, const QString &textIn, QSt while (!ch->isNull()) { if (*ch == greaterThan) { QStringRef tag(&textIn, tagStart, tagLength); - if (tag.at(0) == QLatin1Char('b')) { + const QChar char0 = tag.at(0); + if (char0 == QLatin1Char('b')) { if (tagLength == 1) { format.setFontWeight(QFont::Bold); return true; @@ -247,6 +196,11 @@ bool QmlStyledTextPrivate::parseTag(const QChar *&ch, const QString &textIn, QSt textOut.append(QChar(QChar::LineSeparator)); return true; } + } else if (char0 == QLatin1Char('i')) { + if (tagLength == 1) { + format.setFontItalic(true); + return true; + } } return false; } else if (ch->isSpace()) { @@ -274,11 +228,15 @@ bool QmlStyledTextPrivate::parseCloseTag(const QChar *&ch, const QString &textIn while (!ch->isNull()) { if (*ch == greaterThan) { QStringRef tag(&textIn, tagStart, tagLength); - if (tag.at(0) == QLatin1Char('b')) { + const QChar char0 = tag.at(0); + if (char0 == QLatin1Char('b')) { if (tagLength == 1) return true; else if (tag.at(1) == QLatin1Char('r') && tagLength == 2) return true; + } else if (char0 == QLatin1Char('i')) { + if (tagLength == 1) + return true; } else if (tag == QLatin1String("font")) { return true; } @@ -292,6 +250,26 @@ bool QmlStyledTextPrivate::parseCloseTag(const QChar *&ch, const QString &textIn return false; } +void QmlStyledTextPrivate::parseEntity(const QChar *&ch, const QString &textIn, QString &textOut) +{ + int entityStart = ch - textIn.constData(); + int entityLength = 0; + while (!ch->isNull()) { + if (*ch == QLatin1Char(';')) { + QStringRef entity(&textIn, entityStart, entityLength); + if (entity == QLatin1String("gt")) + textOut += QChar(62); + else if (entity == QLatin1String("lt")) + textOut += QChar(60); + else if (entity == QLatin1String("amp")) + textOut += QChar(38); + return; + } + ++entityLength; + ++ch; + } +} + bool QmlStyledTextPrivate::parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format) { bool valid = false; @@ -301,6 +279,15 @@ bool QmlStyledTextPrivate::parseFontAttributes(const QChar *&ch, const QString & if (attr.first == QLatin1String("color")) { valid = true; format.setForeground(QColor(attr.second.toString())); + } else if (attr.first == QLatin1String("size")) { + valid = true; + int size = attr.second.toString().toInt(); + if (attr.second.at(0) == QLatin1Char('-') || attr.second.at(0) == QLatin1Char('+')) + size += 3; + if (size >= 1 && size <= 7) { + static const qreal scaling[] = { 0.7, 0.8, 1.0, 1.2, 1.5, 2.0, 2.4 }; + format.setFontPointSize(baseFont.pointSize() * scaling[size-1]); + } } } while (!ch->isNull() && !attr.first.isEmpty()); diff --git a/src/declarative/util/qmltransitionmanager.cpp b/src/declarative/util/qmltransitionmanager.cpp index bb935d0..e2a25c5 100644 --- a/src/declarative/util/qmltransitionmanager.cpp +++ b/src/declarative/util/qmltransitionmanager.cpp @@ -247,7 +247,8 @@ void QmlTransitionManager::transition(const QList<Action> &list, qWarning() << " No transition for event:" << action.event->typeName(); else qWarning() << " No transition for:" << action.property.object() - << action.property.name() << action.fromValue << action.toValue; + << action.property.name() << "From:" << action.fromValue + << "To:" << action.toValue; } } if (!transition) |