diff options
Diffstat (limited to 'src/declarative/util')
64 files changed, 18383 insertions, 0 deletions
diff --git a/src/declarative/util/qfxperf.cpp b/src/declarative/util/qfxperf.cpp new file mode 100644 index 0000000..f62f810 --- /dev/null +++ b/src/declarative/util/qfxperf.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** 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 "qfxperf_p_p.h" + +QT_BEGIN_NAMESPACE + +Q_DEFINE_PERFORMANCE_LOG(QmlPerf, "QmlGraphics") { + Q_DEFINE_PERFORMANCE_METRIC(QmlParsing, "Compilation: QML Parsing") + Q_DEFINE_PERFORMANCE_METRIC(Compilation, " QML Compilation") + Q_DEFINE_PERFORMANCE_METRIC(VMEExecution, "Execution: QML VME Execution") + Q_DEFINE_PERFORMANCE_METRIC(BindInit, "BindValue Initialization") + Q_DEFINE_PERFORMANCE_METRIC(BindValue, "BindValue execution") + Q_DEFINE_PERFORMANCE_METRIC(BindValueSSE, "BindValue execution SSE") + Q_DEFINE_PERFORMANCE_METRIC(BindValueQt, "BindValue execution QtScript") + Q_DEFINE_PERFORMANCE_METRIC(BindableValueUpdate, "QmlBinding::update") + Q_DEFINE_PERFORMANCE_METRIC(PixmapLoad, "Pixmap loading") + Q_DEFINE_PERFORMANCE_METRIC(FontDatabase, "Font database creation") + Q_DEFINE_PERFORMANCE_METRIC(QmlGraphicsPathViewPathCache, "FX Items: QmlGraphicsPathView: Path cache") + Q_DEFINE_PERFORMANCE_METRIC(CreateParticle, " QmlGraphicsParticles: Particle creation") + Q_DEFINE_PERFORMANCE_METRIC(ItemComponentComplete, " QmlGraphicsItem::componentComplete") + Q_DEFINE_PERFORMANCE_METRIC(ImageComponentComplete, " QmlGraphicsImage::componentComplete") + Q_DEFINE_PERFORMANCE_METRIC(BaseLayoutComponentComplete, " QmlGraphicsBasePositioner::componentComplete") + Q_DEFINE_PERFORMANCE_METRIC(TextComponentComplete, " QmlGraphicsText::componentComplete") + Q_DEFINE_PERFORMANCE_METRIC(QmlGraphicsText_setText, " QmlGraphicsText::setText") + Q_DEFINE_PERFORMANCE_METRIC(AddScript, "QmlScript::addScriptToEngine") +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qfxperf_p_p.h b/src/declarative/util/qfxperf_p_p.h new file mode 100644 index 0000000..e3f820c --- /dev/null +++ b/src/declarative/util/qfxperf_p_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** 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 QFXPERF_H +#define QFXPERF_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qperformancelog_p_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +Q_DECLARE_PERFORMANCE_LOG(QmlPerf) { + Q_DECLARE_PERFORMANCE_METRIC(QmlParsing) + + Q_DECLARE_PERFORMANCE_METRIC(Compilation) + Q_DECLARE_PERFORMANCE_METRIC(VMEExecution) + + Q_DECLARE_PERFORMANCE_METRIC(BindInit) + Q_DECLARE_PERFORMANCE_METRIC(BindValue) + Q_DECLARE_PERFORMANCE_METRIC(BindValueSSE) + Q_DECLARE_PERFORMANCE_METRIC(BindValueQt) + Q_DECLARE_PERFORMANCE_METRIC(BindableValueUpdate) + Q_DECLARE_PERFORMANCE_METRIC(PixmapLoad) + Q_DECLARE_PERFORMANCE_METRIC(FontDatabase) + Q_DECLARE_PERFORMANCE_METRIC(QmlGraphicsPathViewPathCache) + Q_DECLARE_PERFORMANCE_METRIC(CreateParticle) + Q_DECLARE_PERFORMANCE_METRIC(ItemComponentComplete) + Q_DECLARE_PERFORMANCE_METRIC(ImageComponentComplete) + Q_DECLARE_PERFORMANCE_METRIC(BaseLayoutComponentComplete) + Q_DECLARE_PERFORMANCE_METRIC(TextComponentComplete) + Q_DECLARE_PERFORMANCE_METRIC(QmlGraphicsText_setText) + Q_DECLARE_PERFORMANCE_METRIC(AddScript) +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXPERF_H diff --git a/src/declarative/util/qmlanimation.cpp b/src/declarative/util/qmlanimation.cpp new file mode 100644 index 0000000..2d1d3b9 --- /dev/null +++ b/src/declarative/util/qmlanimation.cpp @@ -0,0 +1,2329 @@ +/**************************************************************************** +** +** 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 "qmlanimation_p.h" +#include "qmlanimation_p_p.h" + +#include "qmlbehavior_p.h" +#include "qmlstateoperations_p.h" + +#include <qmlpropertyvaluesource.h> +#include <qml.h> +#include <qmlinfo.h> +#include <qmlexpression.h> +#include <qmlstringconverters_p.h> +#include <qmlglobal_p.h> + +#include <qvariant.h> +#include <qcolor.h> +#include <qfile.h> +#include <QParallelAnimationGroup> +#include <QSequentialAnimationGroup> +#include <QtCore/qset.h> +#include <QtCore/qrect.h> +#include <QtCore/qpoint.h> +#include <QtCore/qsize.h> + +#include <private/qvariantanimation_p.h> + +QT_BEGIN_NAMESPACE + +static QEasingCurve stringToCurve(const QString &curve, QObject *obj) +{ + QEasingCurve easingCurve; + + QString normalizedCurve = curve; + bool hasParams = curve.contains(QLatin1Char('(')); + QStringList props; + + if (hasParams) { + QString easeName = curve.trimmed(); + if (!easeName.endsWith(QLatin1Char(')'))) { + qmlInfo(obj) << obj->tr("Unmatched parenthesis in easing function \"%1\"").arg(curve); + return easingCurve; + } + + int idx = easeName.indexOf(QLatin1Char('(')); + QString prop_str = + easeName.mid(idx + 1, easeName.length() - 1 - idx - 1); + normalizedCurve = easeName.left(idx); + if (!normalizedCurve.startsWith(QLatin1String("ease"))) { + qmlInfo(obj) << obj->tr("Easing function \"%1\" must start with \"ease\"").arg(curve); + return easingCurve; + } + + props = prop_str.split(QLatin1Char(',')); + } + + if (normalizedCurve.startsWith(QLatin1String("ease"))) + normalizedCurve = normalizedCurve.mid(4); + + static int index = QEasingCurve::staticMetaObject.indexOfEnumerator("Type"); + static QMetaEnum me = QEasingCurve::staticMetaObject.enumerator(index); + + int value = me.keyToValue(normalizedCurve.toUtf8().constData()); + if (value < 0) { + qmlInfo(obj) << obj->tr("Unknown easing curve \"%1\"").arg(curve); + return easingCurve; + } + easingCurve.setType((QEasingCurve::Type)value); + + if (hasParams) { + foreach(const QString &str, props) { + int sep = str.indexOf(QLatin1Char(':')); + + if (sep == -1) { + qmlInfo(obj) << obj->tr("Improperly specified parameter in easing function \"%1\"").arg(curve); + continue; + } + + QString propName = str.left(sep).trimmed(); + bool isOk; + qreal propValue = str.mid(sep + 1).trimmed().toDouble(&isOk); + + if (propName.isEmpty() || !isOk) { + qmlInfo(obj) << obj->tr("Improperly specified parameter in easing function \"%1\"").arg(curve); + continue; + } + + if (propName == QLatin1String("amplitude")) { + easingCurve.setAmplitude(propValue); + } else if (propName == QLatin1String("period")) { + easingCurve.setPeriod(propValue); + } else if (propName == QLatin1String("overshoot")) { + easingCurve.setOvershoot(propValue); + } else { + qmlInfo(obj) << obj->tr("Unknown easing parameter \"%1\"").arg(propName); + continue; + } + } + } + return easingCurve; +} + +QML_DEFINE_NOCREATE_TYPE(QmlAbstractAnimation) + +/*! + \qmlclass Animation QmlAbstractAnimation + \brief The Animation element is the base of all QML animations. + + The Animation element cannot be used directly in a QML file. It exists + to provide a set of common properties and methods, available across all the + other animation types that inherit from it. Attempting to use the Animation + element directly will result in an error. +*/ + +/*! + \class QmlAbstractAnimation + \internal +*/ + +QmlAbstractAnimation::QmlAbstractAnimation(QObject *parent) +: QObject(*(new QmlAbstractAnimationPrivate), parent) +{ +} + +QmlAbstractAnimation::~QmlAbstractAnimation() +{ +} + +QmlAbstractAnimation::QmlAbstractAnimation(QmlAbstractAnimationPrivate &dd, QObject *parent) +: QObject(dd, parent) +{ +} + +/*! + \qmlproperty bool Animation::running + This property holds whether the animation is currently running. + + The \c running property can be set to declaratively control whether or not + an animation is running. The following example will animate a rectangle + whenever the \l MouseRegion is pressed. + + \code + Rectangle { + width: 100; height: 100 + x: NumberAnimation { + running: myMouse.pressed + from: 0; to: 100 + } + MouseRegion { id: myMouse } + } + \endcode + + Likewise, the \c running property can be read to determine if the animation + is running. In the following example the text element will indicate whether + or not the animation is running. + + \code + NumberAnimation { id: myAnimation } + Text { text: myAnimation.running ? "Animation is running" : "Animation is not running" } + \endcode + + Animations can also be started and stopped imperatively from JavaScript + using the \c start() and \c stop() methods. + + By default, animations are not running. +*/ +bool QmlAbstractAnimation::isRunning() const +{ + Q_D(const QmlAbstractAnimation); + return d->running; +} + +void QmlAbstractAnimationPrivate::commence() +{ + Q_Q(QmlAbstractAnimation); + + q->prepare(userProperty.value); + q->qtAnimation()->start(); + if (q->qtAnimation()->state() != QAbstractAnimation::Running) { + running = false; + emit q->completed(); + } +} + +QmlMetaProperty QmlAbstractAnimationPrivate::createProperty(QObject *obj, const QString &str, QObject *infoObj) +{ + QmlMetaProperty prop = QmlMetaProperty::createProperty(obj, str, qmlContext(infoObj)); + if (!prop.isValid()) { + qmlInfo(infoObj) << QmlAbstractAnimation::tr("Cannot animate non-existant property \"%1\"").arg(str); + return QmlMetaProperty(); + } else if (!prop.isWritable()) { + qmlInfo(infoObj) << QmlAbstractAnimation::tr("Cannot animate read-only property \"%1\"").arg(str); + return QmlMetaProperty(); + } + return prop; +} + +void QmlAbstractAnimation::setRunning(bool r) +{ + Q_D(QmlAbstractAnimation); + if (d->running == r) + return; + + if (d->group) { + qWarning("QmlAbstractAnimation: setRunning() cannot be used on non-root animation nodes"); + return; + } + + d->running = r; + if (d->running) { + if (d->alwaysRunToEnd && d->repeat + && qtAnimation()->state() == QAbstractAnimation::Running) { + qtAnimation()->setLoopCount(-1); + } + + if (!d->connectedTimeLine) { + QObject::connect(qtAnimation(), SIGNAL(finished()), + this, SLOT(timelineComplete())); + d->connectedTimeLine = true; + } + if (d->componentComplete) + d->commence(); + else + d->startOnCompletion = true; + emit started(); + } else { + if (d->alwaysRunToEnd) { + if (d->repeat) + qtAnimation()->setLoopCount(qtAnimation()->currentLoop()+1); + } else + qtAnimation()->stop(); + + emit completed(); + } + + emit runningChanged(d->running); +} + +/*! + \qmlproperty bool Animation::paused + This property holds whether the animation is currently paused. + + The \c paused property can be set to declaratively control whether or not + an animation is paused. + + Animations can also be paused and resumed imperatively from JavaScript + using the \c pause() and \c resume() methods. + + By default, animations are not paused. +*/ +bool QmlAbstractAnimation::isPaused() const +{ + Q_D(const QmlAbstractAnimation); + return d->paused; +} + +void QmlAbstractAnimation::setPaused(bool p) +{ + Q_D(QmlAbstractAnimation); + if (d->paused == p) + return; + + if (d->group) { + qWarning("QmlAbstractAnimation: setPaused() cannot be used on non-root animation nodes"); + return; + } + + d->paused = p; + if (d->paused) + qtAnimation()->pause(); + else + qtAnimation()->resume(); + + emit pausedChanged(d->running); +} + +void QmlAbstractAnimation::classBegin() +{ + Q_D(QmlAbstractAnimation); + d->componentComplete = false; +} + +void QmlAbstractAnimation::componentComplete() +{ + Q_D(QmlAbstractAnimation); + if (d->startOnCompletion) + d->commence(); + d->componentComplete = true; +} + +/*! + \qmlproperty bool Animation::alwaysRunToEnd + This property holds whether the animation should run to completion when it is stopped. + + If this true the animation will complete its current iteration when it + is stopped - either by setting the \c running property to false, or by + calling the \c stop() method. The \c complete() method is not effected + by this value. + + This behavior is most useful when the \c repeat property is set, as the + animation will finish playing normally but not restart. + + By default, the alwaysRunToEnd property is not set. +*/ +bool QmlAbstractAnimation::alwaysRunToEnd() const +{ + Q_D(const QmlAbstractAnimation); + return d->alwaysRunToEnd; +} + +void QmlAbstractAnimation::setAlwaysRunToEnd(bool f) +{ + Q_D(QmlAbstractAnimation); + if (d->alwaysRunToEnd == f) + return; + + d->alwaysRunToEnd = f; + emit alwaysRunToEndChanged(f); +} + +/*! + \qmlproperty bool Animation::repeat + This property holds whether the animation should repeat. + + If set, the animation will continuously repeat until it is explicitly + stopped - either by setting the \c running property to false, or by calling + the \c stop() method. + + In the following example, the rectangle will spin indefinately. + + \code + Rectangle { + rotation: NumberAnimation { running: true; repeat: true; from: 0 to: 360 } + } + \endcode +*/ +bool QmlAbstractAnimation::repeat() const +{ + Q_D(const QmlAbstractAnimation); + return d->repeat; +} + +void QmlAbstractAnimation::setRepeat(bool r) +{ + Q_D(QmlAbstractAnimation); + if (r == d->repeat) + return; + + d->repeat = r; + int lc = r ? -1 : 1; + qtAnimation()->setLoopCount(lc); + emit repeatChanged(r); +} + +int QmlAbstractAnimation::currentTime() +{ + return qtAnimation()->currentLoopTime(); +} + +void QmlAbstractAnimation::setCurrentTime(int time) +{ + qtAnimation()->setCurrentTime(time); +} + +QmlAnimationGroup *QmlAbstractAnimation::group() const +{ + Q_D(const QmlAbstractAnimation); + return d->group; +} + +void QmlAbstractAnimation::setGroup(QmlAnimationGroup *g) +{ + Q_D(QmlAbstractAnimation); + if (d->group == g) + return; + if (d->group) + static_cast<QmlAnimationGroupPrivate *>(d->group->d_func())->animations.removeAll(this); + + d->group = g; + + if (d->group && !static_cast<QmlAnimationGroupPrivate *>(d->group->d_func())->animations.contains(this)) + static_cast<QmlAnimationGroupPrivate *>(d->group->d_func())->animations.append(this); + + if (d->group) + ((QAnimationGroup*)d->group->qtAnimation())->addAnimation(qtAnimation()); + + //if (g) //if removed from a group, then the group should no longer be the parent + setParent(g); +} + +/*! + \qmlmethod Animation::start() + \brief Starts the animation. + + If the animation is already running, calling this method has no effect. The + \c running property will be true following a call to \c start(). +*/ +void QmlAbstractAnimation::start() +{ + setRunning(true); +} + +/*! + \qmlmethod Animation::pause() + \brief Pauses the animation. + + If the animation is already paused, calling this method has no effect. The + \c paused property will be true following a call to \c pause(). +*/ +void QmlAbstractAnimation::pause() +{ + setPaused(true); +} + +/*! + \qmlmethod Animation::resume() + \brief Resumes a paused animation. + + If the animation is not paused, calling this method has no effect. The + \c paused property will be false following a call to \c resume(). +*/ +void QmlAbstractAnimation::resume() +{ + setPaused(false); +} + +/*! + \qmlmethod Animation::stop() + \brief Stops the animation. + + If the animation is not running, calling this method has no effect. The + \c running property will be false following a call to \c stop(). + + Normally \c stop() stops the animation immediately, and the animation has + no further influence on property values. In this example animation + \code + Rectangle { + x: NumberAnimation { from: 0; to: 100; duration: 500 } + } + \endcode + was stopped at time 250ms, the \c x property will have a value of 50. + + However, if the \c alwaysRunToEnd property is set, the animation will + continue running until it completes and then stop. The \c running property + will still become false immediately. +*/ +void QmlAbstractAnimation::stop() +{ + setRunning(false); +} + +/*! + \qmlmethod Animation::restart() + \brief Restarts the animation. + + This is a convenience method, and is equivalent to calling \c stop() and + then \c start(). +*/ +void QmlAbstractAnimation::restart() +{ + stop(); + start(); +} + +/*! + \qmlmethod Animation::complete() + \brief Stops the animation, jumping to the final property values. + + If the animation is not running, calling this method has no effect. The + \c running property will be false following a call to \c complete(). + + Unlike \c stop(), \c complete() immediately fast-forwards the animation to + its end. In the following example, + \code + Rectangle { + x: NumberAnimation { from: 0; to: 100; duration: 500 } + } + \endcode + calling \c stop() at time 250ms will result in the \c x property having + a value of 50, while calling \c complete() will set the \c x property to + 100, exactly as though the animation had played the whole way through. +*/ +void QmlAbstractAnimation::complete() +{ + if (isRunning()) { + qtAnimation()->setCurrentTime(qtAnimation()->duration()); + } +} + +void QmlAbstractAnimation::setTarget(const QmlMetaProperty &p) +{ + Q_D(QmlAbstractAnimation); + if (d->userProperty.isNull) + d->userProperty = p; +} + +//prepare is called before an animation begins +//(when an animation is used as a simple animation, and not as part of a transition) +void QmlAbstractAnimation::prepare(QmlMetaProperty &) +{ +} + +void QmlAbstractAnimation::transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction) +{ + Q_UNUSED(actions); + Q_UNUSED(modified); + Q_UNUSED(direction); +} + +void QmlAbstractAnimation::timelineComplete() +{ + Q_D(QmlAbstractAnimation); + setRunning(false); + if (d->alwaysRunToEnd && d->repeat) { + qtAnimation()->setLoopCount(-1); + } +} + +/*! + \qmlclass PauseAnimation QmlPauseAnimation + \inherits Animation + \brief The PauseAnimation element provides a pause for an animation. + + When used in a SequentialAnimation, PauseAnimation is a step when + nothing happens, for a specified duration. + + A 500ms animation sequence, with a 100ms pause between two animations: + \code + SequentialAnimation { + NumberAnimation { ... duration: 200 } + PauseAnimation { duration: 100 } + NumberAnimation { ... duration: 200 } + } + \endcode +*/ +/*! + \internal + \class QmlPauseAnimation +*/ + +QML_DEFINE_TYPE(Qt,4,6,PauseAnimation,QmlPauseAnimation) +QmlPauseAnimation::QmlPauseAnimation(QObject *parent) +: QmlAbstractAnimation(*(new QmlPauseAnimationPrivate), parent) +{ + Q_D(QmlPauseAnimation); + d->init(); +} + +QmlPauseAnimation::~QmlPauseAnimation() +{ +} + +void QmlPauseAnimationPrivate::init() +{ + Q_Q(QmlPauseAnimation); + pa = new QPauseAnimation; + QmlGraphics_setParent_noEvent(pa, q); +} + +/*! + \qmlproperty int PauseAnimation::duration + This property holds the duration of the pause in milliseconds + + The default value is 250. +*/ +int QmlPauseAnimation::duration() const +{ + Q_D(const QmlPauseAnimation); + return d->pa->duration(); +} + +void QmlPauseAnimation::setDuration(int duration) +{ + if (duration < 0) { + qmlInfo(this) << tr("Cannot set a duration of < 0"); + return; + } + + Q_D(QmlPauseAnimation); + if (d->pa->duration() == duration) + return; + d->pa->setDuration(duration); + emit durationChanged(duration); +} + +QAbstractAnimation *QmlPauseAnimation::qtAnimation() +{ + Q_D(QmlPauseAnimation); + return d->pa; +} + +/*! + \qmlclass ColorAnimation QmlColorAnimation + \inherits PropertyAnimation + \brief The ColorAnimation element allows you to animate color changes. + + \code + ColorAnimation { from: "white"; to: "#c0c0c0"; duration: 100 } + \endcode + + When used in a transition, ColorAnimation will by default animate + all properties of type color that are changing. If a property or properties + are explicitly set for the animation, then those will be used instead. +*/ +/*! + \internal + \class QmlColorAnimation +*/ + +QmlColorAnimation::QmlColorAnimation(QObject *parent) +: QmlPropertyAnimation(parent) +{ + Q_D(QmlPropertyAnimation); + d->interpolatorType = QMetaType::QColor; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + d->defaultToInterpolatorType = true; +} + +QmlColorAnimation::~QmlColorAnimation() +{ +} + +/*! + \qmlproperty color ColorAnimation::from + This property holds the starting color. +*/ +QColor QmlColorAnimation::from() const +{ + Q_D(const QmlPropertyAnimation); + return d->from.value<QColor>(); +} + +void QmlColorAnimation::setFrom(const QColor &f) +{ + QmlPropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty color ColorAnimation::from + This property holds the ending color. +*/ +QColor QmlColorAnimation::to() const +{ + Q_D(const QmlPropertyAnimation); + return d->to.value<QColor>(); +} + +void QmlColorAnimation::setTo(const QColor &t) +{ + QmlPropertyAnimation::setTo(t); +} + +QML_DEFINE_TYPE(Qt,4,6,ColorAnimation,QmlColorAnimation) + +/*! + \qmlclass ScriptAction QmlScriptAction + \inherits Animation + \brief The ScriptAction element allows scripts to be run during an animation. + +*/ +/*! + \internal + \class QmlScriptAction +*/ +QmlScriptAction::QmlScriptAction(QObject *parent) + :QmlAbstractAnimation(*(new QmlScriptActionPrivate), parent) +{ + Q_D(QmlScriptAction); + d->init(); +} + +QmlScriptAction::~QmlScriptAction() +{ +} + +void QmlScriptActionPrivate::init() +{ + Q_Q(QmlScriptAction); + rsa = new QActionAnimation(&proxy); + QmlGraphics_setParent_noEvent(rsa, q); +} + +/*! + \qmlproperty script ScriptAction::script + This property holds the script to run. +*/ +QmlScriptString QmlScriptAction::script() const +{ + Q_D(const QmlScriptAction); + return d->script; +} + +void QmlScriptAction::setScript(const QmlScriptString &script) +{ + Q_D(QmlScriptAction); + d->script = script; +} + +/*! + \qmlproperty QString ScriptAction::stateChangeScriptName + This property holds the the name of the StateChangeScript to run. + + This property is only valid when ScriptAction is used as part of a transition. + If both script and stateChangeScriptName are set, stateChangeScriptName will be used. +*/ +QString QmlScriptAction::stateChangeScriptName() const +{ + Q_D(const QmlScriptAction); + return d->name; +} + +void QmlScriptAction::setStateChangeScriptName(const QString &name) +{ + Q_D(QmlScriptAction); + d->name = name; +} + +void QmlScriptActionPrivate::execute() +{ + QmlScriptString scriptStr = hasRunScriptScript ? runScriptScript : script; + + const QString &str = scriptStr.script(); + if (!str.isEmpty()) { + QmlExpression expr(scriptStr.context(), str, scriptStr.scopeObject()); + expr.setTrackChange(false); + expr.value(); + } +} + +void QmlScriptAction::transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction) +{ + Q_D(QmlScriptAction); + Q_UNUSED(modified); + Q_UNUSED(direction); + + d->hasRunScriptScript = false; + for (int ii = 0; ii < actions.count(); ++ii) { + QmlAction &action = actions[ii]; + + if (action.event && action.event->typeName() == QLatin1String("StateChangeScript") + && static_cast<QmlStateChangeScript*>(action.event)->name() == d->name) { + //### how should we handle reverse direction? + d->runScriptScript = static_cast<QmlStateChangeScript*>(action.event)->script(); + d->hasRunScriptScript = true; + action.actionDone = true; + break; //assumes names are unique + } + } +} + +QAbstractAnimation *QmlScriptAction::qtAnimation() +{ + Q_D(QmlScriptAction); + return d->rsa; +} + +QML_DEFINE_TYPE(Qt,4,6,ScriptAction,QmlScriptAction) + +/*! + \qmlclass PropertyAction QmlPropertyAction + \inherits Animation + \brief The PropertyAction element allows immediate property changes during animation. + + Explicitly set \c theimage.smooth=true during a transition: + \code + PropertyAction { target: theimage; property: "smooth"; value: true } + \endcode + + Set \c thewebview.url to the value set for the destination state: + \code + PropertyAction { matchTargets: thewebview; matchProperties: "url" } + \endcode + + The PropertyAction is immediate - + the target property is not animated to the selected value in any way. +*/ +/*! + \internal + \class QmlPropertyAction +*/ +QmlPropertyAction::QmlPropertyAction(QObject *parent) +: QmlAbstractAnimation(*(new QmlPropertyActionPrivate), parent) +{ + Q_D(QmlPropertyAction); + d->init(); +} + +QmlPropertyAction::~QmlPropertyAction() +{ +} + +void QmlPropertyActionPrivate::init() +{ + Q_Q(QmlPropertyAction); + spa = new QActionAnimation; + QmlGraphics_setParent_noEvent(spa, q); +} + +/*! + \qmlproperty Object PropertyAction::target + This property holds an explicit target object to animate. + + The exact effect of the \c target property depends on how the animation + is being used. Refer to the \l animation documentation for details. +*/ + +QObject *QmlPropertyAction::target() const +{ + Q_D(const QmlPropertyAction); + return d->target; +} + +void QmlPropertyAction::setTarget(QObject *o) +{ + Q_D(QmlPropertyAction); + if (d->target == o) + return; + + d->target = o; + if (d->target && !d->propertyName.isEmpty()) { + d->userProperty = d->createProperty(d->target, d->propertyName, this); + } else { + d->userProperty.invalidate(); + } + + emit targetChanged(d->target, d->propertyName); +} + +/*! + \qmlproperty string PropertyAction::property + This property holds an explicit property to animated. + + The exact effect of the \c property property depends on how the animation + is being used. Refer to the \l animation documentation for details. +*/ + +QString QmlPropertyAction::property() const +{ + Q_D(const QmlPropertyAction); + return d->propertyName; +} + +void QmlPropertyAction::setProperty(const QString &n) +{ + Q_D(QmlPropertyAction); + if (d->propertyName == n) + return; + + d->propertyName = n; + if (d->target && !d->propertyName.isEmpty()) { + d->userProperty = d->createProperty(d->target, d->propertyName, this); + } else { + d->userProperty.invalidate(); + } + + emit targetChanged(d->target, d->propertyName); +} + +/*! + \qmlproperty string PropertyAction::matchProperties + This property holds a comma-separated list of property names this action + will match against. These names are used in conjunction with matchTargets + to create a list of properties that the action will set, assuming those + properties have changed. + + This property is typically used for an action appearing as part of a Transition. + + By default, no property names will be matched. + + \sa matchTargets PropertyAnimation::matchProperties +*/ +QString QmlPropertyAction::properties() const +{ + Q_D(const QmlPropertyAction); + return d->properties; +} + +void QmlPropertyAction::setProperties(const QString &p) +{ + Q_D(QmlPropertyAction); + if (d->properties == p) + return; + d->properties = p; + emit propertiesChanged(p); +} + +/*! + \qmlproperty list<Object> PropertyAction::matchTargets + This property holds a list of objects this action will match against. + These objects are used in conjunction with matchProperties to create a list of properties + that the action will set, assuming those properties have changed. + + This property is typically used for an action appearing as part of a Transition. + + By default, all changing targets will be matched. + + \sa exclude matchProperties PropertyAnimation::matchTargets +*/ +QmlListProperty<QObject> QmlPropertyAction::targets() +{ + Q_D(QmlPropertyAction); + return QmlListProperty<QObject>(this, d->targets); +} + +/*! + \qmlproperty list<Object> PropertyAction::exclude + This property holds the objects not to be affected by this animation. + \sa matchTargets +*/ +QmlListProperty<QObject> QmlPropertyAction::exclude() +{ + Q_D(QmlPropertyAction); + return QmlListProperty<QObject>(this, d->exclude); +} + +/*! + \qmlproperty any PropertyAction::value + This property holds the value to be set on the property. + If not set, then the value defined for the end state of the transition. +*/ +QVariant QmlPropertyAction::value() const +{ + Q_D(const QmlPropertyAction); + return d->value; +} + +void QmlPropertyAction::setValue(const QVariant &v) +{ + Q_D(QmlPropertyAction); + if (d->value.isNull || d->value != v) { + d->value = v; + emit valueChanged(v); + } +} + +void QmlPropertyActionPrivate::doAction() +{ + property.write(value, QmlMetaProperty::BypassInterceptor | QmlMetaProperty::DontRemoveBinding); +} + +QAbstractAnimation *QmlPropertyAction::qtAnimation() +{ + Q_D(QmlPropertyAction); + return d->spa; +} + +void QmlPropertyAction::prepare(QmlMetaProperty &p) +{ + Q_D(QmlPropertyAction); + + if (d->userProperty.isNull) + d->property = p; + else + d->property = d->userProperty; + + d->spa->setAnimAction(&d->proxy, QAbstractAnimation::KeepWhenStopped); +} + +void QmlPropertyAction::transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction) +{ + Q_D(QmlPropertyAction); + Q_UNUSED(direction); + + struct QmlSetPropertyAnimationAction : public QAbstractAnimationAction + { + QmlStateActions actions; + virtual void doAction() + { + for (int ii = 0; ii < actions.count(); ++ii) { + const QmlAction &action = actions.at(ii); + action.property.write(action.toValue, QmlMetaProperty::BypassInterceptor | QmlMetaProperty::DontRemoveBinding); + } + } + }; + + QStringList props = d->properties.isEmpty() ? QStringList() : d->properties.split(QLatin1Char(',')); + for (int ii = 0; ii < props.count(); ++ii) + props[ii] = props.at(ii).trimmed(); + + bool hasSelectors = !props.isEmpty() || !d->targets.isEmpty() || !d->exclude.isEmpty(); + bool hasTarget = !d->propertyName.isEmpty() || d->target; + + if (hasSelectors && hasTarget) { + qmlInfo(this) << tr("matchTargets/matchProperties/exclude and target/property are mutually exclusive."); + return; + } + + QmlSetPropertyAnimationAction *data = new QmlSetPropertyAnimationAction; + + bool hasExplicit = false; + if (hasTarget && d->value.isValid()) { + QmlAction 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) { + QmlAction &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) { + QmlAction &action = actions[ii]; + + QObject *obj = action.property.object(); + QString propertyName = action.property.name(); + QObject *sObj = action.specifiedObject; + QString sPropertyName = action.specifiedProperty; + bool same = (obj == sObj); + + if ((d->targets.isEmpty() || d->targets.contains(obj) || (!same && d->targets.contains(sObj))) && + (!d->exclude.contains(obj)) && (same || (!d->exclude.contains(sObj))) && + (props.contains(propertyName) || (!same && props.contains(sPropertyName)))) { + QmlAction myAction = action; + + if (d->value.isValid()) + myAction.toValue = d->value; + + modified << action.property; + data->actions << myAction; + action.fromValue = myAction.toValue; + } else if (d->userProperty.isValid() && + !hasSelectors && !hasTarget) { + if ((d->userProperty.value.object() == obj || (!same && d->userProperty.value.object() == sObj)) && + (d->userProperty.value.name() == propertyName || (!same && d->userProperty.value.name() == sPropertyName))) { + //### same as above. merge + QmlAction myAction = action; + + if (d->value.isValid()) + myAction.toValue = d->value; + + modified << action.property; + data->actions << myAction; + action.fromValue = myAction.toValue; + } + } + } + + if (data->actions.count()) { + d->spa->setAnimAction(data, QAbstractAnimation::DeleteWhenStopped); + } else { + delete data; + } +} + +QML_DEFINE_TYPE(Qt,4,6,PropertyAction,QmlPropertyAction) + +/*! + \qmlclass ParentAction QmlParentAction + \inherits Animation + \brief The ParentAction element allows parent changes during animation. + + ParentAction provides a way to specify at what point in a Transition a ParentChange should + occur. + \qml + State { + ParentChange { + target: myItem + parent: newParent + } + } + Transition { + SequentialAnimation { + PropertyAnimation { ... } + ParentAction {} //reparent myItem now + PropertyAnimation { ... } + } + } + \endqml + + It also provides a way to explicitly reparent an item during an animation. + \qml + SequentialAnimation { + ParentAction { target: myItem; parent: newParent } + PropertyAnimation {} + } + \endqml + + The ParentAction is immediate - it is not animated in any way. +*/ + +QmlParentAction::QmlParentAction(QObject *parent) +: QmlAbstractAnimation(*(new QmlParentActionPrivate), parent) +{ + Q_D(QmlParentAction); + d->init(); +} + +QmlParentAction::~QmlParentAction() +{ +} + +void QmlParentActionPrivate::init() +{ + Q_Q(QmlParentAction); + cpa = new QActionAnimation; + QmlGraphics_setParent_noEvent(cpa, q); +} + +/*! + \qmlproperty Item ParentAction::target + + This property holds an explicit target item to reparent. + */ +QmlGraphicsItem *QmlParentAction::object() const +{ + Q_D(const QmlParentAction); + return d->pcTarget; +} + +void QmlParentAction::setObject(QmlGraphicsItem *target) +{ + Q_D(QmlParentAction); + d->pcTarget = target; +} + +/*! + \qmlproperty Item ParentAction::matchTarget + This property holds the item this action will match against -- the item + that the action will reparent, assuming its parent has changed. + + In the following example, \c myItem will be reparented by the ParentAction, while + \c myOtherItem will not. + \qml + State { + ParentChange { + target: myItem + parent: newParent + } + ParentChange { + target: myOtherItem + parent: otherNewParent + } + } + Transition { + SequentialAnimation { + PropertyAnimation { ... } + ParentAction { matchTargets: myItem } + PropertyAnimation { ... } + } + } + \endqml + + This property is typically used for an action appearing as part of a Transition. + */ +QmlGraphicsItem *QmlParentAction::matchTarget() const +{ + Q_D(const QmlParentAction); + return d->pcTarget; +} + +void QmlParentAction::setMatchTarget(QmlGraphicsItem *target) +{ + Q_D(QmlParentAction); + d->pcMatchTarget = target; +} + +/*! + \qmlproperty Item ParentAction::parent + + The item to reparent to (i.e. the new parent). + */ +QmlGraphicsItem *QmlParentAction::parent() const +{ + Q_D(const QmlParentAction); + return d->pcParent; +} + +void QmlParentAction::setParent(QmlGraphicsItem *parent) +{ + Q_D(QmlParentAction); + d->pcParent = parent; +} + +void QmlParentActionPrivate::doAction() +{ + QmlParentChange pc; + pc.setObject(pcTarget); + pc.setParent(pcParent); + pc.execute(); +} + +QAbstractAnimation *QmlParentAction::qtAnimation() +{ + Q_D(QmlParentAction); + return d->cpa; +} + +void QmlParentAction::transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction) +{ + Q_D(QmlParentAction); + Q_UNUSED(modified); + Q_UNUSED(direction); + + struct QmlParentActionData : public QAbstractAnimationAction + { + QmlParentActionData(): pc(0) {} + ~QmlParentActionData() { delete pc; } + + QmlStateActions actions; + bool reverse; + QmlParentChange *pc; + virtual void doAction() + { + for (int ii = 0; ii < actions.count(); ++ii) { + const QmlAction &action = actions.at(ii); + if (reverse) + action.event->reverse(); + else + action.event->execute(); + } + } + }; + + QmlParentActionData *data = new QmlParentActionData; + + if (d->pcTarget && d->pcMatchTarget) { + qmlInfo(this) << tr("matchTarget and target are mutually exclusive."); + return; + } + + for (int ii = 0; ii < actions.count(); ++ii) { + QmlAction &action = actions[ii]; + + if (action.event && action.event->typeName() == QLatin1String("ParentChange") + && !d->pcTarget + && (!d->pcMatchTarget || static_cast<QmlParentChange*>(action.event)->object() == d->pcMatchTarget)) { + QmlAction myAction = action; + data->reverse = action.reverseEvent; + //### this logic differs from PropertyAnimation + // (probably a result of modified vs. done) + if (d->pcParent) { + //### should we disallow this case? + QmlParentChange *pc = new QmlParentChange; + pc->setObject(d->pcTarget); + pc->setParent(static_cast<QmlParentChange*>(action.event)->parent()); + myAction.event = pc; + data->pc = pc; + data->actions << myAction; + break; //only match one + } else { + action.actionDone = true; + data->actions << myAction; + } + } + } + + if (d->pcTarget && d->pcParent) { + data->reverse = false; + QmlAction myAction; + QmlParentChange *pc = new QmlParentChange; + pc->setObject(d->pcTarget); + pc->setParent(d->pcParent); + myAction.event = pc; + data->pc = pc; + data->actions << myAction; + } + + if (data->actions.count()) { + d->cpa->setAnimAction(data, QAbstractAnimation::DeleteWhenStopped); + } else { + delete data; + } +} + +QML_DEFINE_TYPE(Qt,4,6,ParentAction,QmlParentAction) + +/*! + \qmlclass NumberAnimation QmlNumberAnimation + \inherits PropertyAnimation + \brief The NumberAnimation element allows you to animate changes in properties of type qreal. + + Animate a set of properties over 200ms, from their values in the start state to + their values in the end state of the transition: + \code + NumberAnimation { matchProperties: "x,y,scale"; duration: 200 } + \endcode +*/ + +/*! + \internal + \class QmlNumberAnimation +*/ + +QmlNumberAnimation::QmlNumberAnimation(QObject *parent) +: QmlPropertyAnimation(parent) +{ + Q_D(QmlPropertyAnimation); + d->interpolatorType = QMetaType::QReal; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); +} + +QmlNumberAnimation::~QmlNumberAnimation() +{ +} + +/*! + \qmlproperty real NumberAnimation::from + This property holds the starting value. + If not set, then the value defined in the start state of the transition. +*/ +qreal QmlNumberAnimation::from() const +{ + Q_D(const QmlPropertyAnimation); + return d->from.toReal(); +} + +void QmlNumberAnimation::setFrom(qreal f) +{ + QmlPropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty real NumberAnimation::to + This property holds the ending value. + If not set, then the value defined in the end state of the transition. +*/ +qreal QmlNumberAnimation::to() const +{ + Q_D(const QmlPropertyAnimation); + return d->to.toReal(); +} + +void QmlNumberAnimation::setTo(qreal t) +{ + QmlPropertyAnimation::setTo(t); +} + +QML_DEFINE_TYPE(Qt,4,6,NumberAnimation,QmlNumberAnimation) + +/*! + \qmlclass Vector3dAnimation QmlVector3dAnimation + \inherits PropertyAnimation + \brief The Vector3dAnimation element allows you to animate changes in properties of type QVector3d. +*/ + +/*! + \internal + \class QmlVector3dAnimation +*/ + +QmlVector3dAnimation::QmlVector3dAnimation(QObject *parent) +: QmlPropertyAnimation(parent) +{ + Q_D(QmlPropertyAnimation); + d->interpolatorType = QMetaType::QVector3D; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + d->defaultToInterpolatorType = true; +} + +QmlVector3dAnimation::~QmlVector3dAnimation() +{ +} + +/*! + \qmlproperty real Vector3dAnimation::from + This property holds the starting value. + If not set, then the value defined in the start state of the transition. +*/ +QVector3D QmlVector3dAnimation::from() const +{ + Q_D(const QmlPropertyAnimation); + return d->from.value<QVector3D>(); +} + +void QmlVector3dAnimation::setFrom(QVector3D f) +{ + QmlPropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty real Vector3dAnimation::to + This property holds the ending value. + If not set, then the value defined in the end state of the transition. +*/ +QVector3D QmlVector3dAnimation::to() const +{ + Q_D(const QmlPropertyAnimation); + return d->to.value<QVector3D>(); +} + +void QmlVector3dAnimation::setTo(QVector3D t) +{ + QmlPropertyAnimation::setTo(t); +} + +QML_DEFINE_TYPE(Qt,4,6,Vector3dAnimation,QmlVector3dAnimation) + +QmlAnimationGroup::QmlAnimationGroup(QObject *parent) +: QmlAbstractAnimation(*(new QmlAnimationGroupPrivate), parent) +{ +} + +QmlAnimationGroup::~QmlAnimationGroup() +{ +} + +QmlList<QmlAbstractAnimation *> *QmlAnimationGroup::animations() +{ + Q_D(QmlAnimationGroup); + return &d->animations; +} + +/*! + \qmlclass SequentialAnimation QmlSequentialAnimation + \inherits Animation + \brief The SequentialAnimation element allows you to run animations sequentially. + + Animations controlled in SequentialAnimation will be run one after the other. + + The following example chains two numeric animations together. The \c MyItem + object will animate from its current x position to 100, and then back to 0. + + \code + SequentialAnimation { + NumberAnimation { target: MyItem; property: "x"; to: 100 } + NumberAnimation { target: MyItem; property: "x"; to: 0 } + } + \endcode + + \sa ParallelAnimation +*/ + +QmlSequentialAnimation::QmlSequentialAnimation(QObject *parent) : + QmlAnimationGroup(parent) +{ + Q_D(QmlAnimationGroup); + d->ag = new QSequentialAnimationGroup(this); +} + +QmlSequentialAnimation::~QmlSequentialAnimation() +{ +} + +void QmlSequentialAnimation::prepare(QmlMetaProperty &p) +{ + Q_D(QmlAnimationGroup); + if (d->userProperty.isNull) + d->property = p; + else + d->property = d->userProperty; + + for (int i = 0; i < d->animations.size(); ++i) + d->animations.at(i)->prepare(d->property); +} + +QAbstractAnimation *QmlSequentialAnimation::qtAnimation() +{ + Q_D(QmlAnimationGroup); + return d->ag; +} + +void QmlSequentialAnimation::transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction) +{ + Q_D(QmlAnimationGroup); + + int inc = 1; + int from = 0; + if (direction == Backward) { + inc = -1; + from = d->animations.count() - 1; + } + + //needed for Behavior + if (d->userProperty.isValid()) { + for (int i = 0; i < d->animations.count(); ++i) + d->animations.at(i)->setTarget(d->userProperty); + } + + for (int ii = from; ii < d->animations.count() && ii >= 0; ii += inc) { + d->animations.at(ii)->transition(actions, modified, direction); + } +} + +QML_DEFINE_TYPE(Qt,4,6,SequentialAnimation,QmlSequentialAnimation) + +/*! + \qmlclass ParallelAnimation QmlParallelAnimation + \inherits Animation + \brief The ParallelAnimation element allows you to run animations in parallel. + + Animations contained in ParallelAnimation will be run at the same time. + + The following animation demonstrates animating the \c MyItem item + to (100,100) by animating the x and y properties in parallel. + + \code + ParallelAnimation { + NumberAnimation { target: MyItem; property: "x"; to: 100 } + NumberAnimation { target: MyItem; property: "y"; to: 100 } + } + \endcode + + \sa SequentialAnimation +*/ +/*! + \internal + \class QmlParallelAnimation +*/ + +QmlParallelAnimation::QmlParallelAnimation(QObject *parent) : + QmlAnimationGroup(parent) +{ + Q_D(QmlAnimationGroup); + d->ag = new QParallelAnimationGroup(this); +} + +QmlParallelAnimation::~QmlParallelAnimation() +{ +} + +void QmlParallelAnimation::prepare(QmlMetaProperty &p) +{ + Q_D(QmlAnimationGroup); + if (d->userProperty.isNull) + d->property = p; + else + d->property = d->userProperty; + + for (int i = 0; i < d->animations.size(); ++i) + d->animations.at(i)->prepare(d->property); +} + +QAbstractAnimation *QmlParallelAnimation::qtAnimation() +{ + Q_D(QmlAnimationGroup); + return d->ag; +} + +void QmlParallelAnimation::transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction) +{ + Q_D(QmlAnimationGroup); + + //needed for Behavior + if (d->userProperty.isValid()) { + for (int i = 0; i < d->animations.count(); ++i) + d->animations.at(i)->setTarget(d->userProperty); + } + + for (int ii = 0; ii < d->animations.count(); ++ii) { + d->animations.at(ii)->transition(actions, modified, direction); + } +} + +QML_DEFINE_TYPE(Qt,4,6,ParallelAnimation,QmlParallelAnimation) + +//convert a variant from string type to another animatable type +void QmlPropertyAnimationPrivate::convertVariant(QVariant &variant, int type) +{ + if (variant.userType() != QVariant::String) { + variant.convert((QVariant::Type)type); + return; + } + + switch (type) { + case QVariant::Rect: { + variant.setValue(QmlStringConverters::rectFFromString(variant.toString()).toRect()); + break; + } + case QVariant::RectF: { + variant.setValue(QmlStringConverters::rectFFromString(variant.toString())); + break; + } + case QVariant::Point: { + variant.setValue(QmlStringConverters::pointFFromString(variant.toString()).toPoint()); + break; + } + case QVariant::PointF: { + variant.setValue(QmlStringConverters::pointFFromString(variant.toString())); + break; + } + case QVariant::Size: { + variant.setValue(QmlStringConverters::sizeFFromString(variant.toString()).toSize()); + break; + } + case QVariant::SizeF: { + variant.setValue(QmlStringConverters::sizeFFromString(variant.toString())); + break; + } + case QVariant::Color: { + variant.setValue(QmlStringConverters::colorFromString(variant.toString())); + break; + } + case QVariant::Vector3D: { + variant.setValue(QmlStringConverters::vector3DFromString(variant.toString())); + break; + } + default: + if ((uint)type >= QVariant::UserType) { + QmlMetaType::StringConverter converter = QmlMetaType::customStringConverter(type); + if (converter) + variant = converter(variant.toString()); + } else + variant.convert((QVariant::Type)type); + break; + } +} + +/*! + \qmlclass PropertyAnimation QmlPropertyAnimation + \inherits Animation + \brief The PropertyAnimation element allows you to animate property changes. + + Animate theObject's size property over 200ms, from its current size to 20-by-20: + \code + 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) +: QmlAbstractAnimation(*(new QmlPropertyAnimationPrivate), parent) +{ + Q_D(QmlPropertyAnimation); + d->init(); +} + +QmlPropertyAnimation::~QmlPropertyAnimation() +{ +} + +void QmlPropertyAnimationPrivate::init() +{ + Q_Q(QmlPropertyAnimation); + va = new QmlTimeLineValueAnimator; + QmlGraphics_setParent_noEvent(va, q); +} + +/*! + \qmlproperty int PropertyAnimation::duration + This property holds the duration of the transition, in milliseconds. + + The default value is 250. +*/ +int QmlPropertyAnimation::duration() const +{ + Q_D(const QmlPropertyAnimation); + return d->va->duration(); +} + +void QmlPropertyAnimation::setDuration(int duration) +{ + if (duration < 0) { + qmlInfo(this) << tr("Cannot set a duration of < 0"); + return; + } + + Q_D(QmlPropertyAnimation); + if (d->va->duration() == duration) + return; + d->va->setDuration(duration); + emit durationChanged(duration); +} + +/*! + \qmlproperty real PropertyAnimation::from + This property holds the starting value. + If not set, then the value defined in the start state of the transition. +*/ +QVariant QmlPropertyAnimation::from() const +{ + Q_D(const QmlPropertyAnimation); + return d->from; +} + +void QmlPropertyAnimation::setFrom(const QVariant &f) +{ + Q_D(QmlPropertyAnimation); + if (d->fromIsDefined && f == d->from) + return; + d->from = f; + d->fromIsDefined = f.isValid(); + emit fromChanged(f); +} + +/*! + \qmlproperty real PropertyAnimation::to + This property holds the ending value. + If not set, then the value defined in the end state of the transition. +*/ +QVariant QmlPropertyAnimation::to() const +{ + Q_D(const QmlPropertyAnimation); + return d->to; +} + +void QmlPropertyAnimation::setTo(const QVariant &t) +{ + Q_D(QmlPropertyAnimation); + if (d->toIsDefined && t == d->to) + return; + d->to = t; + d->toIsDefined = t.isValid(); + emit toChanged(t); +} + +/*! + \qmlproperty string PropertyAnimation::easing + \brief the easing curve used for the transition. + + Available values are: + + \table + \row + \o \c easeLinear + \o Easing curve for a linear (t) function: velocity is constant. + \o \inlineimage qeasingcurve-linear.png + \row + \o \c easeInQuad + \o Easing curve for a quadratic (t^2) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inquad.png + \row + \o \c easeOutQuad + \o Easing curve for a quadratic (t^2) function: decelerating to zero velocity. + \o \inlineimage qeasingcurve-outquad.png + \row + \o \c easeInOutQuad + \o Easing curve for a quadratic (t^2) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutquad.png + \row + \o \c easeOutInQuad + \o Easing curve for a quadratic (t^2) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinquad.png + \row + \o \c easeInCubic + \o Easing curve for a cubic (t^3) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-incubic.png + \row + \o \c easeOutCubic + \o Easing curve for a cubic (t^3) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outcubic.png + \row + \o \c easeInOutCubic + \o Easing curve for a cubic (t^3) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutcubic.png + \row + \o \c easeOutInCubic + \o Easing curve for a cubic (t^3) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outincubic.png + \row + \o \c easeInQuart + \o Easing curve for a quartic (t^4) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inquart.png + \row + \o \c easeOutQuart + \o Easing curve for a cubic (t^4) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outquart.png + \row + \o \c easeInOutQuart + \o Easing curve for a cubic (t^4) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutquart.png + \row + \o \c easeOutInQuart + \o Easing curve for a cubic (t^4) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinquart.png + \row + \o \c easeInQuint + \o Easing curve for a quintic (t^5) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inquint.png + \row + \o \c easeOutQuint + \o Easing curve for a cubic (t^5) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outquint.png + \row + \o \c easeInOutQuint + \o Easing curve for a cubic (t^5) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutquint.png + \row + \o \c easeOutInQuint + \o Easing curve for a cubic (t^5) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinquint.png + \row + \o \c easeInSine + \o Easing curve for a sinusoidal (sin(t)) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-insine.png + \row + \o \c easeOutSine + \o Easing curve for a sinusoidal (sin(t)) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outsine.png + \row + \o \c easeInOutSine + \o Easing curve for a sinusoidal (sin(t)) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutsine.png + \row + \o \c easeOutInSine + \o Easing curve for a sinusoidal (sin(t)) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinsine.png + \row + \o \c easeInExpo + \o Easing curve for an exponential (2^t) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inexpo.png + \row + \o \c easeOutExpo + \o Easing curve for an exponential (2^t) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outexpo.png + \row + \o \c easeInOutExpo + \o Easing curve for an exponential (2^t) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutexpo.png + \row + \o \c easeOutInExpo + \o Easing curve for an exponential (2^t) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinexpo.png + \row + \o \c easeInCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-incirc.png + \row + \o \c easeOutCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outcirc.png + \row + \o \c easeInOutCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutcirc.png + \row + \o \c easeOutInCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outincirc.png + \row + \o \c easeInElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: accelerating from zero velocity. + \br The peak amplitude can be set with the \e amplitude parameter, and the period of decay by the \e period parameter. + \o \inlineimage qeasingcurve-inelastic.png + \row + \o \c easeOutElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: decelerating from zero velocity. + \br The peak amplitude can be set with the \e amplitude parameter, and the period of decay by the \e period parameter. + \o \inlineimage qeasingcurve-outelastic.png + \row + \o \c easeInOutElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutelastic.png + \row + \o \c easeOutInElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinelastic.png + \row + \o \c easeInBack + \o Easing curve for a back (overshooting cubic function: (s+1)*t^3 - s*t^2) easing in: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inback.png + \row + \o \c easeOutBack + \o Easing curve for a back (overshooting cubic function: (s+1)*t^3 - s*t^2) easing out: decelerating to zero velocity. + \o \inlineimage qeasingcurve-outback.png + \row + \o \c easeInOutBack + \o Easing curve for a back (overshooting cubic function: (s+1)*t^3 - s*t^2) easing in/out: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutback.png + \row + \o \c easeOutInBack + \o Easing curve for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing out/in: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinback.png + \row + \o \c easeInBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inbounce.png + \row + \o \c easeOutBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outbounce.png + \row + \o \c easeInOutBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function easing in/out: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutbounce.png + \row + \o \c easeOutInBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function easing out/in: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinbounce.png + \endtable + +*/ +QString QmlPropertyAnimation::easing() const +{ + Q_D(const QmlPropertyAnimation); + return d->easing; +} + +void QmlPropertyAnimation::setEasing(const QString &e) +{ + Q_D(QmlPropertyAnimation); + if (d->easing == e) + return; + + d->easing = e; + d->va->setEasingCurve(stringToCurve(d->easing, this)); + emit easingChanged(e); +} + +/*! + \qmlproperty Object PropertyAnimation::target + This property holds an explicit target object to animate. + + target is used in conjunction with property to determine + what property should be animated. + + \sa property matchTargets +*/ + +QObject *QmlPropertyAnimation::target() const +{ + Q_D(const QmlPropertyAnimation); + return d->target; +} + +void QmlPropertyAnimation::setTarget(QObject *o) +{ + Q_D(QmlPropertyAnimation); + if (d->target == o) + return; + + d->target = o; + if (d->target && !d->propertyName.isEmpty()) { + d->userProperty = d->createProperty(d->target, d->propertyName, this); + } else { + d->userProperty.invalidate(); + } + + emit targetChanged(d->target, d->propertyName); +} + +/*! + \qmlproperty string PropertyAnimation::property + This property holds an explicit property name to animate. + + property is used in conjunction with target to determine + what property should be animated. + + \sa target matchProperties +*/ + +QString QmlPropertyAnimation::property() const +{ + Q_D(const QmlPropertyAnimation); + return d->propertyName; +} + +void QmlPropertyAnimation::setProperty(const QString &n) +{ + Q_D(QmlPropertyAnimation); + if (d->propertyName == n) + return; + + d->propertyName = n; + if (d->target && !d->propertyName.isEmpty()) { + d->userProperty = d->createProperty(d->target, d->propertyName, this); + } else { + d->userProperty.invalidate(); + } + + emit targetChanged(d->target, d->propertyName); +} + +/*! + \qmlproperty string PropertyAnimation::matchProperties + This property holds a comma-separated list of property names this animation + will match against. These names are used in conjunction with matchTargets + to create a list of properties that the animation will animate, assuming those + properties have changed. + + In the following example, the change in 'x' will be animated by the transition, while + the change in 'y' will not. + \qml + State { + PropertyChanges { + target: myItem + x: 15; y: 15 + } + } + Transition { + PropertyAnimation { + matchProperties: "x" + } + } + \endqml + + This property is typically used for an animation appearing as part of a Transition. + + By default, no property names will be matched. + + \sa matchTargets PropertyAction::matchTargets +*/ +QString QmlPropertyAnimation::properties() const +{ + Q_D(const QmlPropertyAnimation); + return d->properties; +} + +void QmlPropertyAnimation::setProperties(const QString &prop) +{ + Q_D(QmlPropertyAnimation); + if (d->properties == prop) + return; + + d->properties = prop; + emit propertiesChanged(prop); +} + +/*! + \qmlproperty list<Object> PropertyAnimation::matchTargets + This property holds a list of objects this animation will match against. + These objects are used in conjunction with matchProperties to create a list of properties + that the animation will animate, assuming those properties have changed. + + In the following example, the changes to \c myItem will be animated by the transition, while + the changes to \c myOtherItem will not. + \qml + State { + PropertyChanges { + target: myItem + x: 15; y: 15 + } + PropertyChanges { + target: myOtherItem + x: 30; y: 30 + } + } + Transition { + PropertyAnimation { + matchTargets: myItem + matchProperties: "x,y" + } + } + \endqml + + This property is typically used for an animation appearing as part of a Transition. + + By default, all changing targets will be matched. + + \sa exclude matchProperties +*/ +QmlListProperty<QObject> QmlPropertyAnimation::targets() +{ + Q_D(QmlPropertyAnimation); + return QmlListProperty<QObject>(this, d->targets); +} + +/*! + \qmlproperty list<Object> PropertyAnimation::exclude + This property holds the items not to be affected by this animation. + \sa matchTargets +*/ +QmlListProperty<QObject> QmlPropertyAnimation::exclude() +{ + Q_D(QmlPropertyAnimation); + return QmlListProperty<QObject>(this, d->exclude); +} + +void QmlPropertyAnimationPrivate::valueChanged(qreal r) +{ + if (!fromSourced) { + if (!fromIsDefined) { + from = property.read(); + convertVariant(from, interpolatorType ? interpolatorType : property.propertyType()); + //### check for invalid variant if using property type + } + fromSourced = true; + } + + if (r == 1.) { + property.write(to, QmlMetaProperty::BypassInterceptor | QmlMetaProperty::DontRemoveBinding); + } else { + if (interpolator) + property.write(interpolator(from.constData(), to.constData(), r), QmlMetaProperty::BypassInterceptor | QmlMetaProperty::DontRemoveBinding); + } +} + +QAbstractAnimation *QmlPropertyAnimation::qtAnimation() +{ + Q_D(QmlPropertyAnimation); + return d->va; +} + +void QmlPropertyAnimation::prepare(QmlMetaProperty &p) +{ + Q_D(QmlPropertyAnimation); + if (d->userProperty.isNull) + d->property = p; + else + d->property = d->userProperty; + + if (!d->rangeIsSet) { + d->va->setStartValue(qreal(0)); + d->va->setEndValue(qreal(1)); + d->rangeIsSet = true; + } + + int propType = d->property.propertyType(); + d->convertVariant(d->to, d->interpolatorType ? d->interpolatorType : propType); + if (d->fromIsDefined) + d->convertVariant(d->from, d->interpolatorType ? d->interpolatorType : propType); + + if (!d->interpolatorType) { + //### check for invalid variants + d->interpolator = QVariantAnimationPrivate::getInterpolator(propType); + } + + d->fromSourced = false; + d->value.QmlTimeLineValue::setValue(0.); + d->va->setAnimValue(&d->value, QAbstractAnimation::KeepWhenStopped); + d->va->setFromSourcedValue(&d->fromSourced); +} + +void QmlPropertyAnimation::transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction) +{ + Q_D(QmlPropertyAnimation); + Q_UNUSED(direction); + + struct PropertyUpdater : public QmlTimeLineValue + { + QmlStateActions actions; + int interpolatorType; //for Number/ColorAnimation + int prevInterpolatorType; //for generic + QVariantAnimation::Interpolator interpolator; + bool reverse; + bool *wasDeleted; + PropertyUpdater() : wasDeleted(0) {} + ~PropertyUpdater() { if (wasDeleted) *wasDeleted = true; } + void setValue(qreal v) + { + bool deleted = false; + wasDeleted = &deleted; + if (reverse) //QVariantAnimation sends us 1->0 when reversed, but we are expecting 0->1 + v = 1 - v; + QmlTimeLineValue::setValue(v); + for (int ii = 0; ii < actions.count(); ++ii) { + QmlAction &action = actions[ii]; + + if (v == 1.) + action.property.write(action.toValue, QmlMetaProperty::BypassInterceptor | QmlMetaProperty::DontRemoveBinding); + else { + if (action.fromValue.isNull()) { + action.fromValue = action.property.read(); + if (interpolatorType) + QmlPropertyAnimationPrivate::convertVariant(action.fromValue, interpolatorType); + } + if (!interpolatorType) { + int propType = action.property.propertyType(); + if (!prevInterpolatorType || prevInterpolatorType != propType) { + prevInterpolatorType = propType; + interpolator = QVariantAnimationPrivate::getInterpolator(prevInterpolatorType); + } + } + if (interpolator) + action.property.write(interpolator(action.fromValue.constData(), action.toValue.constData(), v), QmlMetaProperty::BypassInterceptor | QmlMetaProperty::DontRemoveBinding); + } + if (deleted) + return; + } + wasDeleted = 0; + } + }; + + QStringList props = d->properties.isEmpty() ? QStringList() : d->properties.split(QLatin1Char(',')); + for (int ii = 0; ii < props.count(); ++ii) + props[ii] = props.at(ii).trimmed(); + + bool hasSelectors = !props.isEmpty() || !d->targets.isEmpty() || !d->exclude.isEmpty(); + bool hasTarget = !d->propertyName.isEmpty() || d->target; + + if (hasSelectors && hasTarget) { + qmlInfo(this) << tr("matchTargets/matchProperties/exclude and target/property are mutually exclusive."); + return; + } + + bool useType = (props.isEmpty() && d->propertyName.isEmpty() && d->defaultToInterpolatorType) ? true : false; + + PropertyUpdater *data = new PropertyUpdater; + data->interpolatorType = d->interpolatorType; + data->interpolator = d->interpolator; + data->reverse = direction == Backward ? true : false; + + bool hasExplicit = false; + //an explicit animation has been specified + if (hasTarget && d->toIsDefined) { + QmlAction myAction; + myAction.property = d->createProperty(target(), d->propertyName, this); + if (myAction.property.isValid()) { + if (d->fromIsDefined) { + d->convertVariant(d->from, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + myAction.fromValue = d->from; + } + 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) { + QmlAction &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) { + QmlAction &action = actions[ii]; + + QObject *obj = action.property.object(); + QString propertyName = action.property.name(); + QObject *sObj = action.specifiedObject; + QString sPropertyName = action.specifiedProperty; + bool same = (obj == sObj); + + if ((d->targets.isEmpty() || d->targets.contains(obj) || (!same && d->targets.contains(sObj))) && + (!d->exclude.contains(obj)) && (same || (!d->exclude.contains(sObj))) && + (props.contains(propertyName) || (!same && props.contains(sPropertyName)) + || (useType && action.property.propertyType() == d->interpolatorType))) { + QmlAction myAction = action; + + if (d->fromIsDefined) + myAction.fromValue = d->from; + else + myAction.fromValue = QVariant(); + if (d->toIsDefined) + myAction.toValue = d->to; + + d->convertVariant(myAction.fromValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + d->convertVariant(myAction.toValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + + modified << action.property; + + data->actions << myAction; + action.fromValue = myAction.toValue; + } else if (d->userProperty.isValid() && + !hasSelectors && !hasTarget) { + if ((d->userProperty.value.object() == obj || (!same && d->userProperty.value.object() == sObj)) && + (d->userProperty.value.name() == propertyName || (!same && d->userProperty.value.name() == sPropertyName))) { + //### same as above. merge + QmlAction myAction = action; + + if (d->fromIsDefined) + myAction.fromValue = d->from; + else + myAction.fromValue = QVariant(); + if (d->toIsDefined) + myAction.toValue = d->to; + + d->convertVariant(myAction.fromValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + d->convertVariant(myAction.toValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + + modified << action.property; + + data->actions << myAction; + action.fromValue = myAction.toValue; + } + } + } + + if (data->actions.count()) { + if (!d->rangeIsSet) { + d->va->setStartValue(qreal(0)); + d->va->setEndValue(qreal(1)); + d->rangeIsSet = true; + } + d->va->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped); + } else { + delete data; + } +} + +QML_DEFINE_TYPE(Qt,4,6,PropertyAnimation,QmlPropertyAnimation) + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlanimation_p.h b/src/declarative/util/qmlanimation_p.h new file mode 100644 index 0000000..a566074 --- /dev/null +++ b/src/declarative/util/qmlanimation_p.h @@ -0,0 +1,440 @@ +/**************************************************************************** +** +** 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 QMLANIMATION_H +#define QMLANIMATION_H + +#include "qmltransition_p.h" +#include "qmlstate_p.h" +#include <QtGui/qvector3d.h> + +#include <qmlpropertyvaluesource.h> +#include <qml.h> +#include <qmlscriptstring.h> + +#include <QtCore/qvariant.h> +#include <QtCore/QAbstractAnimation> +#include <QtGui/qcolor.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlAbstractAnimationPrivate; +class QmlAnimationGroup; +class Q_AUTOTEST_EXPORT QmlAbstractAnimation : public QObject, public QmlPropertyValueSource, public QmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlAbstractAnimation) + + Q_INTERFACES(QmlParserStatus) + Q_INTERFACES(QmlPropertyValueSource) + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged) + Q_PROPERTY(bool alwaysRunToEnd READ alwaysRunToEnd WRITE setAlwaysRunToEnd NOTIFY alwaysRunToEndChanged()) + Q_PROPERTY(bool repeat READ repeat WRITE setRepeat NOTIFY repeatChanged) + Q_CLASSINFO("DefaultMethod", "start()") + +public: + QmlAbstractAnimation(QObject *parent=0); + virtual ~QmlAbstractAnimation(); + + bool isRunning() const; + void setRunning(bool); + bool isPaused() const; + void setPaused(bool); + bool alwaysRunToEnd() const; + void setAlwaysRunToEnd(bool); + bool repeat() const; + void setRepeat(bool); + + int currentTime(); + void setCurrentTime(int); + + QmlAnimationGroup *group() const; + void setGroup(QmlAnimationGroup *); + + virtual void setTarget(const QmlMetaProperty &); + + void classBegin(); + void componentComplete(); + +Q_SIGNALS: + void started(); + void completed(); + void runningChanged(bool); + void pausedChanged(bool); + void repeatChanged(bool); + void alwaysRunToEndChanged(bool); + +public Q_SLOTS: + void restart(); + void start(); + void pause(); + void resume(); + void stop(); + void complete(); + +protected: + QmlAbstractAnimation(QmlAbstractAnimationPrivate &dd, QObject *parent); + +public: + enum TransitionDirection { Forward, Backward }; + virtual void transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction); + virtual void prepare(QmlMetaProperty &); + virtual QAbstractAnimation *qtAnimation() = 0; + +private Q_SLOTS: + void timelineComplete(); +}; + +class QmlPauseAnimationPrivate; +class Q_AUTOTEST_EXPORT QmlPauseAnimation : public QmlAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlPauseAnimation) + + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) + +public: + QmlPauseAnimation(QObject *parent=0); + virtual ~QmlPauseAnimation(); + + int duration() const; + void setDuration(int); + +Q_SIGNALS: + void durationChanged(int); + +protected: + virtual QAbstractAnimation *qtAnimation(); +}; + +class QmlScriptActionPrivate; +class QmlScriptAction : public QmlAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlScriptAction) + + Q_PROPERTY(QmlScriptString script READ script WRITE setScript) + Q_PROPERTY(QString stateChangeScriptName READ stateChangeScriptName WRITE setStateChangeScriptName) + +public: + QmlScriptAction(QObject *parent=0); + virtual ~QmlScriptAction(); + + QmlScriptString script() const; + void setScript(const QmlScriptString &); + + QString stateChangeScriptName() const; + void setStateChangeScriptName(const QString &); + +protected: + virtual void transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QmlPropertyActionPrivate; +class QmlPropertyAction : public QmlAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlPropertyAction) + + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY targetChanged) + Q_PROPERTY(QString matchProperties READ properties WRITE setProperties NOTIFY propertiesChanged) + Q_PROPERTY(QmlListProperty<QObject> matchTargets READ targets) + Q_PROPERTY(QmlListProperty<QObject> exclude READ exclude) + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) + +public: + QmlPropertyAction(QObject *parent=0); + virtual ~QmlPropertyAction(); + + QObject *target() const; + void setTarget(QObject *); + + QString property() const; + void setProperty(const QString &); + + QString properties() const; + void setProperties(const QString &); + + QmlListProperty<QObject> targets(); + QmlListProperty<QObject> exclude(); + + QVariant value() const; + void setValue(const QVariant &); + +Q_SIGNALS: + void valueChanged(const QVariant &); + void propertiesChanged(const QString &); + void targetChanged(QObject *, const QString &); + +protected: + virtual void transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); + virtual void prepare(QmlMetaProperty &); +}; + +class QmlGraphicsItem; +class QmlParentActionPrivate; +class QmlParentAction : public QmlAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlParentAction) + + Q_PROPERTY(QmlGraphicsItem *target READ object WRITE setObject) + Q_PROPERTY(QmlGraphicsItem *matchTarget READ matchTarget WRITE setMatchTarget) + Q_PROPERTY(QmlGraphicsItem *parent READ parent WRITE setParent) + +public: + QmlParentAction(QObject *parent=0); + virtual ~QmlParentAction(); + + QmlGraphicsItem *object() const; + void setObject(QmlGraphicsItem *); + + QmlGraphicsItem *matchTarget() const; + void setMatchTarget(QmlGraphicsItem *); + + QmlGraphicsItem *parent() const; + void setParent(QmlGraphicsItem *); + +protected: + virtual void transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QmlPropertyAnimationPrivate; +class Q_AUTOTEST_EXPORT QmlPropertyAnimation : public QmlAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlPropertyAnimation) + + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) + Q_PROPERTY(QVariant from READ from WRITE setFrom NOTIFY fromChanged) + Q_PROPERTY(QVariant to READ to WRITE setTo NOTIFY toChanged) + Q_PROPERTY(QString easing READ easing WRITE setEasing NOTIFY easingChanged) + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY targetChanged) + Q_PROPERTY(QString matchProperties READ properties WRITE setProperties NOTIFY propertiesChanged) + Q_PROPERTY(QmlListProperty<QObject> matchTargets READ targets) + Q_PROPERTY(QmlListProperty<QObject> exclude READ exclude) + +public: + QmlPropertyAnimation(QObject *parent=0); + virtual ~QmlPropertyAnimation(); + + int duration() const; + void setDuration(int); + + QVariant from() const; + void setFrom(const QVariant &); + + QVariant to() const; + void setTo(const QVariant &); + + QString easing() const; + void setEasing(const QString &); + + QObject *target() const; + void setTarget(QObject *); + + QString property() const; + void setProperty(const QString &); + + QString properties() const; + void setProperties(const QString &); + + QmlListProperty<QObject> targets(); + QmlListProperty<QObject> exclude(); + +protected: + virtual void transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); + virtual void prepare(QmlMetaProperty &); + +Q_SIGNALS: + void durationChanged(int); + void fromChanged(QVariant); + void toChanged(QVariant); + void easingChanged(const QString &); + void propertiesChanged(const QString &); + void targetChanged(QObject *, const QString &); +}; + +class Q_AUTOTEST_EXPORT QmlColorAnimation : public QmlPropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlPropertyAnimation) + Q_PROPERTY(QColor from READ from WRITE setFrom NOTIFY fromChanged) + Q_PROPERTY(QColor to READ to WRITE setTo NOTIFY toChanged) + +public: + QmlColorAnimation(QObject *parent=0); + virtual ~QmlColorAnimation(); + + QColor from() const; + void setFrom(const QColor &); + + QColor to() const; + void setTo(const QColor &); +}; + +class Q_AUTOTEST_EXPORT QmlNumberAnimation : public QmlPropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlPropertyAnimation) + + Q_PROPERTY(qreal from READ from WRITE setFrom NOTIFY fromChanged) + Q_PROPERTY(qreal to READ to WRITE setTo NOTIFY toChanged) + +public: + QmlNumberAnimation(QObject *parent=0); + virtual ~QmlNumberAnimation(); + + qreal from() const; + void setFrom(qreal); + + qreal to() const; + void setTo(qreal); +}; + +class Q_AUTOTEST_EXPORT QmlVector3dAnimation : public QmlPropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlPropertyAnimation) + + Q_PROPERTY(QVector3D from READ from WRITE setFrom NOTIFY fromChanged) + Q_PROPERTY(QVector3D to READ to WRITE setTo NOTIFY toChanged) + +public: + QmlVector3dAnimation(QObject *parent=0); + virtual ~QmlVector3dAnimation(); + + QVector3D from() const; + void setFrom(QVector3D); + + QVector3D to() const; + void setTo(QVector3D); +}; + +class QmlAnimationGroupPrivate; +class QmlAnimationGroup : public QmlAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlAnimationGroup) + + Q_CLASSINFO("DefaultProperty", "animations") + Q_PROPERTY(QmlList<QmlAbstractAnimation *> *animations READ animations) + +public: + QmlAnimationGroup(QObject *parent); + virtual ~QmlAnimationGroup(); + + QmlList<QmlAbstractAnimation *>* animations(); + friend class QmlAbstractAnimation; +}; + +class QmlSequentialAnimation : public QmlAnimationGroup +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlAnimationGroup) + +public: + QmlSequentialAnimation(QObject *parent=0); + virtual ~QmlSequentialAnimation(); + +protected: + virtual void transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); + virtual void prepare(QmlMetaProperty &); +}; + +class QmlParallelAnimation : public QmlAnimationGroup +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlAnimationGroup) + +public: + QmlParallelAnimation(QObject *parent=0); + virtual ~QmlParallelAnimation(); + +protected: + virtual void transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); + virtual void prepare(QmlMetaProperty &); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlAbstractAnimation) +QML_DECLARE_TYPE(QmlPauseAnimation) +QML_DECLARE_TYPE(QmlScriptAction) +QML_DECLARE_TYPE(QmlPropertyAction) +QML_DECLARE_TYPE(QmlParentAction) +QML_DECLARE_TYPE(QmlPropertyAnimation) +QML_DECLARE_TYPE(QmlColorAnimation) +QML_DECLARE_TYPE(QmlNumberAnimation) +QML_DECLARE_TYPE(QmlSequentialAnimation) +QML_DECLARE_TYPE(QmlParallelAnimation) +QML_DECLARE_TYPE(QmlVector3dAnimation) + +QT_END_HEADER + +#endif // QMLANIMATION_H diff --git a/src/declarative/util/qmlanimation_p_p.h b/src/declarative/util/qmlanimation_p_p.h new file mode 100644 index 0000000..2f911b4 --- /dev/null +++ b/src/declarative/util/qmlanimation_p_p.h @@ -0,0 +1,383 @@ +/**************************************************************************** +** +** 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 QMLANIMATION_P_H +#define QMLANIMATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qmlanimation_p.h" + +#include "qmlnullablevalue_p_p.h" +#include "qmltimeline_p_p.h" + +#include <qml.h> +#include <qmlcontext.h> + +#include <QtCore/QPauseAnimation> +#include <QtCore/QVariantAnimation> +#include <QtCore/QAnimationGroup> +#include <QtGui/QColor> +#include <QDebug> + +#include <private/qobject_p.h> +#include <private/qvariantanimation_p.h> + +QT_BEGIN_NAMESPACE + +//interface for classes that provide animation actions for QActionAnimation +class QAbstractAnimationAction +{ +public: + virtual ~QAbstractAnimationAction() {} + virtual void doAction() = 0; +}; + +//templated animation action +//allows us to specify an action that calls a function of a class. +//(so that class doesn't have to inherit QmlAbstractAnimationAction) +template<class T, void (T::*method)()> +class QAnimationActionProxy : public QAbstractAnimationAction +{ +public: + QAnimationActionProxy(T *p) : m_p(p) {} + virtual void doAction() { (m_p->*method)(); } + +private: + T *m_p; +}; + +//performs an action of type QAbstractAnimationAction +class QActionAnimation : public QAbstractAnimation +{ + Q_OBJECT +public: + QActionAnimation(QObject *parent = 0) : QAbstractAnimation(parent), animAction(0), policy(KeepWhenStopped), running(false) {} + QActionAnimation(QAbstractAnimationAction *action, QObject *parent = 0) + : QAbstractAnimation(parent), animAction(action), policy(KeepWhenStopped), running(false) {} + ~QActionAnimation() { if (policy == DeleteWhenStopped) { delete animAction; animAction = 0; } } + virtual int duration() const { return 0; } + void setAnimAction(QAbstractAnimationAction *action, DeletionPolicy p) + { + if (state() == Running) + stop(); + animAction = action; + policy = p; + } +protected: + virtual void updateCurrentTime(int) {} + + virtual void updateState(State newState, State /*oldState*/) + { + if (newState == Running) { + if (animAction) { + running = true; + animAction->doAction(); + running = false; + if (state() == Stopped && policy == DeleteWhenStopped) { + delete animAction; + animAction = 0; + } + } + } else if (newState == Stopped && policy == DeleteWhenStopped) { + if (!running) { + delete animAction; + animAction = 0; + } + } + } + +private: + QAbstractAnimationAction *animAction; + DeletionPolicy policy; + bool running; +}; + +//animates QmlTimeLineValue (assumes start and end values will be reals or compatible) +class QmlTimeLineValueAnimator : public QVariantAnimation +{ + Q_OBJECT +public: + QmlTimeLineValueAnimator(QObject *parent = 0) : QVariantAnimation(parent), animValue(0), fromSourced(0), policy(KeepWhenStopped) {} + ~QmlTimeLineValueAnimator() { if (policy == DeleteWhenStopped) { delete animValue; animValue = 0; } } + void setAnimValue(QmlTimeLineValue *value, DeletionPolicy p) + { + if (state() == Running) + stop(); + animValue = value; + policy = p; + } + void setFromSourcedValue(bool *value) + { + fromSourced = value; + } +protected: + virtual void updateCurrentValue(const QVariant &value) + { + if (animValue) + animValue->setValue(value.toReal()); + } + virtual void updateState(State newState, State oldState) + { + QVariantAnimation::updateState(newState, oldState); + if (newState == Running) { + //check for new from every loop + if (fromSourced) + *fromSourced = false; + } else if (newState == Stopped && policy == DeleteWhenStopped) { + delete animValue; + animValue = 0; + } + } + +private: + QmlTimeLineValue *animValue; + bool *fromSourced; + DeletionPolicy policy; +}; + +//an animation that just gives a tick +template<class T, void (T::*method)(int)> +class QTickAnimationProxy : public QAbstractAnimation +{ + //Q_OBJECT //doesn't work with templating +public: + QTickAnimationProxy(T *p, QObject *parent = 0) : QAbstractAnimation(parent), m_p(p) {} + virtual int duration() const { return -1; } +protected: + virtual void updateCurrentTime(int msec) { (m_p->*method)(msec); } + +private: + T *m_p; +}; + +class QmlAbstractAnimationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlAbstractAnimation) +public: + QmlAbstractAnimationPrivate() + : running(false), paused(false), alwaysRunToEnd(false), repeat(false), + connectedTimeLine(false), componentComplete(true), startOnCompletion(false), + group(0) {} + + bool running:1; + bool paused:1; + bool alwaysRunToEnd:1; + bool repeat:1; + bool connectedTimeLine:1; + + bool componentComplete:1; + bool startOnCompletion:1; + + void commence(); + + QmlNullableValue<QmlMetaProperty> userProperty; + + QmlMetaProperty property; + QmlAnimationGroup *group; + + static QmlMetaProperty createProperty(QObject *obj, const QString &str, QObject *infoObj); +}; + +class QmlPauseAnimationPrivate : public QmlAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QmlPauseAnimation) +public: + QmlPauseAnimationPrivate() + : QmlAbstractAnimationPrivate(), pa(0) {} + + void init(); + + QPauseAnimation *pa; +}; + +class QmlScriptActionPrivate : public QmlAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QmlScriptAction) +public: + QmlScriptActionPrivate() + : QmlAbstractAnimationPrivate(), hasRunScriptScript(false), proxy(this), rsa(0) {} + + void init(); + + QmlScriptString script; + QString name; + QmlScriptString runScriptScript; + bool hasRunScriptScript; + + void execute(); + + QAnimationActionProxy<QmlScriptActionPrivate, + &QmlScriptActionPrivate::execute> proxy; + QActionAnimation *rsa; +}; + +class QmlPropertyActionPrivate : public QmlAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QmlPropertyAction) +public: + QmlPropertyActionPrivate() + : QmlAbstractAnimationPrivate(), target(0), proxy(this), spa(0) {} + + void init(); + + QObject *target; + QString propertyName; + QString properties; + QList<QObject *> targets; + QList<QObject *> exclude; + + QmlNullableValue<QVariant> value; + + void doAction(); + + QAnimationActionProxy<QmlPropertyActionPrivate, + &QmlPropertyActionPrivate::doAction> proxy; + QActionAnimation *spa; +}; + +class QmlParentActionPrivate : public QmlAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QmlParentAction) +public: + QmlParentActionPrivate() + : QmlAbstractAnimationPrivate(), pcTarget(0), pcMatchTarget(0), pcParent(0) {} + + void init(); + + QmlGraphicsItem *pcTarget; + QmlGraphicsItem *pcMatchTarget; + QmlGraphicsItem *pcParent; + + void doAction(); + QActionAnimation *cpa; +}; + +class QmlAnimationGroupPrivate : public QmlAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QmlAnimationGroup) +public: + QmlAnimationGroupPrivate() + : QmlAbstractAnimationPrivate(), animations(this), ag(0) {} + + struct AnimationList : public QmlConcreteList<QmlAbstractAnimation *> + { + AnimationList(QmlAnimationGroupPrivate *p) + : anim(p) {} + virtual void append(QmlAbstractAnimation *a) { + QmlConcreteList<QmlAbstractAnimation *>::append(a); + a->setGroup(anim->q_func()); + } + virtual void clear() + { + for (int i = 0; i < count(); ++i) + at(i)->setGroup(0); + QmlConcreteList<QmlAbstractAnimation *>::clear(); + } + virtual void removeAt(int i) + { + at(i)->setGroup(0); + QmlConcreteList<QmlAbstractAnimation *>::removeAt(i); + } + virtual void insert(int i, QmlAbstractAnimation *a) + { + QmlConcreteList<QmlAbstractAnimation *>::insert(i, a); + a->setGroup(anim->q_func()); + } + + QmlAnimationGroupPrivate *anim; + }; + + AnimationList animations; + QAnimationGroup *ag; +}; + +class QmlPropertyAnimationPrivate : public QmlAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QmlPropertyAnimation) +public: + QmlPropertyAnimationPrivate() + : QmlAbstractAnimationPrivate(), target(0), fromSourced(false), fromIsDefined(false), toIsDefined(false), + rangeIsSet(false), defaultToInterpolatorType(0), interpolatorType(0), interpolator(0), va(0), + value(this, &QmlPropertyAnimationPrivate::valueChanged) {} + + void init(); + + QVariant from; + QVariant to; + + QString easing; + + QObject *target; + QString propertyName; + QString properties; + QList<QObject *> targets; + QList<QObject *> exclude; + + bool fromSourced; + bool fromIsDefined:1; + bool toIsDefined:1; + bool rangeIsSet:1; + bool defaultToInterpolatorType:1; + int interpolatorType; + QVariantAnimation::Interpolator interpolator; + + QmlTimeLineValueAnimator *va; + virtual void valueChanged(qreal); + + QmlTimeLineValueProxy<QmlPropertyAnimationPrivate> value; + + static QVariant interpolateVariant(const QVariant &from, const QVariant &to, qreal progress); + static void convertVariant(QVariant &variant, int type); +}; + +QT_END_NAMESPACE + +#endif // QMLANIMATION_P_H diff --git a/src/declarative/util/qmlbehavior.cpp b/src/declarative/util/qmlbehavior.cpp new file mode 100644 index 0000000..b9c77f5 --- /dev/null +++ b/src/declarative/util/qmlbehavior.cpp @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** 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 "qmlbehavior_p.h" + +#include "qmlanimation_p.h" +#include "qmltransition_p.h" + +#include <qmlcontext.h> +#include <qmlinfo.h> + +#include <QtCore/qparallelanimationgroup.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +QML_DEFINE_TYPE(Qt,4,6,Behavior,QmlBehavior) + +class QmlBehaviorPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlBehavior) +public: + QmlBehaviorPrivate() : animation(0), enabled(true) {} + + QmlMetaProperty property; + QVariant currentValue; + QmlAbstractAnimation *animation; + bool enabled; +}; + +/*! + \qmlclass Behavior QmlBehavior + \brief The Behavior element allows you to specify a default animation for a property change. + + Behaviors provide one way to specify \l{qmlanimation.html}{animations} in QML. + + In the example below, the rect will use a bounce easing curve over 200 millisecond for any changes to its y property: + \code + Rectangle { + width: 20; height: 20 + color: "#00ff00" + y: 200 //initial value + y: Behavior { + NumberAnimation { + easing: "easeOutBounce(amplitude:100)" + duration: 200 + } + } + } + \endcode + + Currently only a single Behavior may be specified for a property; + this Behavior can be enabled and disabled via the \l{enabled} property. +*/ + + +QmlBehavior::QmlBehavior(QObject *parent) + : QObject(*(new QmlBehaviorPrivate), parent) +{ +} + +QmlBehavior::~QmlBehavior() +{ +} + +/*! + \qmlproperty Animation Behavior::animation + \default + + The animation to use when the behavior is triggered. +*/ + +QmlAbstractAnimation *QmlBehavior::animation() +{ + Q_D(QmlBehavior); + return d->animation; +} + +void QmlBehavior::setAnimation(QmlAbstractAnimation *animation) +{ + Q_D(QmlBehavior); + if (d->animation) { + qmlInfo(this) << tr("Can't change the animation assigned to a Behavior."); + return; + } + + d->animation = animation; + if (d->animation) + d->animation->setTarget(d->property); +} + +/*! + \qmlproperty bool Behavior::enabled + Whether the Behavior will be triggered when the property it is tracking changes. + + By default a Behavior is enabled. +*/ + +bool QmlBehavior::enabled() const +{ + Q_D(const QmlBehavior); + return d->enabled; +} + +void QmlBehavior::setEnabled(bool enabled) +{ + Q_D(QmlBehavior); + if (d->enabled == enabled) + return; + d->enabled = enabled; + emit enabledChanged(); +} + +void QmlBehavior::write(const QVariant &value) +{ + Q_D(QmlBehavior); + if (!d->animation || !d->enabled) { + d->property.write(value, QmlMetaProperty::BypassInterceptor | QmlMetaProperty::DontRemoveBinding); + return; + } + + d->currentValue = d->property.read(); + + d->animation->qtAnimation()->stop(); + + QmlStateOperation::ActionList actions; + QmlAction action; + action.property = d->property; + action.fromValue = d->currentValue; + action.toValue = value; + actions << action; + + QList<QmlMetaProperty> after; + if (d->animation) + d->animation->transition(actions, after, QmlAbstractAnimation::Forward); + d->animation->qtAnimation()->start(); + if (!after.contains(d->property)) + d->property.write(value, QmlMetaProperty::BypassInterceptor | QmlMetaProperty::DontRemoveBinding); +} + +void QmlBehavior::setTarget(const QmlMetaProperty &property) +{ + Q_D(QmlBehavior); + d->property = property; + d->currentValue = property.read(); + if (d->animation) + d->animation->setTarget(property); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlbehavior_p.h b/src/declarative/util/qmlbehavior_p.h new file mode 100644 index 0000000..da3b40f --- /dev/null +++ b/src/declarative/util/qmlbehavior_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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 QMLBEHAVIOR_H +#define QMLBEHAVIOR_H + +#include "qmlstate_p.h" + +#include <qmlpropertyvaluesource.h> +#include <qmlpropertyvalueinterceptor.h> +#include <qml.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlAbstractAnimation; +class QmlBehaviorPrivate; +class Q_DECLARATIVE_EXPORT QmlBehavior : public QObject, public QmlPropertyValueInterceptor +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlBehavior) + + Q_INTERFACES(QmlPropertyValueInterceptor) + Q_CLASSINFO("DefaultProperty", "animation") + Q_PROPERTY(QmlAbstractAnimation *animation READ animation WRITE setAnimation) + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + +public: + QmlBehavior(QObject *parent=0); + ~QmlBehavior(); + + virtual void setTarget(const QmlMetaProperty &); + virtual void write(const QVariant &value); + + QmlAbstractAnimation *animation(); + void setAnimation(QmlAbstractAnimation *); + + bool enabled() const; + void setEnabled(bool enabled); + +Q_SIGNALS: + void enabledChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlBehavior) + +QT_END_HEADER + +#endif // QMLBEHAVIOR_H diff --git a/src/declarative/util/qmlbind.cpp b/src/declarative/util/qmlbind.cpp new file mode 100644 index 0000000..c68cef2 --- /dev/null +++ b/src/declarative/util/qmlbind.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** 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 "qmlbind_p.h" + +#include "qmlnullablevalue_p_p.h" + +#include <qmlengine.h> +#include <qmlcontext.h> + +#include <QtCore/qfile.h> +#include <QtCore/qdebug.h> +#include <QtScript/qscriptvalue.h> +#include <QtScript/qscriptcontext.h> +#include <QtScript/qscriptengine.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QmlBindPrivate : public QObjectPrivate +{ +public: + QmlBindPrivate() : when(true), componentComplete(false), obj(0) {} + + bool when : 1; + bool componentComplete : 1; + QObject *obj; + QString prop; + QmlNullableValue<QVariant> value; +}; + +QML_DEFINE_TYPE(Qt,4,6,Binding,QmlBind) +/*! + \qmlclass Binding QmlBind + \brief The Binding element allows arbitrary property bindings to be created. + + Sometimes it is necessary to bind to a property of an object that wasn't + directly instantiated by QML - generally a property of a class exported + to QML by C++. In these cases, regular property binding doesn't work. Binding + allows you to bind any value to any property. + + For example, imagine a C++ application that maps an "app.enteredText" + property into QML. You could use Binding to update the enteredText property + like this. + \code + TextEdit { id: myTextField; text: "Please type here..." } + Binding { target: app; property: "enteredText"; value: myTextField.text } + \endcode + Whenever the text in the TextEdit is updated, the C++ property will be + updated also. + + If the binding target or binding property is changed, the bound value is + immediately pushed onto the new target. + */ +/*! + \internal + \class QmlBind + \ingroup group_utility + \brief The QmlBind class allows arbitrary property bindings to be created. + + Simple bindings are usually earier to do in-place rather than creating a + QmlBind item. For that reason, QmlBind is usually used to transfer property information + from Qml to C++. + + \sa cppqml + */ +QmlBind::QmlBind(QObject *parent) + : QObject(*(new QmlBindPrivate), parent) +{ +} + +QmlBind::~QmlBind() +{ +} + +bool QmlBind::when() const +{ + Q_D(const QmlBind); + return d->when; +} + +void QmlBind::setWhen(bool v) +{ + Q_D(QmlBind); + d->when = v; + eval(); +} + +/*! + \qmlproperty Object Binding::target + + The object to be updated. +*/ +QObject *QmlBind::object() +{ + Q_D(const QmlBind); + return d->obj; +} + +void QmlBind::setObject(QObject *obj) +{ + Q_D(QmlBind); + d->obj = obj; + eval(); +} + +/*! + \qmlproperty string Binding::property + + The property to be updated. +*/ +QString QmlBind::property() const +{ + Q_D(const QmlBind); + return d->prop; +} + +void QmlBind::setProperty(const QString &p) +{ + Q_D(QmlBind); + d->prop = p; + eval(); +} + +/*! + \qmlproperty any Binding::value + + The value to be set on the target object and property. This can be a + constant (which isn't very useful), or a bound expression. +*/ +QVariant QmlBind::value() const +{ + Q_D(const QmlBind); + return d->value.value; +} + +void QmlBind::setValue(const QVariant &v) +{ + Q_D(QmlBind); + d->value.value = v; + d->value.isNull = false; + eval(); +} + +void QmlBind::componentComplete() +{ + Q_D(QmlBind); + d->componentComplete = true; + eval(); +} + +void QmlBind::eval() +{ + Q_D(QmlBind); + if (!d->obj || d->value.isNull || !d->when || !d->componentComplete) + return; + + QmlMetaProperty prop(d->obj, d->prop); + prop.write(d->value.value); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlbind_p.h b/src/declarative/util/qmlbind_p.h new file mode 100644 index 0000000..4d7cd1f --- /dev/null +++ b/src/declarative/util/qmlbind_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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 QMLBIND_H +#define QMLBIND_H + +#include <qml.h> + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlBindPrivate; +class Q_DECLARATIVE_EXPORT QmlBind : public QObject, public QmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlBind) + Q_INTERFACES(QmlParserStatus) + Q_PROPERTY(QObject *target READ object WRITE setObject) + Q_PROPERTY(QString property READ property WRITE setProperty) + Q_PROPERTY(QVariant value READ value WRITE setValue) + Q_PROPERTY(bool when READ when WRITE setWhen) + +public: + QmlBind(QObject *parent=0); + ~QmlBind(); + + bool when() const; + void setWhen(bool); + + QObject *object(); + void setObject(QObject *); + + QString property() const; + void setProperty(const QString &); + + QVariant value() const; + void setValue(const QVariant &); + +protected: + virtual void componentComplete(); + +private: + void eval(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlBind) + +QT_END_HEADER + +#endif diff --git a/src/declarative/util/qmlconnection.cpp b/src/declarative/util/qmlconnection.cpp new file mode 100644 index 0000000..800fd6b --- /dev/null +++ b/src/declarative/util/qmlconnection.cpp @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** 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 "qmlconnection_p.h" + +#include <qmlexpression.h> +#include <qmlboundsignal_p.h> +#include <qmlcontext.h> + +#include <QtCore/qdebug.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QmlConnectionPrivate : public QObjectPrivate +{ +public: + QmlConnectionPrivate() : boundsignal(0), signalSender(0), scriptset(false), componentcomplete(false) {} + + QmlBoundSignal *boundsignal; + QObject *signalSender; + QmlScriptString script; + bool scriptset; + QString signal; + bool componentcomplete; +}; + +/*! + \qmlclass Connection QmlConnection + \brief A Connection object describes generalized connections to signals. + + When connecting to signals in QML, the usual way is to create an + "on<Signal>" handler that reacts when a signal is received, like this: + + \qml + MouseRegion { + onClicked: { foo(x+123,y+456) } + } + \endqml + + However, in some cases, it is not possible to connect to a signal in this + way. For example, JavaScript-in-HTML style signal properties do not allow: + + \list + \i connecting to signals with the same name but different parameters + \i conformance checking that parameters are correctly named + \i multiple connections to the same signal + \i connections outside the scope of the signal sender + \i signals in classes with coincidentally-named on<Signal> properties + \endlist + + When any of these are needed, the Connection object can be used instead. + + For example, the above code can be changed to use a Connection object, + like this: + + \qml + MouseRegion { + Connection { + signal: "clicked(x,y)" + script: { foo(x+123,y+456) } + } + } + \endqml + + More generally, the Connection object can be a child of some other object than + the sender of the signal, and the script is the default attribute: + + \qml + MouseRegion { + id: mr + } + ... + Connection { + sender: mr + signal: "clicked(x,y)" + script: { foo(x+123,y+456) } + } + \endqml +*/ + +/*! + \internal + \class QmlConnection + \brief The QmlConnection class describes generalized connections to signals. + +*/ +QmlConnection::QmlConnection(QObject *parent) : + QObject(*(new QmlConnectionPrivate), parent) +{ +} + +QmlConnection::~QmlConnection() +{ + Q_D(QmlConnection); + delete d->boundsignal; +} + +/*! + \qmlproperty Object Connection::sender + This property holds the object that sends the signal. + + By default, the sender is assumed to be the parent of the Connection. +*/ +QObject *QmlConnection::signalSender() const +{ + Q_D(const QmlConnection); + return d->signalSender ? d->signalSender : parent(); +} + +void QmlConnection::setSignalSender(QObject *obj) +{ + Q_D(QmlConnection); + if (d->signalSender == obj) + return; + disconnectIfValid(); + d->signalSender = obj; + connectIfValid(); +} + +void QmlConnection::connectIfValid() +{ + Q_D(QmlConnection); + if (!d->componentcomplete) + return; + // boundsignal must not exist + if ((d->signalSender || parent()) && !d->signal.isEmpty() && d->scriptset) { + // create + // XXX scope? + int sigIdx = -1; + int lparen = d->signal.indexOf(QLatin1Char('(')); + QList<QByteArray> sigparams; + 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.trimmed().toUtf8()); + } + } + QString signalname = d->signal.left(lparen); + QObject *sender = d->signalSender ? d->signalSender : parent(); + const QMetaObject *mo = sender->metaObject(); + int methods = mo->methodCount(); + for (int ii = 0; ii < methods; ++ii) { + QMetaMethod method = mo->method(ii); + QString methodName = QString::fromUtf8(method.signature()); + int idx = methodName.indexOf(QLatin1Char('(')); + methodName = methodName.left(idx); + if (methodName == signalname && (lparen<0 || method.parameterNames() == sigparams)) { + sigIdx = ii; + break; + } + } + if (sigIdx < 0) { + // Cannot usefully warn, since could be in middle of + // changing sender and signal. + // XXX need state change transactions to do better + return; + } + + d->boundsignal = new QmlBoundSignal(qmlContext(this), d->script.script(), sender, mo->method(sigIdx), this); + } +} + +void QmlConnection::disconnectIfValid() +{ + Q_D(QmlConnection); + if (!d->componentcomplete) + return; + if ((d->signalSender || parent()) && !d->signal.isEmpty() && d->scriptset) { + // boundsignal must exist + // destroy + delete d->boundsignal; + d->boundsignal = 0; + } +} + +void QmlConnection::componentComplete() +{ + Q_D(QmlConnection); + d->componentcomplete=true; + connectIfValid(); +} + + +/*! + \qmlproperty script Connection::script + This property holds the JavaScript executed whenever the signal is sent. + + This is the default attribute of Connection. +*/ +QmlScriptString QmlConnection::script() const +{ + Q_D(const QmlConnection); + return d->script; +} + +void QmlConnection::setScript(const QmlScriptString& script) +{ + Q_D(QmlConnection); + if ((d->signalSender || parent()) && !d->signal.isEmpty()) { + if (!d->scriptset) { + // mustn't exist - create + d->scriptset = true; + d->script = script; + connectIfValid(); + } else { + // must exist - update + d->script = script; + d->boundsignal->expression()->setExpression(script.script()); + } + } else { + d->scriptset = true; + d->script = script; + } +} + +/*! + \qmlproperty string Connection::signal + This property holds the signal from the sender to which the script is attached. + + The signal's formal parameter names must be given in parentheses: + + \qml +Connection { + signal: "clicked(x,y)" + script: { ... } +} + \endqml +*/ +QString QmlConnection::signal() const +{ + Q_D(const QmlConnection); + return d->signal; +} + +void QmlConnection::setSignal(const QString& sig) +{ + Q_D(QmlConnection); + if (d->signal == sig) + return; + disconnectIfValid(); + d->signal = sig; + connectIfValid(); +} + +QML_DEFINE_TYPE(Qt,4,6,Connection,QmlConnection) + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlconnection_p.h b/src/declarative/util/qmlconnection_p.h new file mode 100644 index 0000000..52bc247 --- /dev/null +++ b/src/declarative/util/qmlconnection_p.h @@ -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$ +** +****************************************************************************/ + +#ifndef QMLCONNECTION_H +#define QMLCONNECTION_H + +#include <qml.h> +#include <qmlscriptstring.h> + +#include <QtCore/qobject.h> +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlBoundSignal; +class QmlContext; +class QmlConnectionPrivate; +class Q_DECLARATIVE_EXPORT QmlConnection : public QObject, public QmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlConnection) + + Q_INTERFACES(QmlParserStatus) + Q_PROPERTY(QObject *sender READ signalSender WRITE setSignalSender) + Q_PROPERTY(QmlScriptString script READ script WRITE setScript) + Q_PROPERTY(QString signal READ signal WRITE setSignal) + +public: + QmlConnection(QObject *parent=0); + ~QmlConnection(); + + QObject *signalSender() const; + void setSignalSender(QObject *); + QmlScriptString script() const; + void setScript(const QmlScriptString&); + QString signal() const; + void setSignal(const QString&); + +private: + void disconnectIfValid(); + void connectIfValid(); + void componentComplete(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlConnection) + +QT_END_HEADER + +#endif diff --git a/src/declarative/util/qmldatetimeformatter.cpp b/src/declarative/util/qmldatetimeformatter.cpp new file mode 100644 index 0000000..9d216cf --- /dev/null +++ b/src/declarative/util/qmldatetimeformatter.cpp @@ -0,0 +1,372 @@ +/**************************************************************************** +** +** 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 "qmldatetimeformatter_p.h" + +#include <QtCore/qlocale.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +//TODO: may need optimisation as the QDateTime member may not be needed? +// be able to set a locale? + +class QmlDateTimeFormatterPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlDateTimeFormatter) +public: + QmlDateTimeFormatterPrivate() : locale(QLocale::system()), longStyle(false), componentComplete(true) {} + + void updateText(); + + QDateTime dateTime; + QDate date; + QTime time; + QLocale locale; + QString dateTimeText; + QString dateText; + QString timeText; + QString dateTimeFormat; //set for convienience? + QString dateFormat; + QString timeFormat; + bool longStyle; + bool componentComplete; +}; + +/*! + \qmlclass DateTimeFormatter QmlDateTimeFormatter + \brief The DateTimeFormatter allows you to control the format of a date string. + + \code + DateTimeFormatter { id: formatter; date: System.date } + Text { text: formatter.dateText } + \endcode + + By default, the text properties (dateText, timeText, and dateTimeText) will return the + date and time using the current system locale's format. +*/ + +/*! + \internal + \class QmlDateTimeFormatter + \ingroup group_utility + \brief The QmlDateTimeFormatter class allows you to format a date string. +*/ + +QmlDateTimeFormatter::QmlDateTimeFormatter(QObject *parent) +: QObject(*(new QmlDateTimeFormatterPrivate), parent) +{ +} + +QmlDateTimeFormatter::~QmlDateTimeFormatter() +{ +} + +/*! + \qmlproperty string DateTimeFormatter::dateText + \qmlproperty string DateTimeFormatter::timeText + \qmlproperty string DateTimeFormatter::dateTimeText + + Formatted text representations of the \c date, \c time, + and \c {date and time}, respectively. + + If there is no explictly specified format the DateTimeFormatter + will use the system locale's default 'short' setting. + + \code + // specify source date (assuming today is February 19, 2009) + DateTimeFormatter { id: formatter; dateTime: Today.date } + + // display the full date and time + Text { text: formatter.dateText } + \endcode + + Would be equivalent to the following for a US English locale: + + \code + // display the date + Text { text: "2/19/09" } + \endcode +*/ +QString QmlDateTimeFormatter::dateTimeText() const +{ + Q_D(const QmlDateTimeFormatter); + return d->dateTimeText; +} + +QString QmlDateTimeFormatter::dateText() const +{ + Q_D(const QmlDateTimeFormatter); + return d->dateText; +} + +QString QmlDateTimeFormatter::timeText() const +{ + Q_D(const QmlDateTimeFormatter); + return d->timeText; +} + +/*! + \qmlproperty date DateTimeFormatter::date + \qmlproperty time DateTimeFormatter::time + \qmlproperty datetime DateTimeFormatter::dateTime + + The source date and time to be used by the formatter. + + \code + // setting the date and time + DateTimeFormatter { date: System.date; time: System.time } + \endcode + + For convienience it is possible to set the datetime property to set both the date and the time. + \code + // setting the datetime + DateTimeFormatter { dateTime: System.dateTime } + \endcode + + There can only be one instance of date and time per formatter; if date, time, and dateTime are all + set the actual date and time used is not guaranteed. + + \note If no date is set, dateTimeText will be just the date; + If no time is set, the dateTimeText will be just the time. + +*/ +QDate QmlDateTimeFormatter::date() const +{ + Q_D(const QmlDateTimeFormatter); + return d->date; +} + +QTime QmlDateTimeFormatter::time() const +{ + Q_D(const QmlDateTimeFormatter); + return d->time; +} + +QDateTime QmlDateTimeFormatter::dateTime() const +{ + Q_D(const QmlDateTimeFormatter); + return d->dateTime; +} + +/*! + \qmlproperty string DateTimeFormatter::dateFormat + \qmlproperty string DateTimeFormatter::timeFormat + \qmlproperty string DateTimeFormatter::dateTimeFormat + + Specifies a custom format which the DateTime Formatter can use. + + If there is no explictly specified format the DateTimeFormatter + will use the system locale's default 'short' setting. + + The text's format may be modified by setting: + \list + \i \c dateFormat + \i \c timeFormat + \i \c dateTimeFormat + \endlist + + If only the format for date is defined, the time and dateTime formats will be defined + as the system locale default and likewise for the others. + + Syntax for the format is based on the QDateTime::toString() formatting options. + + \code + // Format the date such that the dateText is: '1997-12-12' + DateTimeFormatter { id: formatter; dateTime: Today.dateTime; formatDate: "yyyy-MM-d" } + \endcode + + Assigning an empty string to a particular format will reset it. +*/ +QString QmlDateTimeFormatter::dateTimeFormat() const +{ + Q_D(const QmlDateTimeFormatter); + return d->dateTimeFormat; +} + +QString QmlDateTimeFormatter::dateFormat() const +{ + Q_D(const QmlDateTimeFormatter); + return d->dateFormat; +} + +QString QmlDateTimeFormatter::timeFormat() const +{ + Q_D(const QmlDateTimeFormatter); + return d->timeFormat; +} + +/*! + \qmlproperty bool DateTimeFormatter::longStyle + + This property causes the formatter to use the system locale's long format rather than short format + by default. + + This setting is off by default. +*/ +bool QmlDateTimeFormatter::longStyle() const +{ + Q_D(const QmlDateTimeFormatter); + return d->longStyle; +} + +void QmlDateTimeFormatter::setDateTime(const QDateTime &dateTime) +{ + Q_D(QmlDateTimeFormatter); + if (d->dateTime == dateTime) + return; + d->dateTime = dateTime; + d->date = d->dateTime.date(); + d->time = d->dateTime.time(); + d->updateText(); +} + +void QmlDateTimeFormatter::setTime(const QTime &time) +{ + Q_D(QmlDateTimeFormatter); + if (d->dateTime.time() == time) + return; + d->time = time; + d->dateTime.setTime(time); + d->updateText(); +} + +void QmlDateTimeFormatter::setDate(const QDate &date) +{ + Q_D(QmlDateTimeFormatter); + if (d->dateTime.date() == date) + return; + d->date = date; + bool clearTime = d->dateTime.time().isValid() ? false : true; //because setting date generates default time + d->dateTime.setDate(date); + if (clearTime) + d->dateTime.setTime(QTime()); + d->updateText(); +} + +//DateTime formatting may be a combination of date and time? +void QmlDateTimeFormatter::setDateTimeFormat(const QString &format) +{ + Q_D(QmlDateTimeFormatter); + //no format checking + d->dateTimeFormat = format; + d->updateText(); +} + +void QmlDateTimeFormatter::setDateFormat(const QString &format) +{ + Q_D(QmlDateTimeFormatter); + //no format checking + d->dateFormat = format; + d->updateText(); +} + +void QmlDateTimeFormatter::setTimeFormat(const QString &format) +{ + Q_D(QmlDateTimeFormatter); + //no format checking + d->timeFormat = format; + d->updateText(); +} + +void QmlDateTimeFormatter::setLongStyle(bool longStyle) +{ + Q_D(QmlDateTimeFormatter); + d->longStyle = longStyle; + d->updateText(); +} + +void QmlDateTimeFormatterPrivate::updateText() +{ + Q_Q(QmlDateTimeFormatter); + if (!componentComplete) + return; + + QString str; + QString str1; + QString str2; + + Qt::DateFormat defaultFormat = longStyle ? Qt::SystemLocaleLongDate : Qt::SystemLocaleShortDate; + + if (dateFormat.isEmpty()) + str1 = date.toString(defaultFormat); + else + str1 = date.toString(dateFormat); + + if (timeFormat.isEmpty()) + str2 = time.toString(defaultFormat); + else + str2 = time.toString(timeFormat); + + if (dateTimeFormat.isEmpty()) + str = dateTime.toString(defaultFormat); + //else if (!formatTime.isEmpty() && !formatDate.isEmpty()) + // str = str1 + QLatin1Char(' ') + str2; + else + str = dateTime.toString(dateTimeFormat); + + if (dateTimeText == str && dateText == str1 && timeText == str2) + return; + + dateTimeText = str; + dateText = str1; + timeText = str2; + + emit q->textChanged(); +} + +void QmlDateTimeFormatter::classBegin() +{ + Q_D(QmlDateTimeFormatter); + d->componentComplete = false; +} + +void QmlDateTimeFormatter::componentComplete() +{ + Q_D(QmlDateTimeFormatter); + d->componentComplete = true; + d->updateText(); +} + +QML_DEFINE_TYPE(Qt,4,6,DateTimeFormatter,QmlDateTimeFormatter) + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmldatetimeformatter_p.h b/src/declarative/util/qmldatetimeformatter_p.h new file mode 100644 index 0000000..c90ee8c --- /dev/null +++ b/src/declarative/util/qmldatetimeformatter_p.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** 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 QMLDATETIMEFORMATTER_H +#define QMLDATETIMEFORMATTER_H + +#include <qml.h> + +#include <QtCore/qdatetime.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlDateTimeFormatterPrivate; +class Q_DECLARATIVE_EXPORT QmlDateTimeFormatter : public QObject, public QmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QmlParserStatus) + + Q_PROPERTY(QString dateText READ dateText NOTIFY textChanged) + Q_PROPERTY(QString timeText READ timeText NOTIFY textChanged) + Q_PROPERTY(QString dateTimeText READ dateTimeText NOTIFY textChanged) + Q_PROPERTY(QDate date READ date WRITE setDate) + Q_PROPERTY(QTime time READ time WRITE setTime) + Q_PROPERTY(QDateTime dateTime READ dateTime WRITE setDateTime) + Q_PROPERTY(QString dateFormat READ dateFormat WRITE setDateFormat) + Q_PROPERTY(QString timeFormat READ timeFormat WRITE setTimeFormat) + Q_PROPERTY(QString dateTimeFormat READ dateTimeFormat WRITE setDateTimeFormat) + Q_PROPERTY(bool longStyle READ longStyle WRITE setLongStyle) +public: + QmlDateTimeFormatter(QObject *parent=0); + ~QmlDateTimeFormatter(); + + QString dateTimeText() const; + QString dateText() const; + QString timeText() const; + + QDate date() const; + void setDate(const QDate &); + + QTime time() const; + void setTime(const QTime &); + + QDateTime dateTime() const; + void setDateTime(const QDateTime &); + + QString dateTimeFormat() const; + void setDateTimeFormat(const QString &); + + QString dateFormat() const; + void setDateFormat(const QString &); + + QString timeFormat() const; + void setTimeFormat(const QString &); + + bool longStyle() const; + void setLongStyle(bool); + + virtual void classBegin(); + virtual void componentComplete(); + +Q_SIGNALS: + void textChanged(); + +private: + Q_DISABLE_COPY(QmlDateTimeFormatter) + Q_DECLARE_PRIVATE(QmlDateTimeFormatter) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlDateTimeFormatter) + +QT_END_HEADER + +#endif diff --git a/src/declarative/util/qmleasefollow.cpp b/src/declarative/util/qmleasefollow.cpp new file mode 100644 index 0000000..e3153b1 --- /dev/null +++ b/src/declarative/util/qmleasefollow.cpp @@ -0,0 +1,527 @@ +/**************************************************************************** +** +** 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 "qmleasefollow_p.h" + +#include "qmlanimation_p_p.h" + +#include <qmlmetaproperty.h> + +#include <QtCore/qdebug.h> + +#include <math.h> + +QT_BEGIN_NAMESPACE + +QML_DEFINE_TYPE(Qt,4,6,EaseFollow,QmlEaseFollow); + +class QmlEaseFollowPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlEaseFollow) +public: + QmlEaseFollowPrivate() + : source(0), velocity(200), duration(-1), maximumEasingTime(-1), + reversingMode(QmlEaseFollow::Eased), initialVelocity(0), + initialValue(0), invert(false), enabled(true), trackVelocity(0), clockOffset(0), + lastTick(0), clock(this) + {} + + qreal source; + qreal velocity; + qreal duration; + qreal maximumEasingTime; + QmlEaseFollow::ReversingMode reversingMode; + + qreal initialVelocity; + qreal initialValue; + bool invert; + bool enabled; + + qreal trackVelocity; + + QmlMetaProperty target; + + int clockOffset; + int lastTick; + void tick(int); + void clockStart(); + void clockStop(); + QTickAnimationProxy<QmlEaseFollowPrivate, &QmlEaseFollowPrivate::tick> clock; + + void restart(); + + // Parameters for use in tick() + qreal a; // Acceleration + qreal d; // Deceleration + qreal tf; // Total time + qreal tp; // Time at which peak velocity occurs + qreal td; // Time at which decelleration begins + qreal vp; // Velocity at tp + qreal sp; // Displacement at tp + qreal sd; // Displacement at td + qreal vi; // "Normalized" initialvelocity + bool recalc(); +}; + +bool QmlEaseFollowPrivate::recalc() +{ + qreal s = source - initialValue; + vi = initialVelocity; + + s = (invert?-1.0:1.0) * s; + vi = (invert?-1.0:1.0) * vi; + + if (duration > 0 && velocity > 0) { + tf = s / velocity; + if (tf > (duration / 1000.)) tf = (duration / 1000.); + } else if (duration > 0) { + tf = duration / 1000.; + } else if (velocity > 0) { + tf = s / velocity; + } else { + return false; + } + + if (maximumEasingTime == 0) { + a = 0; + d = 0; + tp = 0; + td = tf; + vp = velocity; + sp = 0; + sd = s; + } else if (maximumEasingTime != -1 && tf > (maximumEasingTime / 1000.)) { + + qreal met = maximumEasingTime / 1000.; + td = tf - met; + + qreal c1 = td; + qreal c2 = (tf - td) * vi - tf * velocity; + qreal c3 = -0.5 * (tf - td) * vi * vi; + + qreal vp1 = (-c2 + sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1); + // qreal vp2 = (-c2 - sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1); + + vp = vp1; + a = vp / met; + d = a; + tp = (vp - vi) / a; + sp = vi * tp + 0.5 * a * tp * tp; + sd = sp + (td - tp) * vp; + } else { + + qreal c1 = 0.25 * tf * tf; + qreal c2 = 0.5 * vi * tf - s; + qreal c3 = -0.25 * vi * vi; + + qreal a1 = (-c2 + sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1); + //qreal a2 = (-c2 - sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1); + + qreal tp1 = 0.5 * tf - 0.5 * vi / a1; + //qreal tp2 = 0.5 * tf - 0.5 * vi / a2; + qreal vp1 = a1 * tp1 + vi; + //qreal vp2 = a2 * tp2 + vi; + + qreal sp1 = 0.5 * a1 * tp1 * tp1 + vi * tp1; + //qreal sp2 = 0.5 * a2 * tp2 * tp2 + vi * tp2; + + a = a1; + d = a1; + tp = tp1; + td = tp1; + vp = vp1; + sp = sp1; + sd = sp1; + } + + /* + qWarning() << "a:" << a << "tf:" << tf << "tp:" << tp << "vp:" + << vp << "sp:" << sp << "vi:" << vi << "invert:" << invert; + */ + return true; +} + +void QmlEaseFollowPrivate::clockStart() +{ + if (clock.state() == QAbstractAnimation::Running) { + clockOffset = lastTick; + return; + } else { + clockOffset = 0; + lastTick = 0; + clock.start(); + } +} + +void QmlEaseFollowPrivate::clockStop() +{ + clockOffset = 0; + lastTick = 0; + clock.stop(); +} + +void QmlEaseFollowPrivate::tick(int t) +{ + lastTick = t; + t -= clockOffset; + + qreal time_seconds = qreal(t) / 1000.; + + qreal out = 0; + if (time_seconds < tp) { + + trackVelocity = vi + time_seconds * a; + trackVelocity = (invert?-1.0:1.0) * trackVelocity; + + qreal value = 0.5 * a * time_seconds * time_seconds + vi * time_seconds; + value = (invert?-1.0:1.0) * value; + target.write(initialValue + value); + out = initialValue + value; + } else if (time_seconds < td) { + + time_seconds -= tp; + trackVelocity = (invert?-1.0:1.0) * vp; + qreal value = sp + time_seconds * vp; + value = (invert?-1.0:1.0) * value; + + target.write(initialValue + value); + + out = initialValue + value; + } else if (time_seconds < tf) { + + time_seconds -= td; + + trackVelocity = vp - time_seconds * a; + trackVelocity = (invert?-1.0:1.0) * trackVelocity; + + qreal value = sd - 0.5 * d * time_seconds * time_seconds + vp * time_seconds; + value = (invert?-1.0:1.0) * value; + + target.write(initialValue + value); + + out = initialValue + value; + } else { + + clock.stop(); + + trackVelocity = 0; + target.write(source); + } + + //qWarning() << out << trackVelocity << t << a; +} + +/*! + \qmlclass EaseFollow QmlEaseFollow + \brief The EaseFollow element allows a property to smoothly track a value. + + The EaseFollow smoothly animates a property's value to a set target value + using an ease in/out quad easing curve. If the target value changes while + the animation is in progress, the easing curves used to animate to the old + and the new target values are spliced together to avoid any obvious visual + glitches. + + The property animation is configured by setting the velocity at which the + animation should occur, or the duration that the animation should take. + If both a velocity and a duration are specified, the one that results in + the quickest animation is chosen for each change in the target value. + + For example, animating from 0 to 800 will take 4 seconds if a velocity + of 200 is set, will take 8 seconds with a duration of 8000 set, and will + take 4 seconds with both a velocity of 200 and a duration of 8000 set. + Animating from 0 to 20000 will take 10 seconds if a velocity of 200 is set, + will take 8 seconds with a duration of 8000 set, and will take 8 seconds + with both a velocity of 200 and a duration of 8000 set. + + The follow example shows one rectangle tracking the position of another. +\code +import Qt 4.6 + +Rectangle { + width: 800; height: 600; color: "blue" + + Rectangle { + color: "green" + width: 60; height: 60; + x: -5; y: -5; + x: EaseFollow { source: rect1.x - 5; velocity: 200 } + y: EaseFollow { source: rect1.y - 5; velocity: 200 } + } + + Rectangle { + id: rect1 + color: "red" + width: 50; height: 50; + } + + focus: true + Keys.onRightPressed: rect1.x = rect1.x + 100 + Keys.onLeftPressed: rect1.x = rect1.x - 100 + Keys.onUpPressed: rect1.y = rect1.y - 100 + Keys.onDownPressed: rect1.y = rect1.y + 100 +} +\endcode + + \sa SpringFollow +*/ + +QmlEaseFollow::QmlEaseFollow(QObject *parent) +: QObject(*(new QmlEaseFollowPrivate), parent) +{ +} + +QmlEaseFollow::~QmlEaseFollow() +{ +} + +/*! + \qmlproperty qreal EaseFollow::source + This property holds the source value which will be tracked. + + Bind to a property in order to track its changes. +*/ +qreal QmlEaseFollow::sourceValue() const +{ + Q_D(const QmlEaseFollow); + return d->source; +} + +/*! + \qmlproperty enumeration EaseFollow::reversingMode + + Sets how the EaseFollow behaves if an animation direction is reversed. + + If reversing mode is \c Eased, the animation will smoothly decelerate, and + then reverse direction. If the reversing mode is \c Immediate, the + animation will immediately begin accelerating in the reverse direction, + begining with a velocity of 0. If the reversing mode is \c Sync, the + property is immediately set to the target value. +*/ +QmlEaseFollow::ReversingMode QmlEaseFollow::reversingMode() const +{ + Q_D(const QmlEaseFollow); + return d->reversingMode; +} + +void QmlEaseFollow::setReversingMode(ReversingMode m) +{ + Q_D(QmlEaseFollow); + if (d->reversingMode == m) + return; + + d->reversingMode = m; + emit reversingModeChanged(); +} + +void QmlEaseFollowPrivate::restart() +{ + if (!enabled || velocity == 0) { + clockStop(); + return; + } + + initialValue = target.read().toReal(); + + if (source == initialValue) { + clockStop(); + return; + } + + bool hasReversed = trackVelocity != 0. && + ((trackVelocity > 0) == ((initialValue - source) > 0)); + + if (hasReversed) { + switch (reversingMode) { + default: + case QmlEaseFollow::Eased: + break; + case QmlEaseFollow::Sync: + target.write(source); + return; + case QmlEaseFollow::Immediate: + initialVelocity = 0; + clockStop(); + break; + } + } + + trackVelocity = initialVelocity; + + invert = (source < initialValue); + + if (!recalc()) { + target.write(source); + clockStop(); + return; + } + + clockStart(); +} + +void QmlEaseFollow::setSourceValue(qreal s) +{ + Q_D(QmlEaseFollow); + + if (d->clock.state() == QAbstractAnimation::Running && d->source == s) + return; + + d->source = s; + d->initialVelocity = d->trackVelocity; + d->restart(); + + emit sourceChanged(); +} + +/*! + \qmlproperty qreal EaseFollow::duration + + This property holds the animation duration used when tracking the source. + + Setting this to -1 disables the duration value. +*/ +qreal QmlEaseFollow::duration() const +{ + Q_D(const QmlEaseFollow); + return d->duration; +} + +void QmlEaseFollow::setDuration(qreal v) +{ + Q_D(QmlEaseFollow); + if (d->duration == v) + return; + + d->duration = v; + d->trackVelocity = 0; + + if (d->clock.state() == QAbstractAnimation::Running) + d->restart(); + + emit durationChanged(); +} + +qreal QmlEaseFollow::velocity() const +{ + Q_D(const QmlEaseFollow); + return d->velocity; +} + +/*! + \qmlproperty qreal EaseFollow::velocity + + This property holds the average velocity allowed when tracking the source. + + Setting this to -1 disables the velocity value. +*/ +void QmlEaseFollow::setVelocity(qreal v) +{ + Q_D(QmlEaseFollow); + if (d->velocity == v) + return; + + d->velocity = v; + d->trackVelocity = 0; + + if (d->clock.state() == QAbstractAnimation::Running) + d->restart(); + + emit velocityChanged(); +} + +/*! + \qmlproperty bool EaseFollow::enabled + This property holds whether the target will track the source. +*/ +bool QmlEaseFollow::enabled() const +{ + Q_D(const QmlEaseFollow); + return d->enabled; +} + +void QmlEaseFollow::setEnabled(bool enabled) +{ + Q_D(QmlEaseFollow); + if (d->enabled == enabled) + return; + + d->enabled = enabled; + if (enabled) + d->restart(); + else + d->clockStop(); + + emit enabledChanged(); +} + +void QmlEaseFollow::setTarget(const QmlMetaProperty &t) +{ + Q_D(QmlEaseFollow); + d->target = t; +} + +/*! +\qmlproperty qreal EaseFollow::maximumEasingTime + +This property specifies the maximum time an "eases" during the follow should take. +Setting this property causes the velocity to "level out" after at a time. Setting +a negative value reverts to the normal mode of easing over the entire animation +duration. + +The default value is -1. +*/ +qreal QmlEaseFollow::maximumEasingTime() const +{ + Q_D(const QmlEaseFollow); + return d->maximumEasingTime; +} + +void QmlEaseFollow::setMaximumEasingTime(qreal v) +{ + Q_D(QmlEaseFollow); + d->maximumEasingTime = v; + + if (d->clock.state() == QAbstractAnimation::Running) + d->restart(); + + emit maximumEasingTimeChanged(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmleasefollow_p.h b/src/declarative/util/qmleasefollow_p.h new file mode 100644 index 0000000..ef095a3 --- /dev/null +++ b/src/declarative/util/qmleasefollow_p.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** 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 QMLEASEFOLLOW_H +#define QMLEASEFOLLOW_H + +#include <qml.h> +#include <qmlpropertyvaluesource.h> + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlMetaProperty; +class QmlEaseFollowPrivate; +class Q_DECLARATIVE_EXPORT QmlEaseFollow : public QObject, + public QmlPropertyValueSource +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlEaseFollow) + Q_INTERFACES(QmlPropertyValueSource) + Q_ENUMS(ReversingMode) + + Q_PROPERTY(qreal source READ sourceValue WRITE setSourceValue NOTIFY sourceChanged) + Q_PROPERTY(qreal velocity READ velocity WRITE setVelocity NOTIFY velocityChanged) + Q_PROPERTY(qreal duration READ duration WRITE setDuration NOTIFY durationChanged) + Q_PROPERTY(ReversingMode reversingMode READ reversingMode WRITE setReversingMode NOTIFY reversingModeChanged) + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(qreal maximumEasingTime READ maximumEasingTime WRITE setMaximumEasingTime NOTIFY maximumEasingTimeChanged) + +public: + enum ReversingMode { Eased, Immediate, Sync }; + + QmlEaseFollow(QObject *parent = 0); + ~QmlEaseFollow(); + + ReversingMode reversingMode() const; + void setReversingMode(ReversingMode); + + qreal sourceValue() const; + void setSourceValue(qreal); + + qreal velocity() const; + void setVelocity(qreal); + + qreal duration() const; + void setDuration(qreal); + + bool enabled() const; + void setEnabled(bool enabled); + + qreal maximumEasingTime() const; + void setMaximumEasingTime(qreal); + + virtual void setTarget(const QmlMetaProperty &); + +Q_SIGNALS: + void sourceChanged(); + void velocityChanged(); + void durationChanged(); + void reversingModeChanged(); + void enabledChanged(); + void maximumEasingTimeChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlEaseFollow); + +QT_END_HEADER + +#endif // QMLEASEFOLLOW_H diff --git a/src/declarative/util/qmlfontloader.cpp b/src/declarative/util/qmlfontloader.cpp new file mode 100644 index 0000000..b56043b --- /dev/null +++ b/src/declarative/util/qmlfontloader.cpp @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** 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 "qmlfontloader_p.h" + +#include <qmlcontext.h> +#include <qmlengine.h> + +#include <QUrl> +#include <QDebug> +#include <QNetworkRequest> +#include <QNetworkReply> +#include <QFile> +#include <QFontDatabase> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QmlFontLoaderPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlFontLoader) + +public: + QmlFontLoaderPrivate() : reply(0), status(QmlFontLoader::Null) {} + + void addFontToDatabase(const QByteArray &); + + QUrl url; + QString name; + QNetworkReply *reply; + QmlFontLoader::Status status; +}; + +QML_DEFINE_TYPE(Qt,4,6,FontLoader,QmlFontLoader) + +/*! + \qmlclass FontLoader QmlFontLoader + \ingroup group_utility + \brief This item allows using fonts by name or url. + + Example: + \qml + FontLoader { id: fixedFont; name: "Courier" } + FontLoader { id: webFont; source: "http://www.mysite.com/myfont.ttf" } + + Text { text: "Fixed-size font"; font.family: fixedFont.name } + Text { text: "Fancy font"; font.family: webFont.name } + \endqml +*/ +QmlFontLoader::QmlFontLoader(QObject *parent) + : QObject(*(new QmlFontLoaderPrivate), parent) +{ +} + +QmlFontLoader::~QmlFontLoader() +{ +} + +static QString toLocalFileOrQrc(const QUrl& url) +{ + QString r = url.toLocalFile(); + if (r.isEmpty() && url.scheme() == QLatin1String("qrc")) + r = QLatin1Char(':') + url.path(); + return r; +} + + +/*! + \qmlproperty url FontLoader::source + The url of the font to load. +*/ +QUrl QmlFontLoader::source() const +{ + Q_D(const QmlFontLoader); + return d->url; +} + +void QmlFontLoader::setSource(const QUrl &url) +{ + Q_D(QmlFontLoader); + if (url == d->url) + return; + d->url = qmlContext(this)->resolvedUrl(url); + + d->status = Loading; + emit statusChanged(); +#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML + QString lf = toLocalFileOrQrc(d->url); + if (!lf.isEmpty()) { + int id = QFontDatabase::addApplicationFont(lf); + if (id != -1) { + d->name = QFontDatabase::applicationFontFamilies(id).at(0); + emit nameChanged(); + d->status = QmlFontLoader::Ready; + } else { + d->status = QmlFontLoader::Error; + qWarning() << "Cannot load font:" << url; + } + emit statusChanged(); + } else +#endif + { + QNetworkRequest req(d->url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + d->reply = qmlEngine(this)->networkAccessManager()->get(req); + QObject::connect(d->reply, SIGNAL(finished()), this, SLOT(replyFinished())); + } +} + +/*! + \qmlproperty string FontLoader::name + + This property holds the name of the font family. + It is set automatically when a font is loaded using the \c url property. + + Use this to set the \c font.family property of a \c Text item. + + Example: + \qml + FontLoader { id: webFont; source: "http://www.mysite.com/myfont.ttf" } + Text { text: "Fancy font"; font.family: webFont.name } + \endqml +*/ +QString QmlFontLoader::name() const +{ + Q_D(const QmlFontLoader); + return d->name; +} + +void QmlFontLoader::setName(const QString &name) +{ + Q_D(QmlFontLoader); + if (d->name == name ) + return; + d->name = name; + emit nameChanged(); + d->status = Ready; + emit statusChanged(); +} + +/*! + \qmlproperty enum FontLoader::status + + This property holds the status of font loading. It can be one of: + \list + \o Null - no font has been set + \o Ready - the font has been loaded + \o Loading - the font is currently being loaded + \o Error - an error occurred while loading the font + \endlist +*/ +QmlFontLoader::Status QmlFontLoader::status() const +{ + Q_D(const QmlFontLoader); + return d->status; +} + +void QmlFontLoader::replyFinished() +{ + Q_D(QmlFontLoader); + if (d->reply) { + if (!d->reply->error()) { + QByteArray ba = d->reply->readAll(); + d->addFontToDatabase(ba); + } else { + d->status = Error; + emit statusChanged(); + } + d->reply->deleteLater(); + d->reply = 0; + } +} + +void QmlFontLoaderPrivate::addFontToDatabase(const QByteArray &ba) +{ + Q_Q(QmlFontLoader); + + int id = QFontDatabase::addApplicationFontFromData(ba); + if (id != -1) { + name = QFontDatabase::applicationFontFamilies(id).at(0); + emit q->nameChanged(); + status = QmlFontLoader::Ready; + } else { + status = QmlFontLoader::Error; + qWarning() << "Cannot load font:" << url; + } + emit q->statusChanged(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlfontloader_p.h b/src/declarative/util/qmlfontloader_p.h new file mode 100644 index 0000000..aac8a71 --- /dev/null +++ b/src/declarative/util/qmlfontloader_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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 QMLFONTLOADER_H +#define QMLFONTLOADER_H + +#include <qml.h> + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlFontLoaderPrivate; +class Q_DECLARATIVE_EXPORT QmlFontLoader : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlFontLoader) + Q_ENUMS(Status) + + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + +public: + enum Status { Null = 0, Ready, Loading, Error }; + + QmlFontLoader(QObject *parent = 0); + ~QmlFontLoader(); + + QUrl source() const; + void setSource(const QUrl &url); + + QString name() const; + void setName(const QString &name); + + Status status() const; + +private Q_SLOTS: + void replyFinished(); + +Q_SIGNALS: + void nameChanged(); + void statusChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlFontLoader) + +QT_END_HEADER + +#endif // QMLFONTLOADER_H + diff --git a/src/declarative/util/qmllistaccessor.cpp b/src/declarative/util/qmllistaccessor.cpp new file mode 100644 index 0000000..e060097 --- /dev/null +++ b/src/declarative/util/qmllistaccessor.cpp @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** 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 "qmllistaccessor_p.h" + +#include <qmlmetatype.h> + +#include <QStringList> +#include <QtCore/qdebug.h> + +// ### Remove me +#include <qmlengine_p.h> + +QT_BEGIN_NAMESPACE + +QmlListAccessor::QmlListAccessor() +: m_type(Invalid) +{ +} + +QmlListAccessor::~QmlListAccessor() +{ +} + +QVariant QmlListAccessor::list() const +{ + return d; +} + +void QmlListAccessor::setList(const QVariant &v, QmlEngine *engine) +{ + d = v; + + QmlEnginePrivate *enginePrivate = engine?QmlEnginePrivate::get(engine):0; + + if (!d.isValid()) { + m_type = Invalid; + } else if (d.userType() == QVariant::StringList) { + m_type = StringList; + } else if (d.userType() == QMetaType::QVariantList) { + m_type = VariantList; + } else if (d.canConvert(QVariant::Int)) { + m_type = Integer; + } else if ((!enginePrivate && QmlMetaType::isObject(d.userType())) || + (enginePrivate && enginePrivate->isObject(d.userType()))) { + QObject *data = 0; + data = *(QObject **)v.constData(); + d = QVariant::fromValue(data); + m_type = Instance; + } else if ((!enginePrivate && QmlMetaType::isQmlList(d.userType())) || + (enginePrivate && enginePrivate->isQmlList(d.userType()))) { + m_type = QmlList; + } else if (QmlMetaType::isList(d.userType())) { + m_type = ListProperty; + } else { + m_type = Instance; + } +} + +int QmlListAccessor::count() const +{ + switch(m_type) { + case StringList: + return qvariant_cast<QStringList>(d).count(); + case VariantList: + return qvariant_cast<QVariantList>(d).count(); + case QmlList: + { + QmlPrivate::ListInterface *li = *(QmlPrivate::ListInterface **)d.constData(); + return li->count(); + } + case ListProperty: + { + QmlListProperty<void> *li = (QmlListProperty<void>*)d.constData(); + if (li->count) return li->count(li); + else return 0; + } + case Instance: + return 1; + case Integer: + return d.toInt(); + default: + case Invalid: + return 0; + } +} + +QVariant QmlListAccessor::at(int idx) const +{ + Q_ASSERT(idx >= 0 && idx < count()); + switch(m_type) { + case StringList: + return QVariant::fromValue(qvariant_cast<QStringList>(d).at(idx)); + case VariantList: + return qvariant_cast<QVariantList>(d).at(idx); + case QmlList: + { + QmlPrivate::ListInterface *li = *(QmlPrivate::ListInterface **)d.constData(); + void *ptr[1]; + li->at(idx, ptr); + return QVariant::fromValue((QObject*)ptr[0]); + } + case ListProperty: + { + QmlListProperty<void> *li = (QmlListProperty<void>*)d.constData(); + if (li->at) return QVariant::fromValue((QObject *)li->at(li, idx)); + else return QVariant(); + } + case Instance: + return d; + case Integer: + return QVariant(idx); + default: + case Invalid: + return QVariant(); + } +} + +bool QmlListAccessor::append(const QVariant &value) +{ + switch(m_type) { + case QmlList: + { + QmlPrivate::ListInterface *li = *(QmlPrivate::ListInterface **)d.constData(); + li->append(const_cast<void *>(value.constData())); //XXX Typesafety + return true; + } + case ListProperty: + { + QmlListProperty<void> *li = (QmlListProperty<void>*)d.constData(); + if (li->append) li->append(li, *(void **)value.constData()); // XXX Typesafety + return true; + } + case StringList: + case VariantList: + case Invalid: + case Instance: + case Integer: + default: + return false; + } +} + +bool QmlListAccessor::insert(int index, const QVariant &value) +{ + switch(m_type) { + case QmlList: + { + QmlPrivate::ListInterface *li = *(QmlPrivate::ListInterface **)d.constData(); + li->insert(index, const_cast<void *>(value.constData())); //XXX Typesafety + return true; + } + case ListProperty: + { + QmlListProperty<void> *li = (QmlListProperty<void>*)d.constData(); + if (li->insert) li->insert(li, index, *(void **)value.constData()); // XXX Typesafety + return true; + } + case StringList: + case VariantList: + case Invalid: + case Instance: + case Integer: + default: + return false; + } +} + +bool QmlListAccessor::removeAt(int index) +{ + switch(m_type) { + case QmlList: + { + QmlPrivate::ListInterface *li = *(QmlPrivate::ListInterface **)d.constData(); + li->removeAt(index); + return true; + } + case ListProperty: + { + QmlListProperty<void> *li = (QmlListProperty<void>*)d.constData(); + if (li->removeAt) li->removeAt(li, index); + return true; + } + case StringList: + case VariantList: + case Invalid: + case Instance: + case Integer: + default: + return false; + } +} + +bool QmlListAccessor::clear() +{ + switch(m_type) { + case QmlList: + { + QmlPrivate::ListInterface *li = *(QmlPrivate::ListInterface **)d.constData(); + li->clear(); + return true; + } + case ListProperty: + { + QmlListProperty<void> *li = (QmlListProperty<void>*)d.constData(); + if (li->clear) li->clear(li); + return true; + } + case StringList: + case VariantList: + case Invalid: + case Instance: + case Integer: + default: + return false; + } +} + +bool QmlListAccessor::isValid() const +{ + return m_type != Invalid; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmllistaccessor_p.h b/src/declarative/util/qmllistaccessor_p.h new file mode 100644 index 0000000..6866150 --- /dev/null +++ b/src/declarative/util/qmllistaccessor_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** 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 QMLLISTACCESSOR_H +#define QMLLISTACCESSOR_H + +#include <QtCore/QVariant> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlEngine; +class Q_DECLARATIVE_EXPORT QmlListAccessor +{ +public: + QmlListAccessor(); + ~QmlListAccessor(); + + QVariant list() const; + void setList(const QVariant &, QmlEngine * = 0); + + bool isValid() const; + + int count() const; + QVariant at(int) const; + + bool append(const QVariant &); + bool insert(int, const QVariant &); + bool removeAt(int); + bool clear(); + + enum Type { Invalid, StringList, VariantList, QmlList, ListProperty, Instance, Integer }; + Type type() const { return m_type; } + +private: + Type m_type; + QVariant d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLLISTACCESSOR_H diff --git a/src/declarative/util/qmllistmodel.cpp b/src/declarative/util/qmllistmodel.cpp new file mode 100644 index 0000000..0b19574 --- /dev/null +++ b/src/declarative/util/qmllistmodel.cpp @@ -0,0 +1,991 @@ +/**************************************************************************** +** +** 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 "qmllistmodel_p.h" + +#include "qmlopenmetaobject_p.h" + +#include <qmlcustomparser_p.h> +#include <qmlparser_p.h> +#include <qmlengine_p.h> +#include <qmlcontext.h> +#include <qmlinfo.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qstack.h> +#include <QXmlStreamReader> +#include <QtScript/qscriptvalueiterator.h> + +Q_DECLARE_METATYPE(QListModelInterface *) + +QT_BEGIN_NAMESPACE + +#define DATA_ROLE_ID 1 +#define DATA_ROLE_NAME "data" + +struct ListInstruction +{ + enum { Push, Pop, Value, Set } type; + int dataIdx; +}; + +struct ListModelData +{ + int dataOffset; + int instrCount; + ListInstruction *instructions() const { return (ListInstruction *)((char *)this + sizeof(ListModelData)); } +}; + +static void dump(ModelNode *node, int ind); + +/*! + \qmlclass ListModel QmlListModel + \brief The ListModel element defines a free-form list data source. + + The ListModel is a simple hierarchy of elements containing data roles. The contents can + be defined dynamically, or explicitly in QML: + + For example: + + \code + ListModel { + id: fruitModel + ListElement { + name: "Apple" + cost: 2.45 + } + ListElement { + name: "Orange" + cost: 3.25 + } + ListElement { + name: "Banana" + cost: 1.95 + } + } + \endcode + + Roles (properties) must begin with a lower-case letter. The above example defines a + ListModel containing three elements, with the roles "name" and "cost". + + The defined model can be used in views such as ListView: + \code + Component { + id: fruitDelegate + Item { + width: 200; height: 50 + Text { text: name } + Text { text: '$'+cost; anchors.right: parent.right } + } + } + + ListView { + model: fruitModel + delegate: fruitDelegate + anchors.fill: parent + } + \endcode + + It is possible for roles to contain list data. In the example below we create a list of fruit attributes: + + \code + ListModel { + id: fruitModel + ListElement { + name: "Apple" + cost: 2.45 + attributes: [ + ListElement { description: "Core" }, + ListElement { description: "Deciduous" } + ] + } + ListElement { + name: "Orange" + cost: 3.25 + attributes: [ + ListElement { description: "Citrus" } + ] + } + ListElement { + name: "Banana" + cost: 1.95 + attributes: [ + ListElement { description: "Tropical" }, + ListElement { description: "Seedless" } + ] + } + } + \endcode + + The delegate below will list all the fruit attributes: + \code + Component { + id: fruitDelegate + Item { + width: 200; height: 50 + Text { id: name; text: name } + Text { text: '$'+cost; anchors.right: parent.right } + Row { + anchors.top: name.bottom + spacing: 5 + Text { text: "Attributes:" } + Repeater { + dataSource: attributes + Component { Text { text: description } } + } + } + } + } + \endcode + + The content of a ListModel may be created and modified using the clear(), + append(), and set() methods. For example: + + \code + Component { + id: fruitDelegate + Item { + width: 200; height: 50 + Text { text: name } + Text { text: '$'+cost; anchors.right: parent.right } + + // Double the price when clicked. + MouseRegion { + anchors.fill: parent + onClicked: fruitModel.set(index, "cost", cost*2) + } + } + } + \endcode + + When creating content dynamically, note that the set of available properties cannot be changed + except by first clearing the model - whatever properties are first added are then the + only permitted properties in the model. + + \sa {qmlmodels}{Data Models} +*/ + +class ModelObject : public QObject +{ + Q_OBJECT +public: + ModelObject(); + + void setValue(const QByteArray &name, const QVariant &val) + { + _mo->setValue(name, val); + } + +private: + QmlOpenMetaObject *_mo; +}; + +struct ModelNode +{ + ModelNode(); + ~ModelNode(); + + QList<QVariant> values; + QHash<QString, ModelNode *> properties; + + QmlListModel *model(const QmlListModel *parent) { + if (!modelCache) { + modelCache = new QmlListModel; + QmlEngine::setContextForObject(modelCache,QmlEngine::contextForObject(parent)); + + modelCache->_root = this; + } + return modelCache; + } + + ModelObject *object(const QmlListModel *parent) { + if (!objectCache) { + objectCache = new ModelObject(); + QHash<QString, ModelNode *>::iterator it; + for (it = properties.begin(); it != properties.end(); ++it) { + objectCache->setValue(it.key().toUtf8(), parent->valueForNode(*it)); + } + } + return objectCache; + } + + void setObjectValue(const QScriptValue& valuemap); + void setListValue(const QScriptValue& valuelist); + + void setProperty(const QString& prop, const QVariant& val) { + QHash<QString, ModelNode *>::const_iterator it = properties.find(prop); + if (it != properties.end()) { + (*it)->values[0] = val; + } else { + ModelNode *n = new ModelNode; + n->values << val; + properties.insert(prop,n); + } + if (objectCache) + objectCache->setValue(prop.toUtf8(), val); + } + + QmlListModel *modelCache; + ModelObject *objectCache; + bool isArray; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(ModelNode *) + +QT_BEGIN_NAMESPACE + +void ModelNode::setObjectValue(const QScriptValue& valuemap) { + QScriptValueIterator it(valuemap); + while (it.hasNext()) { + it.next(); + ModelNode *value = new ModelNode; + QScriptValue v = it.value(); + if (v.isArray()) { + value->isArray = true; + value->setListValue(v); + } else { + value->values << v.toVariant(); + } + properties.insert(it.name(),value); + } +} + +void ModelNode::setListValue(const QScriptValue& valuelist) { + QScriptValueIterator it(valuelist); + values.clear(); + while (it.hasNext()) { + it.next(); + ModelNode *value = new ModelNode; + QScriptValue v = it.value(); + if (v.isArray()) { + value->isArray = true; + value->setListValue(v); + } else if (v.isObject()) { + value->setObjectValue(v); + } else { + value->values << v.toVariant(); + } + values.append(qVariantFromValue(value)); + + } +} + + +ModelObject::ModelObject() +: _mo(new QmlOpenMetaObject(this)) +{ +} + +QmlListModel::QmlListModel(QObject *parent) +: QListModelInterface(parent), _rolesOk(false), _root(0) +{ +} + +QmlListModel::~QmlListModel() +{ + delete _root; +} + +void QmlListModel::checkRoles() const +{ + if (_rolesOk || !_root) + return; + + for (int ii = 0; ii < _root->values.count(); ++ii) { + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(ii)); + if (node) { + foreach (const QString &role, node->properties.keys()) + addRole(role); + } + } + + _rolesOk = true; +} + +void QmlListModel::addRole(const QString &role) const +{ + if (!roleStrings.contains(role)) + roleStrings << role; +} + +QList<int> QmlListModel::roles() const +{ + checkRoles(); + QList<int> rv; + for (int ii = 0; ii < roleStrings.count(); ++ii) + rv << ii; + return rv; +} + +QString QmlListModel::toString(int role) const +{ + checkRoles(); + if (role < roleStrings.count()) + return roleStrings.at(role); + else + return QString(); +} + +QVariant QmlListModel::valueForNode(ModelNode *node) const +{ + QObject *rv = 0; + + 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) + return QVariant::fromValue(rv); + else + return QVariant(); +} + +QHash<int,QVariant> QmlListModel::data(int index, const QList<int> &roles) const +{ + checkRoles(); + QHash<int, QVariant> rv; + if (index >= count() || index < 0) + return rv; + + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + if (!node) + return rv; + + for (int ii = 0; ii < roles.count(); ++ii) { + const QString &roleString = roleStrings.at(roles.at(ii)); + + QHash<QString, ModelNode *>::ConstIterator iter = + node->properties.find(roleString); + if (iter != node->properties.end()) { + ModelNode *row = *iter; + rv.insert(roles.at(ii), valueForNode(row)); + } + } + + return rv; +} + +QVariant QmlListModel::data(int index, int role) const +{ + checkRoles(); + QVariant rv; + if (index >= count() || index < 0) + return rv; + + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + if (!node) + return rv; + + const QString &roleString = roleStrings.at(role); + + QHash<QString, ModelNode *>::ConstIterator iter = + node->properties.find(roleString); + if (iter != node->properties.end()) { + ModelNode *row = *iter; + rv = valueForNode(row); + } + + return rv; +} + +/*! + \qmlproperty int ListModel::count + The number of data entries in the model. +*/ +int QmlListModel::count() const +{ + if (!_root) return 0; + return _root->values.count(); +} + +/*! + \qmlmethod ListModel::clear() + + Deletes all content from the model. The properties are cleared such that + different properties may be set on subsequent additions. + + \sa append() remove() +*/ +void QmlListModel::clear() +{ + int cleared = count(); + _rolesOk = false; + delete _root; + _root = 0; + roleStrings.clear(); + emit itemsRemoved(0,cleared); + emit countChanged(0); +} + +/*! + \qmlmethod ListModel::remove(int index) + + Deletes the content at \a index from the model. + + \sa clear() +*/ +void QmlListModel::remove(int index) +{ + if (!_root || index < 0 || index >= _root->values.count()) { + qmlInfo(this) << tr("remove: index %1 out of range").arg(index); + return; + } + + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + _root->values.removeAt(index); + if (node) + delete node; + emit itemsRemoved(index,1); + emit countChanged(_root->values.count()); +} + +/*! + \qmlmethod ListModel::insert(int index, jsobject dict) + + Adds a new item to the list model at position \a index, with the + values in \a dict. + + \code + FruitModel.insert(2, {"cost": 5.95, "name":"Pizza"}) + \endcode + + The \a index must be to an existing item in the list, or one past + the end of the list (equivalent to append). + + \sa set() append() +*/ +void QmlListModel::insert(int index, const QScriptValue& valuemap) +{ + if (!valuemap.isObject() || valuemap.isArray()) { + qmlInfo(this) << tr("insert: value is not an object"); + return; + } + if (!_root) + _root = new ModelNode; + if (index >= _root->values.count() || index<0) { + if (index == _root->values.count()) + append(valuemap); + else + qmlInfo(this) << tr("insert: index %1 out of range").arg(index); + return; + } + ModelNode *mn = new ModelNode; + mn->setObjectValue(valuemap); + _root->values.insert(index,qVariantFromValue(mn)); + emit itemsInserted(index,1); + emit countChanged(_root->values.count()); +} + +/*! + \qmlmethod ListModel::move(int from, int to, int n) + + Moves \a n items \a from one position \a to another. + + The from and to ranges must exist; for example, to move the first 3 items + to the end of the list: + + \code + FruitModel.move(0,FruitModel.count-3,3) + \endcode + + \sa append() +*/ +void QmlListModel::move(int from, int to, int n) +{ + if (n==0 || from==to) + return; + if (from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0) { + qmlInfo(this) << tr("move: out of range"); + return; + } + int origfrom=from; // preserve actual move, so any animations are correct + int origto=to; + int orign=n; + if (from > to) { + // Only move forwards - flip if backwards moving + int tfrom = from; + int tto = to; + from = tto; + to = tto+n; + n = tfrom-tto; + } + if (n==1) { + _root->values.move(from,to); + } else { + QList<QVariant> replaced; + int i=0; + QVariantList::const_iterator it=_root->values.begin(); it += from+n; + for (; i<to-from; ++i,++it) + replaced.append(*it); + i=0; + it=_root->values.begin(); it += from; + for (; i<n; ++i,++it) + replaced.append(*it); + QVariantList::const_iterator f=replaced.begin(); + QVariantList::iterator t=_root->values.begin(); t += from; + for (; f != replaced.end(); ++f, ++t) + *t = *f; + } + emit itemsMoved(origfrom,origto,orign); +} + +/*! + \qmlmethod ListModel::append(jsobject dict) + + Adds a new item to the end of the list model, with the + values in \a dict. + + \code + FruitModel.append({"cost": 5.95, "name":"Pizza"}) + \endcode + + \sa set() remove() +*/ +void QmlListModel::append(const QScriptValue& valuemap) +{ + if (!valuemap.isObject() || valuemap.isArray()) { + qmlInfo(this) << tr("append: value is not an object"); + return; + } + if (!_root) + _root = new ModelNode; + ModelNode *mn = new ModelNode; + mn->setObjectValue(valuemap); + _root->values << qVariantFromValue(mn); + emit itemsInserted(count()-1,1); + emit countChanged(_root->values.count()); +} + +/*! + \qmlmethod object ListModel::get(int index) + + Returns the item at \a index in the list model. + + \code + FruitModel.append({"cost": 5.95, "name":"Jackfruit"}) + FruitModel.get(0).cost + \endcode + + The \a index must be an element in the list. + + Note that properties of the returned object that are themselves objects + will also be models, and this get() method is used to access elements: + + \code + FruitModel.append(..., "attributes": + [{"name":"spikes","value":"7mm"}, + {"name":"color","value":"green"}]); + FruitModel.get(0).attributes.get(1).value; // == "green" + \endcode + + \sa append() +*/ +QScriptValue QmlListModel::get(int index) const +{ + if (index >= count() || index < 0) { + qmlInfo(this) << tr("get: index %1 out of range").arg(index); + return 0; + } + + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + if (!node) + return 0; + QmlEngine *eng = qmlEngine(this); + if (!eng) { + qWarning("Cannot call QmlListModel::get() without a QmlEngine"); + return 0; + } + return QmlEnginePrivate::qmlScriptObject(node->object(this), eng); +} + +/*! + \qmlmethod ListModel::set(int index, jsobject dict) + + Changes the item at \a index in the list model with the + values in \a dict. Properties not appearing in \a valuemap + are left unchanged. + + \code + FruitModel.set(3, {"cost": 5.95, "name":"Pizza"}) + \endcode + + The \a index must be an element in the list. + + \sa append() +*/ +void QmlListModel::set(int index, const QScriptValue& valuemap) +{ + if (!valuemap.isObject() || valuemap.isArray()) { + qmlInfo(this) << tr("set: value is not an object"); + return; + } + if ( !_root || index > _root->values.count() || index < 0) { + qmlInfo(this) << tr("set: index %1 out of range").arg(index); + return; + } + if (index == _root->values.count()) + append(valuemap); + else { + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + QList<int> roles; + node->setObjectValue(valuemap); + QScriptValueIterator it(valuemap); + while (it.hasNext()) { + it.next(); + int r = roleStrings.indexOf(it.name()); + if (r<0) { + r = roleStrings.count(); + roleStrings << it.name(); + } + roles.append(r); + } + emit itemsChanged(index,1,roles); + } +} + +/*! + \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. + + \code + FruitModel.set(3, "cost", 5.95) + \endcode + + The \a index must be an element in the list. + + \sa append() +*/ +void QmlListModel::setProperty(int index, const QString& property, const QVariant& value) +{ + if ( !_root || index >= _root->values.count() || index < 0) { + qmlInfo(this) << tr("set: index %1 out of range").arg(index); + return; + } + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + int r = roleStrings.indexOf(property); + if (r<0) { + r = roleStrings.count(); + roleStrings << property; + } + QList<int> roles; + roles.append(r); + + if (node) + node->setProperty(property,value); + emit itemsChanged(index,1,roles); +} + +class QmlListModelParser : public QmlCustomParser +{ +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) +{ + QList<QVariant> values = prop.assignedValues(); + for(int ii = 0; ii < values.count(); ++ii) { + const QVariant &value = values.at(ii); + + if(value.userType() == qMetaTypeId<QmlCustomParserNode>()) { + QmlCustomParserNode node = + qvariant_cast<QmlCustomParserNode>(value); + + { + ListInstruction li; + li.type = ListInstruction::Push; + li.dataIdx = -1; + instr << li; + } + + QList<QmlCustomParserProperty> props = node.properties(); + for(int jj = 0; jj < props.count(); ++jj) { + const QmlCustomParserProperty &nodeProp = props.at(jj); + if (nodeProp.name() == "") { + error(nodeProp, QmlListModel::tr("ListElement: cannot use default property")); + return false; + } + if (nodeProp.name() == "id") { + error(nodeProp, QmlListModel::tr("ListElement: cannot use reserved \"id\" property")); + return false; + } + + ListInstruction li; + int ref = data.count(); + data.append(nodeProp.name()); + data.append('\0'); + li.type = ListInstruction::Set; + li.dataIdx = ref; + instr << li; + + if(!compileProperty(nodeProp, instr, data)) + return false; + + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + + { + ListInstruction li; + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + + } else { + + QmlParser::Variant variant = + qvariant_cast<QmlParser::Variant>(value); + + int ref = data.count(); + QByteArray d = variant.asScript().toUtf8(); + d.append('\0'); + data.append(d); + + ListInstruction li; + li.type = ListInstruction::Value; + li.dataIdx = ref; + instr << li; + + } + } + + return true; +} + +QByteArray QmlListModelParser::compile(const QList<QmlCustomParserProperty> &customProps) +{ + QList<ListInstruction> instr; + QByteArray data; + + for(int ii = 0; ii < customProps.count(); ++ii) { + const QmlCustomParserProperty &prop = customProps.at(ii); + if(prop.name() != "") { // isn't default property + error(prop, QmlListModel::tr("ListModel: undefined property '%1'").arg(QString::fromUtf8(prop.name()))); + return QByteArray(); + } + + if(!compileProperty(prop, instr, data)) { + return QByteArray(); + } + } + + int size = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction) + + data.count(); + + QByteArray rv; + rv.resize(size); + + ListModelData *lmd = (ListModelData *)rv.data(); + lmd->dataOffset = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction); + lmd->instrCount = instr.count(); + for (int ii = 0; ii < instr.count(); ++ii) + lmd->instructions()[ii] = instr.at(ii); + ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count()); + + return rv; +} + +void QmlListModelParser::setCustomData(QObject *obj, const QByteArray &d) +{ + QmlListModel *rv = static_cast<QmlListModel *>(obj); + + ModelNode *root = new ModelNode; + rv->_root = root; + QStack<ModelNode *> nodes; + nodes << root; + + bool processingSet = false; + + const ListModelData *lmd = (const ListModelData *)d.constData(); + const char *data = ((const char *)lmd) + lmd->dataOffset; + + for (int ii = 0; ii < lmd->instrCount; ++ii) { + const ListInstruction &instr = lmd->instructions()[ii]; + + switch(instr.type) { + case ListInstruction::Push: + { + ModelNode *n = nodes.top(); + ModelNode *n2 = new ModelNode; + n->values << qVariantFromValue(n2); + nodes.push(n2); + if (processingSet) + n->isArray = true; + } + break; + + case ListInstruction::Pop: + nodes.pop(); + break; + + case ListInstruction::Value: + { + ModelNode *n = nodes.top(); + 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; + + case ListInstruction::Set: + { + ModelNode *n = nodes.top(); + 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) + +/*! + \qmlclass ListElement + \brief The ListElement element defines a data item in a ListModel. + + \sa ListModel +*/ +// ### FIXME +class QmlListElement : public QObject +{ +Q_OBJECT +}; +QML_DEFINE_TYPE(Qt,4,6,ListElement,QmlListElement) + +static void dump(ModelNode *node, int ind) +{ + QByteArray indentBa(ind * 4, ' '); + const char *indent = indentBa.constData(); + + for (int ii = 0; ii < node->values.count(); ++ii) { + ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(ii)); + if (subNode) { + qWarning().nospace() << indent << "Sub-node " << ii; + dump(subNode, ind + 1); + } else { + qWarning().nospace() << indent << "Sub-node " << ii << ": " << node->values.at(ii).toString(); + } + } + + for (QHash<QString, ModelNode *>::ConstIterator iter = node->properties.begin(); iter != node->properties.end(); ++iter) { + qWarning().nospace() << indent << "Property " << iter.key() << ':'; + dump(iter.value(), ind + 1); + } +} + +ModelNode::ModelNode() +: modelCache(0), objectCache(0), isArray(false) +{ +} + +ModelNode::~ModelNode() +{ + qDeleteAll(properties); + for (int ii = 0; ii < values.count(); ++ii) { + ModelNode *node = qvariant_cast<ModelNode *>(values.at(ii)); + if (node) { delete node; node = 0; } + } + if (modelCache) { modelCache->_root = 0/* ==this */; delete modelCache; modelCache = 0; } + if (objectCache) { delete objectCache; } +} + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlListElement) + +#include <qmllistmodel.moc> diff --git a/src/declarative/util/qmllistmodel_p.h b/src/declarative/util/qmllistmodel_p.h new file mode 100644 index 0000000..e18d3fd --- /dev/null +++ b/src/declarative/util/qmllistmodel_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** 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 QMLLISTMODEL_H +#define QMLLISTMODEL_H + +#include <qml.h> + +#include <QtCore/QObject> +#include <QtCore/QStringList> +#include <QtCore/QHash> +#include <QtCore/QList> +#include <QtCore/QVariant> +#include "../3rdparty/qlistmodelinterface_p.h" +#include <QtScript/qscriptvalue.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +struct ModelNode; +class Q_DECLARATIVE_EXPORT QmlListModel : public QListModelInterface +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + +public: + QmlListModel(QObject *parent=0); + ~QmlListModel(); + + virtual QList<int> roles() const; + virtual QString toString(int role) const; + virtual int count() const; + virtual QHash<int,QVariant> data(int index, const QList<int> &roles = (QList<int>())) const; + virtual QVariant data(int index, int role) const; + + Q_INVOKABLE void clear(); + Q_INVOKABLE void remove(int index); + Q_INVOKABLE void append(const QScriptValue&); + 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 setProperty(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void move(int from, int to, int count); + +Q_SIGNALS: + void countChanged(int); + +private: + QVariant valueForNode(ModelNode *) const; + mutable QStringList roleStrings; + friend class QmlListModelParser; + friend struct ModelNode; + + void checkRoles() const; + void addRole(const QString &) const; + mutable bool _rolesOk; + ModelNode *_root; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlListModel) + +QT_END_HEADER + +#endif // QMLLISTMODEL_H diff --git a/src/declarative/util/qmlnullablevalue_p_p.h b/src/declarative/util/qmlnullablevalue_p_p.h new file mode 100644 index 0000000..151a3d4 --- /dev/null +++ b/src/declarative/util/qmlnullablevalue_p_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** 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 QMLNULLABLEVALUE_P_H +#define QMLNULLABLEVALUE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +template<typename T> +struct QmlNullableValue +{ + QmlNullableValue() + : isNull(true), value(T()) {} + QmlNullableValue(const QmlNullableValue<T> &o) + : isNull(o.isNull), value(o.value) {} + QmlNullableValue(const T &t) + : isNull(false), value(t) {} + QmlNullableValue<T> &operator=(const T &t) + { isNull = false; value = t; return *this; } + QmlNullableValue<T> &operator=(const QmlNullableValue<T> &o) + { isNull = o.isNull; value = o.value; return *this; } + operator T() const { return value; } + + void invalidate() { isNull = true; } + bool isValid() const { return !isNull; } + bool isNull; + T value; +}; + +QT_END_NAMESPACE + +#endif // QMLNULLABLEVALUE_P_H diff --git a/src/declarative/util/qmlnumberformatter.cpp b/src/declarative/util/qmlnumberformatter.cpp new file mode 100644 index 0000000..073dc68 --- /dev/null +++ b/src/declarative/util/qmlnumberformatter.cpp @@ -0,0 +1,260 @@ +/**************************************************************************** +** +** 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 "qmlnumberformatter_p.h" + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +//TODO: set locale +// docs +// this is a wrapper around qnumberformat (test integration) +// if number or format haven't been explictly set, text should be an empty string + +class QmlNumberFormatterPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlNumberFormatter) +public: + QmlNumberFormatterPrivate() : locale(QLocale::system()), number(0), componentComplete(true) {} + + void updateText(); + + QLocale locale; + QString format; + QNumberFormat numberFormat; + QString text; + qreal number; + bool componentComplete; +}; +/*! + \qmlclass NumberFormatter + \brief The NumberFormatter allows you to control the format of a number string. + + The format property documentation has more details on how the format can be manipulated. + + In the following example, the text element will display the text "1,234.57". + \code + NumberFormatter { id: formatter; number: 1234.5678; format: "##,##0.##" } + Text { text: formatter.text } + \endcode + + */ +/*! + \internal + \class QmlNumberFormatter + \ingroup group_utility + \brief The QmlNumberFormatter class allows you to format a number to a particular string format/locale specific number format. +*/ + +QmlNumberFormatter::QmlNumberFormatter(QObject *parent) +: QObject(*(new QmlNumberFormatterPrivate), parent) +{ +} + +QmlNumberFormatter::~QmlNumberFormatter() +{ +} + +/*! + \qmlproperty string NumberFormatter::text + + The number in the specified format. + + If no format is specified the text will be empty. +*/ + +QString QmlNumberFormatter::text() const +{ + Q_D(const QmlNumberFormatter); + return d->text; +} + +/*! + \qmlproperty real NumberFormatter::number + + A single point precision number. (Doubles are not yet supported) + +*/ +qreal QmlNumberFormatter::number() const +{ + Q_D(const QmlNumberFormatter); + return d->number; +} + +/*! + \qmlproperty string NumberFormatter::format + + The particular format the number will adhere to during the conversion to text. + + The format syntax follows a style similar to the Unicode Standard (UTS35). + + The table below shows the characters, patterns that can be used in the format. + + \table + \header + \o Character + \o Meaning + \row + \o # + \o Any digit(s), zero shows as absent (for leading/trailing zeroes). + \row + \o 0 + \o Implicit digit. Zero will show in the case that the input number is too small. + \row + \o . + \o Decimal separator. Output decimal seperator will be dependant on system locale. + \row + \o , + \o Grouping separator. The number of digits (either #, or 0) between the grouping separator and the decimal (or the rightmost digit) will determine the groupingSize). + \row + \o other + \o Any other character will be taken as a string literal and placed directly into the output string. + \endtable + + Invalid formats will not guarantee a meaningful text output. + + \note Input numbers that are too long for the given format will be rounded dependent on precison based on the position of the decimal point. + + The following table illustrates the output text created by applying some examples of numeric formats to the formatter. + + \table + \header + \o Format + \o Number + \o Output + \row + \o ### + \o 123456 + \o 123456 + \row + \o 000 + \o 123456 + \o 123456 + \row + \o ###### + \o 1234 + \o 1234 + \row + \o 000000 + \o 1234 + \o 001234 + \row + \o ##,##0.## + \o 1234.456 + \o 1,234.46 (for US locale) + \codeline 1 234,46 (for FR locale) + \row + \o 000000,000.# + \o 123456 + \o 000,123,456 (for US locale) + \codeline 000 123 456 (for FR locale) + \row + \o 0.0### + \o 0.999997 + \o 1.0 + \row + \o (000) 000 - 000 + \o 12345678 + \o (012) 345 - 678 + \row + \o #A + \o 12 + \o 12A + \endtable + +*/ +QString QmlNumberFormatter::format() const +{ + Q_D(const QmlNumberFormatter); + return d->format; +} + +void QmlNumberFormatter::setNumber(const qreal &number) +{ + Q_D(QmlNumberFormatter); + if (d->number == number) + return; + d->number = number; + d->updateText(); +} + +void QmlNumberFormatter::setFormat(const QString &format) +{ + Q_D(QmlNumberFormatter); + //no format checking + if (format.isEmpty()) + d->format = QString::null; + else + d->format = format; + d->updateText(); +} + +void QmlNumberFormatterPrivate::updateText() +{ + Q_Q(QmlNumberFormatter); + if (!componentComplete) + return; + + QNumberFormat tempFormat; + tempFormat.setFormat(format); + tempFormat.setNumber(number); + + text = tempFormat.text(); + + emit q->textChanged(); +} + +void QmlNumberFormatter::classBegin() +{ + Q_D(QmlNumberFormatter); + d->componentComplete = false; +} + +void QmlNumberFormatter::componentComplete() +{ + Q_D(QmlNumberFormatter); + d->componentComplete = true; + d->updateText(); +} +QML_DEFINE_TYPE(Qt,4,6,NumberFormatter,QmlNumberFormatter); + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlnumberformatter_p.h b/src/declarative/util/qmlnumberformatter_p.h new file mode 100644 index 0000000..71fceb2 --- /dev/null +++ b/src/declarative/util/qmlnumberformatter_p.h @@ -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$ +** +****************************************************************************/ + +#ifndef QMLNUMBERFORMATTER_H +#define QMLNUMBERFORMATTER_H + +#include "qnumberformat_p.h" + +#include <qml.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlNumberFormatterPrivate; +class Q_DECLARATIVE_EXPORT QmlNumberFormatter : public QObject, public QmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QmlParserStatus) + + Q_PROPERTY(QString text READ text NOTIFY textChanged) + Q_PROPERTY(QString format READ format WRITE setFormat) + Q_PROPERTY(qreal number READ number WRITE setNumber) +public: + QmlNumberFormatter(QObject *parent=0); + ~QmlNumberFormatter(); + + QString text() const; + + qreal number() const; + void setNumber(const qreal &); + + QString format() const; + void setFormat(const QString &); + + virtual void classBegin(); + virtual void componentComplete(); + +Q_SIGNALS: + void textChanged(); + +private: + Q_DISABLE_COPY(QmlNumberFormatter) + Q_DECLARE_PRIVATE(QmlNumberFormatter) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlNumberFormatter) + +QT_END_HEADER + +#endif diff --git a/src/declarative/util/qmlopenmetaobject.cpp b/src/declarative/util/qmlopenmetaobject.cpp new file mode 100644 index 0000000..1be81de --- /dev/null +++ b/src/declarative/util/qmlopenmetaobject.cpp @@ -0,0 +1,355 @@ +/**************************************************************************** +** +** 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 "qmlopenmetaobject_p.h" +#include "qmlpropertycache_p.h" +#include "qmldeclarativedata_p.h" +#include <qmetaobjectbuilder_p.h> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + + +class QmlOpenMetaObjectTypePrivate +{ +public: + QmlOpenMetaObjectTypePrivate() : mem(0), cache(0), engine(0) {} + + void init(const QMetaObject *metaObj); + + int propertyOffset; + int signalOffset; + QHash<QByteArray, int> names; + QMetaObjectBuilder mob; + QMetaObject *mem; + QmlPropertyCache *cache; + QmlEngine *engine; + QSet<QmlOpenMetaObject*> referers; +}; + +QmlOpenMetaObjectType::QmlOpenMetaObjectType(const QMetaObject *base, QmlEngine *engine) + : d(new QmlOpenMetaObjectTypePrivate) +{ + d->engine = engine; + d->init(base); +} + +QmlOpenMetaObjectType::~QmlOpenMetaObjectType() +{ + if (d->mem) + qFree(d->mem); + if (d->cache) + d->cache->release(); + delete d; +} + + +int QmlOpenMetaObjectType::propertyOffset() const +{ + return d->propertyOffset; +} + +int QmlOpenMetaObjectType::signalOffset() const +{ + return d->signalOffset; +} + +int QmlOpenMetaObjectType::createProperty(const QByteArray &name) +{ + int id = d->mob.propertyCount(); + d->mob.addSignal("__" + QByteArray::number(id) + "()"); + QMetaPropertyBuilder build = d->mob.addProperty(name, "QVariant", id); + build.setDynamic(true); + propertyCreated(id, build); + qFree(d->mem); + d->mem = d->mob.toMetaObject(); + d->names.insert(name, id); + QSet<QmlOpenMetaObject*>::iterator it = d->referers.begin(); + while (it != d->referers.end()) { + QmlOpenMetaObject *omo = *it; + *static_cast<QMetaObject *>(omo) = *d->mem; + if (d->cache) + d->cache->update(d->engine, omo); + ++it; + } + + return d->propertyOffset + id; +} + +void QmlOpenMetaObjectType::propertyCreated(int id, QMetaPropertyBuilder &builder) +{ + if (d->referers.count()) + (*d->referers.begin())->propertyCreated(id, builder); +} + +void QmlOpenMetaObjectTypePrivate::init(const QMetaObject *metaObj) +{ + if (!mem) { + mob.setSuperClass(metaObj); + mob.setClassName(metaObj->className()); + mob.setFlags(QMetaObjectBuilder::DynamicMetaObject); + + mem = mob.toMetaObject(); + + propertyOffset = mem->propertyOffset(); + signalOffset = mem->methodOffset(); + } +} + +//---------------------------------------------------------------------------- + +class QmlOpenMetaObjectPrivate +{ +public: + QmlOpenMetaObjectPrivate(QmlOpenMetaObject *_q) + : q(_q), parent(0), type(0), cacheProperties(false) {} + + inline QVariant &getData(int idx) { + while (data.count() <= idx) + data << QPair<QVariant, bool>(QVariant(), false); + QPair<QVariant, bool> &prop = data[idx]; + if (!prop.second) { + prop.first = q->initialValue(idx); + prop.second = true; + } + return prop.first; + } + + inline void writeData(int idx, const QVariant &value) { + while (data.count() <= idx) + data << QPair<QVariant, bool>(QVariant(), false); + QPair<QVariant, bool> &prop = data[idx]; + prop.first = value; + prop.second = true; + } + + bool autoCreate; + QmlOpenMetaObject *q; + QAbstractDynamicMetaObject *parent; + QList<QPair<QVariant, bool> > data; + QObject *object; + QmlOpenMetaObjectType *type; + bool cacheProperties; +}; + +QmlOpenMetaObject::QmlOpenMetaObject(QObject *obj, bool automatic) +: d(new QmlOpenMetaObjectPrivate(this)) +{ + d->autoCreate = automatic; + d->object = obj; + + d->type = new QmlOpenMetaObjectType(obj->metaObject(), 0); + d->type->d->referers.insert(this); + + QObjectPrivate *op = QObjectPrivate::get(obj); + *static_cast<QMetaObject *>(this) = *d->type->d->mem; + op->metaObject = this; +} + +QmlOpenMetaObject::QmlOpenMetaObject(QObject *obj, QmlOpenMetaObjectType *type, bool automatic) +: d(new QmlOpenMetaObjectPrivate(this)) +{ + d->autoCreate = automatic; + d->object = obj; + + d->type = type; + d->type->addref(); + d->type->d->referers.insert(this); + + QObjectPrivate *op = QObjectPrivate::get(obj); + *static_cast<QMetaObject *>(this) = *d->type->d->mem; + op->metaObject = this; +} + +QmlOpenMetaObject::~QmlOpenMetaObject() +{ + if (d->parent) + delete d->parent; + d->type->d->referers.remove(this); + d->type->release(); + delete d; +} + +QmlOpenMetaObjectType *QmlOpenMetaObject::type() const +{ + return d->type; +} + +int QmlOpenMetaObject::metaCall(QMetaObject::Call c, int id, void **a) +{ + if (( c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty) + && id >= d->type->d->propertyOffset) { + int propId = id - d->type->d->propertyOffset; + if (c == QMetaObject::ReadProperty) { + propertyRead(propId); + *reinterpret_cast<QVariant *>(a[0]) = d->getData(propId); + } else if (c == QMetaObject::WriteProperty) { + if (d->data[propId].first != *reinterpret_cast<QVariant *>(a[0])) { + propertyWrite(propId); + d->writeData(propId, *reinterpret_cast<QVariant *>(a[0])); + activate(d->object, d->type->d->signalOffset + propId, 0); + } + } + return -1; + } else { + if (d->parent) + return d->parent->metaCall(c, id, a); + else + return d->object->qt_metacall(c, id, a); + } +} + +QAbstractDynamicMetaObject *QmlOpenMetaObject::parent() const +{ + return d->parent; +} + +QVariant QmlOpenMetaObject::value(int id) const +{ + return d->getData(id); +} + +void QmlOpenMetaObject::setValue(int id, const QVariant &value) +{ + d->writeData(id, value); + activate(d->object, id + d->type->d->signalOffset, 0); +} + +QVariant QmlOpenMetaObject::value(const QByteArray &name) const +{ + QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.find(name); + if (iter == d->type->d->names.end()) + return QVariant(); + + return d->getData(*iter); +} + +QVariant &QmlOpenMetaObject::operator[](const QByteArray &name) +{ + QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.find(name); + Q_ASSERT(iter != d->type->d->names.end()); + + return d->getData(*iter); +} + +void QmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val) +{ + QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.find(name); + + int id = -1; + if (iter == d->type->d->names.end()) { + id = d->type->createProperty(name.constData()) - d->type->d->propertyOffset; + } else { + id = *iter; + } + + QVariant &dataVal = d->getData(id); + if (dataVal == val) + return; + + dataVal = val; + activate(d->object, id + d->type->d->signalOffset, 0); +} + +void QmlOpenMetaObject::setCached(bool c) +{ + if (c == d->cacheProperties || !d->type->d->engine) + return; + + d->cacheProperties = c; + + QmlDeclarativeData *qmldata = QmlDeclarativeData::get(d->object, true); + if (d->cacheProperties) { + if (!d->type->d->cache) + d->type->d->cache = QmlPropertyCache::create(d->type->d->engine, this); + qmldata->propertyCache = d->type->d->cache; + d->type->d->cache->addref(); + } else { + if (d->type->d->cache) + d->type->d->cache->release(); + qmldata->propertyCache = 0; + } +} + + +int QmlOpenMetaObject::createProperty(const char *name, const char *) +{ + if (d->autoCreate) + return d->type->createProperty(name); + else + return -1; +} + +void QmlOpenMetaObject::propertyRead(int) +{ +} + +void QmlOpenMetaObject::propertyWrite(int) +{ +} + +void QmlOpenMetaObject::propertyCreated(int, QMetaPropertyBuilder &) +{ +} + +QVariant QmlOpenMetaObject::initialValue(int) +{ + return QVariant(); +} + +int QmlOpenMetaObject::count() const +{ + return d->type->d->names.count(); +} + +QByteArray QmlOpenMetaObject::name(int idx) const +{ + Q_ASSERT(idx >= 0 && idx < d->type->d->names.count()); + + return d->type->d->mob.property(idx).name(); +} + +QObject *QmlOpenMetaObject::object() const +{ + return d->object; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlopenmetaobject_p.h b/src/declarative/util/qmlopenmetaobject_p.h new file mode 100644 index 0000000..c6da71a --- /dev/null +++ b/src/declarative/util/qmlopenmetaobject_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** 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 QMLOPENMETAOBJECT_H +#define QMLOPENMETAOBJECT_H + +#include <private/qmlrefcount_p.h> +#include <QtCore/QMetaObject> +#include <QtCore/QObject> + +#include <private/qobject_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlEngine; +class QMetaPropertyBuilder; +class QmlOpenMetaObjectTypePrivate; +class Q_DECLARATIVE_EXPORT QmlOpenMetaObjectType : public QmlRefCount +{ +public: + QmlOpenMetaObjectType(const QMetaObject *base, QmlEngine *engine); + ~QmlOpenMetaObjectType(); + + int createProperty(const QByteArray &name); + + int propertyOffset() const; + int signalOffset() const; + +protected: + virtual void propertyCreated(int, QMetaPropertyBuilder &); + +private: + QmlOpenMetaObjectTypePrivate *d; + friend class QmlOpenMetaObject; + friend class QmlOpenMetaObjectPrivate; +}; + +class QmlOpenMetaObjectPrivate; +class Q_DECLARATIVE_EXPORT QmlOpenMetaObject : public QAbstractDynamicMetaObject +{ +public: + QmlOpenMetaObject(QObject *, bool = true); + QmlOpenMetaObject(QObject *, QmlOpenMetaObjectType *, bool = true); + ~QmlOpenMetaObject(); + + QVariant value(const QByteArray &) const; + void setValue(const QByteArray &, const QVariant &); + QVariant value(int) const; + void setValue(int, const QVariant &); + QVariant &operator[](const QByteArray &); + + int count() const; + QByteArray name(int) const; + + QObject *object() const; + virtual QVariant initialValue(int); + + // Be careful - once setCached(true) is called createProperty() is no + // longer automatically called for new properties. + void setCached(bool); + + QmlOpenMetaObjectType *type() const; + +protected: + virtual int metaCall(QMetaObject::Call _c, int _id, void **_a); + virtual int createProperty(const char *, const char *); + + virtual void propertyRead(int); + virtual void propertyWrite(int); + virtual void propertyCreated(int, QMetaPropertyBuilder &); + + QAbstractDynamicMetaObject *parent() const; + +private: + QmlOpenMetaObjectPrivate *d; + friend class QmlOpenMetaObjectType; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLOPENMETAOBJECT_H diff --git a/src/declarative/util/qmlpackage.cpp b/src/declarative/util/qmlpackage.cpp new file mode 100644 index 0000000..3214dc8 --- /dev/null +++ b/src/declarative/util/qmlpackage.cpp @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** 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 "qmlpackage_p.h" + +#include <private/qobject_p.h> +#include "private/qmlguard_p.h" + +QT_BEGIN_NAMESPACE + +class QmlPackagePrivate : public QObjectPrivate +{ +public: + QmlPackagePrivate() {} + + 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 +{ +Q_OBJECT +Q_PROPERTY(QString name READ name WRITE setName) +public: + QmlPackageAttached(QObject *parent); + virtual ~QmlPackageAttached(); + + QString name() const; + void setName(const QString &n); + + static QHash<QObject *, QmlPackageAttached *> attached; +private: + QString _name; +}; + +QHash<QObject *, QmlPackageAttached *> QmlPackageAttached::attached; + +QmlPackageAttached::QmlPackageAttached(QObject *parent) +: QObject(parent) +{ + attached.insert(parent, this); +} + +QmlPackageAttached::~QmlPackageAttached() +{ + attached.remove(parent()); +} + +QString QmlPackageAttached::name() const +{ + return _name; +} + +void QmlPackageAttached::setName(const QString &n) +{ + _name = n; +} + +QmlPackage::QmlPackage(QObject *parent) + : QObject(*(new QmlPackagePrivate), parent) +{ +} + +QmlPackage::~QmlPackage() +{ + Q_D(QmlPackage); + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + obj->setParent(this); + } +} + +QmlList<QObject *> *QmlPackage::data() +{ + Q_D(QmlPackage); + return &d->dataList; +} + +bool QmlPackage::hasPart(const QString &name) +{ + Q_D(QmlPackage); + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QmlPackageAttached *a = QmlPackageAttached::attached.value(obj); + if (a && a->name() == name) + return true; + } + return false; +} + +QObject *QmlPackage::part(const QString &name) +{ + Q_D(QmlPackage); + if (name.isEmpty() && !d->dataList.isEmpty()) + return d->dataList.at(0); + + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QmlPackageAttached *a = QmlPackageAttached::attached.value(obj); + if (a && a->name() == name) + return obj; + } + + if (name == QLatin1String("default") && !d->dataList.isEmpty()) + return d->dataList.at(0); + + return 0; +} + +QmlPackageAttached *QmlPackage::qmlAttachedProperties(QObject *o) +{ + return new QmlPackageAttached(o); +} + +QML_DEFINE_TYPE(Qt,4,6,Package,QmlPackage) + +QT_END_NAMESPACE + +#include <qmlpackage.moc> diff --git a/src/declarative/util/qmlpackage_p.h b/src/declarative/util/qmlpackage_p.h new file mode 100644 index 0000000..ff42aad --- /dev/null +++ b/src/declarative/util/qmlpackage_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** 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 QMLPACKAGE_H +#define QMLPACKAGE_H + +#include <qml.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +/***************************************************************************** + ***************************************************************************** + XXX Experimental + ***************************************************************************** +*****************************************************************************/ + +class QmlPackagePrivate; +class QmlPackageAttached; +class QmlPackage : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlPackage) + + Q_CLASSINFO("DefaultProperty", "data") + Q_PROPERTY(QmlList<QObject *> *data READ data SCRIPTABLE false) + +public: + QmlPackage(QObject *parent=0); + virtual ~QmlPackage(); + + QmlList<QObject *> *data(); + + QObject *part(const QString & = QString()); + bool hasPart(const QString &); + + static QmlPackageAttached *qmlAttachedProperties(QObject *); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlPackage) +QML_DECLARE_TYPEINFO(QmlPackage, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER + +#endif // QMLPACKAGE_H diff --git a/src/declarative/util/qmlpixmapcache.cpp b/src/declarative/util/qmlpixmapcache.cpp new file mode 100644 index 0000000..c03b5df --- /dev/null +++ b/src/declarative/util/qmlpixmapcache.cpp @@ -0,0 +1,605 @@ +/**************************************************************************** +** +** 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 "qmlpixmapcache_p.h" +#include "qmlnetworkaccessmanagerfactory.h" + +#include "qfxperf_p_p.h" + +#include <qmlengine.h> +#include <private/qmlglobal_p.h> + +#include <QCoreApplication> +#include <QImageReader> +#include <QHash> +#include <QNetworkReply> +#include <QPixmapCache> +#include <QFile> +#include <QThread> +#include <QMutex> +#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 + +#if (QT_VERSION < QT_VERSION_CHECK(4, 7, 0)) +inline uint qHash(const QUrl &uri) +{ + return qHash(uri.toEncoded(QUrl::FormattingOption(0x100))); +} +#endif + +class QmlImageReaderEvent : public QEvent +{ +public: + enum ReadError { NoError, Loading, Decoding }; + + QmlImageReaderEvent(QmlImageReaderEvent::ReadError err, const QString &errStr, QImage &img) + : QEvent(QEvent::User), error(err), errorString(errStr), image(img) {} + + ReadError error; + QString errorString; + QImage image; +}; + +class QmlImageRequestHandler; +class QmlImageReader : public QThread +{ + Q_OBJECT +public: + QmlImageReader(QmlEngine *eng); + ~QmlImageReader(); + + QmlPixmapReply *getImage(const QUrl &url); + void cancel(QmlPixmapReply *rep); + + static QmlImageReader *instance(QmlEngine *engine); + +protected: + void run(); + +private: + QList<QmlPixmapReply*> jobs; + QList<QmlPixmapReply*> cancelled; + QmlEngine *engine; + QmlImageRequestHandler *handler; + QMutex mutex; + + static QHash<QmlEngine *,QmlImageReader*> readers; + static QMutex readerMutex; + friend class QmlImageRequestHandler; +}; + +QHash<QmlEngine *,QmlImageReader*> QmlImageReader::readers; +QMutex QmlImageReader::readerMutex; + + +class QmlImageRequestHandler : public QObject +{ + Q_OBJECT +public: + QmlImageRequestHandler(QmlImageReader *read, QmlEngine *eng) + : QObject(), accessManager(0), engine(eng), reader(read) + { + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); + } + + QmlPixmapReply *getImage(const QUrl &url); + void cancel(QmlPixmapReply *reply); + +protected: + bool event(QEvent *event); + +private slots: + void networkRequestDone(); + +private: + QNetworkAccessManager *networkAccessManager() { + if (!accessManager) { + if (engine && engine->networkAccessManagerFactory()) { + accessManager = engine->networkAccessManagerFactory()->create(this); + } else { + accessManager = new QNetworkAccessManager(this); + } + } + return accessManager; + } + + QHash<QNetworkReply*,QmlPixmapReply*> replies; + QNetworkAccessManager *accessManager; + QmlEngine *engine; + QmlImageReader *reader; +}; + +//=========================================================================== + +bool QmlImageRequestHandler::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 = QmlImageRequestHandler::staticMetaObject.indexOfSlot("networkRequestDone()"); + } + + while (1) { + reader->mutex.lock(); + + if (reader->cancelled.count()) { + for (int i = 0; i < reader->cancelled.count(); ++i) { + QmlPixmapReply *job = reader->cancelled.at(i); + QNetworkReply *reply = replies.key(job, 0); + if (reply && reply->isRunning()) { + replies.remove(reply); + reply->close(); + job->release(true); + } + } + reader->cancelled.clear(); + } + + if (!reader->jobs.count() || replies.count() > maxImageRequestCount) { + reader->mutex.unlock(); + break; + } + + QmlPixmapReply *runningJob = reader->jobs.takeFirst(); + runningJob->addRef(); + runningJob->setLoading(); + QUrl url = runningJob->url(); + reader->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 QmlImageRequestHandler::networkRequestDone() +{ + QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); + QmlPixmapReply *job = replies.take(reply); + if (job) { + QImage image; + QmlImageReaderEvent::ReadError error; + QString errorString; + if (reply->error()) { + error = QmlImageReaderEvent::Loading; + errorString = reply->errorString(); + } else { + QImageReader imgio(reply); + if (imgio.read(&image)) { + error = QmlImageReaderEvent::NoError; + } else { + errorString = QLatin1String("Error decoding: ") + reply->url().toString() + + QLatin1String(" \"") + imgio.errorString() + QLatin1String("\""); + error = QmlImageReaderEvent::Decoding; + } + } + // send completion event to the QmlPixmapReply + QCoreApplication::postEvent(job, new QmlImageReaderEvent(error, errorString, 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(); +} + +//=========================================================================== + +QmlImageReader::QmlImageReader(QmlEngine *eng) + : QThread(eng), engine(eng), handler(0) +{ + start(QThread::LowPriority); +} + +QmlImageReader::~QmlImageReader() +{ + quit(); + wait(); + readerMutex.lock(); + readers.remove(engine); + readerMutex.unlock(); + delete handler; +} + +QmlImageReader *QmlImageReader::instance(QmlEngine *engine) +{ + readerMutex.lock(); + QmlImageReader *reader = readers.value(engine); + if (!reader) { + reader = new QmlImageReader(engine); + readers.insert(engine, reader); + } + readerMutex.unlock(); + + return reader; +} + +QmlPixmapReply *QmlImageReader::getImage(const QUrl &url) +{ + mutex.lock(); + QmlPixmapReply *reply = new QmlPixmapReply(this, url); + jobs.append(reply); + if (jobs.count() == 1 && handler) + QCoreApplication::postEvent(handler, new QEvent(QEvent::User)); + mutex.unlock(); + return reply; +} + +void QmlImageReader::cancel(QmlPixmapReply *reply) +{ + mutex.lock(); + if (reply->isLoading()) { + // Already requested. Add to cancel list to be cancelled in reader thread. + cancelled.append(reply); + if (cancelled.count() == 1 && handler) + QCoreApplication::postEvent(handler, 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; + } + } + mutex.unlock(); +} + +void QmlImageReader::run() +{ +#if defined(Q_OS_LINUX) && defined(SCHED_IDLE) + struct sched_param param; + int policy; + + pthread_getschedparam(pthread_self(), &policy, ¶m); + pthread_setschedparam(pthread_self(), SCHED_IDLE, ¶m); +#endif + + handler = new QmlImageRequestHandler(this, engine); + + exec(); +} + +//=========================================================================== + +static bool readImage(QIODevice *dev, QPixmap *pixmap, QString &errorString) +{ + QImageReader imgio(dev); + +//#define QT_TEST_SCALED_SIZE +#ifdef QT_TEST_SCALED_SIZE + /* + Some mechanism is needed for loading images at a limited size, especially + for remote images. Loading only thumbnails of remote progressive JPEG + images can be efficient. (Qt jpeg handler does not do so currently) + */ + + QSize limit(60,60); + QSize sz = imgio.size(); + if (sz.width() > limit.width() || sz.height() > limit.height()) { + sz.scale(limit,Qt::KeepAspectRatio); + imgio.setScaledSize(sz); + } +#endif + + QImage img; + if (imgio.read(&img)) { +#ifdef QT_TEST_SCALED_SIZE + if (!sz.isValid()) + img = img.scaled(limit,Qt::KeepAspectRatio); +#endif + *pixmap = QPixmap::fromImage(img); + return true; + } else { + errorString = imgio.errorString(); + return false; + } +} + +/*! + \internal + \class QmlPixmapCache + \brief Enacapsultes a pixmap for QmlGraphics items. + + This class is NOT reentrant. + */ + +static QString toLocalFileOrQrc(const QUrl& url) +{ + QString r = url.toLocalFile(); + if (r.isEmpty() && url.scheme() == QLatin1String("qrc")) + r = QLatin1Char(':') + url.path(); + return r; +} + +typedef QHash<QUrl, QmlPixmapReply *> QmlPixmapReplyHash; +Q_GLOBAL_STATIC(QmlPixmapReplyHash, qmlActivePixmapReplies); + +class QmlPixmapReplyPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlPixmapReply) + +public: + QmlPixmapReplyPrivate(QmlImageReader *r, const QUrl &u) + : QObjectPrivate(), refCount(1), url(u), status(QmlPixmapReply::Loading), loading(false), reader(r) { + } + + int refCount; + QUrl url; + QPixmap pixmap; // ensure reference to pixmap so QPixmapCache does not discard + QmlPixmapReply::Status status; + bool loading; + QmlImageReader *reader; +}; + + +QmlPixmapReply::QmlPixmapReply(QmlImageReader *reader, const QUrl &url) + : QObject(*new QmlPixmapReplyPrivate(reader, url), 0) +{ +} + +QmlPixmapReply::~QmlPixmapReply() +{ +} + +const QUrl &QmlPixmapReply::url() const +{ + 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)) { + QmlImageReaderEvent *de = static_cast<QmlImageReaderEvent*>(event); + d->status = (de->error == QmlImageReaderEvent::NoError) ? Ready : Error; + if (d->status == Ready) + d->pixmap = QPixmap::fromImage(de->image); + else + qWarning() << de->errorString; + 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; + } + + return QObject::event(event); +} + +QmlPixmapReply::Status QmlPixmapReply::status() const +{ + Q_D(const QmlPixmapReply); + return d->status; +} + +bool QmlPixmapReply::isLoading() const +{ + Q_D(const QmlPixmapReply); + return d->loading; +} + +void QmlPixmapReply::setLoading() +{ + Q_D(QmlPixmapReply); + d->loading = true; +} + +void QmlPixmapReply::addRef() +{ + Q_D(QmlPixmapReply); + ++d->refCount; +} + +bool QmlPixmapReply::release(bool defer) +{ + Q_D(QmlPixmapReply); + Q_ASSERT(d->refCount > 0); + --d->refCount; + if (d->refCount == 0) { + qmlActivePixmapReplies()->remove(d->url); + if (d->status == Loading && !d->loading) + d->reader->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); + d->reader->cancel(this); + } + + return false; +} + +/*! + Finds the cached pixmap corresponding to \a url. + If the image is a network resource and has not yet + been retrieved and cached, request() must be called. + + Returns Ready, or Error if the image has been retrieved, + otherwise the current retrieval status. +*/ +QmlPixmapReply::Status QmlPixmapCache::get(const QUrl& url, QPixmap *pixmap) +{ + QmlPixmapReply::Status status = QmlPixmapReply::Unrequested; + +#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML + QString lf = toLocalFileOrQrc(url); + if (!lf.isEmpty()) { + status = QmlPixmapReply::Ready; + if (!QPixmapCache::find(lf,pixmap)) { + QFile f(lf); + if (f.open(QIODevice::ReadOnly)) { + QString errorString; + if (!readImage(&f, pixmap, errorString)) { + errorString = QLatin1String("Error decoding: ") + url.toString() + + QLatin1String(" \"") + errorString + QLatin1String("\""); + qWarning() << errorString; + *pixmap = QPixmap(); + status = QmlPixmapReply::Error; + } + } else { + qWarning() << "Cannot open" << url; + *pixmap = QPixmap(); + status = QmlPixmapReply::Error; + } + if (status == QmlPixmapReply::Ready) + QPixmapCache::insert(lf, *pixmap); + } + return status; + } +#endif + + 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(strKey, pixmap)) { + if (iter != qmlActivePixmapReplies()->end()) { + status = (*iter)->status(); + (*iter)->release(); + } else { + status = pixmap->isNull() ? QmlPixmapReply::Error : QmlPixmapReply::Ready; + } + } else if (iter != qmlActivePixmapReplies()->end()) { + status = QmlPixmapReply::Loading; + } + + return status; +} + +/*! + Starts a network request to load \a url. + + Returns a QmlPixmapReply. Caller should connect to QmlPixmapReply::finished() + and call get() when the image is available. + + The returned QmlPixmapReply will be deleted when all request() calls are + matched by a corresponding get() call. +*/ +QmlPixmapReply *QmlPixmapCache::request(QmlEngine *engine, const QUrl &url) +{ + 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(); + } + + return (*iter); +} + +/*! + Cancels a previous call to request(). + + May also cancel loading (eg. if no other pending request). + + Any connections from the QmlPixmapReply returned by request() to \a obj will be + disconnected. +*/ +void QmlPixmapCache::cancel(const QUrl& url, QObject *obj) +{ + QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies()->find(url); + if (iter == qmlActivePixmapReplies()->end()) + return; + + QmlPixmapReply *reply = *iter; + if (obj) + QObject::disconnect(reply, 0, obj, 0); + reply->release(); +} + +/*! + This function is mainly for test verification. It returns the number of + requests that are still unfinished. +*/ +int QmlPixmapCache::pendingRequests() +{ + return qmlActivePixmapReplies()->count(); +} + +#include <qmlpixmapcache.moc> + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlpixmapcache_p.h b/src/declarative/util/qmlpixmapcache_p.h new file mode 100644 index 0000000..462faf6 --- /dev/null +++ b/src/declarative/util/qmlpixmapcache_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** 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 QMLPIXMAPCACHE_H +#define QMLPIXMAPCACHE_H + +#include <QtCore/QString> +#include <QtGui/QPixmap> +#include <QtCore/qurl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlEngine; +class QNetworkReply; +class QmlImageReader; + +class QmlPixmapReplyPrivate; +class Q_DECLARATIVE_EXPORT QmlPixmapReply : public QObject +{ + Q_OBJECT +public: + ~QmlPixmapReply(); + + enum Status { Ready, Error, Unrequested, Loading }; + Status status() const; + + const QUrl &url() const; + +Q_SIGNALS: + void finished(); + void downloadProgress(qint64, qint64); + +protected: + bool event(QEvent *event); + +private: + void addRef(); + bool release(bool defer=false); + bool isLoading() const; + void setLoading(); + +private: + QmlPixmapReply(QmlImageReader *reader, const QUrl &url); + Q_DISABLE_COPY(QmlPixmapReply) + Q_DECLARE_PRIVATE(QmlPixmapReply) + friend class QmlImageRequestHandler; + friend class QmlImageReader; + friend class QmlPixmapCache; +}; + +class Q_DECLARATIVE_EXPORT QmlPixmapCache +{ +public: + static QmlPixmapReply::Status get(const QUrl& url, QPixmap *pixmap); + static QmlPixmapReply *request(QmlEngine *, const QUrl& url); + static void cancel(const QUrl& url, QObject *obj); + static int pendingRequests(); +}; + + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLPIXMAPCACHE_H diff --git a/src/declarative/util/qmlpropertychanges.cpp b/src/declarative/util/qmlpropertychanges.cpp new file mode 100644 index 0000000..68fc5cc --- /dev/null +++ b/src/declarative/util/qmlpropertychanges.cpp @@ -0,0 +1,468 @@ +/**************************************************************************** +** +** 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 "qmlpropertychanges_p.h" + +#include "qmlopenmetaobject_p.h" + +#include <qmlinfo.h> +#include <qmlcustomparser_p.h> +#include <qmlparser_p.h> +#include <qmlexpression.h> +#include <qmlbinding.h> +#include <qmlcontext.h> +#include <qmlguard_p.h> + +#include <QtCore/qdebug.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass PropertyChanges QmlPropertyChanges + \brief The PropertyChanges element describes new property values for a state. + + PropertyChanges provides a state change that modifies the properties of an item. + + Here is a property change that modifies the text and color of a Text element + when it is clicked: + + \qml + Text { + id: myText + width: 100; height: 100 + text: "Hello" + color: "blue" + + states: State { + name: "myState" + + PropertyChanges { + target: myText + text: "Goodbye" + color: "red" + } + } + + MouseRegion { anchors.fill: parent; onClicked: myText.state = 'myState' } + } + \endqml + + State-specific script for signal handlers can also be specified: + + \qml + PropertyChanges { + target: myMouseRegion + onClicked: doSomethingDifferent() + } + \endqml + + Changes to an Item's parent or anchors should be done using the associated change elements + (ParentChange and AnchorChanges, respectively) rather than PropertyChanges. + + \sa {qmlstate}{States} +*/ + +/*! + \internal + \class QmlPropertyChanges + \brief The QmlPropertyChanges class describes new property values for a state. +*/ + +/*! + \qmlproperty Object PropertyChanges::target + This property holds the object which contains the properties to be changed. +*/ + +class QmlReplaceSignalHandler : public QmlActionEvent +{ +public: + QmlReplaceSignalHandler() : expression(0), reverseExpression(0), + rewindExpression(0), ownedExpression(0) {} + ~QmlReplaceSignalHandler() { + delete ownedExpression; + } + + virtual QString typeName() const { return QLatin1String("ReplaceSignalHandler"); } + + QmlMetaProperty property; + QmlExpression *expression; + QmlExpression *reverseExpression; + QmlExpression *rewindExpression; + QmlGuard<QmlExpression> ownedExpression; + + virtual void execute() { + ownedExpression = property.setSignalExpression(expression); + } + + virtual bool isReversable() { return true; } + virtual void reverse() { + ownedExpression = property.setSignalExpression(reverseExpression); + } + + virtual void saveOriginals() { + saveCurrentValues(); + reverseExpression = rewindExpression; + } + + virtual void rewind() { + ownedExpression = property.setSignalExpression(rewindExpression); + } + virtual void saveCurrentValues() { rewindExpression = property.signalExpression(); } + + virtual bool override(QmlActionEvent*other) { + if (other == this) + return true; + if (other->typeName() != typeName()) + return false; + if (static_cast<QmlReplaceSignalHandler*>(other)->property == property) + return true; + return false; + } +}; + + +class QmlPropertyChangesPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlPropertyChanges) +public: + QmlPropertyChangesPrivate() : object(0), decoded(true), restore(true), + isExplicit(false) {} + + QObject *object; + QByteArray data; + + bool decoded : 1; + bool restore : 1; + bool isExplicit : 1; + + void decode(); + + QList<QPair<QByteArray, QVariant> > properties; + QList<QPair<QByteArray, QmlExpression *> > expressions; + QList<QmlReplaceSignalHandler*> signalReplacements; + + QmlMetaProperty property(const QByteArray &); +}; + +class QmlPropertyChangesParser : public QmlCustomParser +{ +public: + void compileList(QList<QPair<QByteArray, QVariant> > &list, const QByteArray &pre, const QmlCustomParserProperty &prop); + + virtual QByteArray compile(const QList<QmlCustomParserProperty> &); + virtual void setCustomData(QObject *, const QByteArray &); +}; + +void +QmlPropertyChangesParser::compileList(QList<QPair<QByteArray, QVariant> > &list, + const QByteArray &pre, + const QmlCustomParserProperty &prop) +{ + QByteArray propName = pre + prop.name(); + + QList<QVariant> values = prop.assignedValues(); + for (int ii = 0; ii < values.count(); ++ii) { + const QVariant &value = values.at(ii); + + if (value.userType() == qMetaTypeId<QmlCustomParserNode>()) { + continue; + } else if(value.userType() == qMetaTypeId<QmlCustomParserProperty>()) { + + QmlCustomParserProperty prop = + qvariant_cast<QmlCustomParserProperty>(value); + QByteArray pre = propName + '.'; + compileList(list, pre, prop); + + } else { + list << qMakePair(propName, value); + } + } +} + +QByteArray +QmlPropertyChangesParser::compile(const QList<QmlCustomParserProperty> &props) +{ + QList<QPair<QByteArray, QVariant> > data; + for(int ii = 0; ii < props.count(); ++ii) + compileList(data, QByteArray(), props.at(ii)); + + QByteArray rv; + QDataStream ds(&rv, QIODevice::WriteOnly); + + ds << data.count(); + for(int ii = 0; ii < data.count(); ++ii) { + QmlParser::Variant v = qvariant_cast<QmlParser::Variant>(data.at(ii).second); + QVariant var; + bool isScript = v.isScript(); + switch(v.type()) { + case QmlParser::Variant::Boolean: + var = QVariant(v.asBoolean()); + break; + case QmlParser::Variant::Number: + var = QVariant(v.asNumber()); + break; + case QmlParser::Variant::String: + var = QVariant(v.asString()); + break; + case QmlParser::Variant::Invalid: + case QmlParser::Variant::Script: + var = QVariant(v.asScript()); + break; + } + + ds << data.at(ii).first << isScript << var; + } + + return rv; +} + +void QmlPropertyChangesPrivate::decode() +{ + Q_Q(QmlPropertyChanges); + if (decoded) + return; + + QDataStream ds(&data, QIODevice::ReadOnly); + + int count; + ds >> count; + for (int ii = 0; ii < count; ++ii) { + QByteArray name; + bool isScript; + QVariant data; + ds >> name; + ds >> isScript; + ds >> data; + + QmlMetaProperty prop = property(name); //### better way to check for signal property? + if (prop.type() & QmlMetaProperty::SignalProperty) { + QmlExpression *expression = new QmlExpression(qmlContext(q), data.toString(), object); + expression->setTrackChange(false); + QmlReplaceSignalHandler *handler = new QmlReplaceSignalHandler; + handler->property = prop; + handler->expression = expression; + signalReplacements << handler; + } else if (isScript) { + QmlExpression *expression = new QmlExpression(qmlContext(q), data.toString(), object); + expression->setTrackChange(false); + expressions << qMakePair(name, expression); + } else { + properties << qMakePair(name, data); + } + } + + decoded = true; + data.clear(); +} + +void QmlPropertyChangesParser::setCustomData(QObject *object, + const QByteArray &data) +{ + QmlPropertyChangesPrivate *p = + static_cast<QmlPropertyChangesPrivate *>(QObjectPrivate::get(object)); + p->data = data; + p->decoded = false; +} + +QmlPropertyChanges::QmlPropertyChanges() +: QmlStateOperation(*(new QmlPropertyChangesPrivate)) +{ +} + +QmlPropertyChanges::~QmlPropertyChanges() +{ + Q_D(QmlPropertyChanges); + for(int ii = 0; ii < d->expressions.count(); ++ii) + delete d->expressions.at(ii).second; + for(int ii = 0; ii < d->signalReplacements.count(); ++ii) + delete d->signalReplacements.at(ii); +} + +QObject *QmlPropertyChanges::object() const +{ + Q_D(const QmlPropertyChanges); + return d->object; +} + +void QmlPropertyChanges::setObject(QObject *o) +{ + Q_D(QmlPropertyChanges); + d->object = o; +} + +/*! + \qmlproperty bool PropertyChanges::restoreEntryValues + + Whether or not the previous values should be restored when + leaving the state. By default, restoreEntryValues is true. + + By setting restoreEntryValues to false, you can create a temporary state + that has permanent effects on property values. +*/ +bool QmlPropertyChanges::restoreEntryValues() const +{ + Q_D(const QmlPropertyChanges); + return d->restore; +} + +void QmlPropertyChanges::setRestoreEntryValues(bool v) +{ + Q_D(QmlPropertyChanges); + d->restore = v; +} + +QmlMetaProperty +QmlPropertyChangesPrivate::property(const QByteArray &property) +{ + Q_Q(QmlPropertyChanges); + QmlMetaProperty prop = QmlMetaProperty::createProperty(object, QString::fromUtf8(property)); + if (!prop.isValid()) { + qmlInfo(q) << QmlPropertyChanges::tr("Cannot assign to non-existant property \"%1\"").arg(QString::fromUtf8(property)); + return QmlMetaProperty(); + } else if (!(prop.type() & QmlMetaProperty::SignalProperty) && !prop.isWritable()) { + qmlInfo(q) << QmlPropertyChanges::tr("Cannot assign to read-only property \"%1\"").arg(QString::fromUtf8(property)); + return QmlMetaProperty(); + } + return prop; +} + +QmlPropertyChanges::ActionList QmlPropertyChanges::actions() +{ + Q_D(QmlPropertyChanges); + + d->decode(); + + ActionList list; + + for (int ii = 0; ii < d->properties.count(); ++ii) { + + QByteArray property = d->properties.at(ii).first; + + QmlAction a(d->object, QString::fromLatin1(property), + d->properties.at(ii).second); + + if (a.property.isValid()) { + a.restore = restoreEntryValues(); + + if (a.property.propertyType() == QVariant::Url && + (a.toValue.userType() == QVariant::String || a.toValue.userType() == QVariant::ByteArray) && !a.toValue.isNull()) + a.toValue.setValue(qmlContext(this)->resolvedUrl(QUrl(a.toValue.toString()))); + + list << a; + } + } + + for (int ii = 0; ii < d->signalReplacements.count(); ++ii) { + + QmlReplaceSignalHandler *handler = d->signalReplacements.at(ii); + + if (handler->property.isValid()) { + QmlAction a; + a.event = handler; + list << a; + } + } + + for (int ii = 0; ii < d->expressions.count(); ++ii) { + + QByteArray property = d->expressions.at(ii).first; + QmlMetaProperty prop = d->property(property); + + if (prop.isValid()) { + QmlAction a; + a.restore = restoreEntryValues(); + a.property = prop; + a.fromValue = a.property.read(); + a.specifiedObject = d->object; + a.specifiedProperty = QString::fromLatin1(property); + + if (d->isExplicit) { + a.toValue = d->expressions.at(ii).second->value(); + } else { + QmlBinding *newBinding = new QmlBinding(d->expressions.at(ii).second->expression(), object(), qmlContext(this)); + newBinding->setTarget(prop); + a.toBinding = newBinding; + a.deletableToBinding = true; + } + + list << a; + } + } + + return list; +} + +/*! + \qmlproperty bool PropertyChanges::explicit + + If explicit is set to true, any potential bindings will be interpreted as + once-off assignments that occur when the state is entered. + + In the following example, the addition of explicit prevents myItem.width from + being bound to parent.width. Instead, it is assigned the value of parent.width + at the time of the state change. + \qml + PropertyChanges { + target: myItem + explicit: true + width: parent.width + } + \endqml + + By default, explicit is false. +*/ +bool QmlPropertyChanges::isExplicit() const +{ + Q_D(const QmlPropertyChanges); + return d->isExplicit; +} + +void QmlPropertyChanges::setIsExplicit(bool e) +{ + Q_D(QmlPropertyChanges); + d->isExplicit = e; +} + +QML_DEFINE_CUSTOM_TYPE(Qt, 4,6, PropertyChanges, QmlPropertyChanges, QmlPropertyChangesParser) + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlpropertychanges_p.h b/src/declarative/util/qmlpropertychanges_p.h new file mode 100644 index 0000000..461730d --- /dev/null +++ b/src/declarative/util/qmlpropertychanges_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** 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 QMLPROPERTYCHANGES_H +#define QMLPROPERTYCHANGES_H + +#include "qmlstateoperations_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlPropertyChangesPrivate; +class Q_DECLARATIVE_EXPORT QmlPropertyChanges : public QmlStateOperation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlPropertyChanges) + + Q_PROPERTY(QObject *target READ object WRITE setObject) + Q_PROPERTY(bool restoreEntryValues READ restoreEntryValues WRITE setRestoreEntryValues) + Q_PROPERTY(bool explicit READ isExplicit WRITE setIsExplicit) +public: + QmlPropertyChanges(); + ~QmlPropertyChanges(); + + QObject *object() const; + void setObject(QObject *); + + bool restoreEntryValues() const; + void setRestoreEntryValues(bool); + + bool isExplicit() const; + void setIsExplicit(bool); + + virtual ActionList actions(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlPropertyChanges) + +QT_END_HEADER + +#endif // QMLPROPERTYCHANGES_H diff --git a/src/declarative/util/qmlpropertymap.cpp b/src/declarative/util/qmlpropertymap.cpp new file mode 100644 index 0000000..226f82e --- /dev/null +++ b/src/declarative/util/qmlpropertymap.cpp @@ -0,0 +1,278 @@ +/**************************************************************************** +** +** 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 "qmlpropertymap.h" + +#include "qmlopenmetaobject_p.h" + +#include <QDebug> + +QT_BEGIN_NAMESPACE + +//QmlPropertyMapMetaObject lets us listen for changes coming from QML +//so we can emit the changed signal. +class QmlPropertyMapMetaObject : public QmlOpenMetaObject +{ +public: + QmlPropertyMapMetaObject(QmlPropertyMap *obj, QmlPropertyMapPrivate *objPriv); + +protected: + virtual void propertyWrite(int index); + +private: + QmlPropertyMap *map; + QmlPropertyMapPrivate *priv; +}; + +class QmlPropertyMapPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlPropertyMap) +public: + QmlPropertyMapMetaObject *mo; + QStringList keys; + void emitChanged(const QString &key); +}; + +void QmlPropertyMapPrivate::emitChanged(const QString &key) +{ + Q_Q(QmlPropertyMap); + emit q->valueChanged(key); +} + +QmlPropertyMapMetaObject::QmlPropertyMapMetaObject(QmlPropertyMap *obj, QmlPropertyMapPrivate *objPriv) : QmlOpenMetaObject(obj) +{ + map = obj; + priv = objPriv; +} + +void QmlPropertyMapMetaObject::propertyWrite(int index) +{ + priv->emitChanged(QString::fromUtf8(name(index))); +} + +/*! + \class QmlPropertyMap + \brief The QmlPropertyMap class allows you to set key-value pairs that can be used in bindings. + + QmlPropertyMap provides a convenient way to expose domain data to the UI layer. + The following example shows how you might declare data in C++ and then + access it in QML. + + Setup in C++: + \code + //create our data + QmlPropertyMap ownerData; + ownerData.insert("name", QVariant(QString("John Smith"))); + ownerData.insert("phone", QVariant(QString("555-5555"))); + + //expose it to the UI layer + QmlContext *ctxt = view->bindContext(); + ctxt->setProperty("owner", &data); + \endcode + + Then, in QML: + \code + Text { text: owner.name } + Text { text: owner.phone } + \endcode + + The binding is dynamic - whenever a key's value is updated, anything bound to that + 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 \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 + modify or clear its associated value. +*/ + +/*! + Constructs a bindable map with parent object \a parent. +*/ +QmlPropertyMap::QmlPropertyMap(QObject *parent) +: QObject(*(new QmlPropertyMapPrivate), parent) +{ + Q_D(QmlPropertyMap); + d->mo = new QmlPropertyMapMetaObject(this, d); +} + +/*! + Destroys the bindable map. +*/ +QmlPropertyMap::~QmlPropertyMap() +{ +} + +/*! + Clears the value (if any) associated with \a key. +*/ +void QmlPropertyMap::clear(const QString &key) +{ + Q_D(QmlPropertyMap); + d->mo->setValue(key.toUtf8(), QVariant()); +} + +/*! + Returns the value associated with \a key. + + If no value has been set for this key (or if the value has been cleared), + an invalid QVariant is returned. +*/ +QVariant QmlPropertyMap::value(const QString &key) const +{ + Q_D(const QmlPropertyMap); + return d->mo->value(key.toUtf8()); +} + +/*! + Sets the value associated with \a key to \a value. + + If the key doesn't exist, it is automatically created. +*/ +void QmlPropertyMap::insert(const QString &key, const QVariant &value) +{ + Q_D(QmlPropertyMap); + if (!d->keys.contains(key)) + d->keys.append(key); + d->mo->setValue(key.toUtf8(), value); +} + +/*! + Returns the list of keys. + + Keys that have been cleared will still appear in this list, even though their + associated values are invalid QVariants. +*/ +QStringList QmlPropertyMap::keys() const +{ + Q_D(const QmlPropertyMap); + return d->keys; +} + +/*! + \overload + + Same as size(). +*/ +int QmlPropertyMap::count() const +{ + Q_D(const QmlPropertyMap); + return d->keys.count(); +} + +/*! + Returns the number of keys in the map. + + \sa isEmpty(), count() +*/ +int QmlPropertyMap::size() const +{ + Q_D(const QmlPropertyMap); + return d->keys.size(); +} + +/*! + Returns true if the map contains no keys; otherwise returns + false. + + \sa size() +*/ +bool QmlPropertyMap::isEmpty() const +{ + Q_D(const QmlPropertyMap); + return d->keys.isEmpty(); +} + +/*! + Returns true if the map contains \a key. + + \sa size() +*/ +bool QmlPropertyMap::contains(const QString &key) const +{ + Q_D(const QmlPropertyMap); + return d->keys.contains(key); +} + +/*! + Returns the value associated with the key \a key as a modifiable + reference. + + If the map contains no item with key \a key, the function inserts + an invalid QVariant into the map with key \a key, and + returns a reference to it. + + \sa insert(), value() +*/ +QVariant &QmlPropertyMap::operator[](const QString &key) +{ + //### optimize + Q_D(QmlPropertyMap); + QByteArray utf8key = key.toUtf8(); + if (!d->keys.contains(key)) { + d->keys.append(key); + d->mo->setValue(utf8key, QVariant()); //force creation -- needed below + } + + return (*(d->mo))[utf8key]; +} + +/*! + \overload + + Same as value(). +*/ +const QVariant QmlPropertyMap::operator[](const QString &key) const +{ + return value(key); +} + +/*! + \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/qmlpropertymap.h b/src/declarative/util/qmlpropertymap.h new file mode 100644 index 0000000..bb397fe --- /dev/null +++ b/src/declarative/util/qmlpropertymap.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** 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 QMLPROPERTYMAP_H +#define QMLPROPERTYMAP_H + +#include <QtCore/QObject> +#include <QtCore/QHash> +#include <QtCore/QStringList> +#include <QtCore/QVariant> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlPropertyMapPrivate; +class Q_DECLARATIVE_EXPORT QmlPropertyMap : public QObject +{ + Q_OBJECT +public: + QmlPropertyMap(QObject *parent = 0); + virtual ~QmlPropertyMap(); + + QVariant value(const QString &key) const; + void insert(const QString &key, const QVariant &value); + void clear(const QString &key); + + Q_INVOKABLE QStringList keys() const; + + int count() const; + int size() const; + bool isEmpty() const; + bool contains(const QString &key) const; + + QVariant &operator[](const QString &key); + const QVariant operator[](const QString &key) const; + +Q_SIGNALS: + void valueChanged(const QString &key); + +private: + Q_DECLARE_PRIVATE(QmlPropertyMap) + Q_DISABLE_COPY(QmlPropertyMap) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/declarative/util/qmlspringfollow.cpp b/src/declarative/util/qmlspringfollow.cpp new file mode 100644 index 0000000..764d7f9 --- /dev/null +++ b/src/declarative/util/qmlspringfollow.cpp @@ -0,0 +1,464 @@ +/**************************************************************************** +** +** 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 "qmlspringfollow_p.h" + +#include "qmlanimation_p_p.h" + +#include <QtCore/qdebug.h> + +#include <private/qobject_p.h> + +#include <limits.h> +#include <math.h> + +QT_BEGIN_NAMESPACE + +QML_DEFINE_TYPE(Qt,4,6,SpringFollow,QmlSpringFollow) + +class QmlSpringFollowPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlSpringFollow) +public: + QmlSpringFollowPrivate() + : currentValue(0), sourceValue(0), maxVelocity(0), lastTime(0) + , mass(1.0), spring(0.), damping(0.), velocity(0), epsilon(0.01) + , modulus(0.0), useMass(false), haveModulus(false), enabled(true), mode(Track), clock(this) {} + + QmlMetaProperty property; + qreal currentValue; + qreal sourceValue; + qreal maxVelocity; + qreal velocityms; + int lastTime; + qreal mass; + qreal spring; + qreal damping; + qreal velocity; + qreal epsilon; + qreal modulus; + + bool useMass : 1; + bool haveModulus : 1; + bool enabled : 1; + + enum Mode { + Track, + Velocity, + Spring + }; + Mode mode; + + void tick(int); + void updateMode(); + void start(); + void stop(); + + QTickAnimationProxy<QmlSpringFollowPrivate, &QmlSpringFollowPrivate::tick> clock; +}; + +void QmlSpringFollowPrivate::tick(int time) +{ + Q_Q(QmlSpringFollow); + + int elapsed = time - lastTime; + if (!elapsed) + return; + qreal srcVal = sourceValue; + if (haveModulus) { + currentValue = fmod(currentValue, modulus); + srcVal = fmod(srcVal, modulus); + } + if (mode == Spring) { + if (elapsed < 16) // capped at 62fps. + return; + // Real men solve the spring DEs using RK4. + // We'll do something much simpler which gives a result that looks fine. + int count = elapsed / 16; + for (int i = 0; i < count; ++i) { + qreal diff = srcVal - currentValue; + if (haveModulus && qAbs(diff) > modulus / 2) { + if (diff < 0) + diff += modulus; + else + diff -= modulus; + } + if (useMass) + velocity = velocity + (spring * diff - damping * velocity) / mass; + else + velocity = velocity + spring * diff - damping * velocity; + if (maxVelocity > 0.) { + // limit velocity + if (velocity > maxVelocity) + velocity = maxVelocity; + else if (velocity < -maxVelocity) + velocity = -maxVelocity; + } + currentValue += velocity * 16.0 / 1000.0; + if (haveModulus) { + currentValue = fmod(currentValue, modulus); + if (currentValue < 0.0) + currentValue += modulus; + } + } + if (qAbs(velocity) < epsilon && qAbs(srcVal - currentValue) < epsilon) { + velocity = 0.0; + currentValue = srcVal; + clock.stop(); + } + lastTime = time - (elapsed - count * 16); + } else { + qreal moveBy = elapsed * velocityms; + qreal diff = srcVal - currentValue; + if (haveModulus && qAbs(diff) > modulus / 2) { + if (diff < 0) + diff += modulus; + else + diff -= modulus; + } + if (diff > 0) { + currentValue += moveBy; + if (haveModulus) + currentValue = fmod(currentValue, modulus); + if (currentValue > sourceValue) { + currentValue = sourceValue; + clock.stop(); + } + } else { + currentValue -= moveBy; + if (haveModulus && currentValue < 0.0) + currentValue = fmod(currentValue, modulus) + modulus; + if (currentValue < sourceValue) { + currentValue = sourceValue; + clock.stop(); + } + } + lastTime = time; + } + property.write(currentValue); + emit q->valueChanged(currentValue); + if (clock.state() != QAbstractAnimation::Running) + emit q->syncChanged(); +} + +void QmlSpringFollowPrivate::updateMode() +{ + if (spring == 0. && maxVelocity == 0.) + mode = Track; + else if (spring > 0.) + mode = Spring; + else + mode = Velocity; +} + +void QmlSpringFollowPrivate::start() +{ + if (!enabled) + return; + + Q_Q(QmlSpringFollow); + if (mode == QmlSpringFollowPrivate::Track) { + currentValue = sourceValue; + property.write(currentValue); + } else if (sourceValue != currentValue && clock.state() != QAbstractAnimation::Running) { + lastTime = 0; + currentValue = property.read().toReal(); + clock.start(); // infinity?? + emit q->syncChanged(); + } +} + +void QmlSpringFollowPrivate::stop() +{ + clock.stop(); +} + +/*! + \qmlclass SpringFollow QmlSpringFollow + \brief The SpringFollow element allows a property to track a value. + + In example below, \e rect2 will follow \e rect1 moving with a velocity of up to 200: + \code + Rectangle { + id: rect1 + width: 20; height: 20 + color: "#00ff00" + y: 200 //initial value + y: SequentialAnimation { + running: true + repeat: true + NumberAnimation { + to: 200 + easing: "easeOutBounce(amplitude:100)" + duration: 2000 + } + PauseAnimation { duration: 1000 } + } + } + Rectangle { + id: rect2 + x: rect1.width + width: 20; height: 20 + color: "#ff0000" + y: SpringFollow { source: rect1.y; velocity: 200 } + } + \endcode + + \sa EaseFollow +*/ + +QmlSpringFollow::QmlSpringFollow(QObject *parent) +: QObject(*(new QmlSpringFollowPrivate),parent) +{ +} + +QmlSpringFollow::~QmlSpringFollow() +{ +} + +void QmlSpringFollow::setTarget(const QmlMetaProperty &property) +{ + Q_D(QmlSpringFollow); + d->property = property; + d->currentValue = property.read().toReal(); +} + +qreal QmlSpringFollow::sourceValue() const +{ + Q_D(const QmlSpringFollow); + return d->sourceValue; +} + +/*! + \qmlproperty qreal SpringFollow::source + This property holds the source value which will be tracked. + + Bind to a property in order to track its changes. +*/ + +void QmlSpringFollow::setSourceValue(qreal value) +{ + Q_D(QmlSpringFollow); + if (d->clock.state() == QAbstractAnimation::Running && d->sourceValue == value) + return; + + d->sourceValue = value; + d->start(); +} + +/*! + \qmlproperty qreal SpringFollow::velocity + This property holds the maximum velocity allowed when tracking the source. +*/ + +qreal QmlSpringFollow::velocity() const +{ + Q_D(const QmlSpringFollow); + return d->maxVelocity; +} + +void QmlSpringFollow::setVelocity(qreal velocity) +{ + Q_D(QmlSpringFollow); + d->maxVelocity = velocity; + d->velocityms = velocity / 1000.0; + d->updateMode(); +} + +/*! + \qmlproperty qreal SpringFollow::spring + This property holds the spring constant + + The spring constant describes how strongly the target is pulled towards the + source. Setting spring to 0 turns off spring tracking. Useful values 0 - 5.0 + + When a spring constant is set and the velocity property is greater than 0, + velocity limits the maximum speed. +*/ +qreal QmlSpringFollow::spring() const +{ + Q_D(const QmlSpringFollow); + return d->spring; +} + +void QmlSpringFollow::setSpring(qreal spring) +{ + Q_D(QmlSpringFollow); + d->spring = spring; + d->updateMode(); +} + +/*! + \qmlproperty qreal SpringFollow::damping + This property holds the spring damping constant + + The damping constant describes how quickly a sprung follower comes to rest. + Useful range is 0 - 1.0 +*/ +qreal QmlSpringFollow::damping() const +{ + Q_D(const QmlSpringFollow); + return d->damping; +} + +void QmlSpringFollow::setDamping(qreal damping) +{ + Q_D(QmlSpringFollow); + if (damping > 1.) + damping = 1.; + + d->damping = damping; +} + + +/*! + \qmlproperty qreal SpringFollow::epsilon + This property holds the spring epsilon + + The epsilon is the rate and amount of change in the value which is close enough + to 0 to be considered equal to zero. This will depend on the usage of the value. + For pixel positions, 0.25 would suffice. For scale, 0.005 will suffice. + + The default is 0.01. Tuning this value can provide small performance improvements. +*/ +qreal QmlSpringFollow::epsilon() const +{ + Q_D(const QmlSpringFollow); + return d->epsilon; +} + +void QmlSpringFollow::setEpsilon(qreal epsilon) +{ + Q_D(QmlSpringFollow); + d->epsilon = epsilon; +} + +/*! + \qmlproperty qreal SpringFollow::modulus + This property holds the modulus value. + + Setting a \a modulus forces the target value to "wrap around" at the modulus. + For example, setting the modulus to 360 will cause a value of 370 to wrap around to 10. +*/ +qreal QmlSpringFollow::modulus() const +{ + Q_D(const QmlSpringFollow); + return d->modulus; +} + +void QmlSpringFollow::setModulus(qreal modulus) +{ + Q_D(QmlSpringFollow); + if (d->modulus != modulus) { + d->haveModulus = modulus != 0.0; + d->modulus = modulus; + emit modulusChanged(); + } +} + +/*! + \qmlproperty qreal SpringFollow::mass + This property holds the "mass" of the property being moved. + + mass is 1.0 by default. Setting a different mass changes the dynamics of + a \l spring follow. +*/ +qreal QmlSpringFollow::mass() const +{ + Q_D(const QmlSpringFollow); + return d->mass; +} + +void QmlSpringFollow::setMass(qreal mass) +{ + Q_D(QmlSpringFollow); + if (d->mass != mass && mass > 0.0) { + d->useMass = mass != 1.0; + d->mass = mass; + emit massChanged(); + } +} + +/*! + \qmlproperty qreal SpringFollow::value + The current value. +*/ + +/*! + \qmlproperty bool SpringFollow::enabled + This property holds whether the target will track the source. +*/ +bool QmlSpringFollow::enabled() const +{ + Q_D(const QmlSpringFollow); + return d->enabled; +} + +void QmlSpringFollow::setEnabled(bool enabled) +{ + Q_D(QmlSpringFollow); + d->enabled = enabled; + if (enabled) + d->start(); + else + d->stop(); +} + +/*! + \qmlproperty bool SpringFollow::inSync + This property is true when target is equal to the source; otherwise + false. If inSync is true the target is not being animated. + + If \l enabled is false then inSync will also be false. +*/ +bool QmlSpringFollow::inSync() const +{ + Q_D(const QmlSpringFollow); + return d->enabled && d->clock.state() != QAbstractAnimation::Running; +} + +qreal QmlSpringFollow::value() const +{ + Q_D(const QmlSpringFollow); + return d->currentValue; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlspringfollow_p.h b/src/declarative/util/qmlspringfollow_p.h new file mode 100644 index 0000000..7731b9e --- /dev/null +++ b/src/declarative/util/qmlspringfollow_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** 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 QMLSMOOTHFOLLOW_H +#define QMLSMOOTHFOLLOW_H + +#include <qmlpropertyvaluesource.h> +#include <qml.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlSpringFollowPrivate; +class Q_DECLARATIVE_EXPORT QmlSpringFollow : public QObject, + public QmlPropertyValueSource +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlSpringFollow) + Q_INTERFACES(QmlPropertyValueSource) + + Q_PROPERTY(qreal source READ sourceValue WRITE setSourceValue) + Q_PROPERTY(qreal velocity READ velocity WRITE setVelocity) + Q_PROPERTY(qreal spring READ spring WRITE setSpring) + Q_PROPERTY(qreal damping READ damping WRITE setDamping) + Q_PROPERTY(qreal epsilon READ epsilon WRITE setEpsilon) + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled) + Q_PROPERTY(qreal value READ value NOTIFY valueChanged) + Q_PROPERTY(qreal modulus READ modulus WRITE setModulus NOTIFY modulusChanged) + Q_PROPERTY(qreal mass READ mass WRITE setMass NOTIFY massChanged) + Q_PROPERTY(bool inSync READ inSync NOTIFY syncChanged) + +public: + QmlSpringFollow(QObject *parent=0); + ~QmlSpringFollow(); + + virtual void setTarget(const QmlMetaProperty &); + + qreal sourceValue() const; + void setSourceValue(qreal value); + qreal velocity() const; + void setVelocity(qreal velocity); + qreal spring() const; + void setSpring(qreal spring); + qreal damping() const; + void setDamping(qreal damping); + qreal epsilon() const; + void setEpsilon(qreal epsilon); + qreal mass() const; + void setMass(qreal modulus); + qreal modulus() const; + void setModulus(qreal modulus); + bool enabled() const; + void setEnabled(bool enabled); + bool inSync() const; + + qreal value() const; + +Q_SIGNALS: + void valueChanged(qreal); + void modulusChanged(); + void massChanged(); + void syncChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlSpringFollow) + +QT_END_HEADER + +#endif // QMLSMOOTHFOLLOW_H diff --git a/src/declarative/util/qmlstate.cpp b/src/declarative/util/qmlstate.cpp new file mode 100644 index 0000000..cae8054 --- /dev/null +++ b/src/declarative/util/qmlstate.cpp @@ -0,0 +1,468 @@ +/**************************************************************************** +** +** 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 "qmlstate_p_p.h" +#include "qmlstate_p.h" + +#include "qmltransition_p.h" +#include "qmlstategroup_p.h" +#include "qmlstateoperations_p.h" +#include "qmlanimation_p.h" +#include "qmlanimation_p_p.h" + +#include <qmlbinding.h> +#include <qmlglobal_p.h> + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +QmlAction::QmlAction() +: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), fromBinding(0), toBinding(0), event(0), + specifiedObject(0) +{ +} + +QmlAction::QmlAction(QObject *target, const QString &propertyName, + const QVariant &value) +: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), toValue(value), fromBinding(0), + toBinding(0), event(0), specifiedObject(target), + specifiedProperty(propertyName) +{ + property = QmlMetaProperty::createProperty(target, propertyName); + if (property.isValid()) + fromValue = property.read(); +} + +QmlActionEvent::~QmlActionEvent() +{ +} + +QString QmlActionEvent::typeName() const +{ + return QString(); +} + +void QmlActionEvent::execute() +{ +} + +bool QmlActionEvent::isReversable() +{ + return false; +} + +void QmlActionEvent::reverse() +{ +} + +QList<QmlAction> QmlActionEvent::extraActions() +{ + return QList<QmlAction>(); +} + +bool QmlActionEvent::changesBindings() +{ + return false; +} + +void QmlActionEvent::clearForwardBindings() +{ +} + +void QmlActionEvent::clearReverseBindings() +{ +} + +bool QmlActionEvent::override(QmlActionEvent *other) +{ + Q_UNUSED(other); + return false; +} + +/*! + \internal +*/ +QmlStateOperation::QmlStateOperation(QObjectPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + \qmlclass State QmlState + \brief The State element defines configurations of objects and properties. + + A state is specified as a set of batched changes from the default configuration. + + Note that setting the state of an object from within another state of the same object is + inadvisible. Not only would this have the same effect as going directly to the second state + it may cause the program to crash. + + \sa {qmlstates}{States}, {state-transitions}{Transitions} +*/ + +/*! + \internal + \class QmlState + \brief The QmlState class allows you to define configurations of objects and properties. + + \ingroup group_states + + QmlState allows you to specify a state as a set of batched changes from the default + configuration. + + \sa {states-transitions}{States and Transitions} +*/ + +QML_DEFINE_TYPE(Qt,4,6,State,QmlState) +QmlState::QmlState(QObject *parent) +: QObject(*(new QmlStatePrivate), parent) +{ + Q_D(QmlState); + d->transitionManager.setState(this); +} + +QmlState::~QmlState() +{ + Q_D(QmlState); + if (d->group) + d->group->removeState(this); +} + +/*! + \qmlproperty string State::name + This property holds the name of the state + + Each state should have a unique name. +*/ +QString QmlState::name() const +{ + Q_D(const QmlState); + return d->name; +} + +void QmlState::setName(const QString &n) +{ + Q_D(QmlState); + d->name = n; +} + +bool QmlState::isWhenKnown() const +{ + Q_D(const QmlState); + return d->when != 0; +} + +/*! + \qmlproperty bool State::when + This property holds when the state should be applied + + This should be set to an expression that evaluates to true when you want the state to + be applied. +*/ +QmlBinding *QmlState::when() const +{ + Q_D(const QmlState); + return d->when; +} + +void QmlState::setWhen(QmlBinding *when) +{ + Q_D(QmlState); + d->when = when; + if (d->group) + d->group->updateAutoState(); +} + +/*! + \qmlproperty string State::extend + This property holds the state that this state extends + + The state being extended is treated as the base state in regards to + the changes specified by the extending state. +*/ +QString QmlState::extends() const +{ + Q_D(const QmlState); + return d->extends; +} + +void QmlState::setExtends(const QString &extends) +{ + Q_D(QmlState); + d->extends = extends; +} + +/*! + \qmlproperty list<Change> State::changes + This property holds the changes to apply for this state + \default + + By default these changes are applied against the default state. If the state + extends another state, then the changes are applied against the state being + extended. +*/ +QmlList<QmlStateOperation *> *QmlState::changes() +{ + Q_D(QmlState); + return &d->operations; +} + +QmlState &QmlState::operator<<(QmlStateOperation *op) +{ + Q_D(QmlState); + d->operations.append(op); + return *this; +} + +void QmlStatePrivate::complete() +{ + Q_Q(QmlState); + + for (int ii = 0; ii < reverting.count(); ++ii) { + for (int jj = 0; jj < revertList.count(); ++jj) { + if (revertList.at(jj).property == reverting.at(ii)) { + revertList.removeAt(jj); + break; + } + } + } + reverting.clear(); + + emit q->completed(); +} + +// Generate a list of actions for this state. This includes coelescing state +// actions that this state "extends" +QmlStateOperation::ActionList +QmlStatePrivate::generateActionList(QmlStateGroup *group) const +{ + QmlStateOperation::ActionList applyList; + if (inState) + return applyList; + + // Prevent "extends" recursion + inState = true; + + if (!extends.isEmpty()) { + QList<QmlState *> states = group->states(); + for (int ii = 0; ii < states.count(); ++ii) + if (states.at(ii)->name() == extends) + applyList = static_cast<QmlStatePrivate*>(states.at(ii)->d_func())->generateActionList(group); + } + + foreach(QmlStateOperation *op, operations) + applyList << op->actions(); + + inState = false; + return applyList; +} + +QmlStateGroup *QmlState::stateGroup() const +{ + Q_D(const QmlState); + return d->group; +} + +void QmlState::setStateGroup(QmlStateGroup *group) +{ + Q_D(QmlState); + d->group = group; +} + +void QmlState::cancel() +{ + Q_D(QmlState); + d->transitionManager.cancel(); +} + +void QmlAction::deleteFromBinding() +{ + if (fromBinding) { + property.setBinding(0); + fromBinding->destroy(); + fromBinding = 0; + } +} + +void QmlState::apply(QmlStateGroup *group, QmlTransition *trans, QmlState *revert) +{ + Q_D(QmlState); + + qmlExecuteDeferred(this); + + cancel(); + if (revert) + revert->cancel(); + d->revertList.clear(); + d->reverting.clear(); + + if (revert) { + QmlStatePrivate *revertPrivate = + static_cast<QmlStatePrivate*>(revert->d_func()); + d->revertList = revertPrivate->revertList; + revertPrivate->revertList.clear(); + } + + // List of actions caused by this state + QmlStateOperation::ActionList applyList = d->generateActionList(group); + + // List of actions that need to be reverted to roll back (just) this state + QmlStatePrivate::SimpleActionList additionalReverts; + // First add the reverse of all the applyList actions + for (int ii = 0; ii < applyList.count(); ++ii) { + QmlAction &action = applyList[ii]; + + bool found = false; + + int jj; + if (action.event) { + if (!action.event->isReversable()) + continue; + for (jj = 0; jj < d->revertList.count(); ++jj) { + QmlActionEvent *event = d->revertList.at(jj).event; + if (event && event->typeName() == action.event->typeName()) { + if (action.event->override(event)) { + found = true; + break; + } + } + } + if (!found || action.event != d->revertList.at(jj).event) + action.event->saveOriginals(); + else if (action.event->isRewindable()) + action.event->saveCurrentValues(); + } else { + action.fromBinding = action.property.binding(); + + for (jj = 0; jj < d->revertList.count(); ++jj) { + if (d->revertList.at(jj).property == action.property) { + found = true; + break; + } + } + } + + if (!found) { + if (!action.restore) { + action.deleteFromBinding(); + } else { + // Only need to revert the applyList action if the previous + // state doesn't have a higher priority revert already + QmlSimpleAction r(action); + additionalReverts << r; + } + } else if (d->revertList.at(jj).binding != action.fromBinding) { + action.deleteFromBinding(); + } + } + + // Any reverts from a previous state that aren't carried forth + // into this state need to be translated into apply actions + for (int ii = 0; ii < d->revertList.count(); ++ii) { + bool found = false; + if (d->revertList.at(ii).event) { + QmlActionEvent *event = d->revertList.at(ii).event; + if (!event->isReversable()) + continue; + for (int jj = 0; !found && jj < applyList.count(); ++jj) { + const QmlAction &action = applyList.at(jj); + if (action.event && action.event->typeName() == event->typeName()) { + if (action.event->override(event)) + found = true; + } + } + } else { + for (int jj = 0; !found && jj < applyList.count(); ++jj) { + const QmlAction &action = applyList.at(jj); + if (action.property == d->revertList.at(ii).property) + found = true; + } + } + if (!found) { + QVariant cur = d->revertList.at(ii).property.read(); + QmlAbstractBinding *delBinding = d->revertList.at(ii).property.setBinding(0); + if (delBinding) + delBinding->destroy(); + + QmlAction a; + a.property = d->revertList.at(ii).property; + a.fromValue = cur; + a.toValue = d->revertList.at(ii).value; + a.toBinding = d->revertList.at(ii).binding; + a.specifiedObject = d->revertList.at(ii).specifiedObject; + a.specifiedProperty = d->revertList.at(ii).specifiedProperty; + a.event = d->revertList.at(ii).event; + a.reverseEvent = d->revertList.at(ii).reverseEvent; + if (a.event && a.event->isRewindable()) + a.event->saveCurrentValues(); + applyList << a; + // Store these special reverts in the reverting list + d->reverting << d->revertList.at(ii).property; + } + } + // All the local reverts now become part of the ongoing revertList + d->revertList << additionalReverts; + + // Output for debugging + if (stateChangeDebug()) { + foreach(const QmlAction &action, applyList) { + if (action.event) + qWarning() << " QmlAction event:" << action.event->typeName(); + else + qWarning() << " QmlAction:" << action.property.object() + << action.property.name() << "From:" << action.fromValue + << "To:" << action.toValue; + } + } + + d->transitionManager.transition(applyList, trans); +} + +QML_DEFINE_NOCREATE_TYPE(QmlStateOperation) +QmlStateOperation::ActionList QmlStateOperation::actions() +{ + return ActionList(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlstate_p.h b/src/declarative/util/qmlstate_p.h new file mode 100644 index 0000000..5862c02 --- /dev/null +++ b/src/declarative/util/qmlstate_p.h @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** 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 QMLSTATE_H +#define QMLSTATE_H + +#include <qml.h> + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlActionEvent; +class QmlBinding; +class Q_DECLARATIVE_EXPORT QmlAction +{ +public: + QmlAction(); + QmlAction(QObject *, const QString &, const QVariant &); + + bool restore:1; + bool actionDone:1; + bool reverseEvent:1; + bool deletableToBinding:1; + + QmlMetaProperty property; + QVariant fromValue; + QVariant toValue; + + QmlAbstractBinding *fromBinding; + QmlAbstractBinding *toBinding; + QmlActionEvent *event; + + //strictly for matching + QObject *specifiedObject; + QString specifiedProperty; + + void deleteFromBinding(); +}; + +class QmlActionEvent +{ +public: + virtual ~QmlActionEvent(); + virtual QString typeName() const; + + virtual void execute(); + virtual bool isReversable(); + virtual void reverse(); + virtual void saveOriginals() {} + + virtual bool isRewindable() { return isReversable(); } + virtual void rewind() {} + virtual void saveCurrentValues() {} + + //virtual bool hasExtraActions(); + virtual QList<QmlAction> extraActions(); + + virtual bool changesBindings(); + virtual void clearForwardBindings(); + virtual void clearReverseBindings(); + virtual bool override(QmlActionEvent*other); +}; + +//### rename to QmlStateChange? +class QmlStateGroup; +class Q_DECLARATIVE_EXPORT QmlStateOperation : public QObject +{ + Q_OBJECT +public: + QmlStateOperation(QObject *parent = 0) + : QObject(parent) {} + typedef QList<QmlAction> ActionList; + + virtual ActionList actions(); + +protected: + QmlStateOperation(QObjectPrivate &dd, QObject *parent = 0); +}; + +typedef QmlStateOperation::ActionList QmlStateActions; + +class QmlTransition; +class QmlStatePrivate; +class Q_DECLARATIVE_EXPORT QmlState : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(QmlBinding *when READ when WRITE setWhen) + Q_PROPERTY(QString extend READ extends WRITE setExtends) + Q_PROPERTY(QmlList<QmlStateOperation *>* changes READ changes) + Q_CLASSINFO("DefaultProperty", "changes") + Q_CLASSINFO("DeferredPropertyNames", "changes") + +public: + QmlState(QObject *parent=0); + virtual ~QmlState(); + + QString name() const; + void setName(const QString &); + + /*'when' is a QmlBinding to limit state changes oscillation + due to the unpredictable order of evaluation of bound expressions*/ + bool isWhenKnown() const; + QmlBinding *when() const; + void setWhen(QmlBinding *); + + QString extends() const; + void setExtends(const QString &); + + QmlList<QmlStateOperation *> *changes(); + QmlState &operator<<(QmlStateOperation *); + + void apply(QmlStateGroup *, QmlTransition *, QmlState *revert); + void cancel(); + + QmlStateGroup *stateGroup() const; + void setStateGroup(QmlStateGroup *); + +Q_SIGNALS: + void completed(); + +private: + Q_DECLARE_PRIVATE(QmlState) + Q_DISABLE_COPY(QmlState) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlStateOperation) +QML_DECLARE_TYPE(QmlState) + +QT_END_HEADER + +#endif // QMLSTATE_H diff --git a/src/declarative/util/qmlstate_p_p.h b/src/declarative/util/qmlstate_p_p.h new file mode 100644 index 0000000..235fe62 --- /dev/null +++ b/src/declarative/util/qmlstate_p_p.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** 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 QMLSTATE_P_H +#define QMLSTATE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qmlstate_p.h" + +#include "qmlanimation_p_p.h" +#include "qmltransitionmanager_p_p.h" + +#include <qmlguard_p.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QmlSimpleAction +{ +public: + enum State { StartState, EndState }; + QmlSimpleAction(const QmlAction &a, State state = StartState) + { + property = a.property; + specifiedObject = a.specifiedObject; + specifiedProperty = a.specifiedProperty; + event = a.event; + if (state == StartState) { + value = a.fromValue; + binding = property.binding(); + reverseEvent = true; + } else { + value = a.toValue; + binding = a.toBinding; + reverseEvent = false; + } + } + + QmlMetaProperty property; + QVariant value; + QmlAbstractBinding *binding; + QObject *specifiedObject; + QString specifiedProperty; + QmlActionEvent *event; + bool reverseEvent; +}; + +class QmlStatePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlState) + +public: + QmlStatePrivate() + : when(0), inState(false), group(0) {} + + typedef QList<QmlSimpleAction> SimpleActionList; + + QString name; + QmlBinding *when; + + class OperationList; + struct OperationGuard : public QmlGuard<QmlStateOperation> + { + OperationGuard(QObject *obj, OperationList *l) : list(l) { (QmlGuard<QObject>&)*this = obj; } + OperationList *list; + void objectDestroyed(QmlStateOperation *) { + // we assume priv will always be destroyed after objectDestroyed calls + list->removeOne(*this); + } + }; + + class OperationList : public QList<OperationGuard>, public QmlList<QmlStateOperation*> + { + public: + virtual void append(QmlStateOperation* v) { QList<OperationGuard>::append(OperationGuard(v, this)); } + virtual void insert(int i, QmlStateOperation* v) { QList<OperationGuard>::insert(i, OperationGuard(v, this)); } + virtual void clear() { QList<OperationGuard>::clear(); } + virtual QmlStateOperation* at(int i) const { return QList<OperationGuard>::at(i); } + virtual void removeAt(int i) { QList<OperationGuard>::removeAt(i); } + virtual int count() const { return QList<OperationGuard>::count(); } + }; + OperationList operations; + + QmlTransitionManager transitionManager; + + SimpleActionList revertList; + QList<QmlMetaProperty> reverting; + QString extends; + mutable bool inState; + QmlStateGroup *group; + + QmlStateOperation::ActionList generateActionList(QmlStateGroup *) const; + void complete(); +}; + +QT_END_NAMESPACE + +#endif // QMLSTATE_P_H diff --git a/src/declarative/util/qmlstategroup.cpp b/src/declarative/util/qmlstategroup.cpp new file mode 100644 index 0000000..d4db2b9 --- /dev/null +++ b/src/declarative/util/qmlstategroup.cpp @@ -0,0 +1,423 @@ +/**************************************************************************** +** +** 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 "qmlstategroup_p.h" + +#include "qmltransition_p.h" +#include "qmlstate_p_p.h" + +#include <qmlbinding.h> +#include <qmlglobal_p.h> + +#include <QtCore/qdebug.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +QML_DEFINE_TYPE(Qt,4,6,StateGroup,QmlStateGroup) + +class QmlStateGroupPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlStateGroup) +public: + QmlStateGroupPrivate(QmlStateGroup *p) + : nullState(0), states(p), componentComplete(true), + ignoreTrans(false), applyingState(false) {} + + QString currentState; + QmlState *nullState; + + struct StateList : public QmlConcreteList<QmlState *> + { + StateList(QmlStateGroup *g) + :group(g) {} + void append(QmlState *s) { + QmlConcreteList<QmlState *>::append(s); + if (s) s->setStateGroup(group); + } + private: + QmlStateGroup *group; + }; + StateList states; + + QmlConcreteList<QmlTransition *> transitions; + bool componentComplete; + bool ignoreTrans; + bool applyingState; + + QmlTransition *findTransition(const QString &from, const QString &to); + void setCurrentStateInternal(const QString &state, bool = false); + bool updateAutoState(); +}; + +/*! + \qmlclass StateGroup QmlStateGroup + \brief The StateGroup element provides state support for non-Item elements. + + Item (and all dervied elements) provides built in support for states and transitions + via its state, states and transitions properties. StateGroup provides an easy way to + use this support in other (non-Item-derived) elements. + + \qml + MyCustomObject { + StateGroup { + id: myStateGroup + states: State { + name: "state1" + ... + } + transitions: Transition { + ... + } + } + + onSomethingHappened: myStateGroup.state = "state1"; + } + \endqml + + \sa {qmlstate}{States} {state-transitions}{Transitions} +*/ + +QmlStateGroup::QmlStateGroup(QObject *parent) + : QObject(*(new QmlStateGroupPrivate(this)), parent) +{ +} + +QmlStateGroup::~QmlStateGroup() +{ + Q_D(const QmlStateGroup); + for (int i = 0; i < d->states.count(); ++i) + d->states.at(i)->setStateGroup(0); +} + +QList<QmlState *> QmlStateGroup::states() const +{ + Q_D(const QmlStateGroup); + return d->states; +} + +/*! + \qmlproperty list<State> StateGroup::states + This property holds a list of states defined by the state group. + + \qml + StateGroup { + states: [ + State { ... }, + State { ... } + ... + ] + } + \endqml + + \sa {qmlstate}{States} +*/ +QmlList<QmlState *>* QmlStateGroup::statesProperty() +{ + Q_D(QmlStateGroup); + return &(d->states); +} + +/*! + \qmlproperty list<Transition> StateGroup::transitions + This property holds a list of transitions defined by the state group. + + \qml + StateGroup { + transitions: [ + Transition { ... }, + Transition { ... } + ... + ] + } + \endqml + + \sa {state-transitions}{Transitions} +*/ +QmlList<QmlTransition *>* QmlStateGroup::transitionsProperty() +{ + Q_D(QmlStateGroup); + return &(d->transitions); +} + +/*! + \qmlproperty string StateGroup::state + + This property holds the name of the current state of the state group. + + This property is often used in scripts to change between states. For + example: + + \qml + Script { + function toggle() { + if (button.state == 'On') + button.state = 'Off'; + else + button.state = 'On'; + } + } + \endqml + + If the state group is in its base state (i.e. no explicit state has been + set), \c state will be a blank string. Likewise, you can return a + state group to its base state by setting its current state to \c ''. + + \sa {qmlstates}{States} +*/ +QString QmlStateGroup::state() const +{ + Q_D(const QmlStateGroup); + return d->currentState; +} + +void QmlStateGroup::setState(const QString &state) +{ + Q_D(QmlStateGroup); + if (d->currentState == state) + return; + + d->setCurrentStateInternal(state); +} + +void QmlStateGroup::classBegin() +{ + Q_D(QmlStateGroup); + d->componentComplete = false; +} + +void QmlStateGroup::componentComplete() +{ + Q_D(QmlStateGroup); + d->componentComplete = true; + + if (d->updateAutoState()) { + return; + } else if (!d->currentState.isEmpty()) { + QString cs = d->currentState; + d->currentState = QString(); + d->setCurrentStateInternal(cs, true); + } +} + +/*! + Returns true if the state was changed, otherwise false. +*/ +bool QmlStateGroup::updateAutoState() +{ + Q_D(QmlStateGroup); + return d->updateAutoState(); +} + +bool QmlStateGroupPrivate::updateAutoState() +{ + Q_Q(QmlStateGroup); + if (!componentComplete) + return false; + + bool revert = false; + for (int ii = 0; ii < states.count(); ++ii) { + QmlState *state = states.at(ii); + if (state->isWhenKnown()) { + if (!state->name().isEmpty()) { + if (state->when() && state->when()->value().toBool()) { + if (stateChangeDebug()) + qWarning() << "Setting auto state due to:" + << state->when()->expression(); + if (currentState != state->name()) { + q->setState(state->name()); + return true; + } else { + return false; + } + } else if (state->name() == currentState) { + revert = true; + } + } + } + } + if (revert) { + bool rv = currentState != QString(); + q->setState(QString()); + return rv; + } else { + return false; + } +} + +QmlTransition *QmlStateGroupPrivate::findTransition(const QString &from, const QString &to) +{ + QmlTransition *highest = 0; + int score = 0; + bool reversed = false; + bool done = false; + + for (int ii = 0; !done && ii < transitions.count(); ++ii) { + QmlTransition *t = transitions.at(ii); + for (int ii = 0; ii < 2; ++ii) + { + if (ii && (!t->reversible() || + (t->fromState() == QLatin1String("*") && + t->toState() == QLatin1String("*")))) + break; + QStringList fromState; + QStringList toState; + + fromState = t->fromState().split(QLatin1Char(',')); + toState = t->toState().split(QLatin1Char(',')); + if (ii == 1) + qSwap(fromState, toState); + int tScore = 0; + if (fromState.contains(from)) + tScore += 2; + else if (fromState.contains(QLatin1String("*"))) + tScore += 1; + else + continue; + + if (toState.contains(to)) + tScore += 2; + else if (toState.contains(QLatin1String("*"))) + tScore += 1; + else + continue; + + if (ii == 1) + reversed = true; + else + reversed = false; + + if (tScore == 4) { + highest = t; + done = true; + break; + } else if (tScore > score) { + score = tScore; + highest = t; + } + } + } + + if (highest) + highest->setReversed(reversed); + + return highest; +} + +void QmlStateGroupPrivate::setCurrentStateInternal(const QString &state, + bool ignoreTrans) +{ + Q_Q(QmlStateGroup); + if (!componentComplete) { + currentState = state; + return; + } + + if (applyingState) { + qWarning() << "Can't apply a state change as part of a state definition."; + return; + } + + applyingState = true; + + QmlTransition *transition = (ignoreTrans || ignoreTrans) ? 0 : findTransition(currentState, state); + if (stateChangeDebug()) { + qWarning() << this << "Changing state. From" << currentState << ". To" << state; + if (transition) + qWarning() << " using transition" << transition->fromState() + << transition->toState(); + } + + QmlState *oldState = 0; + if (!currentState.isEmpty()) { + for (int ii = 0; ii < states.count(); ++ii) { + if (states.at(ii)->name() == currentState) { + oldState = states.at(ii); + break; + } + } + } + + currentState = state; + emit q->stateChanged(currentState); + + QmlState *newState = 0; + for (int ii = 0; ii < states.count(); ++ii) { + if (states.at(ii)->name() == currentState) { + newState = states.at(ii); + break; + } + } + + if (oldState == 0 || newState == 0) { + if (!nullState) { nullState = new QmlState; QmlGraphics_setParent_noEvent(nullState, q); } + if (!oldState) oldState = nullState; + if (!newState) newState = nullState; + } + + newState->apply(q, transition, oldState); + applyingState = false; + if (!transition) + static_cast<QmlStatePrivate*>(QObjectPrivate::get(newState))->complete(); +} + +QmlState *QmlStateGroup::findState(const QString &name) const +{ + Q_D(const QmlStateGroup); + for (int i = 0; i < d->states.count(); ++i) { + QmlState *state = d->states.at(i); + if (state->name() == name) + return state; + } + + return 0; +} + +void QmlStateGroup::removeState(QmlState *state) +{ + Q_D(QmlStateGroup); + d->states.removeOne(state); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlstategroup_p.h b/src/declarative/util/qmlstategroup_p.h new file mode 100644 index 0000000..112c9eb --- /dev/null +++ b/src/declarative/util/qmlstategroup_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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 QMLSTATEGROUP_H +#define QMLSTATEGROUP_H + +#include "qmlstate_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlStateGroupPrivate; +class Q_DECLARATIVE_EXPORT QmlStateGroup : public QObject, public QmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QmlParserStatus) + Q_DECLARE_PRIVATE(QmlStateGroup) + + Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged) + Q_PROPERTY(QmlList<QmlState *>* states READ statesProperty DESIGNABLE false) + Q_PROPERTY(QmlList<QmlTransition *>* transitions READ transitionsProperty DESIGNABLE false) + +public: + QmlStateGroup(QObject * = 0); + virtual ~QmlStateGroup(); + + QString state() const; + void setState(const QString &); + + QmlList<QmlState *>* statesProperty(); + QList<QmlState *> states() const; + + QmlList<QmlTransition *>* transitionsProperty(); + + QmlState *findState(const QString &name) const; + + virtual void classBegin(); + virtual void componentComplete(); +Q_SIGNALS: + void stateChanged(const QString &); + +private: + friend class QmlState; + bool updateAutoState(); + void removeState(QmlState *state); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlStateGroup) + +QT_END_HEADER + +#endif // QMLSTATEGROUP_H diff --git a/src/declarative/util/qmlstateoperations.cpp b/src/declarative/util/qmlstateoperations.cpp new file mode 100644 index 0000000..35c8f7d --- /dev/null +++ b/src/declarative/util/qmlstateoperations.cpp @@ -0,0 +1,834 @@ +/**************************************************************************** +** +** 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 "qmlstateoperations_p.h" + +#include <qml.h> +#include <qmlcontext.h> +#include <qmlexpression.h> +#include <qmlinfo.h> +#include <qmlgraphicsanchors_p_p.h> +#include <qmlgraphicsitem_p.h> +#include <qmlguard_p.h> + +#include <QtCore/qdebug.h> +#include <QtGui/qgraphicsitem.h> +#include <QtCore/qmath.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QmlParentChangePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlParentChange) +public: + QmlParentChangePrivate() : target(0), parent(0), origParent(0), origStackBefore(0), + rewindParent(0), rewindStackBefore(0) {} + + QmlGraphicsItem *target; + QmlGraphicsItem *parent; + QmlGuard<QmlGraphicsItem> origParent; + QmlGuard<QmlGraphicsItem> origStackBefore; + QmlGraphicsItem *rewindParent; + QmlGraphicsItem *rewindStackBefore; + + void doChange(QmlGraphicsItem *targetParent, QmlGraphicsItem *stackBefore = 0); +}; + +void QmlParentChangePrivate::doChange(QmlGraphicsItem *targetParent, QmlGraphicsItem *stackBefore) +{ + if (targetParent && target && target->parentItem()) { + //### for backwards direction, can we just restore original x, y, scale, rotation + Q_Q(QmlParentChange); + bool ok; + const QTransform &transform = target->itemTransform(targetParent, &ok); + if (transform.type() >= QTransform::TxShear || !ok) { + qmlInfo(q) << QObject::tr("Unable to preserve appearance under complex transform"); + ok = false; + } + + qreal scale = 1; + qreal rotation = 0; + if (ok && transform.type() != QTransform::TxRotate) { + if (transform.m11() == transform.m22()) + scale = transform.m11(); + else { + qmlInfo(q) << QObject::tr("Unable to preserve appearance under non-uniform scale"); + ok = false; + } + } else if (ok && transform.type() == QTransform::TxRotate) { + if (transform.m11() == transform.m22()) + scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12()); + else { + qmlInfo(q) << QObject::tr("Unable to preserve appearance under non-uniform scale"); + ok = false; + } + + if (scale != 0) + rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI; + else { + qmlInfo(q) << QObject::tr("Unable to preserve appearance under scale of 0"); + ok = false; + } + } + + qreal xt = transform.dx(); + qreal yt = transform.dy(); + if (ok && target->transformOrigin() != QmlGraphicsItem::TopLeft) { + qreal tempxt = target->transformOriginPoint().x(); + qreal tempyt = target->transformOriginPoint().y(); + QTransform t; + t.translate(-tempxt, -tempyt); + t.rotate(rotation); + t.scale(scale, scale); + t.translate(tempxt, tempyt); + QPointF offset = t.map(QPointF(0,0)); + xt += offset.x(); + yt += offset.y(); + } + + target->setParentItem(targetParent); + if (ok) { + //qDebug() << xt << yt << rotation << scale; + target->setX(xt); + target->setY(yt); + target->setRotation(rotation); + target->setScale(scale); + } + } else if (target) { + target->setParentItem(targetParent); + } + + //restore the original stack position. + //### if stackBefore has also been reparented this won't work + if (stackBefore) + target->stackBefore(stackBefore); +} + +/*! + \preliminary + \qmlclass ParentChange QmlParentChange + \brief The ParentChange element allows you to reparent an Item in a state change. + + ParentChange reparents an Item while preserving its visual appearance (position, rotation, + and scale) on screen. You can then specify a transition to move/rotate/scale the Item to + its final intended appearance. + + ParentChange can only preserve visual appearance if no complex transforms are involved. + More specifically, it will not work if the transform property has been set for any + Items involved in the reparenting (defined as any Items in the common ancestor tree + for the original and new parent). + + You can specify at which point in a transition you want a ParentChange to occur by + using a ParentAction. +*/ + +QML_DEFINE_TYPE(Qt,4,6,ParentChange,QmlParentChange) +QmlParentChange::QmlParentChange(QObject *parent) + : QmlStateOperation(*(new QmlParentChangePrivate), parent) +{ +} + +QmlParentChange::~QmlParentChange() +{ +} + +/*! + \qmlproperty Item ParentChange::target + This property holds the item to be reparented +*/ + +QmlGraphicsItem *QmlParentChange::object() const +{ + Q_D(const QmlParentChange); + return d->target; +} + +void QmlParentChange::setObject(QmlGraphicsItem *target) +{ + Q_D(QmlParentChange); + d->target = target; +} + +/*! + \qmlproperty Item ParentChange::parent + This property holds the parent for the item in this state +*/ + +QmlGraphicsItem *QmlParentChange::parent() const +{ + Q_D(const QmlParentChange); + return d->parent; +} + +void QmlParentChange::setParent(QmlGraphicsItem *parent) +{ + Q_D(QmlParentChange); + d->parent = parent; +} + +QmlStateOperation::ActionList QmlParentChange::actions() +{ + Q_D(QmlParentChange); + if (!d->target || !d->parent) + return ActionList(); + + QmlAction a; + a.event = this; + + return ActionList() << a; +} + +class AccessibleFxItem : public QmlGraphicsItem +{ + Q_OBJECT + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QmlGraphicsItem) +public: + int siblingIndex() { + Q_D(QmlGraphicsItem); + return d->siblingIndex; + } +}; + +void QmlParentChange::saveOriginals() +{ + Q_D(QmlParentChange); + saveCurrentValues(); + d->origParent = d->rewindParent; + d->origStackBefore = d->rewindStackBefore; +} + +void QmlParentChange::execute() +{ + Q_D(QmlParentChange); + d->doChange(d->parent); +} + +bool QmlParentChange::isReversable() +{ + return true; +} + +void QmlParentChange::reverse() +{ + Q_D(QmlParentChange); + d->doChange(d->origParent, d->origStackBefore); +} + +QString QmlParentChange::typeName() const +{ + return QLatin1String("ParentChange"); +} + +bool QmlParentChange::override(QmlActionEvent*other) +{ + Q_D(QmlParentChange); + if (other->typeName() != QLatin1String("ParentChange")) + return false; + if (QmlParentChange *otherPC = static_cast<QmlParentChange*>(other)) + return (d->target == otherPC->object()); + return false; +} + +void QmlParentChange::saveCurrentValues() +{ + Q_D(QmlParentChange); + if (!d->target) { + d->rewindParent = 0; + d->rewindStackBefore = 0; + return; + } + + d->rewindParent = d->target->parentItem(); + + if (!d->rewindParent) { + d->rewindStackBefore = 0; + return; + } + + //try to determine the item's original stack position so we can restore it + int siblingIndex = ((AccessibleFxItem*)d->target)->siblingIndex() + 1; + QList<QGraphicsItem*> children = d->rewindParent->childItems(); + for (int i = 0; i < children.count(); ++i) { + QmlGraphicsItem *child = qobject_cast<QmlGraphicsItem*>(children.at(i)); + if (!child) + continue; + if (((AccessibleFxItem*)child)->siblingIndex() == siblingIndex) { + d->rewindStackBefore = child; + break; + } + } +} + +void QmlParentChange::rewind() +{ + Q_D(QmlParentChange); + d->doChange(d->rewindParent, d->rewindStackBefore); +} + +class QmlStateChangeScriptPrivate : public QObjectPrivate +{ +public: + QmlStateChangeScriptPrivate() {} + + QmlScriptString script; + QString name; +}; + +/*! + \qmlclass StateChangeScript QmlStateChangeScript + \brief The StateChangeScript element allows you to run a script in a state. + + The script specified will be run immediately when the state is made current. + Alternatively you can use a ScriptAction to specify at which point in the transition + you want the StateChangeScript to be run. +*/ +QML_DEFINE_TYPE(Qt,4,6,StateChangeScript,QmlStateChangeScript) +QmlStateChangeScript::QmlStateChangeScript(QObject *parent) +: QmlStateOperation(*(new QmlStateChangeScriptPrivate), parent) +{ +} + +QmlStateChangeScript::~QmlStateChangeScript() +{ +} + +/*! + \qmlproperty script StateChangeScript::script + This property holds the script to run when the state is current. +*/ +QmlScriptString QmlStateChangeScript::script() const +{ + Q_D(const QmlStateChangeScript); + return d->script; +} + +void QmlStateChangeScript::setScript(const QmlScriptString &s) +{ + Q_D(QmlStateChangeScript); + d->script = s; +} + +/*! + \qmlproperty script StateChangeScript::script + This property holds the name of the script. This name can be used by a + ScriptAction to target a specific script. + + \sa ScriptAction::stateChangeScriptName +*/ +QString QmlStateChangeScript::name() const +{ + Q_D(const QmlStateChangeScript); + return d->name; +} + +void QmlStateChangeScript::setName(const QString &n) +{ + Q_D(QmlStateChangeScript); + d->name = n; +} + +void QmlStateChangeScript::execute() +{ + Q_D(QmlStateChangeScript); + const QString &script = d->script.script(); + if (!script.isEmpty()) { + QmlExpression expr(d->script.context(), script, d->script.scopeObject()); + expr.setTrackChange(false); + expr.value(); + } +} + +QmlStateChangeScript::ActionList QmlStateChangeScript::actions() +{ + ActionList rv; + QmlAction a; + a.event = this; + rv << a; + return rv; +} + +QString QmlStateChangeScript::typeName() const +{ + return QLatin1String("StateChangeScript"); +} + +/*! + \qmlclass AnchorChanges QmlAnchorChanges + \brief The AnchorChanges element allows you to change the anchors of an item in a state. + + In the following example we change the top and bottom anchors of an item: + \snippet examples/declarative/anchors/anchor-changes.qml 0 + + AnchorChanges will 'inject' \c x, \c y, \c width, and \c height changes into the transition, + so you can animate them as you would normally changes to these properties: + \qml + //animate our anchor changes + NumberAnimation { matchTargets: content; matchProperties: "x,y,width,height" } + \endqml + + For more information on anchors see \l {anchor-layout}{Anchor Layouts}. +*/ + +QML_DEFINE_TYPE(Qt,4,6,AnchorChanges,QmlAnchorChanges) + +class QmlAnchorChangesPrivate : public QObjectPrivate +{ +public: + QmlAnchorChangesPrivate() : target(0) {} + + QString name; + QmlGraphicsItem *target; + QString resetString; + QStringList resetList; + QmlGraphicsAnchorLine left; + QmlGraphicsAnchorLine right; + QmlGraphicsAnchorLine horizontalCenter; + QmlGraphicsAnchorLine top; + QmlGraphicsAnchorLine bottom; + QmlGraphicsAnchorLine verticalCenter; + QmlGraphicsAnchorLine baseline; + + QmlGraphicsAnchorLine origLeft; + QmlGraphicsAnchorLine origRight; + QmlGraphicsAnchorLine origHCenter; + QmlGraphicsAnchorLine origTop; + QmlGraphicsAnchorLine origBottom; + QmlGraphicsAnchorLine origVCenter; + QmlGraphicsAnchorLine origBaseline; + + QmlGraphicsAnchorLine rewindLeft; + QmlGraphicsAnchorLine rewindRight; + QmlGraphicsAnchorLine rewindHCenter; + QmlGraphicsAnchorLine rewindTop; + QmlGraphicsAnchorLine rewindBottom; + QmlGraphicsAnchorLine rewindVCenter; + QmlGraphicsAnchorLine rewindBaseline; + + qreal fromX; + qreal fromY; + qreal fromWidth; + qreal fromHeight; +}; + +/*! + \qmlproperty Item AnchorChanges::target + This property holds the Item whose anchors will change +*/ + +QmlAnchorChanges::QmlAnchorChanges(QObject *parent) + : QmlStateOperation(*(new QmlAnchorChangesPrivate), parent) +{ +} + +QmlAnchorChanges::~QmlAnchorChanges() +{ +} + +QmlAnchorChanges::ActionList QmlAnchorChanges::actions() +{ + QmlAction a; + a.event = this; + return ActionList() << a; +} + +QmlGraphicsItem *QmlAnchorChanges::object() const +{ + Q_D(const QmlAnchorChanges); + return d->target; +} + +void QmlAnchorChanges::setObject(QmlGraphicsItem *target) +{ + Q_D(QmlAnchorChanges); + d->target = target; +} + +QString QmlAnchorChanges::reset() const +{ + Q_D(const QmlAnchorChanges); + return d->resetString; +} + +void QmlAnchorChanges::setReset(const QString &reset) +{ + Q_D(QmlAnchorChanges); + d->resetString = reset; + d->resetList = d->resetString.split(QLatin1Char(',')); + for (int i = 0; i < d->resetList.count(); ++i) + d->resetList[i] = d->resetList.at(i).trimmed(); +} + +/*! + \qmlproperty AnchorLine AnchorChanges::left + \qmlproperty AnchorLine AnchorChanges::right + \qmlproperty AnchorLine AnchorChanges::horizontalCenter + \qmlproperty AnchorLine AnchorChanges::top + \qmlproperty AnchorLine AnchorChanges::bottom + \qmlproperty AnchorLine AnchorChanges::verticalCenter + \qmlproperty AnchorLine AnchorChanges::baseline + + These properties change the respective anchors of the item. +*/ + +QmlGraphicsAnchorLine QmlAnchorChanges::left() const +{ + Q_D(const QmlAnchorChanges); + return d->left; +} + +void QmlAnchorChanges::setLeft(const QmlGraphicsAnchorLine &edge) +{ + Q_D(QmlAnchorChanges); + d->left = edge; +} + +QmlGraphicsAnchorLine QmlAnchorChanges::right() const +{ + Q_D(const QmlAnchorChanges); + return d->right; +} + +void QmlAnchorChanges::setRight(const QmlGraphicsAnchorLine &edge) +{ + Q_D(QmlAnchorChanges); + d->right = edge; +} + +QmlGraphicsAnchorLine QmlAnchorChanges::horizontalCenter() const +{ + Q_D(const QmlAnchorChanges); + return d->horizontalCenter; +} + +void QmlAnchorChanges::setHorizontalCenter(const QmlGraphicsAnchorLine &edge) +{ + Q_D(QmlAnchorChanges); + d->horizontalCenter = edge; +} + +QmlGraphicsAnchorLine QmlAnchorChanges::top() const +{ + Q_D(const QmlAnchorChanges); + return d->top; +} + +void QmlAnchorChanges::setTop(const QmlGraphicsAnchorLine &edge) +{ + Q_D(QmlAnchorChanges); + d->top = edge; +} + +QmlGraphicsAnchorLine QmlAnchorChanges::bottom() const +{ + Q_D(const QmlAnchorChanges); + return d->bottom; +} + +void QmlAnchorChanges::setBottom(const QmlGraphicsAnchorLine &edge) +{ + Q_D(QmlAnchorChanges); + d->bottom = edge; +} + +QmlGraphicsAnchorLine QmlAnchorChanges::verticalCenter() const +{ + Q_D(const QmlAnchorChanges); + return d->verticalCenter; +} + +void QmlAnchorChanges::setVerticalCenter(const QmlGraphicsAnchorLine &edge) +{ + Q_D(QmlAnchorChanges); + d->verticalCenter = edge; +} + +QmlGraphicsAnchorLine QmlAnchorChanges::baseline() const +{ + Q_D(const QmlAnchorChanges); + return d->baseline; +} + +void QmlAnchorChanges::setBaseline(const QmlGraphicsAnchorLine &edge) +{ + Q_D(QmlAnchorChanges); + d->baseline = edge; +} + +void QmlAnchorChanges::execute() +{ + Q_D(QmlAnchorChanges); + if (!d->target) + return; + + //set any anchors that have been specified + if (d->left.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setLeft(d->left); + if (d->right.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setRight(d->right); + if (d->horizontalCenter.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setHorizontalCenter(d->horizontalCenter); + if (d->top.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setTop(d->top); + if (d->bottom.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setBottom(d->bottom); + if (d->verticalCenter.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setVerticalCenter(d->verticalCenter); + if (d->baseline.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setBaseline(d->baseline); +} + +bool QmlAnchorChanges::isReversable() +{ + return true; +} + +void QmlAnchorChanges::reverse() +{ + Q_D(QmlAnchorChanges); + if (!d->target) + return; + + //restore previous anchors + if (d->origLeft.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setLeft(d->origLeft); + if (d->origRight.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setRight(d->origRight); + if (d->origHCenter.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setHorizontalCenter(d->origHCenter); + if (d->origTop.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setTop(d->origTop); + if (d->origBottom.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setBottom(d->origBottom); + if (d->origVCenter.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setVerticalCenter(d->origVCenter); + if (d->origBaseline.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setBaseline(d->origBaseline); +} + +QString QmlAnchorChanges::typeName() const +{ + return QLatin1String("AnchorChanges"); +} + +QList<QmlAction> QmlAnchorChanges::extraActions() +{ + Q_D(QmlAnchorChanges); + QList<QmlAction> extra; + + //### try to be smarter about which ones we add. + // or short-circuit later on if they haven't actually changed. + // we shouldn't set explicit width if there wasn't one before. + if (d->target) { + QmlAction a; + a.fromValue = d->fromX; + a.property = QmlMetaProperty(d->target, QLatin1String("x")); + extra << a; + + a.fromValue = d->fromY; + a.property = QmlMetaProperty(d->target, QLatin1String("y")); + extra << a; + + a.fromValue = d->fromWidth; + a.property = QmlMetaProperty(d->target, QLatin1String("width")); + extra << a; + + a.fromValue = d->fromHeight; + a.property = QmlMetaProperty(d->target, QLatin1String("height")); + extra << a; + } + + return extra; +} + +bool QmlAnchorChanges::changesBindings() +{ + return true; +} + +void QmlAnchorChanges::saveOriginals() +{ + Q_D(QmlAnchorChanges); + d->origLeft = d->target->anchors()->left(); + d->origRight = d->target->anchors()->right(); + d->origHCenter = d->target->anchors()->horizontalCenter(); + d->origTop = d->target->anchors()->top(); + d->origBottom = d->target->anchors()->bottom(); + d->origVCenter = d->target->anchors()->verticalCenter(); + d->origBaseline = d->target->anchors()->baseline(); + + saveCurrentValues(); +} + +void QmlAnchorChanges::clearForwardBindings() +{ + Q_D(QmlAnchorChanges); + d->fromX = d->target->x(); + d->fromY = d->target->y(); + d->fromWidth = d->target->width(); + d->fromHeight = d->target->height(); + + //reset any anchors that have been specified + if (d->resetList.contains(QLatin1String("left"))) + d->target->anchors()->resetLeft(); + if (d->resetList.contains(QLatin1String("right"))) + d->target->anchors()->resetRight(); + if (d->resetList.contains(QLatin1String("horizontalCenter"))) + d->target->anchors()->resetHorizontalCenter(); + if (d->resetList.contains(QLatin1String("top"))) + d->target->anchors()->resetTop(); + if (d->resetList.contains(QLatin1String("bottom"))) + d->target->anchors()->resetBottom(); + if (d->resetList.contains(QLatin1String("verticalCenter"))) + d->target->anchors()->resetVerticalCenter(); + if (d->resetList.contains(QLatin1String("baseline"))) + d->target->anchors()->resetBaseline(); + + //reset any anchors that we'll be setting in the state + if (d->left.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetLeft(); + if (d->right.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetRight(); + if (d->horizontalCenter.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetHorizontalCenter(); + if (d->top.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetTop(); + if (d->bottom.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetBottom(); + if (d->verticalCenter.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetVerticalCenter(); + if (d->baseline.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetBaseline(); +} + +void QmlAnchorChanges::clearReverseBindings() +{ + Q_D(QmlAnchorChanges); + d->fromX = d->target->x(); + d->fromY = d->target->y(); + d->fromWidth = d->target->width(); + d->fromHeight = d->target->height(); + + //reset any anchors that were set in the state + if (d->left.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetLeft(); + if (d->right.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetRight(); + if (d->horizontalCenter.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetHorizontalCenter(); + if (d->top.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetTop(); + if (d->bottom.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetBottom(); + if (d->verticalCenter.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetVerticalCenter(); + if (d->baseline.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetBaseline(); + + //reset any anchors that were set in the original state + if (d->origLeft.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetLeft(); + if (d->origRight.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetRight(); + if (d->origHCenter.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetHorizontalCenter(); + if (d->origTop.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetTop(); + if (d->origBottom.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetBottom(); + if (d->origVCenter.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetVerticalCenter(); + if (d->origBaseline.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->resetBaseline(); +} + +bool QmlAnchorChanges::override(QmlActionEvent*other) +{ + if (other->typeName() != QLatin1String("AnchorChanges")) + return false; + if (static_cast<QmlActionEvent*>(this) == other) + return true; + if (static_cast<QmlAnchorChanges*>(other)->object() == object()) + return true; + return false; +} + +void QmlAnchorChanges::rewind() +{ + Q_D(QmlAnchorChanges); + if (!d->target) + return; + + //restore previous anchors + if (d->rewindLeft.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setLeft(d->rewindLeft); + if (d->rewindRight.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setRight(d->rewindRight); + if (d->rewindHCenter.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setHorizontalCenter(d->rewindHCenter); + if (d->rewindTop.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setTop(d->rewindTop); + if (d->rewindBottom.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setBottom(d->rewindBottom); + if (d->rewindVCenter.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setVerticalCenter(d->rewindVCenter); + if (d->rewindBaseline.anchorLine != QmlGraphicsAnchorLine::Invalid) + d->target->anchors()->setBaseline(d->rewindBaseline); +} + +void QmlAnchorChanges::saveCurrentValues() +{ + Q_D(QmlAnchorChanges); + d->rewindLeft = d->target->anchors()->left(); + d->rewindRight = d->target->anchors()->right(); + d->rewindHCenter = d->target->anchors()->horizontalCenter(); + d->rewindTop = d->target->anchors()->top(); + d->rewindBottom = d->target->anchors()->bottom(); + d->rewindVCenter = d->target->anchors()->verticalCenter(); + d->rewindBaseline = d->target->anchors()->baseline(); +} + +#include <qmlstateoperations.moc> +#include <moc_qmlstateoperations_p.cpp> + +QT_END_NAMESPACE + diff --git a/src/declarative/util/qmlstateoperations_p.h b/src/declarative/util/qmlstateoperations_p.h new file mode 100644 index 0000000..1a9f76f --- /dev/null +++ b/src/declarative/util/qmlstateoperations_p.h @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** 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 QMLSTATEOPERATIONS_H +#define QMLSTATEOPERATIONS_H + +#include "qmlstate_p.h" + +#include <qmlgraphicsitem.h> +#include "private/qmlgraphicsanchors_p.h" +#include <qmlscriptstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlParentChangePrivate; +class Q_DECLARATIVE_EXPORT QmlParentChange : public QmlStateOperation, public QmlActionEvent +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlParentChange) + + Q_PROPERTY(QmlGraphicsItem *target READ object WRITE setObject) + Q_PROPERTY(QmlGraphicsItem *parent READ parent WRITE setParent) +public: + QmlParentChange(QObject *parent=0); + ~QmlParentChange(); + + QmlGraphicsItem *object() const; + void setObject(QmlGraphicsItem *); + + QmlGraphicsItem *parent() const; + void setParent(QmlGraphicsItem *); + + virtual ActionList actions(); + + virtual void saveOriginals(); + virtual void execute(); + virtual bool isReversable(); + virtual void reverse(); + virtual QString typeName() const; + virtual bool override(QmlActionEvent*other); + virtual void rewind(); + virtual void saveCurrentValues(); +}; + +class QmlStateChangeScriptPrivate; +class Q_DECLARATIVE_EXPORT QmlStateChangeScript : public QmlStateOperation, public QmlActionEvent +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlStateChangeScript) + + Q_PROPERTY(QmlScriptString script READ script WRITE setScript) + Q_PROPERTY(QString name READ name WRITE setName) + +public: + QmlStateChangeScript(QObject *parent=0); + ~QmlStateChangeScript(); + + virtual ActionList actions(); + + virtual QString typeName() const; + + QmlScriptString script() const; + void setScript(const QmlScriptString &); + + QString name() const; + void setName(const QString &); + + virtual void execute(); +}; + +class QmlAnchorChangesPrivate; +class Q_DECLARATIVE_EXPORT QmlAnchorChanges : public QmlStateOperation, public QmlActionEvent +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlAnchorChanges) + + Q_PROPERTY(QmlGraphicsItem *target READ object WRITE setObject) + Q_PROPERTY(QString reset READ reset WRITE setReset) + Q_PROPERTY(QmlGraphicsAnchorLine left READ left WRITE setLeft) + Q_PROPERTY(QmlGraphicsAnchorLine right READ right WRITE setRight) + Q_PROPERTY(QmlGraphicsAnchorLine horizontalCenter READ horizontalCenter WRITE setHorizontalCenter) + Q_PROPERTY(QmlGraphicsAnchorLine top READ top WRITE setTop) + Q_PROPERTY(QmlGraphicsAnchorLine bottom READ bottom WRITE setBottom) + Q_PROPERTY(QmlGraphicsAnchorLine verticalCenter READ verticalCenter WRITE setVerticalCenter) + Q_PROPERTY(QmlGraphicsAnchorLine baseline READ baseline WRITE setBaseline) + +public: + QmlAnchorChanges(QObject *parent=0); + ~QmlAnchorChanges(); + + virtual ActionList actions(); + + QmlGraphicsItem *object() const; + void setObject(QmlGraphicsItem *); + + QString reset() const; + void setReset(const QString &); + + QmlGraphicsAnchorLine left() const; + void setLeft(const QmlGraphicsAnchorLine &edge); + + QmlGraphicsAnchorLine right() const; + void setRight(const QmlGraphicsAnchorLine &edge); + + QmlGraphicsAnchorLine horizontalCenter() const; + void setHorizontalCenter(const QmlGraphicsAnchorLine &edge); + + QmlGraphicsAnchorLine top() const; + void setTop(const QmlGraphicsAnchorLine &edge); + + QmlGraphicsAnchorLine bottom() const; + void setBottom(const QmlGraphicsAnchorLine &edge); + + QmlGraphicsAnchorLine verticalCenter() const; + void setVerticalCenter(const QmlGraphicsAnchorLine &edge); + + QmlGraphicsAnchorLine baseline() const; + void setBaseline(const QmlGraphicsAnchorLine &edge); + + virtual void execute(); + virtual bool isReversable(); + virtual void reverse(); + virtual QString typeName() const; + virtual bool override(QmlActionEvent*other); + virtual QList<QmlAction> extraActions(); + virtual bool changesBindings(); + virtual void saveOriginals(); + virtual void clearForwardBindings(); + virtual void clearReverseBindings(); + virtual void rewind(); + virtual void saveCurrentValues(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlParentChange) +QML_DECLARE_TYPE(QmlStateChangeScript) +QML_DECLARE_TYPE(QmlAnchorChanges) + +QT_END_HEADER + +#endif // QMLSTATEOPERATIONS_H diff --git a/src/declarative/util/qmlstyledtext.cpp b/src/declarative/util/qmlstyledtext.cpp new file mode 100644 index 0000000..36b5e49 --- /dev/null +++ b/src/declarative/util/qmlstyledtext.cpp @@ -0,0 +1,343 @@ +/**************************************************************************** +** +** 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 <QStack> +#include <QVector> +#include <QPainter> +#include <QTextLayout> +#include <QDebug> +#include <qmath.h> +#include "qmlstyledtext_p.h" + +/* + QmlStyledText supports few tags: + + <b></b> - bold + <i></i> - italic + <br> - new line + <font color="color_name" size="1-7"></font> + + The opening and closing tags must be correctly nested. +*/ + +class QmlStyledTextPrivate +{ +public: + 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); + + inline void skipSpace(const QChar *&ch) { + while (ch->isSpace() && !ch->isNull()) + ++ch; + } + + QString text; + QTextLayout &layout; + QFont baseFont; + + static const QChar lessThan; + static const QChar greaterThan; + static const QChar equals; + static const QChar singleQuote; + static const QChar doubleQuote; + static const QChar slash; + static const QChar ampersand; +}; + +const QChar QmlStyledTextPrivate::lessThan(QLatin1Char('<')); +const QChar QmlStyledTextPrivate::greaterThan(QLatin1Char('>')); +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)) +{ +} + +QmlStyledText::~QmlStyledText() +{ + delete d; +} + +void QmlStyledText::parse(const QString &string, QTextLayout &layout) +{ + QmlStyledText styledText(string, layout); + styledText.d->parse(); +} + +void QmlStyledTextPrivate::parse() +{ + QList<QTextLayout::FormatRange> ranges; + QStack<QTextCharFormat> formatStack; + + QString drawText; + drawText.reserve(text.count()); + + int textStart = 0; + int textLength = 0; + int rangeStart = 0; + const QChar *ch = text.constData(); + while (!ch->isNull()) { + if (*ch == lessThan) { + 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; + if (parseCloseTag(ch, text)) + formatStack.pop(); + } else { + QTextCharFormat format; + if (formatStack.count()) + format = formatStack.top(); + else + 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) + 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); +} + +bool QmlStyledTextPrivate::parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format) +{ + skipSpace(ch); + + int tagStart = ch - textIn.constData(); + int tagLength = 0; + while (!ch->isNull()) { + if (*ch == greaterThan) { + QStringRef tag(&textIn, tagStart, tagLength); + const QChar char0 = tag.at(0); + if (char0 == QLatin1Char('b')) { + if (tagLength == 1) { + format.setFontWeight(QFont::Bold); + return true; + } else if (tagLength == 2 && tag.at(1) == QLatin1Char('r')) { + 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()) { + // may have params. + QStringRef tag(&textIn, tagStart, tagLength); + if (tag == QLatin1String("font")) + return parseFontAttributes(ch, textIn, format); + if (*ch == greaterThan || ch->isNull()) + continue; + } else if (*ch != slash){ + tagLength++; + } + ++ch; + } + + return false; +} + +bool QmlStyledTextPrivate::parseCloseTag(const QChar *&ch, const QString &textIn) +{ + skipSpace(ch); + + int tagStart = ch - textIn.constData(); + int tagLength = 0; + while (!ch->isNull()) { + if (*ch == greaterThan) { + QStringRef tag(&textIn, tagStart, tagLength); + 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; + } + return false; + } else if (!ch->isSpace()){ + tagLength++; + } + ++ch; + } + + 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; + QPair<QStringRef,QStringRef> attr; + do { + attr = parseAttribute(ch, textIn); + 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()); + + return valid; +} + +QPair<QStringRef,QStringRef> QmlStyledTextPrivate::parseAttribute(const QChar *&ch, const QString &textIn) +{ + skipSpace(ch); + + int attrStart = ch - textIn.constData(); + int attrLength = 0; + while (!ch->isNull()) { + if (*ch == greaterThan) { + break; + } else if (*ch == equals) { + ++ch; + if (*ch != singleQuote && *ch != doubleQuote) { + while (*ch != greaterThan && !ch->isNull()) + ++ch; + break; + } + ++ch; + if (!attrLength) + break; + QStringRef attr(&textIn, attrStart, attrLength); + QStringRef val = parseValue(ch, textIn); + if (!val.isEmpty()) + return QPair<QStringRef,QStringRef>(attr,val); + break; + } else { + ++attrLength; + } + ++ch; + } + + return QPair<QStringRef,QStringRef>(); +} + +QStringRef QmlStyledTextPrivate::parseValue(const QChar *&ch, const QString &textIn) +{ + int valStart = ch - textIn.constData(); + int valLength = 0; + while (*ch != singleQuote && *ch != doubleQuote && !ch->isNull()) { + ++valLength; + ++ch; + } + if (ch->isNull()) + return QStringRef(); + ++ch; // skip quote + + return QStringRef(&textIn, valStart, valLength); +} diff --git a/src/declarative/util/qmlstyledtext_p.h b/src/declarative/util/qmlstyledtext_p.h new file mode 100644 index 0000000..0cfb43e --- /dev/null +++ b/src/declarative/util/qmlstyledtext_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** 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 QMLSTYLEDTEXT_H +#define QMLSTYLEDTEXT_H + +#include <QSizeF> + +class QPainter; +class QPointF; +class QString; +class QmlStyledTextPrivate; +class Q_DECLARATIVE_EXPORT QmlStyledText +{ +public: + static void parse(const QString &string, QTextLayout &layout); + +private: + QmlStyledText(const QString &string, QTextLayout &layout); + ~QmlStyledText(); + + QmlStyledTextPrivate *d; +}; + +#endif diff --git a/src/declarative/util/qmlsystempalette.cpp b/src/declarative/util/qmlsystempalette.cpp new file mode 100644 index 0000000..39d8f3e --- /dev/null +++ b/src/declarative/util/qmlsystempalette.cpp @@ -0,0 +1,303 @@ +/**************************************************************************** +** +** 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 "qmlsystempalette_p.h" + +#include <QApplication> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QmlSystemPalettePrivate : public QObjectPrivate +{ +public: + QPalette palette; + QPalette::ColorGroup group; +}; + +QML_DEFINE_TYPE(Qt,4,6,SystemPalette,QmlSystemPalette) + +/*! + \qmlclass SystemPalette QmlSystemPalette + \ingroup group_utility + \brief The SystemPalette item gives access to the Qt palettes. + \sa QPalette + + Example: + \qml + SystemPalette { id: myPalette; colorGroup: Qt.Active } + + Rectangle { + width: 640; height: 480 + color: myPalette.window + Text { + anchors.fill: parent + text: "Hello!"; color: myPalette.windowText + } + } + \endqml +*/ +QmlSystemPalette::QmlSystemPalette(QObject *parent) + : QObject(*(new QmlSystemPalettePrivate), parent) +{ + Q_D(QmlSystemPalette); + d->palette = QApplication::palette(); + d->group = QPalette::Active; + qApp->installEventFilter(this); +} + +QmlSystemPalette::~QmlSystemPalette() +{ +} + +/*! + \qmlproperty color SystemPalette::window + The window (general background) color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QmlSystemPalette::window() const +{ + Q_D(const QmlSystemPalette); + return d->palette.color(d->group, QPalette::Window); +} + +/*! + \qmlproperty color SystemPalette::windowText + The window text (general foreground) color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QmlSystemPalette::windowText() const +{ + Q_D(const QmlSystemPalette); + return d->palette.color(d->group, QPalette::WindowText); +} + +/*! + \qmlproperty color SystemPalette::base + The base color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QmlSystemPalette::base() const +{ + Q_D(const QmlSystemPalette); + return d->palette.color(d->group, QPalette::Base); +} + +/*! + \qmlproperty color SystemPalette::text + The text color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QmlSystemPalette::text() const +{ + Q_D(const QmlSystemPalette); + return d->palette.color(d->group, QPalette::Text); +} + +/*! + \qmlproperty color SystemPalette::alternateBase + The alternate base color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QmlSystemPalette::alternateBase() const +{ + Q_D(const QmlSystemPalette); + return d->palette.color(d->group, QPalette::AlternateBase); +} + +/*! + \qmlproperty color SystemPalette::button + The button color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QmlSystemPalette::button() const +{ + Q_D(const QmlSystemPalette); + return d->palette.color(d->group, QPalette::Button); +} + +/*! + \qmlproperty color SystemPalette::buttonText + The button text foreground color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QmlSystemPalette::buttonText() const +{ + Q_D(const QmlSystemPalette); + return d->palette.color(d->group, QPalette::ButtonText); +} + +/*! + \qmlproperty color SystemPalette::light + The light color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QmlSystemPalette::light() const +{ + Q_D(const QmlSystemPalette); + return d->palette.color(d->group, QPalette::Light); +} + +/*! + \qmlproperty color SystemPalette::midlight + The midlight color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QmlSystemPalette::midlight() const +{ + Q_D(const QmlSystemPalette); + return d->palette.color(d->group, QPalette::Midlight); +} + +/*! + \qmlproperty color SystemPalette::dark + The dark color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QmlSystemPalette::dark() const +{ + Q_D(const QmlSystemPalette); + return d->palette.color(d->group, QPalette::Dark); +} + +/*! + \qmlproperty color SystemPalette::mid + The mid color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QmlSystemPalette::mid() const +{ + Q_D(const QmlSystemPalette); + return d->palette.color(d->group, QPalette::Mid); +} + +/*! + \qmlproperty color SystemPalette::shadow + The shadow color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QmlSystemPalette::shadow() const +{ + Q_D(const QmlSystemPalette); + return d->palette.color(d->group, QPalette::Shadow); +} + +/*! + \qmlproperty color SystemPalette::highlight + The highlight color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QmlSystemPalette::highlight() const +{ + Q_D(const QmlSystemPalette); + return d->palette.color(d->group, QPalette::Highlight); +} + +/*! + \qmlproperty color SystemPalette::highlightedText + The highlighted text color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QmlSystemPalette::highlightedText() const +{ + Q_D(const QmlSystemPalette); + return d->palette.color(d->group, QPalette::HighlightedText); +} + +/*! + \qmlproperty QmlSystemPalette::ColorGroup SystemPalette::colorGroup + + The color group of the palette. It can be Active, Inactive or Disabled. + Active is the default. + + \sa QPalette::ColorGroup +*/ +QmlSystemPalette::ColorGroup QmlSystemPalette::colorGroup() const +{ + Q_D(const QmlSystemPalette); + return (QmlSystemPalette::ColorGroup)d->group; +} + +void QmlSystemPalette::setColorGroup(QmlSystemPalette::ColorGroup colorGroup) +{ + Q_D(QmlSystemPalette); + d->group = (QPalette::ColorGroup)colorGroup; + emit paletteChanged(); +} + +bool QmlSystemPalette::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == qApp) { + if (event->type() == QEvent::ApplicationPaletteChange) { + QApplication::postEvent(this, new QEvent(QEvent::ApplicationPaletteChange)); + return false; + } + } + return QObject::eventFilter(watched, event); +} + +bool QmlSystemPalette::event(QEvent *event) +{ + Q_D(QmlSystemPalette); + if (event->type() == QEvent::ApplicationPaletteChange) { + d->palette = QApplication::palette(); + emit paletteChanged(); + return true; + } + return QObject::event(event); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlsystempalette_p.h b/src/declarative/util/qmlsystempalette_p.h new file mode 100644 index 0000000..7c39d4a --- /dev/null +++ b/src/declarative/util/qmlsystempalette_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** 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 QMLSYSTEMPALETTE_H +#define QMLSYSTEMPALETTE_H + +#include <qml.h> + +#include <QtCore/qobject.h> +#include <QPalette> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlSystemPalettePrivate; +class Q_DECLARATIVE_EXPORT QmlSystemPalette : public QObject +{ + Q_OBJECT + Q_ENUMS(ColorGroup) + Q_DECLARE_PRIVATE(QmlSystemPalette) + + Q_PROPERTY(QmlSystemPalette::ColorGroup colorGroup READ colorGroup WRITE setColorGroup NOTIFY paletteChanged) + Q_PROPERTY(QColor window READ window NOTIFY paletteChanged) + Q_PROPERTY(QColor windowText READ windowText NOTIFY paletteChanged) + Q_PROPERTY(QColor base READ base NOTIFY paletteChanged) + Q_PROPERTY(QColor text READ text NOTIFY paletteChanged) + Q_PROPERTY(QColor alternateBase READ alternateBase NOTIFY paletteChanged) + Q_PROPERTY(QColor button READ button NOTIFY paletteChanged) + Q_PROPERTY(QColor buttonText READ buttonText NOTIFY paletteChanged) + Q_PROPERTY(QColor light READ light NOTIFY paletteChanged) + Q_PROPERTY(QColor midlight READ midlight NOTIFY paletteChanged) + Q_PROPERTY(QColor dark READ dark NOTIFY paletteChanged) + Q_PROPERTY(QColor mid READ mid NOTIFY paletteChanged) + Q_PROPERTY(QColor shadow READ shadow NOTIFY paletteChanged) + Q_PROPERTY(QColor highlight READ highlight NOTIFY paletteChanged) + Q_PROPERTY(QColor highlightedText READ highlightedText NOTIFY paletteChanged) + +public: + QmlSystemPalette(QObject *parent=0); + ~QmlSystemPalette(); + + enum ColorGroup { Active = QPalette::Active, Inactive = QPalette::Inactive, Disabled = QPalette::Disabled }; + + QColor window() const; + QColor windowText() const; + + QColor base() const; + QColor text() const; + QColor alternateBase() const; + + QColor button() const; + QColor buttonText() const; + + QColor light() const; + QColor midlight() const; + QColor dark() const; + QColor mid() const; + QColor shadow() const; + + QColor highlight() const; + QColor highlightedText() const; + + QmlSystemPalette::ColorGroup colorGroup() const; + void setColorGroup(QmlSystemPalette::ColorGroup); + +Q_SIGNALS: + void paletteChanged(); + +private: + bool eventFilter(QObject *watched, QEvent *event); + bool event(QEvent *event); + +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlSystemPalette) + +QT_END_HEADER + +#endif // QMLSYSTEMPALETTE_H diff --git a/src/declarative/util/qmltimeline.cpp b/src/declarative/util/qmltimeline.cpp new file mode 100644 index 0000000..130e02d --- /dev/null +++ b/src/declarative/util/qmltimeline.cpp @@ -0,0 +1,941 @@ +/**************************************************************************** +** +** 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 "qmltimeline_p_p.h" + +#include <QDebug> +#include <QMutex> +#include <QThread> +#include <QWaitCondition> +#include <QEvent> +#include <QCoreApplication> +#include <QEasingCurve> +#include <QTime> + +QT_BEGIN_NAMESPACE + +struct Update { + Update(QmlTimeLineValue *_g, qreal _v) + : g(_g), v(_v) {} + Update(const QmlTimeLineEvent &_e) + : g(0), v(0), e(_e) {} + + QmlTimeLineValue *g; + qreal v; + QmlTimeLineEvent e; +}; + +struct QmlTimeLinePrivate +{ + QmlTimeLinePrivate(QmlTimeLine *); + + struct Op { + enum Type { + Pause, + Set, + Move, + MoveBy, + Accel, + AccelDistance, + Execute + }; + Op() {} + Op(Type t, int l, qreal v, qreal v2, int o, + const QmlTimeLineEvent &ev = QmlTimeLineEvent(), const QEasingCurve &es = QEasingCurve()) + : type(t), length(l), value(v), value2(v2), order(o), event(ev), + easing(es) {} + Op(const Op &o) + : type(o.type), length(o.length), value(o.value), value2(o.value2), + order(o.order), event(o.event), easing(o.easing) {} + Op &operator=(const Op &o) { + type = o.type; length = o.length; value = o.value; + value2 = o.value2; order = o.order; event = o.event; + easing = o.easing; + return *this; + } + + Type type; + int length; + qreal value; + qreal value2; + + int order; + QmlTimeLineEvent event; + QEasingCurve easing; + }; + struct TimeLine + { + TimeLine() : length(0), consumedOpLength(0), base(0.) {} + QList<Op> ops; + int length; + int consumedOpLength; + qreal base; + }; + + int length; + int syncPoint; + typedef QHash<QmlTimeLineObject *, TimeLine> Ops; + Ops ops; + QmlTimeLine *q; + + void add(QmlTimeLineObject &, const Op &); + qreal value(const Op &op, int time, qreal base, bool *) const; + + int advance(int); + + bool clockRunning; + int prevTime; + + int order; + + QmlTimeLine::SyncMode syncMode; + int syncAdj; + QList<QPair<int, Update> > *updateQueue; +}; + +QmlTimeLinePrivate::QmlTimeLinePrivate(QmlTimeLine *parent) +: length(0), syncPoint(0), q(parent), clockRunning(false), prevTime(0), order(0), syncMode(QmlTimeLine::LocalSync), syncAdj(0), updateQueue(0) +{ +} + +void QmlTimeLinePrivate::add(QmlTimeLineObject &g, const Op &o) +{ + if (g._t && g._t != q) { + qWarning() << "QmlTimeLine: Cannot modify a QmlTimeLineValue owned by" + << "another timeline."; + return; + } + g._t = q; + + Ops::Iterator iter = ops.find(&g); + if (iter == ops.end()) { + iter = ops.insert(&g, TimeLine()); + if (syncPoint > 0) + q->pause(g, syncPoint); + } + if (!iter->ops.isEmpty() && + o.type == Op::Pause && + iter->ops.last().type == Op::Pause) { + iter->ops.last().length += o.length; + iter->length += o.length; + } else { + iter->ops.append(o); + iter->length += o.length; + } + + if (iter->length > length) + length = iter->length; + + if (!clockRunning) { + q->stop(); + prevTime = 0; + clockRunning = true; + + if (syncMode == QmlTimeLine::LocalSync) { + syncAdj = -1; + } else { + syncAdj = 0; + } + q->start(); +/* q->tick(0); + if (syncMode == QmlTimeLine::LocalSync) { + syncAdj = -1; + } else { + syncAdj = 0; + } + */ + } +} + +qreal QmlTimeLinePrivate::value(const Op &op, int time, qreal base, bool *changed) const +{ + Q_ASSERT(time >= 0); + Q_ASSERT(time <= op.length); + *changed = true; + + switch(op.type) { + case Op::Pause: + *changed = false; + return base; + case Op::Set: + return op.value; + case Op::Move: + if (time == 0) { + return base; + } else if (time == (op.length)) { + return op.value; + } else { + qreal delta = op.value - base; + qreal pTime = (qreal)(time) / (qreal)op.length; + if (op.easing.type() == QEasingCurve::Linear) + return base + delta * pTime; + else + return base + delta * op.easing.valueForProgress(pTime); + } + case Op::MoveBy: + if (time == 0) { + return base; + } else if (time == (op.length)) { + return base + op.value; + } else { + qreal delta = op.value; + qreal pTime = (qreal)(time) / (qreal)op.length; + if (op.easing.type() == QEasingCurve::Linear) + return base + delta * pTime; + else + return base + delta * op.easing.valueForProgress(pTime); + } + case Op::Accel: + if (time == 0) { + return base; + } else { + qreal t = (qreal)(time) / 1000.0f; + qreal delta = op.value * t + 0.5f * op.value2 * t * t; + return base + delta; + } + case Op::AccelDistance: + if (time == 0) { + return base; + } else if (time == (op.length)) { + return base + op.value2; + } else { + qreal t = (qreal)(time) / 1000.0f; + qreal accel = -1.0f * 1000.0f * op.value / (qreal)op.length; + qreal delta = op.value * t + 0.5f * accel * t * t; + return base + delta; + + } + case Op::Execute: + op.event.execute(); + *changed = false; + return -1; + } + + return base; +} + +/*! + \internal + \class QmlTimeLine + \ingroup group_animation + \brief The QmlTimeLine class provides a timeline for controlling animations. + + QmlTimeLine is similar to QTimeLine except: + \list + \i It updates QmlTimeLineValue instances directly, rather than maintaining a single + current value. + + For example, the following animates a simple value over 200 milliseconds: + \code + QmlTimeLineValue v(<starting value>); + QmlTimeLine tl; + tl.move(v, 100., 200); + tl.start() + \endcode + + If your program needs to know when values are changed, it can either + connect to the QmlTimeLine's updated() signal, or inherit from QmlTimeLineValue + and reimplement the QmlTimeLineValue::setValue() method. + + \i Supports multiple QmlTimeLineValue, arbitrary start and end values and allows + animations to be strung together for more complex effects. + + For example, the following animation moves the x and y coordinates of + an object from wherever they are to the position (100, 100) in 50 + milliseconds and then further animates them to (100, 200) in 50 + milliseconds: + + \code + QmlTimeLineValue x(<starting value>); + QmlTimeLineValue y(<starting value>); + + QmlTimeLine tl; + tl.start(); + + tl.move(x, 100., 50); + tl.move(y, 100., 50); + tl.move(y, 200., 50); + \endcode + + \i All QmlTimeLine instances share a single, synchronized clock. + + Actions scheduled within the same event loop tick are scheduled + synchronously against each other, regardless of the wall time between the + scheduling. Synchronized scheduling applies both to within the same + QmlTimeLine and across separate QmlTimeLine's within the same process. + + \endlist + + Currently easing functions are not supported. +*/ + + +/*! + Construct a new QmlTimeLine with the specified \a parent. +*/ +QmlTimeLine::QmlTimeLine(QObject *parent) +: QAbstractAnimation(parent) +{ + d = new QmlTimeLinePrivate(this); +} + +/*! + Destroys the time line. Any inprogress animations are canceled, but not + completed. +*/ +QmlTimeLine::~QmlTimeLine() +{ + for (QmlTimeLinePrivate::Ops::Iterator iter = d->ops.begin(); + iter != d->ops.end(); + ++iter) + iter.key()->_t = 0; + + delete d; d = 0; +} + +/*! + \enum QmlTimeLine::SyncMode + */ + +/*! + Return the timeline's synchronization mode. + */ +QmlTimeLine::SyncMode QmlTimeLine::syncMode() const +{ + return d->syncMode; +} + +/*! + Set the timeline's synchronization mode to \a syncMode. + */ +void QmlTimeLine::setSyncMode(SyncMode syncMode) +{ + d->syncMode = syncMode; +} + +/*! + Pause \a obj for \a time milliseconds. +*/ +void QmlTimeLine::pause(QmlTimeLineObject &obj, int time) +{ + if (time <= 0) return; + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::Pause, time, 0., 0., d->order++); + d->add(obj, op); +} + +/*! + Execute the \a event. + */ +void QmlTimeLine::execute(const QmlTimeLineEvent &event) +{ + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::Execute, 0, 0, 0., d->order++, event); + d->add(*event.eventObject(), op); +} + +/*! + Set the \a value of \a timeLineValue. +*/ +void QmlTimeLine::set(QmlTimeLineValue &timeLineValue, qreal value) +{ + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::Set, 0, value, 0., d->order++); + d->add(timeLineValue, op); +} + +/*! + Decelerate \a timeLineValue from the starting \a velocity to zero at the + given \a acceleration rate. Although the \a acceleration is technically + a deceleration, it should always be positive. The QmlTimeLine will ensure + that the deceleration is in the opposite direction to the initial velocity. +*/ +int QmlTimeLine::accel(QmlTimeLineValue &timeLineValue, qreal velocity, qreal acceleration) +{ + if ((velocity > 0.0f) == (acceleration > 0.0f)) + acceleration = acceleration * -1.0f; + + int time = static_cast<int>(-1000 * velocity / acceleration); + + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++); + d->add(timeLineValue, op); + + return time; +} + +/*! + \overload + + Decelerate \a timeLineValue from the starting \a velocity to zero at the + given \a acceleration rate over a maximum distance of maxDistance. + + If necessary, QmlTimeLine will reduce the acceleration to ensure that the + entire operation does not require a move of more than \a maxDistance. + \a maxDistance should always be positive. +*/ +int QmlTimeLine::accel(QmlTimeLineValue &timeLineValue, qreal velocity, qreal acceleration, qreal maxDistance) +{ + Q_ASSERT(acceleration >= 0.0f && maxDistance >= 0.0f); + + qreal maxAccel = (velocity * velocity) / (2.0f * maxDistance); + if (maxAccel > acceleration) + acceleration = maxAccel; + + if ((velocity > 0.0f) == (acceleration > 0.0f)) + acceleration = acceleration * -1.0f; + + int time = static_cast<int>(-1000 * velocity / acceleration); + + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++); + d->add(timeLineValue, op); + + return time; +} + +/*! + Decelerate \a timeLineValue from the starting \a velocity to zero over the given + \a distance. This is like accel(), but the QmlTimeLine calculates the exact + deceleration to use. + + \a distance should be positive. +*/ +int QmlTimeLine::accelDistance(QmlTimeLineValue &timeLineValue, qreal velocity, qreal distance) +{ + if (distance == 0.0f || velocity == 0.0f) + return -1; + Q_ASSERT((distance >= 0.0f) == (velocity >= 0.0f)); + + int time = static_cast<int>(1000 * (2.0f * distance) / velocity); + + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::AccelDistance, time, velocity, distance, d->order++); + d->add(timeLineValue, op); + + return time; +} + +/*! + Linearly change the \a timeLineValue from its current value to the given + \a destination value over \a time milliseconds. +*/ +void QmlTimeLine::move(QmlTimeLineValue &timeLineValue, qreal destination, int time) +{ + if (time <= 0) return; + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++); + d->add(timeLineValue, op); +} + +/*! + Change the \a timeLineValue from its current value to the given \a destination + value over \a time milliseconds using the \a easing curve. + */ +void QmlTimeLine::move(QmlTimeLineValue &timeLineValue, qreal destination, const QEasingCurve &easing, int time) +{ + if (time <= 0) return; + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++, QmlTimeLineEvent(), easing); + d->add(timeLineValue, op); +} + +/*! + Linearly change the \a timeLineValue from its current value by the \a change amount + over \a time milliseconds. +*/ +void QmlTimeLine::moveBy(QmlTimeLineValue &timeLineValue, qreal change, int time) +{ + if (time <= 0) return; + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++); + d->add(timeLineValue, op); +} + +/*! + Change the \a timeLineValue from its current value by the \a change amount over + \a time milliseconds using the \a easing curve. + */ +void QmlTimeLine::moveBy(QmlTimeLineValue &timeLineValue, qreal change, const QEasingCurve &easing, int time) +{ + if (time <= 0) return; + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++, QmlTimeLineEvent(), easing); + d->add(timeLineValue, op); +} + +/*! + Cancel (but don't complete) all scheduled actions for \a timeLineValue. +*/ +void QmlTimeLine::reset(QmlTimeLineValue &timeLineValue) +{ + if (!timeLineValue._t) + return; + if (timeLineValue._t != this) { + qWarning() << "QmlTimeLine: Cannot reset a QmlTimeLineValue owned by another timeline."; + return; + } + remove(&timeLineValue); + timeLineValue._t = 0; +} + +int QmlTimeLine::duration() const +{ + return -1; +} + +/*! + Synchronize the end point of \a timeLineValue to the endpoint of \a syncTo + within this timeline. + + Following operations on \a timeLineValue in this timeline will be scheduled after + all the currently scheduled actions on \a syncTo are complete. In + psuedo-code this is equivalent to: + \code + QmlTimeLine::pause(timeLineValue, min(0, length_of(syncTo) - length_of(timeLineValue))) + \endcode +*/ +void QmlTimeLine::sync(QmlTimeLineValue &timeLineValue, QmlTimeLineValue &syncTo) +{ + QmlTimeLinePrivate::Ops::Iterator iter = d->ops.find(&syncTo); + if (iter == d->ops.end()) + return; + int length = iter->length; + + iter = d->ops.find(&timeLineValue); + if (iter == d->ops.end()) { + pause(timeLineValue, length); + } else { + int glength = iter->length; + pause(timeLineValue, length - glength); + } +} + +/*! + Synchronize the end point of \a timeLineValue to the endpoint of the longest + action cursrently scheduled in the timeline. + + In psuedo-code, this is equivalent to: + \code + QmlTimeLine::pause(timeLineValue, length_of(timeline) - length_of(timeLineValue)) + \endcode +*/ +void QmlTimeLine::sync(QmlTimeLineValue &timeLineValue) +{ + QmlTimeLinePrivate::Ops::Iterator iter = d->ops.find(&timeLineValue); + if (iter == d->ops.end()) { + pause(timeLineValue, d->length); + } else { + pause(timeLineValue, d->length - iter->length); + } +} + +/* + Synchronize all currently and future scheduled values in this timeline to + the longest action currently scheduled. + + For example: + \code + value1->setValue(0.); + value2->setValue(0.); + value3->setValue(0.); + QmlTimeLine tl; + ... + tl.move(value1, 10, 200); + tl.move(value2, 10, 100); + tl.sync(); + tl.move(value2, 20, 100); + tl.move(value3, 20, 100); + \endcode + + will result in: + + \table + \header \o \o 0ms \o 50ms \o 100ms \o 150ms \o 200ms \o 250ms \o 300ms + \row \o value1 \o 0 \o 2.5 \o 5.0 \o 7.5 \o 10 \o 10 \o 10 + \row \o value2 \o 0 \o 5.0 \o 10.0 \o 10.0 \o 10.0 \o 15.0 \o 20.0 + \row \o value2 \o 0 \o 0 \o 0 \o 0 \o 0 \o 10.0 \o 20.0 + \endtable +*/ + +/*void QmlTimeLine::sync() +{ + for (QmlTimeLinePrivate::Ops::Iterator iter = d->ops.begin(); + iter != d->ops.end(); + ++iter) + pause(*iter.key(), d->length - iter->length); + d->syncPoint = d->length; +}*/ + +/*! + \internal + + Temporary hack. + */ +void QmlTimeLine::setSyncPoint(int sp) +{ + d->syncPoint = sp; +} + +/*! + \internal + + Temporary hack. + */ +int QmlTimeLine::syncPoint() const +{ + return d->syncPoint; +} + +/*! + Returns true if the timeline is active. An active timeline is one where + QmlTimeLineValue actions are still pending. +*/ +bool QmlTimeLine::isActive() const +{ + return !d->ops.isEmpty(); +} + +/*! + Completes the timeline. All queued actions are played to completion, and then discarded. For example, + \code + QmlTimeLineValue v(0.); + QmlTimeLine tl; + tl.move(v, 100., 1000.); + // 500 ms passes + // v.value() == 50. + tl.complete(); + // v.value() == 100. + \endcode +*/ +void QmlTimeLine::complete() +{ + d->advance(d->length); +} + +/*! + Resets the timeline. All queued actions are discarded and QmlTimeLineValue's retain their current value. For example, + \code + QmlTimeLineValue v(0.); + QmlTimeLine tl; + tl.move(v, 100., 1000.); + // 500 ms passes + // v.value() == 50. + tl.clear(); + // v.value() == 50. + \endcode +*/ +void QmlTimeLine::clear() +{ + for (QmlTimeLinePrivate::Ops::ConstIterator iter = d->ops.begin(); iter != d->ops.end(); ++iter) + iter.key()->_t = 0; + d->ops.clear(); + d->length = 0; + d->syncPoint = 0; + //XXX need stop here? +} + +int QmlTimeLine::time() const +{ + return d->prevTime; +} + +/*! + \fn void QmlTimeLine::updated() + + Emitted each time the timeline modifies QmlTimeLineValues. Even if multiple + QmlTimeLineValues are changed, this signal is only emitted once for each clock tick. +*/ + +void QmlTimeLine::updateCurrentTime(int v) +{ + if (d->syncAdj == -1) + d->syncAdj = v; + v -= d->syncAdj; + + int timeChanged = v - d->prevTime; +#if 0 + if (!timeChanged) + return; +#endif + d->prevTime = v; + d->advance(timeChanged); + emit updated(); + + // Do we need to stop the clock? + if (d->ops.isEmpty()) { + stop(); + d->prevTime = 0; + d->clockRunning = false; + emit completed(); + } /*else if (pauseTime > 0) { + GfxClock::cancelClock(); + d->prevTime = 0; + GfxClock::pauseFor(pauseTime); + d->syncAdj = 0; + d->clockRunning = false; + }*/ else if (/*!GfxClock::isActive()*/ state() != Running) { + stop(); + d->prevTime = 0; + d->clockRunning = true; + d->syncAdj = 0; + start(); + } +} + +bool operator<(const QPair<int, Update> &lhs, + const QPair<int, Update> &rhs) +{ + return lhs.first < rhs.first; +} + +int QmlTimeLinePrivate::advance(int t) +{ + int pauseTime = -1; + + // XXX - surely there is a more efficient way? + do { + pauseTime = -1; + // Minimal advance time + int advanceTime = t; + for (Ops::Iterator iter = ops.begin(); iter != ops.end(); ++iter) { + TimeLine &tl = *iter; + Op &op = tl.ops.first(); + int length = op.length - tl.consumedOpLength; + + if (length < advanceTime) { + advanceTime = length; + if (advanceTime == 0) + break; + } + } + t -= advanceTime; + + // Process until then. A zero length advance time will only process + // sets. + QList<QPair<int, Update> > updates; + + for (Ops::Iterator iter = ops.begin(); iter != ops.end(); ) { + QmlTimeLineValue *v = static_cast<QmlTimeLineValue *>(iter.key()); + TimeLine &tl = *iter; + Q_ASSERT(!tl.ops.isEmpty()); + + do { + Op &op = tl.ops.first(); + if (advanceTime == 0 && op.length != 0) + continue; + + if (tl.consumedOpLength == 0 && + op.type != Op::Pause && + op.type != Op::Execute) + tl.base = v->value(); + + if ((tl.consumedOpLength + advanceTime) == op.length) { + if (op.type == Op::Execute) { + updates << qMakePair(op.order, Update(op.event)); + } else { + bool changed = false; + qreal val = value(op, op.length, tl.base, &changed); + if (changed) + updates << qMakePair(op.order, Update(v, val)); + } + tl.length -= qMin(advanceTime, tl.length); + tl.consumedOpLength = 0; + tl.ops.removeFirst(); + } else { + tl.consumedOpLength += advanceTime; + bool changed = false; + qreal val = value(op, tl.consumedOpLength, tl.base, &changed); + if (changed) + updates << qMakePair(op.order, Update(v, val)); + tl.length -= qMin(advanceTime, tl.length); + break; + } + + } while(!tl.ops.isEmpty() && advanceTime == 0 && tl.ops.first().length == 0); + + + if (tl.ops.isEmpty()) { + iter = ops.erase(iter); + v->_t = 0; + } else { + if (tl.ops.first().type == Op::Pause && pauseTime != 0) { + int opPauseTime = tl.ops.first().length - tl.consumedOpLength; + if (pauseTime == -1 || opPauseTime < pauseTime) + pauseTime = opPauseTime; + } else { + pauseTime = 0; + } + ++iter; + } + } + + length -= qMin(length, advanceTime); + syncPoint -= advanceTime; + + qSort(updates.begin(), updates.end()); + updateQueue = &updates; + for (int ii = 0; ii < updates.count(); ++ii) { + const Update &v = updates.at(ii).second; + if (v.g) + v.g->setValue(v.v); + else + v.e.execute(); + } + updateQueue = 0; + } while(t); + + return pauseTime; +} + +void QmlTimeLine::remove(QmlTimeLineObject *v) +{ + QmlTimeLinePrivate::Ops::Iterator iter = d->ops.find(v); + Q_ASSERT(iter != d->ops.end()); + + int len = iter->length; + d->ops.erase(iter); + if (len == d->length) { + // We need to recalculate the length + d->length = 0; + for (QmlTimeLinePrivate::Ops::Iterator iter = d->ops.begin(); + iter != d->ops.end(); + ++iter) { + + if (iter->length > d->length) + d->length = iter->length; + + } + } + if (d->ops.isEmpty()) { + stop(); + d->clockRunning = false; + } else if (/*!GfxClock::isActive()*/ state() != Running) { + stop(); + d->prevTime = 0; + d->clockRunning = true; + + if (d->syncMode == QmlTimeLine::LocalSync) { + d->syncAdj = -1; + } else { + d->syncAdj = 0; + } + start(); + } + + if (d->updateQueue) { + for (int ii = 0; ii < d->updateQueue->count(); ++ii) { + if (d->updateQueue->at(ii).second.g == v || + d->updateQueue->at(ii).second.e.eventObject() == v) { + d->updateQueue->removeAt(ii); + --ii; + } + } + } + + +} + +/*! + \internal + \class QmlTimeLineValue + \ingroup group_animation + \brief The QmlTimeLineValue class provides a value that can be modified by QmlTimeLine. +*/ + +/*! + \fn QmlTimeLineValue::QmlTimeLineValue(qreal value = 0) + + Construct a new QmlTimeLineValue with an initial \a value. +*/ + +/*! + \fn qreal QmlTimeLineValue::value() const + + Return the current value. +*/ + +/*! + \fn void QmlTimeLineValue::setValue(qreal value) + + Set the current \a value. +*/ + +/*! + \fn QmlTimeLine *QmlTimeLineValue::timeLine() const + + If a QmlTimeLine is operating on this value, return a pointer to it, + otherwise return null. +*/ + + +QmlTimeLineObject::QmlTimeLineObject() +: _t(0) +{ +} + +QmlTimeLineObject::~QmlTimeLineObject() +{ + if (_t) { + _t->remove(this); + _t = 0; + } +} + +QmlTimeLineEvent::QmlTimeLineEvent() +: d0(0), d1(0), d2(0) +{ +} + +QmlTimeLineEvent::QmlTimeLineEvent(const QmlTimeLineEvent &o) +: d0(o.d0), d1(o.d1), d2(o.d2) +{ +} + +QmlTimeLineEvent &QmlTimeLineEvent::operator=(const QmlTimeLineEvent &o) +{ + d0 = o.d0; + d1 = o.d1; + d2 = o.d2; + return *this; +} + +void QmlTimeLineEvent::execute() const +{ + d0(d1); +} + +QmlTimeLineObject *QmlTimeLineEvent::eventObject() const +{ + return d2; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmltimeline_p_p.h b/src/declarative/util/qmltimeline_p_p.h new file mode 100644 index 0000000..09d46ba --- /dev/null +++ b/src/declarative/util/qmltimeline_p_p.h @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** 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 QMLTIMELINE_H +#define QMLTIMELINE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/QObject> +#include <QtCore/QAbstractAnimation> + +QT_BEGIN_NAMESPACE + +class QEasingCurve; +class QmlTimeLineValue; +class QmlTimeLineEvent; +struct QmlTimeLinePrivate; +class QmlTimeLineObject; +class Q_DECLARATIVE_EXPORT QmlTimeLine : public QAbstractAnimation +{ +Q_OBJECT +public: + QmlTimeLine(QObject *parent = 0); + ~QmlTimeLine(); + + enum SyncMode { LocalSync, GlobalSync }; + SyncMode syncMode() const; + void setSyncMode(SyncMode); + + void pause(QmlTimeLineObject &, int); + void execute(const QmlTimeLineEvent &); + void set(QmlTimeLineValue &, qreal); + + int accel(QmlTimeLineValue &, qreal velocity, qreal accel); + int accel(QmlTimeLineValue &, qreal velocity, qreal accel, qreal maxDistance); + int accelDistance(QmlTimeLineValue &, qreal velocity, qreal distance); + + void move(QmlTimeLineValue &, qreal destination, int time = 500); + void move(QmlTimeLineValue &, qreal destination, const QEasingCurve &, int time = 500); + void moveBy(QmlTimeLineValue &, qreal change, int time = 500); + void moveBy(QmlTimeLineValue &, qreal change, const QEasingCurve &, int time = 500); + + void sync(); + void setSyncPoint(int); + int syncPoint() const; + + void sync(QmlTimeLineValue &); + void sync(QmlTimeLineValue &, QmlTimeLineValue &); + + void reset(QmlTimeLineValue &); + + void complete(); + void clear(); + bool isActive() const; + + int time() const; + + virtual int duration() const; +Q_SIGNALS: + void updated(); + void completed(); + +protected: + virtual void updateCurrentTime(int); + +private: + void remove(QmlTimeLineObject *); + friend class QmlTimeLineObject; + friend struct QmlTimeLinePrivate; + QmlTimeLinePrivate *d; +}; + +class Q_DECLARATIVE_EXPORT QmlTimeLineObject +{ +public: + QmlTimeLineObject(); + virtual ~QmlTimeLineObject(); + +protected: + friend class QmlTimeLine; + friend struct QmlTimeLinePrivate; + QmlTimeLine *_t; +}; + +class Q_DECLARATIVE_EXPORT QmlTimeLineValue : public QmlTimeLineObject +{ +public: + QmlTimeLineValue(qreal v = 0.) : _v(v) {} + + virtual qreal value() const { return _v; } + virtual void setValue(qreal v) { _v = v; } + + QmlTimeLine *timeLine() const { return _t; } + + operator qreal() const { return _v; } + QmlTimeLineValue &operator=(qreal v) { setValue(v); return *this; } +private: + friend class QmlTimeLine; + friend struct QmlTimeLinePrivate; + qreal _v; +}; + +class Q_DECLARATIVE_EXPORT QmlTimeLineEvent +{ +public: + QmlTimeLineEvent(); + QmlTimeLineEvent(const QmlTimeLineEvent &o); + + template<class T, void (T::*method)()> + QmlTimeLineEvent(QmlTimeLineObject *b, T *c) + { + d0 = &callFunc<T, method>; + d1 = (void *)c; + d2 = b; + } + + template<class T, void (T::*method)()> + static QmlTimeLineEvent timeLineEvent(QmlTimeLineObject *b, T *c) + { + QmlTimeLineEvent rv; + rv.d0 = &callFunc<T, method>; + rv.d1 = (void *)c; + rv.d2 = b; + return rv; + } + + QmlTimeLineEvent &operator=(const QmlTimeLineEvent &o); + void execute() const; + QmlTimeLineObject *eventObject() const; + +private: + typedef void (*CallFunc)(void *c); + + template <class T, void (T::*method)()> + static void callFunc(void *c) + { + T *cls = (T *)c; + (cls->*method)(); + } + CallFunc d0; + void *d1; + QmlTimeLineObject *d2; +}; + +template<class T> +class QmlTimeLineValueProxy : public QmlTimeLineValue +{ +public: + QmlTimeLineValueProxy(T *cls, void (T::*func)(qreal), qreal v = 0.) + : QmlTimeLineValue(v), _class(cls), _setFunctionReal(func), _setFunctionInt(0) + { + Q_ASSERT(_class); + } + + QmlTimeLineValueProxy(T *cls, void (T::*func)(int), qreal v = 0.) + : QmlTimeLineValue(v), _class(cls), _setFunctionReal(0), _setFunctionInt(func) + { + Q_ASSERT(_class); + } + + virtual void setValue(qreal v) + { + QmlTimeLineValue::setValue(v); + if (_setFunctionReal) (_class->*_setFunctionReal)(v); + else if (_setFunctionInt) (_class->*_setFunctionInt)((int)v); + } + +private: + T *_class; + void (T::*_setFunctionReal)(qreal); + void (T::*_setFunctionInt)(int); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/util/qmltimer.cpp b/src/declarative/util/qmltimer.cpp new file mode 100644 index 0000000..046dfe9 --- /dev/null +++ b/src/declarative/util/qmltimer.cpp @@ -0,0 +1,313 @@ +/**************************************************************************** +** +** 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 "qmltimer_p.h" + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qpauseanimation.h> +#include <qdebug.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +QML_DEFINE_TYPE(Qt,4,6,Timer,QmlTimer) + +class QmlTimerPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlTimer) +public: + QmlTimerPrivate() + : interval(1000), running(false), repeating(false), triggeredOnStart(false) + , classBegun(false), componentComplete(false), firstTick(true) {} + int interval; + QPauseAnimation pause; + bool running : 1; + bool repeating : 1; + bool triggeredOnStart : 1; + bool classBegun : 1; + bool componentComplete : 1; + bool firstTick : 1; +}; + +/*! + \qmlclass Timer QmlTimer + \brief The Timer item triggers a handler at a specified interval. + + A timer can be used to trigger an action either once, or repeatedly + at a given interval. + + Here is a timer that shows the current date and time, and updates + the text every 500 milliseconds: + + \qml + Timer { + interval: 500; running: true; repeat: true + onTriggered: time.text = Date().toString() + } + Text { + id: time + } + \endqml + + QmlTimer is synchronized with the animation timer. Since the animation + timer is usually set to 60fps, the resolution of QmlTimer will be + at best 16ms. + + If the Timer is running and one of its properties is changed, the + elapsed time will be reset. For example, if a Timer with interval of + 1000ms has its \e repeat property changed 500ms after starting, the + elapsed time will be reset to 0, and the Timer will be triggered + 1000ms later. +*/ + +QmlTimer::QmlTimer(QObject *parent) + : QObject(*(new QmlTimerPrivate), parent) +{ + Q_D(QmlTimer); + connect(&d->pause, SIGNAL(currentLoopChanged(int)), this, SLOT(ticked())); + connect(&d->pause, SIGNAL(finished()), this, SLOT(finished())); + d->pause.setLoopCount(1); + d->pause.setDuration(d->interval); +} + +/*! + \qmlproperty int Timer::interval + + Sets the \a interval between triggers, in milliseconds. + + The default interval is 1000 milliseconds. +*/ +void QmlTimer::setInterval(int interval) +{ + Q_D(QmlTimer); + if (interval != d->interval) { + d->interval = interval; + update(); + } +} + +int QmlTimer::interval() const +{ + Q_D(const QmlTimer); + return d->interval; +} + +/*! + \qmlproperty bool Timer::running + + If set to true, starts the timer; otherwise stops the timer. + For a non-repeating timer, \a running is set to false after the + timer has been triggered. + + \a running defaults to false. + + \sa repeat +*/ +bool QmlTimer::isRunning() const +{ + Q_D(const QmlTimer); + return d->running; +} + +void QmlTimer::setRunning(bool running) +{ + Q_D(QmlTimer); + if (d->running != running) { + d->running = running; + d->firstTick = true; + emit runningChanged(); + update(); + } +} + +/*! + \qmlproperty bool Timer::repeat + + If \a repeat is true the timer is triggered repeatedly at the + specified interval; otherwise, the timer will trigger once at the + specified interval and then stop (i.e. running will be set to false). + + \a repeat defaults to false. + + \sa running +*/ +bool QmlTimer::isRepeating() const +{ + Q_D(const QmlTimer); + return d->repeating; +} + +void QmlTimer::setRepeating(bool repeating) +{ + Q_D(QmlTimer); + if (repeating != d->repeating) { + d->repeating = repeating; + update(); + } +} + +/*! + \qmlproperty bool Timer::triggeredOnStart + + When a timer is started, the first trigger is usually after the specified + interval has elapsed. It is sometimes desirable to trigger immediately + when the timer is started; for example, to establish an initial + state. + + If \a triggeredOnStart is true, the timer is triggered immediately + when started, and subsequently at the specified interval. Note that if + \e repeat is set to false, the timer is triggered twice; once on start, + and again at the interval. + + \a triggeredOnStart defaults to false. + + \sa running +*/ +bool QmlTimer::triggeredOnStart() const +{ + Q_D(const QmlTimer); + return d->triggeredOnStart; +} + +void QmlTimer::setTriggeredOnStart(bool triggeredOnStart) +{ + Q_D(QmlTimer); + if (d->triggeredOnStart != triggeredOnStart) { + d->triggeredOnStart = triggeredOnStart; + update(); + } +} + +/*! + \qmlmethod Timer::start() + \brief Starts the timer. + + If the timer is already running, calling this method has no effect. The + \c running property will be true following a call to \c start(). +*/ +void QmlTimer::start() +{ + setRunning(true); +} + +/*! + \qmlmethod Timer::stop() + \brief Stops the timer. + + If the timer is not running, calling this method has no effect. The + \c running property will be false following a call to \c stop(). +*/ +void QmlTimer::stop() +{ + setRunning(false); +} + +/*! + \qmlmethod Timer::restart() + \brief Restarts the timer. + + If the Timer is not running it will be started, otherwise it will be + stopped, reset to initial state and started. The \c running property + will be true following a call to \c restart(). +*/ +void QmlTimer::restart() +{ + setRunning(false); + setRunning(true); +} + +void QmlTimer::update() +{ + Q_D(QmlTimer); + if (d->classBegun && !d->componentComplete) + return; + d->pause.stop(); + if (d->running) { + d->pause.setCurrentTime(0); + d->pause.setLoopCount(d->repeating ? -1 : 1); + d->pause.setDuration(d->interval); + d->pause.start(); + if (d->triggeredOnStart && d->firstTick) { + QCoreApplication::removePostedEvents(this, QEvent::MetaCall); + QMetaObject::invokeMethod(this, "ticked", Qt::QueuedConnection); + } + } +} + +void QmlTimer::classBegin() +{ + Q_D(QmlTimer); + d->classBegun = true; +} + +void QmlTimer::componentComplete() +{ + Q_D(QmlTimer); + d->componentComplete = true; + update(); +} + +/*! + \qmlsignal Timer::onTriggered() + + This handler is called when the Timer is triggered. +*/ +void QmlTimer::ticked() +{ + Q_D(QmlTimer); + if (d->running && (d->pause.currentTime() > 0 || (d->triggeredOnStart && d->firstTick))) + emit triggered(); + d->firstTick = false; +} + +void QmlTimer::finished() +{ + Q_D(QmlTimer); + if (d->repeating || !d->running) + return; + emit triggered(); + d->running = false; + d->firstTick = false; + emit runningChanged(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmltimer_p.h b/src/declarative/util/qmltimer_p.h new file mode 100644 index 0000000..fcd6c84 --- /dev/null +++ b/src/declarative/util/qmltimer_p.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** 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 QMLTIMER_H +#define QMLTIMER_H + +#include <qml.h> + +#include <QtCore/qobject.h> +#include <QtCore/qabstractanimation.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlTimerPrivate; +class Q_DECLARATIVE_EXPORT QmlTimer : public QObject, public QmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlTimer) + Q_INTERFACES(QmlParserStatus) + Q_PROPERTY(int interval READ interval WRITE setInterval) + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(bool repeat READ isRepeating WRITE setRepeating) + Q_PROPERTY(bool triggeredOnStart READ triggeredOnStart WRITE setTriggeredOnStart) + +public: + QmlTimer(QObject *parent=0); + + void setInterval(int interval); + int interval() const; + + bool isRunning() const; + void setRunning(bool running); + + bool isRepeating() const; + void setRepeating(bool repeating); + + bool triggeredOnStart() const; + void setTriggeredOnStart(bool triggeredOnStart); + +protected: + void classBegin(); + void componentComplete(); + +public Q_SLOTS: + void start(); + void stop(); + void restart(); + +Q_SIGNALS: + void triggered(); + void runningChanged(); + +private: + void update(); + +private Q_SLOTS: + void ticked(); + void finished(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlTimer) + +QT_END_HEADER + +#endif diff --git a/src/declarative/util/qmltransition.cpp b/src/declarative/util/qmltransition.cpp new file mode 100644 index 0000000..215fc91 --- /dev/null +++ b/src/declarative/util/qmltransition.cpp @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** 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 "qmlstate_p.h" +#include "qmlstategroup_p.h" +#include "qmlstate_p_p.h" +#include "qmlstateoperations_p.h" +#include "qmlanimation_p.h" +#include "qmlanimation_p_p.h" +#include "qmltransitionmanager_p_p.h" + +#include <QParallelAnimationGroup> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Transition QmlTransition + \brief The Transition element defines animated transitions that occur on state changes. + + \sa {qmlstates}{States}, {state-transitions}{Transitions} +*/ + +/*! + \internal + \class QmlTransition + \brief The QmlTransition class allows you to define animated transitions that occur on state changes. + + \ingroup group_states +*/ + +//ParallelAnimationWrapperallows us to do a "callback" when the animation finishes, rather than connecting +//and disconnecting signals and slots frequently +class ParallelAnimationWrapper : public QParallelAnimationGroup +{ + Q_OBJECT +public: + ParallelAnimationWrapper(QObject *parent = 0) : QParallelAnimationGroup(parent) {} + QmlTransitionPrivate *trans; +protected: + virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState); +}; + +class QmlTransitionPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlTransition) +public: + QmlTransitionPrivate() : fromState(QLatin1String("*")), toState(QLatin1String("*")) + , reversed(false), reversible(false), endState(0) + { + animations.parent = this; + } + + QString fromState; + QString toState; + bool reversed; + bool reversible; + ParallelAnimationWrapper group; + QmlTransitionManager *endState; + + void init() + { + group.trans = this; + } + + void complete() + { + endState->complete(); + } + + class AnimationList : public QmlConcreteList<QmlAbstractAnimation *> + { + public: + AnimationList() : parent(0) {} + virtual void append(QmlAbstractAnimation *a); + virtual void clear() { QmlConcreteList<QmlAbstractAnimation *>::clear(); } //### + + QmlTransitionPrivate *parent; + }; + AnimationList animations; +}; + +void QmlTransitionPrivate::AnimationList::append(QmlAbstractAnimation *a) +{ + QmlConcreteList<QmlAbstractAnimation *>::append(a); + parent->group.addAnimation(a->qtAnimation()); +} + +void ParallelAnimationWrapper::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) +{ + QParallelAnimationGroup::updateState(newState, oldState); + if (newState == Stopped && + ((direction() == QAbstractAnimation::Forward && currentLoopTime() == duration()) || + (direction() == QAbstractAnimation::Backward && currentLoopTime() == 0))) + { + trans->complete(); + } +} + + +QML_DEFINE_TYPE(Qt,4,6,Transition,QmlTransition) +QmlTransition::QmlTransition(QObject *parent) + : QObject(*(new QmlTransitionPrivate), parent) +{ + Q_D(QmlTransition); + d->init(); +} + +QmlTransition::~QmlTransition() +{ +} + +void QmlTransition::stop() +{ + Q_D(QmlTransition); + d->group.stop(); +} + +void QmlTransition::setReversed(bool r) +{ + Q_D(QmlTransition); + d->reversed = r; +} + +void QmlTransition::prepare(QmlStateOperation::ActionList &actions, + QList<QmlMetaProperty> &after, + QmlTransitionManager *endState) +{ + Q_D(QmlTransition); + + qmlExecuteDeferred(this); + + if (d->reversed) { + for (int ii = d->animations.count() - 1; ii >= 0; --ii) { + d->animations.at(ii)->transition(actions, after, QmlAbstractAnimation::Backward); + } + } else { + for (int ii = 0; ii < d->animations.count(); ++ii) { + d->animations.at(ii)->transition(actions, after, QmlAbstractAnimation::Forward); + } + } + + d->endState = endState; + d->group.setDirection(d->reversed ? QAbstractAnimation::Backward : QAbstractAnimation::Forward); + d->group.start(); +} + +/*! + \qmlproperty string Transition::from + \qmlproperty string Transition::to + These properties are selectors indicating which state changes should trigger the transition. + + from is used in conjunction with to to determine when a transition should + be applied. By default from and to are both "*" (any state). In the following example, + the transition is applied when changing from state1 to state2. + \code + Transition { + from: "state1" + to: "state2" + ... + } + \endcode +*/ +QString QmlTransition::fromState() const +{ + Q_D(const QmlTransition); + return d->fromState; +} + +void QmlTransition::setFromState(const QString &f) +{ + Q_D(QmlTransition); + d->fromState = f; +} + +/*! + \qmlproperty bool Transition::reversible + This property holds whether the transition should be automatically reversed when the conditions that triggered this transition are reversed. + + The default value is false. +*/ +bool QmlTransition::reversible() const +{ + Q_D(const QmlTransition); + return d->reversible; +} + +void QmlTransition::setReversible(bool r) +{ + Q_D(QmlTransition); + d->reversible = r; +} + +QString QmlTransition::toState() const +{ + Q_D(const QmlTransition); + return d->toState; +} + +void QmlTransition::setToState(const QString &t) +{ + Q_D(QmlTransition); + d->toState = t; +} + +/*! + \qmlproperty list<Animation> Transition::animations + \default + This property holds a list of the animations to be run for this transition. + + The top-level animations are run in parallel. To run them sequentially, + you can create a single SequentialAnimation which contains all the animations, + and assign that to animations the animations property. + \default +*/ +QmlList<QmlAbstractAnimation *>* QmlTransition::animations() +{ + Q_D(QmlTransition); + return &d->animations; +} + +QT_END_NAMESPACE + +#include <qmltransition.moc> diff --git a/src/declarative/util/qmltransition_p.h b/src/declarative/util/qmltransition_p.h new file mode 100644 index 0000000..c1a6f66 --- /dev/null +++ b/src/declarative/util/qmltransition_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** 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 QMLTRANSITION_H +#define QMLTRANSITION_H + +#include "qmlstate_p.h" + +#include <qml.h> + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlAbstractAnimation; +class QmlTransitionPrivate; +class QmlTransitionManager; +class Q_DECLARATIVE_EXPORT QmlTransition : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlTransition) + + Q_PROPERTY(QString from READ fromState WRITE setFromState) + Q_PROPERTY(QString to READ toState WRITE setToState) + Q_PROPERTY(bool reversible READ reversible WRITE setReversible) + Q_PROPERTY(QmlList<QmlAbstractAnimation *>* animations READ animations) + Q_CLASSINFO("DefaultProperty", "animations") + Q_CLASSINFO("DeferredPropertyNames", "animations") + +public: + QmlTransition(QObject *parent=0); + ~QmlTransition(); + + QString fromState() const; + void setFromState(const QString &); + + QString toState() const; + void setToState(const QString &); + + bool reversible() const; + void setReversible(bool); + + QmlList<QmlAbstractAnimation *>* animations(); + + void prepare(QmlStateOperation::ActionList &actions, + QList<QmlMetaProperty> &after, + QmlTransitionManager *end); + + void setReversed(bool r); + void stop(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlTransition) + +QT_END_HEADER + +#endif // QMLTRANSITION_H diff --git a/src/declarative/util/qmltransitionmanager.cpp b/src/declarative/util/qmltransitionmanager.cpp new file mode 100644 index 0000000..a3a16ca --- /dev/null +++ b/src/declarative/util/qmltransitionmanager.cpp @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** 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 "qmltransitionmanager_p_p.h" + +#include "qmlstate_p_p.h" + +#include <qmlbinding.h> +#include <qmlglobal_p.h> + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +class QmlTransitionManagerPrivate +{ +public: + QmlTransitionManagerPrivate() + : state(0), transition(0) {} + + void applyBindings(); + typedef QList<QmlSimpleAction> SimpleActionList; + QmlState *state; + QmlTransition *transition; + QmlStateOperation::ActionList bindingsList; + SimpleActionList completeList; +}; + +QmlTransitionManager::QmlTransitionManager() +: d(new QmlTransitionManagerPrivate) +{ +} + +void QmlTransitionManager::setState(QmlState *s) +{ + d->state = s; +} + +QmlTransitionManager::~QmlTransitionManager() +{ + delete d; d = 0; +} + +void QmlTransitionManager::complete() +{ + d->applyBindings(); + + for (int ii = 0; ii < d->completeList.count(); ++ii) { + const QmlMetaProperty &prop = d->completeList.at(ii).property; + prop.write(d->completeList.at(ii).value); + } + + d->completeList.clear(); + + if (d->state) + static_cast<QmlStatePrivate*>(QObjectPrivate::get(d->state))->complete(); +} + +void QmlTransitionManagerPrivate::applyBindings() +{ + foreach(const QmlAction &action, bindingsList) { + if (action.toBinding) { + action.property.setBinding(action.toBinding); + } else if (action.event) { + if (action.reverseEvent) + action.event->reverse(); + else + action.event->execute(); + } + + } + + bindingsList.clear(); +} + +void QmlTransitionManager::transition(const QList<QmlAction> &list, + QmlTransition *transition) +{ + cancel(); + + QmlStateOperation::ActionList applyList = list; + // Determine which actions are binding changes. + foreach(const QmlAction &action, applyList) { + if (action.toBinding) + d->bindingsList << action; + if (action.fromBinding) + action.property.setBinding(0); // Disable current binding + if (action.event && action.event->changesBindings()) { //### assume isReversable()? + d->bindingsList << action; + if (action.reverseEvent) + action.event->clearReverseBindings(); + else + action.event->clearForwardBindings(); + } + } + + // Animated transitions need both the start and the end value for + // each property change. In the presence of bindings, the end values + // are non-trivial to calculate. As a "best effort" attempt, we first + // apply all the property and binding changes, then read all the actual + // final values, then roll back the changes and proceed as normal. + // + // This doesn't catch everything, and it might be a little fragile in + // some cases - but whatcha going to do? + + if (!d->bindingsList.isEmpty()) { + + //### do extra actions here? + + // Apply all the property and binding changes + for (int ii = 0; ii < applyList.size(); ++ii) { + const QmlAction &action = applyList.at(ii); + if (action.toBinding) { + action.property.setBinding(action.toBinding, QmlMetaProperty::BypassInterceptor | QmlMetaProperty::DontRemoveBinding); + } else if (!action.event) { + action.property.write(action.toValue, QmlMetaProperty::BypassInterceptor | QmlMetaProperty::DontRemoveBinding); + } else if (action.event->isReversable()) { + if (action.reverseEvent) + action.event->reverse(); + else + action.event->execute(); + applyList << action.event->extraActions(); + } + } + + // Read all the end values for binding changes + for (int ii = 0; ii < applyList.size(); ++ii) { + QmlAction *action = &applyList[ii]; + if (action->event) + continue; + const QmlMetaProperty &prop = action->property; + if (action->toBinding || !action->toValue.isValid()) { //### is this always right (used for exta actions) + action->toValue = prop.read(); + } + } + + // Revert back to the original values + foreach(const QmlAction &action, applyList) { + if (action.event) { + if (action.event->isReversable()) { + if (action.reverseEvent) { //reverse the reverse + action.event->clearForwardBindings(); + action.event->rewind(); + action.event->clearReverseBindings(); + } else { + action.event->clearReverseBindings(); + action.event->rewind(); + action.event->clearForwardBindings(); + } + } + continue; + } + + if (action.toBinding) + action.property.setBinding(0); // Make sure this is disabled during the transition + + action.property.write(action.fromValue, QmlMetaProperty::BypassInterceptor | QmlMetaProperty::DontRemoveBinding); + } + } + + if (transition) { + QList<QmlMetaProperty> touched; + d->transition = transition; + d->transition->prepare(applyList, touched, this); + + // Modify the action list to remove actions handled in the transition + for (int ii = 0; ii < applyList.count(); ++ii) { + const QmlAction &action = applyList.at(ii); + + if (action.event) { + + if (action.actionDone) { + applyList.removeAt(ii); + --ii; + } + + } else { + + if (touched.contains(action.property)) { + if (action.toValue != action.fromValue) + d->completeList << + QmlSimpleAction(action, QmlSimpleAction::EndState); + + applyList.removeAt(ii); + --ii; + } + + } + } + } + + // Any actions remaining have not been handled by the transition and should + // be applied immediately. We skip applying bindings, as they are all + // applied at the end in applyBindings() to avoid any nastiness mid + // transition + foreach(const QmlAction &action, applyList) { + if (action.event && !action.event->changesBindings()) { + if (action.event->isReversable() && action.reverseEvent) + action.event->reverse(); + else + action.event->execute(); + } else if (!action.event && !action.toBinding) { + action.property.write(action.toValue); + } + } + if (stateChangeDebug()) { + foreach(const QmlAction &action, applyList) { + if (action.event) + qWarning() << " No transition for event:" << action.event->typeName(); + else + qWarning() << " No transition for:" << action.property.object() + << action.property.name() << "From:" << action.fromValue + << "To:" << action.toValue; + } + } + if (!transition) + d->applyBindings(); +} + +void QmlTransitionManager::cancel() +{ + if (d->transition) { + // ### this could potentially trigger a complete in rare circumstances + d->transition->stop(); + d->transition = 0; + } + + for(int i = 0; i < d->bindingsList.count(); ++i) { + QmlAction action = d->bindingsList[i]; + if (action.toBinding && action.deletableToBinding) { + action.property.setBinding(0); + action.toBinding->destroy(); + action.toBinding = 0; + action.deletableToBinding = false; + } else if (action.event) { + //### what do we do here? + } + + } + d->bindingsList.clear(); + d->completeList.clear(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmltransitionmanager_p_p.h b/src/declarative/util/qmltransitionmanager_p_p.h new file mode 100644 index 0000000..cb4111c --- /dev/null +++ b/src/declarative/util/qmltransitionmanager_p_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** 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 QMLTRANSITIONMANAGER_P_H +#define QMLTRANSITIONMANAGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qmlstateoperations_p.h" + +QT_BEGIN_NAMESPACE + +class QmlStatePrivate; +class QmlTransitionManagerPrivate; +class QmlTransitionManager +{ +public: + QmlTransitionManager(); + ~QmlTransitionManager(); + + void transition(const QList<QmlAction> &, QmlTransition *transition); + + void cancel(); + +private: + Q_DISABLE_COPY(QmlTransitionManager); + QmlTransitionManagerPrivate *d; + + void complete(); + void setState(QmlState *); + + friend class QmlState; + friend class QmlTransitionPrivate; +}; + +QT_END_NAMESPACE + +#endif // QMLTRANSITIONMANAGER_P_H diff --git a/src/declarative/util/qmlview.cpp b/src/declarative/util/qmlview.cpp new file mode 100644 index 0000000..690924f --- /dev/null +++ b/src/declarative/util/qmlview.cpp @@ -0,0 +1,586 @@ +/**************************************************************************** +** +** 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 "qmlview.h" + +#include "qperformancelog_p_p.h" +#include "qfxperf_p_p.h" + +#include <qml.h> +#include <qmlgraphicsitem.h> +#include <qmlengine.h> +#include <qmlcontext.h> +#include <qmldebug_p.h> +#include <qmldebugservice_p.h> +#include <qmlglobal_p.h> + +#include <qscriptvalueiterator.h> +#include <qdebug.h> +#include <qtimer.h> +#include <qevent.h> +#include <qdir.h> +#include <qcoreapplication.h> +#include <qfontdatabase.h> +#include <qicon.h> +#include <qurl.h> +#include <qboxlayout.h> +#include <qbasictimer.h> +#include <QtCore/qabstractanimation.h> + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(frameRateDebug, QML_SHOW_FRAMERATE) + +class QmlViewDebugServer; +class FrameBreakAnimation : public QAbstractAnimation +{ +public: + FrameBreakAnimation(QmlViewDebugServer *s) + : QAbstractAnimation((QObject*)s), server(s) + { + start(); + } + + virtual int duration() const { return -1; } + virtual void updateCurrentTime(int msecs); + +private: + QmlViewDebugServer *server; +}; + +class QmlViewDebugServer : public QmlDebugService +{ +public: + QmlViewDebugServer(QObject *parent = 0) : QmlDebugService(QLatin1String("CanvasFrameRate"), parent), breaks(0) + { + timer.start(); + new FrameBreakAnimation(this); + } + + void addTiming(int pe, int tbf) + { + if (!isEnabled()) + return; + + bool isFrameBreak = breaks > 1; + breaks = 0; + int e = timer.elapsed(); + QByteArray data; + QDataStream ds(&data, QIODevice::WriteOnly); + ds << (int)pe << (int)tbf << (int)e + << (bool)isFrameBreak; + sendMessage(data); + } + + void frameBreak() { ++breaks; } + +private: + QTime timer; + int breaks; +}; + +Q_GLOBAL_STATIC(QmlViewDebugServer, qfxViewDebugServer); + +void FrameBreakAnimation::updateCurrentTime(int msecs) +{ + Q_UNUSED(msecs); + server->frameBreak(); +} + +class QmlViewPrivate +{ +public: + QmlViewPrivate(QmlView *w) + : q(w), root(0), component(0), resizable(false) {} + + QmlView *q; + QmlGraphicsItem *root; + + QUrl source; + QString qml; + + QmlEngine engine; + QmlComponent *component; + QBasicTimer resizetimer; + + QSize initialSize; + bool resizable; + QTime frameTimer; + + void init(); + + QGraphicsScene scene; +}; + +/*! + \class QmlView + \brief The QmlView class provides a widget for displaying a Qt Declarative user interface. + + QmlView currently provides a minimal interface for displaying QML + files, and connecting between QML and C++ Qt objects. + + Typical usage: + \code + ... + QmlView *view = new QmlView(this); + vbox->addWidget(view); + + QUrl url(fileName); + view->setUrl(url); + ... + view->execute(); + ... + view->show(); + \endcode + + To receive errors related to loading and executing QML with QmlView, + you can connect to the errors() signal. +*/ + +/*! + \fn QmlView::QmlView(QWidget *parent) + + Constructs a QmlView with the given \a parent. +*/ +QmlView::QmlView(QWidget *parent) +: QGraphicsView(parent), d(new QmlViewPrivate(this)) +{ + setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred); + d->init(); +} + +void QmlViewPrivate::init() +{ +#ifdef Q_ENABLE_PERFORMANCE_LOG + { + QmlPerfTimer<QmlPerf::FontDatabase> perf; + QFontDatabase database; + } +#endif + + q->setScene(&scene); + + q->setOptimizationFlags(QGraphicsView::DontSavePainterState); + q->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + q->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + q->setFrameStyle(0); + + // These seem to give the best performance + q->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); + scene.setItemIndexMethod(QGraphicsScene::NoIndex); + q->viewport()->setFocusPolicy(Qt::NoFocus); + + scene.setStickyFocus(true); //### needed for correct focus handling +} + +/*! + The destructor clears the view's \l {QmlGraphicsItem} {items} and + deletes the internal representation. + + \sa clearItems() + */ +QmlView::~QmlView() +{ + clearItems(); + delete d; d = 0; +} + +/*! + Sets the source to the \a url. The QML string is set to + empty. + */ +void QmlView::setUrl(const QUrl& url) +{ + d->source = url; + d->qml = QString(); +} + +/*! + Returns the source URL, if set. + + \sa setUrl() + */ +QUrl QmlView::url() const +{ + return d->source; +} + +/*! + Sets the source to the URL from the \a filename, and sets + the QML string to \a qml. + */ +void QmlView::setQml(const QString &qml, const QString &filename) +{ + d->source = QUrl::fromLocalFile(filename); + d->qml = qml; +} + +/*! + Returns the QML string. + */ +QString QmlView::qml() const +{ + return d->qml; +} + +/*! + Returns a pointer to the QmlEngine used for instantiating + QML Components. + */ +QmlEngine* QmlView::engine() +{ + return &d->engine; +} + +/*! + This function returns the root of the context hierarchy. Each QML + component is instantiated in a QmlContext. QmlContext's are + essential for passing data to QML components. In QML, contexts are + arranged hierarchically and this hierarchy is managed by the + QmlEngine. + */ +QmlContext* QmlView::rootContext() +{ + return d->engine.rootContext(); +} + +/*! + Displays the Qt Declarative user interface. +*/ +void QmlView::execute() +{ + if (d->qml.isEmpty()) { + d->component = new QmlComponent(&d->engine, d->source, this); + } else { + d->component = new QmlComponent(&d->engine, this); + d->component->setData(d->qml.toUtf8(), d->source); + } + connect (&d->engine, SIGNAL (quit ()), this, SIGNAL (quit ())); + + if (!d->component->isLoading()) { + continueExecute(); + } else { + connect(d->component, SIGNAL(statusChanged(QmlComponent::Status)), this, SLOT(continueExecute())); + } +} + + +/*! + \internal + */ +void QmlView::continueExecute() +{ + disconnect(d->component, SIGNAL(statusChanged(QmlComponent::Status)), this, SLOT(continueExecute())); + + if (!d->component) { + qWarning() << "Error in loading" << d->source; + return; + } + + if(d->component->isError()) { + QList<QmlError> errorList = d->component->errors(); + foreach (const QmlError &error, errorList) { + qWarning() << error; + } + emit errors(errorList); + + return; + } + + QObject *obj = d->component->create(); + + if(d->component->isError()) { + QList<QmlError> errorList = d->component->errors(); + foreach (const QmlError &error, errorList) { + qWarning() << error; + } + emit errors(errorList); + + return; + } + + if (obj) { + if (QmlGraphicsItem *item = qobject_cast<QmlGraphicsItem *>(obj)) { + + d->scene.addItem(item); + + QPerformanceLog::displayData(); + QPerformanceLog::clear(); + d->root = item; + connect(item, SIGNAL(widthChanged()), this, SLOT(sizeChanged())); + connect(item, SIGNAL(heightChanged()), this, SLOT(sizeChanged())); + if (d->initialSize.height() <= 0 && d->root->width() > 0) + d->initialSize.setWidth(d->root->width()); + if (d->initialSize.height() <= 0 && d->root->height() > 0) + d->initialSize.setHeight(d->root->height()); + resize(d->initialSize); + + if (d->resizable) { + d->root->setWidth(width()); + d->root->setHeight(height()); + } else { + QSize sz(d->root->width(),d->root->height()); + emit sceneResized(sz); + resize(sz); + } + updateGeometry(); + emit initialSize(d->initialSize); + } else if (QWidget *wid = qobject_cast<QWidget *>(obj)) { + window()->setAttribute(Qt::WA_OpaquePaintEvent, false); + window()->setAttribute(Qt::WA_NoSystemBackground, false); + if (!layout()) { + setLayout(new QVBoxLayout); + layout()->setContentsMargins(0, 0, 0, 0); + } else if (layout()->count()) { + // Hide the QGraphicsView in GV mode. + QLayoutItem *item = layout()->itemAt(0); + if (item->widget()) + item->widget()->hide(); + } + layout()->addWidget(wid); + emit sceneResized(wid->size()); + emit initialSize(wid->size()); + } + } +} + +/*! \fn void QmlView::sceneResized(QSize size) + This signal is emitted when the view is resized to \a size. + */ + +/*! \fn void QmlView::initialSize(QSize size) + This signal is emitted when the initial \a size of the root item is known. + */ + +/*! \fn void QmlView::errors(const QList<QmlError> &errors) + This signal is emitted when the qml loaded contains \a errors. + */ + +/*! + \internal + */ +void QmlView::sizeChanged() +{ + // delay, so we catch both width and height changing. + d->resizetimer.start(0,this); +} + +/*! + If the \l {QTimerEvent} {timer event} \a e is this + view's resize timer, sceneResized() is emitted. + */ +void QmlView::timerEvent(QTimerEvent* e) +{ + if (!e || e->timerId() == d->resizetimer.timerId()) { + if (d->root) { + QSize sz(d->root->width(),d->root->height()); + emit sceneResized(sz); + //if (!d->resizable) + //resize(sz); + } + d->resizetimer.stop(); + updateGeometry(); + } +} + +// modelled on QScrollArea::widgetResizable +/*! + \property QmlView::contentResizable + \brief whether the view should resize the canvas contents + + If this property is set to false (the default), the view + resizes with the root item in the QML. + + If this property is set to true, the view will + automatically resize the root item. + + Regardless of this property, the sizeHint of the view + is the initial size of the root item. Note though that + since QML may load dynamically, that size may change. + + \sa initialSize() +*/ + +void QmlView::setContentResizable(bool on) +{ + if (d->resizable != on) { + d->resizable = on; + if (d->root) { + if (on) { + d->root->setWidth(width()); + d->root->setHeight(height()); + } else { + d->root->setWidth(d->initialSize.width()); + d->root->setHeight(d->initialSize.height()); + } + } + } +} + +bool QmlView::contentResizable() const +{ + return d->resizable; +} + + +/*! + The size hint is the size of the root item. +*/ +QSize QmlView::sizeHint() const +{ + if (d->root) { + if (d->initialSize.width() <= 0) + d->initialSize.setWidth(d->root->width()); + if (d->initialSize.height() <= 0) + d->initialSize.setHeight(d->root->height()); + } + return d->initialSize; +} + +/*! + Creates a \l{QmlComponent} {component} from the \a qml + string, and returns it as an \l {QmlGraphicsItem} {item}. If the + \a parent item is provided, it becomes the new item's + parent. \a parent should be in this view's item hierarchy. + */ +QmlGraphicsItem* QmlView::addItem(const QString &qml, QmlGraphicsItem* parent) +{ + if (!d->root) + return 0; + + QmlComponent component(&d->engine); + component.setData(qml.toUtf8(), QUrl()); + if(d->component->isError()) { + QList<QmlError> errorList = d->component->errors(); + foreach (const QmlError &error, errorList) { + qWarning() << error; + } + emit errors(errorList); + + return 0; + } + + QObject *obj = component.create(); + if(d->component->isError()) { + QList<QmlError> errorList = d->component->errors(); + foreach (const QmlError &error, errorList) { + qWarning() << error; + } + emit errors(errorList); + + return 0; + } + + if (obj){ + QmlGraphicsItem *item = static_cast<QmlGraphicsItem *>(obj); + if (!parent) + parent = d->root; + + item->setParentItem(parent); + return item; + } + return 0; +} + +/*! + Deletes the view's \l {QmlGraphicsItem} {items} and clears the \l {QmlEngine} + {QML engine's} Component cache. + */ +void QmlView::reset() +{ + clearItems(); + d->engine.clearComponentCache(); + d->initialSize = QSize(); +} + +/*! + Deletes the view's \l {QmlGraphicsItem} {items}. + */ +void QmlView::clearItems() +{ + if (!d->root) + return; + delete d->root; + d->root = 0; +} + +/*! + Returns the view's root \l {QmlGraphicsItem} {item}. + */ +QmlGraphicsItem *QmlView::root() const +{ + return d->root; +} + +/*! + This function handles the \l {QResizeEvent} {resize event} + \a e. + */ +void QmlView::resizeEvent(QResizeEvent *e) +{ + if (d->resizable && d->root) { + d->root->setWidth(width()); + d->root->setHeight(height()); + } + if (d->root) { + setSceneRect(QRectF(0, 0, d->root->width(), d->root->height())); + } else { + setSceneRect(rect()); + } + QGraphicsView::resizeEvent(e); +} + +/*! + \reimp +*/ +void QmlView::paintEvent(QPaintEvent *event) +{ + int time = 0; + if (frameRateDebug() || QmlViewDebugServer::isDebuggingEnabled()) + time = d->frameTimer.restart(); + QGraphicsView::paintEvent(event); + if (QmlViewDebugServer::isDebuggingEnabled()) + qfxViewDebugServer()->addTiming(d->frameTimer.elapsed(), time); + if (frameRateDebug()) + qDebug() << "paintEvent:" << d->frameTimer.elapsed() << "time since last frame:" << time; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlview.h b/src/declarative/util/qmlview.h new file mode 100644 index 0000000..f4f58fd --- /dev/null +++ b/src/declarative/util/qmlview.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** 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 QFXVIEW_H +#define QFXVIEW_H + +#include <QtCore/qdatetime.h> +#include <QtGui/qgraphicssceneevent.h> +#include <QtGui/qgraphicsview.h> +#include <QtGui/qwidget.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlGraphicsItem; +class QmlEngine; +class QmlContext; +class QmlError; + +class QmlViewPrivate; +class Q_DECLARATIVE_EXPORT QmlView : public QGraphicsView +{ + Q_OBJECT + Q_PROPERTY(bool contentResizable READ contentResizable WRITE setContentResizable) +public: + explicit QmlView(QWidget *parent = 0); + + virtual ~QmlView(); + + void setUrl(const QUrl&); + QUrl url() const; + void setQml(const QString &qml, const QString &filename=QString()); + QString qml() const; + QmlEngine* engine(); + QmlContext* rootContext(); + virtual void execute(); + virtual void reset(); + + virtual QmlGraphicsItem* addItem(const QString &qml, QmlGraphicsItem* parent=0); + virtual void clearItems(); + + virtual QmlGraphicsItem *root() const; + + void setContentResizable(bool); + bool contentResizable() const; + QSize sizeHint() const; + +Q_SIGNALS: + void initialSize(QSize size); + void sceneResized(QSize size); + void errors(const QList<QmlError> &error); + void quit (); + +private Q_SLOTS: + void continueExecute(); + void sizeChanged(); + +protected: + virtual void resizeEvent(QResizeEvent *); + virtual void paintEvent(QPaintEvent *event); + void timerEvent(QTimerEvent*); + +private: + friend class QmlViewPrivate; + QmlViewPrivate *d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXVIEW_H diff --git a/src/declarative/util/qmlxmllistmodel.cpp b/src/declarative/util/qmlxmllistmodel.cpp new file mode 100644 index 0000000..d31fadf --- /dev/null +++ b/src/declarative/util/qmlxmllistmodel.cpp @@ -0,0 +1,728 @@ +/**************************************************************************** +** +** 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 "qmlxmllistmodel_p.h" + +#include <qmlcontext.h> +#include <qmlengine.h> + +#include <QDebug> +#include <QApplication> +#include <QThread> +#include <QMutex> +#include <QWaitCondition> +#include <QXmlQuery> +#include <QXmlResultItems> +#include <QXmlNodeModelIndex> +#include <QBuffer> +#include <QNetworkRequest> +#include <QNetworkReply> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +QML_DEFINE_TYPE(Qt,4,6,XmlRole,QmlXmlListModelRole) +QML_DEFINE_TYPE(Qt,4,6,XmlListModel,QmlXmlListModel) + +/*! + \qmlclass XmlRole QmlXmlListModelRole + \brief The XmlRole element allows you to specify a role for an XmlListModel. +*/ + +/*! + \qmlproperty string XmlRole::name + The name for the role. This name is used to access the model data for this role from Qml. + + \qml + XmlRole { name: "title"; query: "title/string()" } + + ... + + Component { + id: myDelegate + Text { text: title } + } + \endqml +*/ + +/*! + \qmlproperty string XmlRole::query + The relative XPath query for this role. The query should not start with a '/' (i.e. it must be + relative). + + \qml + XmlRole { name: "title"; query: "title/string()" } + \endqml +*/ + +class Q_DECLARATIVE_EXPORT QmlXmlListModelRole : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(QString query READ query WRITE setQuery) + +public: + QmlXmlListModelRole() {} + ~QmlXmlListModelRole() {} + + QString name() const { return m_name; } + void setName(const QString &name) { m_name = name; } + + QString query() const { return m_query; } + void setQuery(const QString &query) + { + if (query.startsWith(QLatin1Char('/'))) { + qmlInfo(this) << tr("An XmlRole query must not start with '/'"); + return; + } + m_query = query; + } + + bool isValid() { + return !m_name.isEmpty() && !m_query.isEmpty(); + } + +private: + QString m_name; + QString m_query; +}; + +QML_DECLARE_TYPE(QmlXmlListModelRole) + +class QmlXmlListModelPrivate; +struct QmlXmlRoleList : public QmlConcreteList<QmlXmlListModelRole *> +{ + QmlXmlRoleList(QmlXmlListModelPrivate *p) + : model(p) {} + virtual void append(QmlXmlListModelRole *role); + virtual void clear(); + virtual void removeAt(int i); + virtual void insert(int i, QmlXmlListModelRole *role); + + QmlXmlListModelPrivate *model; +}; + +class QmlXmlQuery : public QThread +{ + Q_OBJECT +public: + QmlXmlQuery(QObject *parent=0) + : QThread(parent), m_quit(false), m_restart(false), m_abort(false), m_queryId(0) { + } + ~QmlXmlQuery() { + m_mutex.lock(); + m_quit = true; + m_condition.wakeOne(); + m_mutex.unlock(); + + wait(); + } + + void abort() { + QMutexLocker locker(&m_mutex); + m_abort = true; + } + + int doQuery(QString query, QString namespaces, QByteArray data, QmlXmlRoleList *roleObjects) { + QMutexLocker locker(&m_mutex); + m_modelData.clear(); + m_size = 0; + m_data = data; + m_query = QLatin1String("doc($src)") + query; + m_namespaces = namespaces; + m_roleObjects = roleObjects; + if (!isRunning()) { + m_abort = false; + start(); + } else { + m_restart = true; + m_condition.wakeOne(); + } + m_queryId++; + return m_queryId; + } + + QList<QList<QVariant> > modelData() { + QMutexLocker locker(&m_mutex); + return m_modelData; + } + +Q_SIGNALS: + void queryCompleted(int queryId, int size); + +protected: + void run() { + while (!m_quit) { + m_mutex.lock(); + int queryId = m_queryId; + doQueryJob(); + if (m_size > 0) + doSubQueryJob(); + m_data.clear(); // no longer needed + m_mutex.unlock(); + + m_mutex.lock(); + if (!m_abort && m_size > 0) + emit queryCompleted(queryId, m_size); + if (!m_restart) + m_condition.wait(&m_mutex); + m_abort = false; + m_restart = false; + m_mutex.unlock(); + } + } + +private: + void doQueryJob(); + void doSubQueryJob(); + +private: + QMutex m_mutex; + QWaitCondition m_condition; + bool m_quit; + bool m_restart; + bool m_abort; + QByteArray m_data; + QString m_query; + QString m_namespaces; + QString m_prefix; + int m_size; + int m_queryId; + const QmlXmlRoleList *m_roleObjects; + QList<QList<QVariant> > m_modelData; +}; + +void QmlXmlQuery::doQueryJob() +{ + QString r; + QXmlQuery query; + QBuffer buffer(&m_data); + buffer.open(QIODevice::ReadOnly); + query.bindVariable(QLatin1String("src"), &buffer); + query.setQuery(m_namespaces + m_query); + query.evaluateTo(&r); + + //qDebug() << r; + + //always need a single root element + QByteArray xml = "<dummy:items xmlns:dummy=\"http://qtsotware.com/dummy\">\n" + r.toUtf8() + "</dummy:items>"; + QBuffer b(&xml); + b.open(QIODevice::ReadOnly); + //qDebug() << xml; + + QString namespaces = QLatin1String("declare namespace dummy=\"http://qtsotware.com/dummy\";\n") + m_namespaces; + QString prefix = QLatin1String("doc($inputDocument)/dummy:items") + + m_query.mid(m_query.lastIndexOf(QLatin1Char('/'))); + + //figure out how many items we are dealing with + int count = -1; + { + QXmlResultItems result; + QXmlQuery countquery; + countquery.bindVariable(QLatin1String("inputDocument"), &b); + countquery.setQuery(namespaces + QLatin1String("count(") + prefix + QLatin1Char(')')); + countquery.evaluateTo(&result); + QXmlItem item(result.next()); + if (item.isAtomicValue()) + count = item.toAtomicValue().toInt(); + } + //qDebug() << count; + + m_prefix = namespaces + prefix + QLatin1Char('/'); + m_data = xml; + if (count > 0) + m_size = count; +} + +void QmlXmlQuery::doSubQueryJob() +{ + m_modelData.clear(); + + QBuffer b(&m_data); + b.open(QIODevice::ReadOnly); + + QXmlQuery subquery; + subquery.bindVariable(QLatin1String("inputDocument"), &b); + + //### we might be able to condense even further (query for everything in one go) + for (int i = 0; i < m_roleObjects->size(); ++i) { + QmlXmlListModelRole *role = m_roleObjects->at(i); + if (!role->isValid()) { + QList<QVariant> resultList; + for (int j = 0; j < m_size; ++j) + resultList << QVariant(); + m_modelData << resultList; + continue; + } + subquery.setQuery(m_prefix + QLatin1String("(let $v := ") + role->query() + QLatin1String(" return if ($v) then ") + role->query() + QLatin1String(" else \"\")")); + QXmlResultItems output3; + subquery.evaluateTo(&output3); + QXmlItem item(output3.next()); + QList<QVariant> resultList; + while (!item.isNull()) { + resultList << item.toAtomicValue(); //### we used to trim strings + item = output3.next(); + } + //### should warn here if things have gone wrong. + while (resultList.count() < m_size) + resultList << QVariant(); + m_modelData << resultList; + b.seek(0); + } + + //this method is much slower, but works better for incremental loading + /*for (int j = 0; j < m_size; ++j) { + QList<QVariant> resultList; + for (int i = 0; i < m_roleObjects->size(); ++i) { + QmlXmlListModelRole *role = m_roleObjects->at(i); + subquery.setQuery(m_prefix.arg(j+1) + role->query()); + if (role->isStringList()) { + QStringList data; + subquery.evaluateTo(&data); + resultList << QVariant(data); + //qDebug() << data; + } else { + QString s; + subquery.evaluateTo(&s); + if (role->isCData()) { + //un-escape + s.replace(QLatin1String("<"), QLatin1String("<")); + s.replace(QLatin1String(">"), QLatin1String(">")); + s.replace(QLatin1String("&"), QLatin1String("&")); + } + resultList << s.trimmed(); + //qDebug() << s; + } + b.seek(0); + } + m_modelData << resultList; + }*/ +} + +class QmlXmlListModelPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlXmlListModel) +public: + QmlXmlListModelPrivate() + : isComponentComplete(true), size(-1), highestRole(Qt::UserRole) + , reply(0), status(QmlXmlListModel::Null), progress(0.0) + , queryId(-1), roleObjects(this) {} + + bool isComponentComplete; + QUrl src; + QString xml; + QString query; + QString namespaces; + int size; + QList<int> roles; + QStringList roleNames; + int highestRole; + QNetworkReply *reply; + QmlXmlListModel::Status status; + qreal progress; + QmlXmlQuery qmlXmlQuery; + int queryId; + QmlXmlRoleList roleObjects; + QList<QList<QVariant> > data; +}; + + +void QmlXmlRoleList::append(QmlXmlListModelRole *role) +{ + insert(size(), role); +} + +//### clear, removeAt, and insert need to invalidate any cached data (in data table) as well +// (and the model should emit the appropriate signals) +void QmlXmlRoleList::clear() +{ + model->roles.clear(); + model->roleNames.clear(); + QmlConcreteList<QmlXmlListModelRole *>::clear(); +} + +void QmlXmlRoleList::removeAt(int i) +{ + model->roles.removeAt(i); + model->roleNames.removeAt(i); + QmlConcreteList<QmlXmlListModelRole *>::removeAt(i); +} + +void QmlXmlRoleList::insert(int i, QmlXmlListModelRole *role) +{ + QmlConcreteList<QmlXmlListModelRole *>::insert(i, role); + if (model->roleNames.contains(role->name())) { + qmlInfo(role) << QObject::tr("\"%1\" duplicates a previous role name and will be disabled.").arg(role->name()); + return; + } + model->roles.insert(i, model->highestRole); + model->roleNames.insert(i, role->name()); + ++model->highestRole; +} + +/*! + \class QmlXmlListModel + \internal +*/ + +/*! + \qmlclass XmlListModel QmlXmlListModel + \brief The XmlListModel element allows you to specify a model using XPath expressions. + + XmlListModel allows you to construct a model from XML data that can then be used as a data source + for the view classes (ListView, PathView, GridView) and any other classes that interact with model + data (like Repeater). + + The following is an example of a model containing news from a Yahoo RSS feed: + \qml + XmlListModel { + id: feedModel + source: "http://rss.news.yahoo.com/rss/oceania" + query: "/rss/channel/item" + XmlRole { name: "title"; query: "title/string()" } + XmlRole { name: "link"; query: "link/string()" } + XmlRole { name: "description"; query: "description/string()" } + } + \endqml + \note The model is currently static, so the above is really just a snapshot of an RSS feed. To force a + reload of the entire model, you can call the reload function. +*/ + +QmlXmlListModel::QmlXmlListModel(QObject *parent) + : QListModelInterface(*(new QmlXmlListModelPrivate), parent) +{ + Q_D(QmlXmlListModel); + connect(&d->qmlXmlQuery, SIGNAL(queryCompleted(int,int)), + this, SLOT(queryCompleted(int,int))); +} + +QmlXmlListModel::~QmlXmlListModel() +{ +} + +/*! + \qmlproperty list<XmlRole> XmlListModel::roles + + The roles to make available for this model. +*/ +QmlList<QmlXmlListModelRole *> *QmlXmlListModel::roleObjects() +{ + Q_D(QmlXmlListModel); + return &d->roleObjects; +} + +QHash<int,QVariant> QmlXmlListModel::data(int index, const QList<int> &roles) const +{ + Q_D(const QmlXmlListModel); + QHash<int, QVariant> rv; + for (int i = 0; i < roles.size(); ++i) { + int role = roles.at(i); + int roleIndex = d->roles.indexOf(role); + rv.insert(role, roleIndex == -1 ? QVariant() : d->data.at(roleIndex).at(index)); + } + return rv; +} + +QVariant QmlXmlListModel::data(int index, int role) const +{ + Q_D(const QmlXmlListModel); + int roleIndex = d->roles.indexOf(role); + return (roleIndex == -1) ? QVariant() : d->data.at(roleIndex).at(index); +} + +/*! + \qmlproperty int XmlListModel::count + The number of data entries in the model. +*/ +int QmlXmlListModel::count() const +{ + Q_D(const QmlXmlListModel); + return d->size; +} + +QList<int> QmlXmlListModel::roles() const +{ + Q_D(const QmlXmlListModel); + return d->roles; +} + +QString QmlXmlListModel::toString(int role) const +{ + Q_D(const QmlXmlListModel); + int index = d->roles.indexOf(role); + if (index == -1) + return QString(); + return d->roleNames.at(index); +} + +/*! + \qmlproperty url XmlListModel::source + The location of the XML data source. + + If both source and xml are set, xml will be used. +*/ +QUrl QmlXmlListModel::source() const +{ + Q_D(const QmlXmlListModel); + return d->src; +} + +void QmlXmlListModel::setSource(const QUrl &src) +{ + Q_D(QmlXmlListModel); + if (d->src != src) { + d->src = src; + reload(); + } +} + +/*! + \qmlproperty string XmlListModel::xml + This property holds XML text set directly. + + The text is assumed to be UTF-8 encoded. + + If both source and xml are set, xml will be used. +*/ +QString QmlXmlListModel::xml() const +{ + Q_D(const QmlXmlListModel); + return d->xml; +} + +void QmlXmlListModel::setXml(const QString &xml) +{ + Q_D(QmlXmlListModel); + d->xml = xml; + reload(); +} + +/*! + \qmlproperty url XmlListModel::query + An absolute XPath query representing the base query for the model items. The query should start with + a '/' or '//'. +*/ +QString QmlXmlListModel::query() const +{ + Q_D(const QmlXmlListModel); + return d->query; +} + +void QmlXmlListModel::setQuery(const QString &query) +{ + Q_D(QmlXmlListModel); + if (!query.startsWith(QLatin1Char('/'))) { + qmlInfo(this) << tr("An XmlListModel query must start with '/' or \"//\""); + return; + } + + if (d->query != query) { + d->query = query; + reload(); + } +} + +/*! + \qmlproperty string XmlListModel::namespaceDeclarations + A set of declarations for the namespaces used in the query. +*/ +QString QmlXmlListModel::namespaceDeclarations() const +{ + Q_D(const QmlXmlListModel); + return d->namespaces; +} + +void QmlXmlListModel::setNamespaceDeclarations(const QString &declarations) +{ + Q_D(QmlXmlListModel); + if (d->namespaces != declarations) { + d->namespaces = declarations; + reload(); + } +} + +/*! + \qmlproperty enum XmlListModel::status + + This property holds the status of data source loading. It can be one of: + \list + \o Null - no data source has been set + \o Ready - nthe data source has been loaded + \o Loading - the data source is currently being loaded + \o Error - an error occurred while loading the data source + \endlist + + \sa progress + +*/ +QmlXmlListModel::Status QmlXmlListModel::status() const +{ + Q_D(const QmlXmlListModel); + return d->status; +} + +/*! + \qmlproperty real XmlListModel::progress + + This property holds the progress of data source loading, from 0.0 (nothing loaded) + to 1.0 (finished). + + \sa status +*/ +qreal QmlXmlListModel::progress() const +{ + Q_D(const QmlXmlListModel); + return d->progress; +} + +void QmlXmlListModel::classBegin() +{ + Q_D(QmlXmlListModel); + d->isComponentComplete = false; +} + +void QmlXmlListModel::componentComplete() +{ + Q_D(QmlXmlListModel); + d->isComponentComplete = true; + reload(); +} + +/*! + \qmlmethod XmlListModel::reload() + + Reloads the model. All the existing model data will be removed, and the model + will be rebuilt from scratch. +*/ +void QmlXmlListModel::reload() +{ + Q_D(QmlXmlListModel); + + if (!d->isComponentComplete) + return; + + d->qmlXmlQuery.abort(); + d->queryId = -1; + + //clear existing data + int count = d->size; + d->size = 0; + d->data.clear(); + if (count > 0) + emit itemsRemoved(0, count); + + if (d->src.isEmpty() && d->xml.isEmpty()) + return; + + if (d->reply) { + d->reply->abort(); + d->reply->deleteLater(); + d->reply = 0; + } + + if (!d->xml.isEmpty()) { + d->queryId = d->qmlXmlQuery.doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects); + d->progress = 1.0; + d->status = Ready; + emit progressChanged(d->progress); + emit statusChanged(d->status); + return; + } + + d->progress = 0.0; + d->status = Loading; + emit progressChanged(d->progress); + emit statusChanged(d->status); + + QNetworkRequest req(d->src); + d->reply = qmlContext(this)->engine()->networkAccessManager()->get(req); + QObject::connect(d->reply, SIGNAL(finished()), this, SLOT(requestFinished())); + QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), + this, SLOT(requestProgress(qint64,qint64))); +} + +void QmlXmlListModel::requestFinished() +{ + Q_D(QmlXmlListModel); + if (d->reply->error() != QNetworkReply::NoError) { + disconnect(d->reply, 0, this, 0); + d->reply->deleteLater(); + d->reply = 0; + d->status = Error; + } else { + d->status = Ready; + QByteArray data = d->reply->readAll(); + d->queryId = d->qmlXmlQuery.doQuery(d->query, d->namespaces, data, &d->roleObjects); + disconnect(d->reply, 0, this, 0); + d->reply->deleteLater(); + d->reply = 0; + } + d->progress = 1.0; + emit progressChanged(d->progress); + emit statusChanged(d->status); +} + +void QmlXmlListModel::requestProgress(qint64 received, qint64 total) +{ + Q_D(QmlXmlListModel); + if (d->status == Loading && total > 0) { + d->progress = qreal(received)/total; + emit progressChanged(d->progress); + } +} + +void QmlXmlListModel::queryCompleted(int id, int size) +{ + Q_D(QmlXmlListModel); + if (id != d->queryId) + return; + d->size = size; + if (size > 0) { + d->data = d->qmlXmlQuery.modelData(); + emit itemsInserted(0, d->size); + emit countChanged(); + } +} + +QT_END_NAMESPACE + +#include <qmlxmllistmodel.moc> diff --git a/src/declarative/util/qmlxmllistmodel_p.h b/src/declarative/util/qmlxmllistmodel_p.h new file mode 100644 index 0000000..e4b8cab --- /dev/null +++ b/src/declarative/util/qmlxmllistmodel_p.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** 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 QMLXMLLISTMODEL_H +#define QMLXMLLISTMODEL_H + +#include <qml.h> +#include <qmlinfo.h> + +#include "../3rdparty/qlistmodelinterface_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlContext; + +class QmlXmlListModelRole; + +class QmlXmlListModelPrivate; +class Q_DECLARATIVE_EXPORT QmlXmlListModel : public QListModelInterface, public QmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QmlParserStatus) + Q_ENUMS(Status) + + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) + Q_PROPERTY(QUrl source READ source WRITE setSource) + Q_PROPERTY(QString xml READ xml WRITE setXml) + Q_PROPERTY(QString query READ query WRITE setQuery) + Q_PROPERTY(QString namespaceDeclarations READ namespaceDeclarations WRITE setNamespaceDeclarations) + Q_PROPERTY(QmlList<QmlXmlListModelRole *> *roles READ roleObjects) + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_CLASSINFO("DefaultProperty", "roles") + +public: + QmlXmlListModel(QObject *parent = 0); + ~QmlXmlListModel(); + + virtual QHash<int,QVariant> data(int index, const QList<int> &roles = (QList<int>())) const; + virtual QVariant data(int index, int role) const; + virtual int count() const; + virtual QList<int> roles() const; + virtual QString toString(int role) const; + + QmlList<QmlXmlListModelRole *> *roleObjects(); + + QUrl source() const; + void setSource(const QUrl&); + + QString xml() const; + void setXml(const QString&); + + QString query() const; + void setQuery(const QString&); + + QString namespaceDeclarations() const; + void setNamespaceDeclarations(const QString&); + + enum Status { Null, Ready, Loading, Error }; + Status status() const; + qreal progress() const; + + virtual void classBegin(); + virtual void componentComplete(); + +Q_SIGNALS: + void statusChanged(Status); + void progressChanged(qreal progress); + void countChanged(); + +public Q_SLOTS: + // ### need to use/expose Expiry to guess when to call this? + // ### property to auto-call this on reasonable Expiry? + // ### LastModified/Age also useful to guess. + // ### Probably also applies to other network-requesting types. + void reload(); + +private Q_SLOTS: + void requestFinished(); + void requestProgress(qint64,qint64); + void queryCompleted(int,int); + +private: + Q_DECLARE_PRIVATE(QmlXmlListModel) + Q_DISABLE_COPY(QmlXmlListModel) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlXmlListModel) + +QT_END_HEADER + +#endif // QMLXMLLISTMODEL_H diff --git a/src/declarative/util/qnumberformat.cpp b/src/declarative/util/qnumberformat.cpp new file mode 100644 index 0000000..fd44db1 --- /dev/null +++ b/src/declarative/util/qnumberformat.cpp @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** 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 "qnumberformat_p.h" + +QT_BEGIN_NAMESPACE + +QML_DEFINE_NOCREATE_TYPE(QNumberFormat) + +QNumberFormat::QNumberFormat(QObject *parent) : QObject(parent), _number(0), _type(Decimal), + _groupingSize(0) +{ + _locale = QLocale::system(); + _groupingSeparator = _locale.groupSeparator(); + _decimalSeparator = _locale.decimalPoint(); + _currencySymbol = QLatin1Char('$'); +} + +QNumberFormat::~QNumberFormat() +{ + +} + +void QNumberFormat::updateText() +{ + QTime t; + t.start(); + static int totalTime; + + handleFormat(); + + totalTime += t.elapsed(); + emit textChanged(); +} + +void QNumberFormat::handleFormat() +{ + // ### is extremely messy + if (_format.isEmpty()) { + _text = QString::number(_number, 'f', -1); + return; + } + + QString inputString; + + // ### possible to use the following parsed data in the future + + int remainingLength = _format.size(); + int currentIndex = _format.size()-1; + + int maxDigits = 0; + int minDigits = 0; + int decimalLength = 0; + + while (remainingLength > 0) { + switch(_format.at(currentIndex).unicode()) { + case ',': + if (decimalLength && !_groupingSize) + setGroupingSize(maxDigits - decimalLength); + else if (!_groupingSize) + setGroupingSize(maxDigits); + break; + case '.': + if (!decimalLength) + decimalLength = maxDigits; + break; + case '0': + minDigits++; + case '#': + maxDigits++; + break; + default: + break; + } + currentIndex--; + remainingLength--; + } + + // round given the decimal length/precision + inputString = QString::number(_number, 'f', decimalLength); + + QStringList parts = inputString.split(QLatin1Char('.')); + QStringList formatParts = _format.split(QLatin1Char('.')); + + if (formatParts.size() > 2 || parts.size() > 2 ) + return; + + QString formatInt = formatParts.at(0); + + QString formatDec; + if (formatParts.size() == 2) + formatDec = formatParts.at(1); + + QString integer = parts.at(0); + + QString decimal; + if (parts.size() == 2) + decimal = parts.at(1); + + QString outputDecimal = formatDecimal(formatDec, decimal); + QString outputInteger = formatInteger(formatInt, integer); + + // insert separators + if (_groupingSize) { + unsigned int count = 0; + for (int i = outputInteger.size()-1; i > 0; i--) { + if (outputInteger.at(i).digitValue() >= 0) { + if (count == _groupingSize - 1) { + count = 0; + outputInteger.insert(i, _groupingSeparator); + } + else + count++; + } + } + } + if (!outputDecimal.isEmpty()) + _text = outputInteger + _decimalSeparator + outputDecimal; + else + _text = outputInteger; +} + +QString QNumberFormat::formatInteger(const QString &formatInt, const QString &integer) +{ + if (formatInt.isEmpty() || integer.isEmpty()) + return QString(); + + QString outputInteger; + int formatIndex = formatInt.size()-1; + + //easier for carry? + for (int index= integer.size()-1; index >= 0; index--) { + if (formatIndex < 0) { + outputInteger.push_front(integer.at(index)); + } + else { + switch(formatInt.at(formatIndex).unicode()) { + case '0': + if (index > integer.size()-1) { + outputInteger.push_front(QLatin1Char('0')); + break; + } + case '#': + outputInteger.push_front(integer.at(index)); + break; + case ',': + index++; + break; + default: + outputInteger.push_front(formatInt.at(formatIndex)); + index++; + break; + } + formatIndex--; + } + } + while (formatIndex >= 0) { + if (formatInt.at(formatIndex).unicode() != '#' && formatInt.at(formatIndex).unicode() != ',') + outputInteger.push_front(formatInt.at(formatIndex)); + formatIndex--; + } + return outputInteger; +} + +QString QNumberFormat::formatDecimal(const QString &formatDec, const QString &decimal) +{ + QString outputDecimal; + + // up to max 6 decimal places + for (int index=formatDec.size()-1; index >= 0; index--) { + switch(formatDec.at(index).unicode()) { + case '0': + outputDecimal.push_front(decimal.at(index)); + break; + case '#': + if (decimal.at(index) != QLatin1Char('0') || outputDecimal.size() > 0) + outputDecimal.push_front(decimal.at(index)); + break; + default: + outputDecimal.push_front(formatDec.at(index)); + break; + } + } + return outputDecimal; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qnumberformat_p.h b/src/declarative/util/qnumberformat_p.h new file mode 100644 index 0000000..c73ef8a --- /dev/null +++ b/src/declarative/util/qnumberformat_p.h @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** 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 NUMBERFORMAT_H +#define NUMBERFORMAT_H + +#include <qml.h> + +#include <QtCore/QLocale> +#include <QtCore/QTime> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +// TODO +// be able to set Locale, instead of default system for dynamic formatting +// add currency support +// add additional syntax, extend to format scientific, percentiles, significant digits etc + + +class QNumberFormat : public QObject +{ + Q_OBJECT + Q_ENUMS(NumberType) +public: + QNumberFormat(QObject *parent=0); + ~QNumberFormat(); + + enum NumberType { + Percent, + Scientific, + Currency, + Decimal + }; + + //external property, only visible + Q_PROPERTY(QString text READ text NOTIFY textChanged) + + //mutatable properties to modify the output (text) + Q_PROPERTY(qreal number READ number WRITE setNumber) + Q_PROPERTY(QString format READ format WRITE setFormat) + Q_PROPERTY(QLocale locale READ locale WRITE setLocale) + + //Format specific settings + Q_PROPERTY(unsigned short groupingSeparator READ groupingSeparator WRITE setGroupingSeparator) + Q_PROPERTY(unsigned short decimalSeperator READ decimalSeparator WRITE setDecimalSeparator) + Q_PROPERTY(unsigned int groupingSize READ groupingSize WRITE setGroupingSize) + Q_PROPERTY(unsigned short currencySymbol READ currencySymbol WRITE setCurrencySymbol) + + + QString text() const { return _text; } + + qreal number() const { return _number; } + void setNumber(qreal n) { + if (_number == n) + return; + _number = n; + updateText(); + } + + QString format() const { return _format; } + void setFormat(const QString &format) { + if (format.isEmpty()) + _format = QString::null; + else if (_format == format) + return; + + _format = format; + updateText(); + } + + QLocale locale() const { return _locale; } + void setLocale(const QLocale &locale) { _locale = locale; updateText(); } + + //Do we deal with unicode standard? or create our own + // ### since this is the backend for the number conversions, we will use the unicode + // the front-end will handle the QChar/QString -> short int + + unsigned short groupingSeparator() { return _groupingSeparator.unicode(); } + void setGroupingSeparator(unsigned short unicodeSymbol) + { + _groupingSeparator = QChar(unicodeSymbol); + } + + unsigned short decimalSeparator() { return _decimalSeparator.unicode(); } + void setDecimalSeparator(unsigned short unicodeSymbol) + { + _decimalSeparator = QChar(unicodeSymbol); + } + + unsigned short currencySymbol() { return _currencySymbol.unicode(); } + void setCurrencySymbol(unsigned short unicodeSymbol) + { + _currencySymbol = QChar(unicodeSymbol); + } + + unsigned int groupingSize() { return _groupingSize; } + void setGroupingSize(unsigned int size) + { + _groupingSize = size; + } + +Q_SIGNALS: + void textChanged(); + +private: + void updateText(); + void handleFormat(); + QString formatInteger(const QString &formatInt, const QString &integer); + QString formatDecimal(const QString &formatDec, const QString &decimal); + + qreal _number; + NumberType _type; + QChar _groupingSeparator; + QChar _decimalSeparator; + QChar _currencySymbol; + unsigned int _groupingSize; + + QLocale _locale; + QString _format; + + // only hooked member at the moment + QString _text; + +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QNumberFormat) + +QT_END_HEADER + +#endif diff --git a/src/declarative/util/qperformancelog.cpp b/src/declarative/util/qperformancelog.cpp new file mode 100644 index 0000000..2f91dfb --- /dev/null +++ b/src/declarative/util/qperformancelog.cpp @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** 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 "qperformancelog_p_p.h" + +#include <QHash> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +#ifdef Q_ENABLE_PERFORMANCE_LOG + +struct QPerformanceLogData +{ + struct Log + { + Log() + : logDescription(0), maxId(-1) {} + + QHash<int, const char *> descriptions; + const char *logDescription; + int maxId; + }; + + typedef QHash<QPerformanceLog::LogData *, Log> Logs; + Logs logs; +}; +Q_GLOBAL_STATIC(QPerformanceLogData, performanceLogData); + +QPerformanceLog::LogData::LogData(const char *desc) +: sumTime(0), data(0) +{ + QPerformanceLogData *logData = performanceLogData(); + + QPerformanceLogData::Log log; + log.logDescription = desc; + logData->logs.insert(this, log); + + timer.start(); +} + +QPerformanceLog::LogMetric::LogMetric(LogData *l, int id, const char *desc) +{ + if (id < 0) + qFatal("QPerformanceLog: Invalid log id %d ('%s')", id, desc); + + QPerformanceLogData *logData = performanceLogData(); + + QPerformanceLogData::Logs::Iterator logIter = logData->logs.find(l); + if (logIter == logData->logs.end()) + qFatal("QPerformanceLog: Unable to locate log for metric '%s'", desc); + QPerformanceLogData::Log &log = *logIter; + if (log.descriptions.contains(id)) + qFatal("QPerformanceLog: Duplicate log metric %d ('%s')", id, desc); + log.descriptions.insert(id, desc); + + if (log.maxId < id) { + log.maxId = id; + if (l->data) delete [] l->data; + l->data = new unsigned int[2 * (log.maxId + 1)]; + ::memset(l->data, 0, 2 * (log.maxId + 1) * sizeof(unsigned int)); + } +} + +static void QPerformanceLog_clear(QPerformanceLog::LogData *l, const QPerformanceLogData::Log *pl) +{ + ::memset(l->data, 0, 2 * (pl->maxId + 1) * sizeof(unsigned int)); +} + +static void QPerformanceLog_displayData(const QPerformanceLog::LogData *l, const QPerformanceLogData::Log *pl) +{ + qWarning() << pl->logDescription << "performance data"; + unsigned int total = 0; + for (QHash<int, const char *>::ConstIterator iter = pl->descriptions.begin(); + iter != pl->descriptions.end(); + ++iter) { + + int id = iter.key(); + unsigned int ms = l->data[id * 2]; + total += ms; + unsigned int inst = l->data[id * 2 + 1]; + float pi = float(ms) / float(inst); + qWarning().nospace() << " " << *iter << ": " << ms << " ms over " + << inst << " instances (" << pi << " ms/instance)"; + } + qWarning().nospace() << " TOTAL: " << total; +} + +void QPerformanceLog::displayData() +{ + QPerformanceLogData *logData = performanceLogData(); + + for (QPerformanceLogData::Logs::ConstIterator iter = logData->logs.begin(); + iter != logData->logs.end(); + ++iter) { + QPerformanceLog_displayData(iter.key(), &(*iter)); + } +} + +void QPerformanceLog::clear() +{ + QPerformanceLogData *logData = performanceLogData(); + + for (QPerformanceLogData::Logs::ConstIterator iter = logData->logs.begin(); + iter != logData->logs.end(); + ++iter) { + QPerformanceLog_clear(iter.key(), &(*iter)); + } +} + +void QPerformanceLog::displayData(LogData *l) +{ + QPerformanceLogData *logData = performanceLogData(); + QPerformanceLogData::Logs::ConstIterator iter = logData->logs.find(l); + if (iter == logData->logs.end()) + qFatal("QPerformanceLog: Internal corruption - unable to locate log"); + + QPerformanceLog_displayData(iter.key(), &(*iter)); +} + +void QPerformanceLog::clear(LogData *l) +{ + QPerformanceLogData *logData = performanceLogData(); + QPerformanceLogData::Logs::ConstIterator iter = logData->logs.find(l); + if (iter == logData->logs.end()) + qFatal("QPerformanceLog: Internal corruption - unable to locate log"); + + QPerformanceLog_clear(iter.key(), &(*iter)); +} + +#else // Q_ENABLE_PERFORMANCE_LOG + +void QPerformanceLog::displayData() +{ +} + +void QPerformanceLog::clear() +{ +} + +#endif // Q_ENABLE_PERFORMANCE_LOG + +QT_END_NAMESPACE diff --git a/src/declarative/util/qperformancelog_p_p.h b/src/declarative/util/qperformancelog_p_p.h new file mode 100644 index 0000000..e7a3b5e --- /dev/null +++ b/src/declarative/util/qperformancelog_p_p.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** 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 QPERFORMANCELOG_H +#define QPERFORMANCELOG_H + +#include <QtCore/qdatetime.h> + +QT_BEGIN_NAMESPACE + +namespace QPerformanceLog +{ + Q_DECLARATIVE_EXPORT void displayData(); + Q_DECLARATIVE_EXPORT void clear(); + +#ifdef Q_ENABLE_PERFORMANCE_LOG + struct LogData { + LogData(const char *); + QTime timer; + int sumTime; + unsigned int *data; + }; + + struct LogMetric { + LogMetric(LogData *, int, const char *); + }; + + // Internal + void displayData(LogData *); + void clear(LogData *); +#endif +} + +#ifdef Q_ENABLE_PERFORMANCE_LOG + +#define Q_DECLARE_PERFORMANCE_METRIC(name) \ + enum { name = ValueChoice<0, ValueTracker<0, __LINE__>::value, __LINE__>::value }; \ + template<int L> \ + struct ValueTracker<name, L> \ + { \ + enum { value = name }; \ + }; \ + extern QPerformanceLog::LogMetric metric ## name; + +#define Q_DECLARE_PERFORMANCE_LOG(name) \ + namespace name { \ + extern QPerformanceLog::LogData log; \ + inline void displayData() { QPerformanceLog::displayData(&log); } \ + inline void clear() { QPerformanceLog::clear(&log); } \ + } \ + template<int N> \ + class name ## Timer { \ + public: \ + name ## Timer() { \ + lastSum = name::log.sumTime + name::log.timer.restart(); \ + name::log.sumTime = 0; \ + } \ + ~ name ## Timer() { \ + name::log.data[2 * N] += name::log.sumTime + name::log.timer.restart(); \ + ++name::log.data[2 * N + 1]; \ + name::log.sumTime = lastSum; \ + } \ + private: \ + int lastSum; \ + }; \ + namespace name { \ + template<int N, int L> \ + struct ValueTracker \ + { \ + enum { value = -1 }; \ + }; \ + template<int DefNextValue, int NextValue, int L> \ + struct ValueChoice \ + { \ + enum { value = ValueChoice<DefNextValue + 1, ValueTracker<DefNextValue + 1, L>::value, L>::value }; \ + }; \ + template<int DefNextValue, int L> \ + struct ValueChoice<DefNextValue, -1, L> \ + { \ + enum { value = DefNextValue }; \ + }; \ + } \ + namespace name + +#define Q_DEFINE_PERFORMANCE_LOG(name, desc) \ + QPerformanceLog::LogData name::log(desc); \ + namespace name + +#define Q_DEFINE_PERFORMANCE_METRIC(name, desc) \ + QPerformanceLog::LogMetric metrix ## name(&log, name, desc); + +#else // Q_ENABLE_PERFORMANCE_LOG + +#define Q_DECLARE_PERFORMANCE_METRIC(name) +#define Q_DECLARE_PERFORMANCE_LOG(name) namespace name +#define Q_DEFINE_PERFORMANCE_LOG(name, desc) namespace name +#define Q_DEFINE_PERFORMANCE_METRIC(name, desc) + +#endif // Q_ENABLE_PERFORMANCE_LOG + +QT_END_NAMESPACE + +#endif // QPERFORMANCELOG_H diff --git a/src/declarative/util/util.pri b/src/declarative/util/util.pri new file mode 100644 index 0000000..b6a5c90 --- /dev/null +++ b/src/declarative/util/util.pri @@ -0,0 +1,72 @@ +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/qmlview.cpp \ + $$PWD/qfxperf.cpp \ + $$PWD/qperformancelog.cpp \ + $$PWD/qmlconnection.cpp \ + $$PWD/qmlpackage.cpp \ + $$PWD/qmlanimation.cpp \ + $$PWD/qmlsystempalette.cpp \ + $$PWD/qmlspringfollow.cpp \ + $$PWD/qmleasefollow.cpp \ + $$PWD/qmlstate.cpp\ + $$PWD/qmltransitionmanager.cpp \ + $$PWD/qmlstateoperations.cpp \ + $$PWD/qmlpropertychanges.cpp \ + $$PWD/qmlstategroup.cpp \ + $$PWD/qmltransition.cpp \ + $$PWD/qmllistmodel.cpp\ + $$PWD/qmllistaccessor.cpp \ + $$PWD/qmlopenmetaobject.cpp \ + $$PWD/qmltimeline.cpp \ + $$PWD/qmltimer.cpp \ + $$PWD/qmlbind.cpp \ + $$PWD/qmlpropertymap.cpp \ + $$PWD/qmlpixmapcache.cpp \ + $$PWD/qnumberformat.cpp \ + $$PWD/qmlnumberformatter.cpp \ + $$PWD/qmldatetimeformatter.cpp \ + $$PWD/qmlbehavior.cpp \ + $$PWD/qmlfontloader.cpp \ + $$PWD/qmlstyledtext.cpp + +HEADERS += \ + $$PWD/qmlview.h \ + $$PWD/qfxperf_p_p.h \ + $$PWD/qperformancelog_p_p.h \ + $$PWD/qmlconnection_p.h \ + $$PWD/qmlpackage_p.h \ + $$PWD/qmlanimation_p.h \ + $$PWD/qmlanimation_p_p.h \ + $$PWD/qmlsystempalette_p.h \ + $$PWD/qmlspringfollow_p.h \ + $$PWD/qmleasefollow_p.h \ + $$PWD/qmlstate_p.h\ + $$PWD/qmlstateoperations_p.h \ + $$PWD/qmlpropertychanges_p.h \ + $$PWD/qmlstate_p_p.h\ + $$PWD/qmltransitionmanager_p_p.h \ + $$PWD/qmlstategroup_p.h \ + $$PWD/qmltransition_p.h \ + $$PWD/qmllistmodel_p.h\ + $$PWD/qmllistaccessor_p.h \ + $$PWD/qmlopenmetaobject_p.h \ + $$PWD/qmlnullablevalue_p_p.h \ + $$PWD/qmltimeline_p_p.h \ + $$PWD/qmltimer_p.h \ + $$PWD/qmlbind_p.h \ + $$PWD/qmlpropertymap.h \ + $$PWD/qmlpixmapcache_p.h \ + $$PWD/qnumberformat_p.h \ + $$PWD/qmlnumberformatter_p.h \ + $$PWD/qmldatetimeformatter_p.h \ + $$PWD/qmlbehavior_p.h \ + $$PWD/qmlfontloader_p.h \ + $$PWD/qmlstyledtext_p.h + +contains(QT_CONFIG, xmlpatterns) { + QT+=xmlpatterns + SOURCES += $$PWD/qmlxmllistmodel.cpp + HEADERS += $$PWD/qmlxmllistmodel_p.h +} |