summaryrefslogtreecommitdiffstats
path: root/src/declarative/util
diff options
context:
space:
mode:
authorWarwick Allison <warwick.allison@nokia.com>2010-02-01 01:41:07 (GMT)
committerWarwick Allison <warwick.allison@nokia.com>2010-02-01 01:41:07 (GMT)
commit7e85c6dc7ba0aaa6dfcad40dc0e9df0e2adb3741 (patch)
tree07e839b8dd88c381471b96917ef5aafa484233b1 /src/declarative/util
parent6e8feab83fa9303b1345f4a27478ba3ee0316e86 (diff)
parent5cf8677758e6fbfa5bf360c73519c14630db808c (diff)
downloadQt-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.cpp28
-rw-r--r--src/declarative/util/qmlconnection.cpp2
-rw-r--r--src/declarative/util/qmllistmodel.cpp92
-rw-r--r--src/declarative/util/qmllistmodel_p.h2
-rw-r--r--src/declarative/util/qmlpackage.cpp24
-rw-r--r--src/declarative/util/qmlpixmapcache.cpp364
-rw-r--r--src/declarative/util/qmlpixmapcache_p.h12
-rw-r--r--src/declarative/util/qmlpropertymap.cpp5
-rw-r--r--src/declarative/util/qmlstate.cpp3
-rw-r--r--src/declarative/util/qmlstateoperations_p.h2
-rw-r--r--src/declarative/util/qmlstyledtext.cpp157
-rw-r--r--src/declarative/util/qmltransitionmanager.cpp3
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, &param);
+ pthread_setschedparam(pthread_self(), SCHED_IDLE, &param);
+#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)