From 12a5ddf456ba8549645a8cb28a8b4ed6197a14da Mon Sep 17 00:00:00 2001 From: Matthew Vogt Date: Mon, 30 Jan 2012 14:16:15 +1000 Subject: Import relevant source from Qt 4.8 Change-Id: I5078db4081d95290c54f39d3c0efc2fc2f62e6a6 --- src/declarative/util/qdeclarativeanimation.cpp | 2952 ++++++++++++++++++++ src/declarative/util/qdeclarativeanimation_p.h | 528 ++++ src/declarative/util/qdeclarativeanimation_p_p.h | 397 +++ src/declarative/util/qdeclarativeapplication.cpp | 112 + src/declarative/util/qdeclarativeapplication_p.h | 86 + src/declarative/util/qdeclarativebehavior.cpp | 230 ++ src/declarative/util/qdeclarativebehavior_p.h | 98 + src/declarative/util/qdeclarativebind.cpp | 214 ++ src/declarative/util/qdeclarativebind_p.h | 96 + src/declarative/util/qdeclarativeconnections.cpp | 289 ++ src/declarative/util/qdeclarativeconnections_p.h | 103 + src/declarative/util/qdeclarativefontloader.cpp | 338 +++ src/declarative/util/qdeclarativefontloader_p.h | 97 + src/declarative/util/qdeclarativelistaccessor.cpp | 138 + src/declarative/util/qdeclarativelistaccessor_p.h | 80 + src/declarative/util/qdeclarativelistmodel.cpp | 1628 +++++++++++ src/declarative/util/qdeclarativelistmodel_p.h | 158 ++ src/declarative/util/qdeclarativelistmodel_p_p.h | 281 ++ .../util/qdeclarativelistmodelworkeragent.cpp | 278 ++ .../util/qdeclarativelistmodelworkeragent_p.h | 163 ++ .../util/qdeclarativenullablevalue_p_p.h | 81 + .../util/qdeclarativeopenmetaobject.cpp | 380 +++ .../util/qdeclarativeopenmetaobject_p.h | 129 + src/declarative/util/qdeclarativepackage.cpp | 201 ++ src/declarative/util/qdeclarativepackage_p.h | 98 + src/declarative/util/qdeclarativepixmapcache.cpp | 1119 ++++++++ src/declarative/util/qdeclarativepixmapcache_p.h | 124 + .../util/qdeclarativepropertychanges.cpp | 798 ++++++ .../util/qdeclarativepropertychanges_p.h | 112 + src/declarative/util/qdeclarativepropertymap.cpp | 296 ++ src/declarative/util/qdeclarativepropertymap.h | 90 + .../util/qdeclarativesmoothedanimation.cpp | 493 ++++ .../util/qdeclarativesmoothedanimation_p.h | 103 + .../util/qdeclarativesmoothedanimation_p_p.h | 135 + .../util/qdeclarativespringanimation.cpp | 462 +++ .../util/qdeclarativespringanimation_p.h | 111 + src/declarative/util/qdeclarativestate.cpp | 742 +++++ src/declarative/util/qdeclarativestate_p.h | 210 ++ src/declarative/util/qdeclarativestate_p_p.h | 255 ++ src/declarative/util/qdeclarativestategroup.cpp | 502 ++++ src/declarative/util/qdeclarativestategroup_p.h | 95 + .../util/qdeclarativestateoperations.cpp | 1587 +++++++++++ .../util/qdeclarativestateoperations_p.h | 299 ++ src/declarative/util/qdeclarativestyledtext.cpp | 347 +++ src/declarative/util/qdeclarativestyledtext_p.h | 69 + src/declarative/util/qdeclarativesystempalette.cpp | 312 +++ src/declarative/util/qdeclarativesystempalette_p.h | 122 + src/declarative/util/qdeclarativetimeline.cpp | 947 +++++++ src/declarative/util/qdeclarativetimeline_p_p.h | 200 ++ src/declarative/util/qdeclarativetimer.cpp | 324 +++ src/declarative/util/qdeclarativetimer_p.h | 115 + src/declarative/util/qdeclarativetransition.cpp | 345 +++ src/declarative/util/qdeclarativetransition_p.h | 106 + .../util/qdeclarativetransitionmanager.cpp | 276 ++ .../util/qdeclarativetransitionmanager_p_p.h | 85 + src/declarative/util/qdeclarativeutilmodule.cpp | 174 ++ src/declarative/util/qdeclarativeutilmodule_p.h | 63 + src/declarative/util/qdeclarativeview.cpp | 739 +++++ src/declarative/util/qdeclarativeview.h | 119 + src/declarative/util/qdeclarativexmllistmodel.cpp | 1157 ++++++++ src/declarative/util/qdeclarativexmllistmodel_p.h | 213 ++ src/declarative/util/qlistmodelinterface.cpp | 105 + src/declarative/util/qlistmodelinterface_p.h | 84 + src/declarative/util/util.pri | 72 + 64 files changed, 22662 insertions(+) create mode 100644 src/declarative/util/qdeclarativeanimation.cpp create mode 100644 src/declarative/util/qdeclarativeanimation_p.h create mode 100644 src/declarative/util/qdeclarativeanimation_p_p.h create mode 100644 src/declarative/util/qdeclarativeapplication.cpp create mode 100644 src/declarative/util/qdeclarativeapplication_p.h create mode 100644 src/declarative/util/qdeclarativebehavior.cpp create mode 100644 src/declarative/util/qdeclarativebehavior_p.h create mode 100644 src/declarative/util/qdeclarativebind.cpp create mode 100644 src/declarative/util/qdeclarativebind_p.h create mode 100644 src/declarative/util/qdeclarativeconnections.cpp create mode 100644 src/declarative/util/qdeclarativeconnections_p.h create mode 100644 src/declarative/util/qdeclarativefontloader.cpp create mode 100644 src/declarative/util/qdeclarativefontloader_p.h create mode 100644 src/declarative/util/qdeclarativelistaccessor.cpp create mode 100644 src/declarative/util/qdeclarativelistaccessor_p.h create mode 100644 src/declarative/util/qdeclarativelistmodel.cpp create mode 100644 src/declarative/util/qdeclarativelistmodel_p.h create mode 100644 src/declarative/util/qdeclarativelistmodel_p_p.h create mode 100644 src/declarative/util/qdeclarativelistmodelworkeragent.cpp create mode 100644 src/declarative/util/qdeclarativelistmodelworkeragent_p.h create mode 100644 src/declarative/util/qdeclarativenullablevalue_p_p.h create mode 100644 src/declarative/util/qdeclarativeopenmetaobject.cpp create mode 100644 src/declarative/util/qdeclarativeopenmetaobject_p.h create mode 100644 src/declarative/util/qdeclarativepackage.cpp create mode 100644 src/declarative/util/qdeclarativepackage_p.h create mode 100644 src/declarative/util/qdeclarativepixmapcache.cpp create mode 100644 src/declarative/util/qdeclarativepixmapcache_p.h create mode 100644 src/declarative/util/qdeclarativepropertychanges.cpp create mode 100644 src/declarative/util/qdeclarativepropertychanges_p.h create mode 100644 src/declarative/util/qdeclarativepropertymap.cpp create mode 100644 src/declarative/util/qdeclarativepropertymap.h create mode 100644 src/declarative/util/qdeclarativesmoothedanimation.cpp create mode 100644 src/declarative/util/qdeclarativesmoothedanimation_p.h create mode 100644 src/declarative/util/qdeclarativesmoothedanimation_p_p.h create mode 100644 src/declarative/util/qdeclarativespringanimation.cpp create mode 100644 src/declarative/util/qdeclarativespringanimation_p.h create mode 100644 src/declarative/util/qdeclarativestate.cpp create mode 100644 src/declarative/util/qdeclarativestate_p.h create mode 100644 src/declarative/util/qdeclarativestate_p_p.h create mode 100644 src/declarative/util/qdeclarativestategroup.cpp create mode 100644 src/declarative/util/qdeclarativestategroup_p.h create mode 100644 src/declarative/util/qdeclarativestateoperations.cpp create mode 100644 src/declarative/util/qdeclarativestateoperations_p.h create mode 100644 src/declarative/util/qdeclarativestyledtext.cpp create mode 100644 src/declarative/util/qdeclarativestyledtext_p.h create mode 100644 src/declarative/util/qdeclarativesystempalette.cpp create mode 100644 src/declarative/util/qdeclarativesystempalette_p.h create mode 100644 src/declarative/util/qdeclarativetimeline.cpp create mode 100644 src/declarative/util/qdeclarativetimeline_p_p.h create mode 100644 src/declarative/util/qdeclarativetimer.cpp create mode 100644 src/declarative/util/qdeclarativetimer_p.h create mode 100644 src/declarative/util/qdeclarativetransition.cpp create mode 100644 src/declarative/util/qdeclarativetransition_p.h create mode 100644 src/declarative/util/qdeclarativetransitionmanager.cpp create mode 100644 src/declarative/util/qdeclarativetransitionmanager_p_p.h create mode 100644 src/declarative/util/qdeclarativeutilmodule.cpp create mode 100644 src/declarative/util/qdeclarativeutilmodule_p.h create mode 100644 src/declarative/util/qdeclarativeview.cpp create mode 100644 src/declarative/util/qdeclarativeview.h create mode 100644 src/declarative/util/qdeclarativexmllistmodel.cpp create mode 100644 src/declarative/util/qdeclarativexmllistmodel_p.h create mode 100644 src/declarative/util/qlistmodelinterface.cpp create mode 100644 src/declarative/util/qlistmodelinterface_p.h create mode 100644 src/declarative/util/util.pri (limited to 'src/declarative/util') diff --git a/src/declarative/util/qdeclarativeanimation.cpp b/src/declarative/util/qdeclarativeanimation.cpp new file mode 100644 index 00000000..496520e6 --- /dev/null +++ b/src/declarative/util/qdeclarativeanimation.cpp @@ -0,0 +1,2952 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeanimation_p.h" +#include "private/qdeclarativeanimation_p_p.h" + +#include "private/qdeclarativebehavior_p.h" +#include "private/qdeclarativestateoperations_p.h" +#include "private/qdeclarativecontext_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Animation QDeclarativeAbstractAnimation + \ingroup qml-animation-transition + \since 4.7 + \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. +*/ + +QDeclarativeAbstractAnimation::QDeclarativeAbstractAnimation(QObject *parent) +: QObject(*(new QDeclarativeAbstractAnimationPrivate), parent) +{ +} + +QDeclarativeAbstractAnimation::~QDeclarativeAbstractAnimation() +{ +} + +QDeclarativeAbstractAnimation::QDeclarativeAbstractAnimation(QDeclarativeAbstractAnimationPrivate &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 MouseArea is pressed. + + \code + Rectangle { + width: 100; height: 100 + NumberAnimation on x { + running: myMouse.pressed + from: 0; to: 100 + } + MouseArea { 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. Though, when the animations are assigned to properties, + as property value sources using the \e on syntax, they are set to running by default. +*/ +bool QDeclarativeAbstractAnimation::isRunning() const +{ + Q_D(const QDeclarativeAbstractAnimation); + return d->running; +} + +// the behavior calls this function +void QDeclarativeAbstractAnimation::notifyRunningChanged(bool running) +{ + Q_D(QDeclarativeAbstractAnimation); + if (d->disableUserControl && d->running != running) { + d->running = running; + emit runningChanged(running); + } +} + +//commence is called to start an animation when it is used as a +//simple animation, and not as part of a transition +void QDeclarativeAbstractAnimationPrivate::commence() +{ + Q_Q(QDeclarativeAbstractAnimation); + + QDeclarativeStateActions actions; + QDeclarativeProperties properties; + q->transition(actions, properties, QDeclarativeAbstractAnimation::Forward); + + q->qtAnimation()->start(); + if (q->qtAnimation()->state() != QAbstractAnimation::Running) { + running = false; + emit q->completed(); + } +} + +QDeclarativeProperty QDeclarativeAbstractAnimationPrivate::createProperty(QObject *obj, const QString &str, QObject *infoObj) +{ + QDeclarativeProperty prop(obj, str, qmlContext(infoObj)); + if (!prop.isValid()) { + qmlInfo(infoObj) << QDeclarativeAbstractAnimation::tr("Cannot animate non-existent property \"%1\"").arg(str); + return QDeclarativeProperty(); + } else if (!prop.isWritable()) { + qmlInfo(infoObj) << QDeclarativeAbstractAnimation::tr("Cannot animate read-only property \"%1\"").arg(str); + return QDeclarativeProperty(); + } + return prop; +} + +void QDeclarativeAbstractAnimation::setRunning(bool r) +{ + Q_D(QDeclarativeAbstractAnimation); + if (!d->componentComplete) { + d->running = r; + if (r == false) + d->avoidPropertyValueSourceStart = true; + else if (!d->registered) { + d->registered = true; + QDeclarativeEnginePrivate *engPriv = QDeclarativeEnginePrivate::get(qmlEngine(this)); + engPriv->registerFinalizedParserStatusObject(this, this->metaObject()->indexOfSlot("componentFinalized()")); + } + return; + } + + if (d->running == r) + return; + + if (d->group || d->disableUserControl) { + qmlInfo(this) << "setRunning() cannot be used on non-root animation nodes."; + return; + } + + d->running = r; + if (d->running) { + bool supressStart = false; + if (d->alwaysRunToEnd && d->loopCount != 1 + && qtAnimation()->state() == QAbstractAnimation::Running) { + //we've restarted before the final loop finished; restore proper loop count + if (d->loopCount == -1) + qtAnimation()->setLoopCount(d->loopCount); + else + qtAnimation()->setLoopCount(qtAnimation()->currentLoop() + d->loopCount); + supressStart = true; //we want the animation to continue, rather than restart + } + + if (!d->connectedTimeLine) { + QObject::connect(qtAnimation(), SIGNAL(finished()), + this, SLOT(timelineComplete())); + d->connectedTimeLine = true; + } + if (!supressStart) + d->commence(); + emit started(); + } else { + if (d->alwaysRunToEnd) { + if (d->loopCount != 1) + qtAnimation()->setLoopCount(qtAnimation()->currentLoop()+1); //finish the current loop + } 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 QDeclarativeAbstractAnimation::isPaused() const +{ + Q_D(const QDeclarativeAbstractAnimation); + return d->paused; +} + +void QDeclarativeAbstractAnimation::setPaused(bool p) +{ + Q_D(QDeclarativeAbstractAnimation); + if (d->paused == p) + return; + + if (d->group || d->disableUserControl) { + qmlInfo(this) << "setPaused() cannot be used on non-root animation nodes."; + return; + } + + d->paused = p; + if (d->paused) + qtAnimation()->pause(); + else + qtAnimation()->resume(); + + emit pausedChanged(d->paused); +} + +void QDeclarativeAbstractAnimation::classBegin() +{ + Q_D(QDeclarativeAbstractAnimation); + d->componentComplete = false; +} + +void QDeclarativeAbstractAnimation::componentComplete() +{ + Q_D(QDeclarativeAbstractAnimation); + d->componentComplete = true; +} + +void QDeclarativeAbstractAnimation::componentFinalized() +{ + Q_D(QDeclarativeAbstractAnimation); + if (d->running) { + d->running = false; + setRunning(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. + + \note alwaysRunToEnd has no effect on animations in a Transition. +*/ +bool QDeclarativeAbstractAnimation::alwaysRunToEnd() const +{ + Q_D(const QDeclarativeAbstractAnimation); + return d->alwaysRunToEnd; +} + +void QDeclarativeAbstractAnimation::setAlwaysRunToEnd(bool f) +{ + Q_D(QDeclarativeAbstractAnimation); + if (d->alwaysRunToEnd == f) + return; + + d->alwaysRunToEnd = f; + emit alwaysRunToEndChanged(f); +} + +/*! + \qmlproperty int Animation::loops + This property holds the number of times the animation should play. + + By default, \c loops is 1: the animation will play through once and then stop. + + If set to Animation.Infinite, 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 indefinitely. + + \code + Rectangle { + width: 100; height: 100; color: "green" + RotationAnimation on rotation { + loops: Animation.Infinite + from: 0 + to: 360 + } + } + \endcode +*/ +int QDeclarativeAbstractAnimation::loops() const +{ + Q_D(const QDeclarativeAbstractAnimation); + return d->loopCount; +} + +void QDeclarativeAbstractAnimation::setLoops(int loops) +{ + Q_D(QDeclarativeAbstractAnimation); + if (loops < 0) + loops = -1; + + if (loops == d->loopCount) + return; + + d->loopCount = loops; + qtAnimation()->setLoopCount(loops); + emit loopCountChanged(loops); +} + + +int QDeclarativeAbstractAnimation::currentTime() +{ + return qtAnimation()->currentLoopTime(); +} + +void QDeclarativeAbstractAnimation::setCurrentTime(int time) +{ + qtAnimation()->setCurrentTime(time); +} + +QDeclarativeAnimationGroup *QDeclarativeAbstractAnimation::group() const +{ + Q_D(const QDeclarativeAbstractAnimation); + return d->group; +} + +void QDeclarativeAbstractAnimation::setGroup(QDeclarativeAnimationGroup *g) +{ + Q_D(QDeclarativeAbstractAnimation); + if (d->group == g) + return; + if (d->group) + static_cast(d->group->d_func())->animations.removeAll(this); + + d->group = g; + + if (d->group && !static_cast(d->group->d_func())->animations.contains(this)) + static_cast(d->group->d_func())->animations.append(this); + + //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 QDeclarativeAbstractAnimation::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 QDeclarativeAbstractAnimation::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 QDeclarativeAbstractAnimation::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 { + NumberAnimation on x { 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 QDeclarativeAbstractAnimation::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 QDeclarativeAbstractAnimation::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 { + NumberAnimation on x { 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 QDeclarativeAbstractAnimation::complete() +{ + if (isRunning()) { + qtAnimation()->setCurrentTime(qtAnimation()->duration()); + } +} + +void QDeclarativeAbstractAnimation::setTarget(const QDeclarativeProperty &p) +{ + Q_D(QDeclarativeAbstractAnimation); + d->defaultProperty = p; + + if (!d->avoidPropertyValueSourceStart) + setRunning(true); +} + +/* + we rely on setTarget only being called when used as a value source + so this function allows us to do the same thing as setTarget without + that assumption +*/ +void QDeclarativeAbstractAnimation::setDefaultTarget(const QDeclarativeProperty &p) +{ + Q_D(QDeclarativeAbstractAnimation); + d->defaultProperty = p; +} + +/* + don't allow start/stop/pause/resume to be manually invoked, + because something else (like a Behavior) already has control + over the animation. +*/ +void QDeclarativeAbstractAnimation::setDisableUserControl() +{ + Q_D(QDeclarativeAbstractAnimation); + d->disableUserControl = true; +} + +void QDeclarativeAbstractAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_UNUSED(actions); + Q_UNUSED(modified); + Q_UNUSED(direction); +} + +void QDeclarativeAbstractAnimation::timelineComplete() +{ + Q_D(QDeclarativeAbstractAnimation); + setRunning(false); + if (d->alwaysRunToEnd && d->loopCount != 1) { + //restore the proper loopCount for the next run + qtAnimation()->setLoopCount(d->loopCount); + } +} + +/*! + \qmlclass PauseAnimation QDeclarativePauseAnimation + \ingroup qml-animation-transition + \since 4.7 + \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 + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativePauseAnimation::QDeclarativePauseAnimation(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QDeclarativePauseAnimationPrivate), parent) +{ + Q_D(QDeclarativePauseAnimation); + d->init(); +} + +QDeclarativePauseAnimation::~QDeclarativePauseAnimation() +{ +} + +void QDeclarativePauseAnimationPrivate::init() +{ + Q_Q(QDeclarativePauseAnimation); + pa = new QPauseAnimation; + QDeclarative_setParent_noEvent(pa, q); +} + +/*! + \qmlproperty int PauseAnimation::duration + This property holds the duration of the pause in milliseconds + + The default value is 250. +*/ +int QDeclarativePauseAnimation::duration() const +{ + Q_D(const QDeclarativePauseAnimation); + return d->pa->duration(); +} + +void QDeclarativePauseAnimation::setDuration(int duration) +{ + if (duration < 0) { + qmlInfo(this) << tr("Cannot set a duration of < 0"); + return; + } + + Q_D(QDeclarativePauseAnimation); + if (d->pa->duration() == duration) + return; + d->pa->setDuration(duration); + emit durationChanged(duration); +} + +QAbstractAnimation *QDeclarativePauseAnimation::qtAnimation() +{ + Q_D(QDeclarativePauseAnimation); + return d->pa; +} + +/*! + \qmlclass ColorAnimation QDeclarativeColorAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits PropertyAnimation + \brief The ColorAnimation element animates changes in color values. + + ColorAnimation is a specialized PropertyAnimation that defines an + animation to be applied when a color value changes. + + Here is a ColorAnimation applied to the \c color property of a \l Rectangle + as a property value source. It animates the \c color property's value from + its current value to a value of "red", over 1000 milliseconds: + + \snippet doc/src/snippets/declarative/coloranimation.qml 0 + + Like any other animation element, a ColorAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + For convenience, when a ColorAnimation is used in a \l Transition, it will + animate any \c color properties that have been modified during the state + change. If a \l{PropertyAnimation::}{property} or + \l{PropertyAnimation::}{properties} are explicitly set for the animation, + then those are used instead. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativeColorAnimation::QDeclarativeColorAnimation(QObject *parent) +: QDeclarativePropertyAnimation(parent) +{ + Q_D(QDeclarativePropertyAnimation); + d->interpolatorType = QMetaType::QColor; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + d->defaultToInterpolatorType = true; +} + +QDeclarativeColorAnimation::~QDeclarativeColorAnimation() +{ +} + +/*! + \qmlproperty color ColorAnimation::from + This property holds the color value at which the animation should begin. + + For example, the following animation is not applied until a color value + has reached "#c0c0c0": + + \qml + Item { + states: [ + // States are defined here... + ] + + transition: Transition { + NumberAnimation { from: "#c0c0c0"; duration: 2000 } + } + } + \endqml + + If the ColorAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ +QColor QDeclarativeColorAnimation::from() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->from.value(); +} + +void QDeclarativeColorAnimation::setFrom(const QColor &f) +{ + QDeclarativePropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty color ColorAnimation::to + + This property holds the color value at which the animation should end. + + If the ColorAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +QColor QDeclarativeColorAnimation::to() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->to.value(); +} + +void QDeclarativeColorAnimation::setTo(const QColor &t) +{ + QDeclarativePropertyAnimation::setTo(t); +} + + + +/*! + \qmlclass ScriptAction QDeclarativeScriptAction + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The ScriptAction element allows scripts to be run during an animation. + + ScriptAction can be used to run a script at a specific point in an animation. + + \qml + SequentialAnimation { + NumberAnimation { + // ... + } + ScriptAction { script: doSomething(); } + NumberAnimation { + // ... + } + } + \endqml + + When used as part of a Transition, you can also target a specific + StateChangeScript to run using the \c scriptName property. + + \snippet doc/src/snippets/declarative/states/statechangescript.qml state and transition + + \sa StateChangeScript +*/ +QDeclarativeScriptAction::QDeclarativeScriptAction(QObject *parent) + :QDeclarativeAbstractAnimation(*(new QDeclarativeScriptActionPrivate), parent) +{ + Q_D(QDeclarativeScriptAction); + d->init(); +} + +QDeclarativeScriptAction::~QDeclarativeScriptAction() +{ +} + +void QDeclarativeScriptActionPrivate::init() +{ + Q_Q(QDeclarativeScriptAction); + rsa = new QActionAnimation(&proxy); + QDeclarative_setParent_noEvent(rsa, q); +} + +/*! + \qmlproperty script ScriptAction::script + This property holds the script to run. +*/ +QDeclarativeScriptString QDeclarativeScriptAction::script() const +{ + Q_D(const QDeclarativeScriptAction); + return d->script; +} + +void QDeclarativeScriptAction::setScript(const QDeclarativeScriptString &script) +{ + Q_D(QDeclarativeScriptAction); + d->script = script; +} + +/*! + \qmlproperty string ScriptAction::scriptName + 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 scriptName are set, scriptName will be used. + + \note When using scriptName in a reversible transition, the script will only + be run when the transition is being run forwards. +*/ +QString QDeclarativeScriptAction::stateChangeScriptName() const +{ + Q_D(const QDeclarativeScriptAction); + return d->name; +} + +void QDeclarativeScriptAction::setStateChangeScriptName(const QString &name) +{ + Q_D(QDeclarativeScriptAction); + d->name = name; +} + +void QDeclarativeScriptActionPrivate::execute() +{ + Q_Q(QDeclarativeScriptAction); + if (hasRunScriptScript && reversing) + return; + + QDeclarativeScriptString scriptStr = hasRunScriptScript ? runScriptScript : script; + + const QString &str = scriptStr.script(); + if (!str.isEmpty()) { + QDeclarativeExpression expr(scriptStr.context(), scriptStr.scopeObject(), str); + QDeclarativeData *ddata = QDeclarativeData::get(q); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expr.setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + expr.evaluate(); + if (expr.hasError()) + qmlInfo(q) << expr.error(); + } +} + +void QDeclarativeScriptAction::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeScriptAction); + Q_UNUSED(modified); + + d->hasRunScriptScript = false; + d->reversing = (direction == Backward); + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + + if (action.event && action.event->typeName() == QLatin1String("StateChangeScript") + && static_cast(action.event)->name() == d->name) { + d->runScriptScript = static_cast(action.event)->script(); + d->hasRunScriptScript = true; + action.actionDone = true; + break; //only match one (names should be unique) + } + } +} + +QAbstractAnimation *QDeclarativeScriptAction::qtAnimation() +{ + Q_D(QDeclarativeScriptAction); + return d->rsa; +} + + + +/*! + \qmlclass PropertyAction QDeclarativePropertyAction + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The PropertyAction element allows immediate property changes during animation. + + PropertyAction is used to specify an immediate property change during an + animation. The property change is not animated. + + It is useful for setting non-animated property values during an animation. + + For example, here is a SequentialAnimation that sets the image's + \l {Image::}{smooth} property to \c true, animates the width of the image, + then sets \l {Image::}{smooth} back to \c false: + + \snippet doc/src/snippets/declarative/propertyaction.qml standalone + + PropertyAction is also useful for setting the exact point at which a property + change should occur during a \l Transition. For example, if PropertyChanges + was used in a \l State to rotate an item around a particular + \l {Item::}{transformOrigin}, it might be implemented like this: + + \snippet doc/src/snippets/declarative/propertyaction.qml transition + + However, with this code, the \c transformOrigin is not set until \e after + the animation, as a \l State is taken to define the values at the \e end of + a transition. The animation would rotate at the default \c transformOrigin, + then jump to \c Item.BottomRight. To fix this, insert a PropertyAction + before the RotationAnimation begins: + + \snippet doc/src/snippets/declarative/propertyaction-sequential.qml sequential + + This immediately sets the \c transformOrigin property to the value defined + in the end state of the \l Transition (i.e. the value defined in the + PropertyAction object) so that the rotation animation begins with the + correct transform origin. + + \sa {QML Animation and Transitions}, QtDeclarative +*/ +QDeclarativePropertyAction::QDeclarativePropertyAction(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QDeclarativePropertyActionPrivate), parent) +{ + Q_D(QDeclarativePropertyAction); + d->init(); +} + +QDeclarativePropertyAction::~QDeclarativePropertyAction() +{ +} + +void QDeclarativePropertyActionPrivate::init() +{ + Q_Q(QDeclarativePropertyAction); + spa = new QActionAnimation; + QDeclarative_setParent_noEvent(spa, q); +} + +QObject *QDeclarativePropertyAction::target() const +{ + Q_D(const QDeclarativePropertyAction); + return d->target; +} + +void QDeclarativePropertyAction::setTarget(QObject *o) +{ + Q_D(QDeclarativePropertyAction); + if (d->target == o) + return; + d->target = o; + emit targetChanged(); +} + +QString QDeclarativePropertyAction::property() const +{ + Q_D(const QDeclarativePropertyAction); + return d->propertyName; +} + +void QDeclarativePropertyAction::setProperty(const QString &n) +{ + Q_D(QDeclarativePropertyAction); + if (d->propertyName == n) + return; + d->propertyName = n; + emit propertyChanged(); +} + +/*! + \qmlproperty Object PropertyAction::target + \qmlproperty list PropertyAction::targets + \qmlproperty string PropertyAction::property + \qmlproperty string PropertyAction::properties + + These properties determine the items and their properties that are + affected by this action. + + The details of how these properties are interpreted in different situations + is covered in the \l{PropertyAnimation::properties}{corresponding} PropertyAnimation + documentation. + + \sa exclude +*/ +QString QDeclarativePropertyAction::properties() const +{ + Q_D(const QDeclarativePropertyAction); + return d->properties; +} + +void QDeclarativePropertyAction::setProperties(const QString &p) +{ + Q_D(QDeclarativePropertyAction); + if (d->properties == p) + return; + d->properties = p; + emit propertiesChanged(p); +} + +QDeclarativeListProperty QDeclarativePropertyAction::targets() +{ + Q_D(QDeclarativePropertyAction); + return QDeclarativeListProperty(this, d->targets); +} + +/*! + \qmlproperty list PropertyAction::exclude + This property holds the objects that should not be affected by this action. + + \sa targets +*/ +QDeclarativeListProperty QDeclarativePropertyAction::exclude() +{ + Q_D(QDeclarativePropertyAction); + return QDeclarativeListProperty(this, d->exclude); +} + +/*! + \qmlproperty any PropertyAction::value + This property holds the value to be set on the property. + + If the PropertyAction is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. +*/ +QVariant QDeclarativePropertyAction::value() const +{ + Q_D(const QDeclarativePropertyAction); + return d->value; +} + +void QDeclarativePropertyAction::setValue(const QVariant &v) +{ + Q_D(QDeclarativePropertyAction); + if (d->value.isNull || d->value != v) { + d->value = v; + emit valueChanged(v); + } +} + +QAbstractAnimation *QDeclarativePropertyAction::qtAnimation() +{ + Q_D(QDeclarativePropertyAction); + return d->spa; +} + +void QDeclarativePropertyAction::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativePropertyAction); + Q_UNUSED(direction); + + struct QDeclarativeSetPropertyAnimationAction : public QAbstractAnimationAction + { + QDeclarativeStateActions actions; + virtual void doAction() + { + for (int ii = 0; ii < actions.count(); ++ii) { + const QDeclarativeAction &action = actions.at(ii); + QDeclarativePropertyPrivate::write(action.property, action.toValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::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(); + if (!d->propertyName.isEmpty()) + props << d->propertyName; + + QList targets = d->targets; + if (d->target) + targets.append(d->target); + + bool hasSelectors = !props.isEmpty() || !targets.isEmpty() || !d->exclude.isEmpty(); + + if (d->defaultProperty.isValid() && !hasSelectors) { + props << d->defaultProperty.name(); + targets << d->defaultProperty.object(); + } + + QDeclarativeSetPropertyAnimationAction *data = new QDeclarativeSetPropertyAnimationAction; + + bool hasExplicit = false; + //an explicit animation has been specified + if (d->value.isValid()) { + for (int i = 0; i < props.count(); ++i) { + for (int j = 0; j < targets.count(); ++j) { + QDeclarativeAction myAction; + myAction.property = d->createProperty(targets.at(j), props.at(i), this); + if (myAction.property.isValid()) { + myAction.toValue = d->value; + QDeclarativePropertyAnimationPrivate::convertVariant(myAction.toValue, myAction.property.propertyType()); + data->actions << myAction; + hasExplicit = true; + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &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) { + QDeclarativeAction &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 ((targets.isEmpty() || targets.contains(obj) || (!same && targets.contains(sObj))) && + (!d->exclude.contains(obj)) && (same || (!d->exclude.contains(sObj))) && + (props.contains(propertyName) || (!same && props.contains(sPropertyName)))) { + QDeclarativeAction myAction = action; + + if (d->value.isValid()) + myAction.toValue = d->value; + QDeclarativePropertyAnimationPrivate::convertVariant(myAction.toValue, myAction.property.propertyType()); + + modified << action.property; + data->actions << myAction; + action.fromValue = myAction.toValue; + } + } + + if (data->actions.count()) { + d->spa->setAnimAction(data, QAbstractAnimation::DeleteWhenStopped); + } else { + delete data; + } +} + +/*! + \qmlclass NumberAnimation QDeclarativeNumberAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits PropertyAnimation + \brief The NumberAnimation element animates changes in qreal-type values. + + NumberAnimation is a specialized PropertyAnimation that defines an + animation to be applied when a numerical value changes. + + Here is a NumberAnimation applied to the \c x property of a \l Rectangle + as a property value source. It animates the \c x value from its current + value to a value of 50, over 1000 milliseconds: + + \snippet doc/src/snippets/declarative/numberanimation.qml 0 + + Like any other animation element, a NumberAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + Note that NumberAnimation may not animate smoothly if there are irregular + changes in the number value that it is tracking. If this is the case, use + SmoothedAnimation instead. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativeNumberAnimation::QDeclarativeNumberAnimation(QObject *parent) +: QDeclarativePropertyAnimation(parent) +{ + init(); +} + +QDeclarativeNumberAnimation::QDeclarativeNumberAnimation(QDeclarativePropertyAnimationPrivate &dd, QObject *parent) +: QDeclarativePropertyAnimation(dd, parent) +{ + init(); +} + +QDeclarativeNumberAnimation::~QDeclarativeNumberAnimation() +{ +} + +void QDeclarativeNumberAnimation::init() +{ + Q_D(QDeclarativePropertyAnimation); + d->interpolatorType = QMetaType::QReal; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); +} + +/*! + \qmlproperty real NumberAnimation::from + This property holds the starting value for the animation. + + For example, the following animation is not applied until the \c x value + has reached 100: + + \qml + Item { + states: [ + // ... + ] + + transition: Transition { + NumberAnimation { properties: "x"; from: 100; duration: 200 } + } + } + \endqml + + If the NumberAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ + +qreal QDeclarativeNumberAnimation::from() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->from.toReal(); +} + +void QDeclarativeNumberAnimation::setFrom(qreal f) +{ + QDeclarativePropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty real NumberAnimation::to + This property holds the end value for the animation. + + If the NumberAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +qreal QDeclarativeNumberAnimation::to() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->to.toReal(); +} + +void QDeclarativeNumberAnimation::setTo(qreal t) +{ + QDeclarativePropertyAnimation::setTo(t); +} + + + +/*! + \qmlclass Vector3dAnimation QDeclarativeVector3dAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits PropertyAnimation + \brief The Vector3dAnimation element animates changes in QVector3d values. + + Vector3dAnimation is a specialized PropertyAnimation that defines an + animation to be applied when a Vector3d value changes. + + Like any other animation element, a Vector3dAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativeVector3dAnimation::QDeclarativeVector3dAnimation(QObject *parent) +: QDeclarativePropertyAnimation(parent) +{ + Q_D(QDeclarativePropertyAnimation); + d->interpolatorType = QMetaType::QVector3D; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + d->defaultToInterpolatorType = true; +} + +QDeclarativeVector3dAnimation::~QDeclarativeVector3dAnimation() +{ +} + +/*! + \qmlproperty real Vector3dAnimation::from + This property holds the starting value for the animation. + + If the Vector3dAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ +QVector3D QDeclarativeVector3dAnimation::from() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->from.value(); +} + +void QDeclarativeVector3dAnimation::setFrom(QVector3D f) +{ + QDeclarativePropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty real Vector3dAnimation::to + This property holds the end value for the animation. + + If the Vector3dAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +QVector3D QDeclarativeVector3dAnimation::to() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->to.value(); +} + +void QDeclarativeVector3dAnimation::setTo(QVector3D t) +{ + QDeclarativePropertyAnimation::setTo(t); +} + + + +/*! + \qmlclass RotationAnimation QDeclarativeRotationAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits PropertyAnimation + \brief The RotationAnimation element animates changes in rotation values. + + RotationAnimation is a specialized PropertyAnimation that gives control + over the direction of rotation during an animation. + + By default, it rotates in the direction + of the numerical change; a rotation from 0 to 240 will rotate 240 degrees + clockwise, while a rotation from 240 to 0 will rotate 240 degrees + counterclockwise. The \l direction property can be set to specify the + direction in which the rotation should occur. + + In the following example we use RotationAnimation to animate the rotation + between states via the shortest path: + + \snippet doc/src/snippets/declarative/rotationanimation.qml 0 + + Notice the RotationAnimation did not need to set a \l target + value. As a convenience, when used in a transition, RotationAnimation will rotate all + properties named "rotation" or "angle". You can override this by providing + your own properties via \l {PropertyAnimation::properties}{properties} or + \l {PropertyAnimation::property}{property}. + + Also, note the \l Rectangle will be rotated around its default + \l {Item::}{transformOrigin} (which is \c Item.Center). To use a different + transform origin, set the origin in the PropertyChanges object and apply + the change at the start of the animation using PropertyAction. See the + PropertyAction documentation for more details. + + Like any other animation element, a RotationAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QVariant _q_interpolateShortestRotation(qreal &f, qreal &t, qreal progress) +{ + qreal newt = t; + qreal diff = t-f; + while(diff > 180.0){ + newt -= 360.0; + diff -= 360.0; + } + while(diff < -180.0){ + newt += 360.0; + diff += 360.0; + } + return QVariant(f + (newt - f) * progress); +} + +QVariant _q_interpolateClockwiseRotation(qreal &f, qreal &t, qreal progress) +{ + qreal newt = t; + qreal diff = t-f; + while(diff < 0.0){ + newt += 360.0; + diff += 360.0; + } + return QVariant(f + (newt - f) * progress); +} + +QVariant _q_interpolateCounterclockwiseRotation(qreal &f, qreal &t, qreal progress) +{ + qreal newt = t; + qreal diff = t-f; + while(diff > 0.0){ + newt -= 360.0; + diff -= 360.0; + } + return QVariant(f + (newt - f) * progress); +} + +QDeclarativeRotationAnimation::QDeclarativeRotationAnimation(QObject *parent) +: QDeclarativePropertyAnimation(*(new QDeclarativeRotationAnimationPrivate), parent) +{ + Q_D(QDeclarativeRotationAnimation); + d->interpolatorType = QMetaType::QReal; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + d->defaultProperties = QLatin1String("rotation,angle"); +} + +QDeclarativeRotationAnimation::~QDeclarativeRotationAnimation() +{ +} + +/*! + \qmlproperty real RotationAnimation::from + This property holds the starting value for the animation. + + For example, the following animation is not applied until the \c angle value + has reached 100: + + \qml + Item { + states: [ + // ... + ] + + transition: Transition { + RotationAnimation { properties: "angle"; from: 100; duration: 2000 } + } + } + \endqml + + If the RotationAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ +qreal QDeclarativeRotationAnimation::from() const +{ + Q_D(const QDeclarativeRotationAnimation); + return d->from.toReal(); +} + +void QDeclarativeRotationAnimation::setFrom(qreal f) +{ + QDeclarativePropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty real RotationAnimation::to + This property holds the end value for the animation.. + + If the RotationAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +qreal QDeclarativeRotationAnimation::to() const +{ + Q_D(const QDeclarativeRotationAnimation); + return d->to.toReal(); +} + +void QDeclarativeRotationAnimation::setTo(qreal t) +{ + QDeclarativePropertyAnimation::setTo(t); +} + +/*! + \qmlproperty enumeration RotationAnimation::direction + This property holds the direction of the rotation. + + Possible values are: + + \list + \o RotationAnimation.Numerical (default) - Rotate by linearly interpolating between the two numbers. + A rotation from 10 to 350 will rotate 340 degrees clockwise. + \o RotationAnimation.Clockwise - Rotate clockwise between the two values + \o RotationAnimation.Counterclockwise - Rotate counterclockwise between the two values + \o RotationAnimation.Shortest - Rotate in the direction that produces the shortest animation path. + A rotation from 10 to 350 will rotate 20 degrees counterclockwise. + \endlist +*/ +QDeclarativeRotationAnimation::RotationDirection QDeclarativeRotationAnimation::direction() const +{ + Q_D(const QDeclarativeRotationAnimation); + return d->direction; +} + +void QDeclarativeRotationAnimation::setDirection(QDeclarativeRotationAnimation::RotationDirection direction) +{ + Q_D(QDeclarativeRotationAnimation); + if (d->direction == direction) + return; + + d->direction = direction; + switch(d->direction) { + case Clockwise: + d->interpolator = reinterpret_cast(&_q_interpolateClockwiseRotation); + break; + case Counterclockwise: + d->interpolator = reinterpret_cast(&_q_interpolateCounterclockwiseRotation); + break; + case Shortest: + d->interpolator = reinterpret_cast(&_q_interpolateShortestRotation); + break; + default: + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + break; + } + + emit directionChanged(); +} + + + +QDeclarativeAnimationGroup::QDeclarativeAnimationGroup(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QDeclarativeAnimationGroupPrivate), parent) +{ +} + +QDeclarativeAnimationGroup::QDeclarativeAnimationGroup(QDeclarativeAnimationGroupPrivate &dd, QObject *parent) + : QDeclarativeAbstractAnimation(dd, parent) +{ +} + +void QDeclarativeAnimationGroupPrivate::append_animation(QDeclarativeListProperty *list, QDeclarativeAbstractAnimation *a) +{ + QDeclarativeAnimationGroup *q = qobject_cast(list->object); + if (q) { + a->setGroup(q); + // This is an optimization for the parenting that already occurs via addAnimation + QDeclarative_setParent_noEvent(a->qtAnimation(), q->d_func()->ag); + q->d_func()->ag->addAnimation(a->qtAnimation()); + } +} + +void QDeclarativeAnimationGroupPrivate::clear_animation(QDeclarativeListProperty *list) +{ + QDeclarativeAnimationGroup *q = qobject_cast(list->object); + if (q) { + while (q->d_func()->animations.count()) { + QDeclarativeAbstractAnimation *firstAnim = q->d_func()->animations.at(0); + QDeclarative_setParent_noEvent(firstAnim->qtAnimation(), 0); + q->d_func()->ag->removeAnimation(firstAnim->qtAnimation()); + firstAnim->setGroup(0); + } + } +} + +QDeclarativeAnimationGroup::~QDeclarativeAnimationGroup() +{ +} + +QDeclarativeListProperty QDeclarativeAnimationGroup::animations() +{ + Q_D(QDeclarativeAnimationGroup); + QDeclarativeListProperty list(this, d->animations); + list.append = &QDeclarativeAnimationGroupPrivate::append_animation; + list.clear = &QDeclarativeAnimationGroupPrivate::clear_animation; + return list; +} + +/*! + \qmlclass SequentialAnimation QDeclarativeSequentialAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The SequentialAnimation element allows animations to be run sequentially. + + The SequentialAnimation and ParallelAnimation elements allow multiple + animations to be run together. Animations defined in a SequentialAnimation + are run one after the other, while animations defined in a ParallelAnimation + are run at the same time. + + The following example runs two number animations in a sequence. The \l Rectangle + animates to a \c x position of 50, then to a \c y position of 50. + + \snippet doc/src/snippets/declarative/sequentialanimation.qml 0 + + Animations defined within a \l Transition are automatically run in parallel, + so SequentialAnimation can be used to enclose the animations in a \l Transition + if this is the preferred behavior. + + Like any other animation element, a SequentialAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \note Once an animation has been grouped into a SequentialAnimation or + ParallelAnimation, it cannot be individually started and stopped; the + SequentialAnimation or ParallelAnimation must be started and stopped as a group. + + \sa ParallelAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ + +QDeclarativeSequentialAnimation::QDeclarativeSequentialAnimation(QObject *parent) : + QDeclarativeAnimationGroup(parent) +{ + Q_D(QDeclarativeAnimationGroup); + d->ag = new QSequentialAnimationGroup; + QDeclarative_setParent_noEvent(d->ag, this); +} + +QDeclarativeSequentialAnimation::~QDeclarativeSequentialAnimation() +{ +} + +QAbstractAnimation *QDeclarativeSequentialAnimation::qtAnimation() +{ + Q_D(QDeclarativeAnimationGroup); + return d->ag; +} + +void QDeclarativeSequentialAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeAnimationGroup); + + int inc = 1; + int from = 0; + if (direction == Backward) { + inc = -1; + from = d->animations.count() - 1; + } + + bool valid = d->defaultProperty.isValid(); + for (int ii = from; ii < d->animations.count() && ii >= 0; ii += inc) { + if (valid) + d->animations.at(ii)->setDefaultTarget(d->defaultProperty); + d->animations.at(ii)->transition(actions, modified, direction); + } +} + + + +/*! + \qmlclass ParallelAnimation QDeclarativeParallelAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The ParallelAnimation element allows animations to be run in parallel. + + The SequentialAnimation and ParallelAnimation elements allow multiple + animations to be run together. Animations defined in a SequentialAnimation + are run one after the other, while animations defined in a ParallelAnimation + are run at the same time. + + The following animation runs two number animations in parallel. The \l Rectangle + moves to (50,50) by animating its \c x and \c y properties at the same time. + + \snippet doc/src/snippets/declarative/parallelanimation.qml 0 + + Like any other animation element, a ParallelAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \note Once an animation has been grouped into a SequentialAnimation or + ParallelAnimation, it cannot be individually started and stopped; the + SequentialAnimation or ParallelAnimation must be started and stopped as a group. + + \sa SequentialAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativeParallelAnimation::QDeclarativeParallelAnimation(QObject *parent) : + QDeclarativeAnimationGroup(parent) +{ + Q_D(QDeclarativeAnimationGroup); + d->ag = new QParallelAnimationGroup; + QDeclarative_setParent_noEvent(d->ag, this); +} + +QDeclarativeParallelAnimation::~QDeclarativeParallelAnimation() +{ +} + +QAbstractAnimation *QDeclarativeParallelAnimation::qtAnimation() +{ + Q_D(QDeclarativeAnimationGroup); + return d->ag; +} + +void QDeclarativeParallelAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeAnimationGroup); + bool valid = d->defaultProperty.isValid(); + for (int ii = 0; ii < d->animations.count(); ++ii) { + if (valid) + d->animations.at(ii)->setDefaultTarget(d->defaultProperty); + d->animations.at(ii)->transition(actions, modified, direction); + } +} + + + +//convert a variant from string type to another animatable type +void QDeclarativePropertyAnimationPrivate::convertVariant(QVariant &variant, int type) +{ + if (variant.userType() != QVariant::String) { + variant.convert((QVariant::Type)type); + return; + } + + switch (type) { + case QVariant::Rect: { + variant.setValue(QDeclarativeStringConverters::rectFFromString(variant.toString()).toRect()); + break; + } + case QVariant::RectF: { + variant.setValue(QDeclarativeStringConverters::rectFFromString(variant.toString())); + break; + } + case QVariant::Point: { + variant.setValue(QDeclarativeStringConverters::pointFFromString(variant.toString()).toPoint()); + break; + } + case QVariant::PointF: { + variant.setValue(QDeclarativeStringConverters::pointFFromString(variant.toString())); + break; + } + case QVariant::Size: { + variant.setValue(QDeclarativeStringConverters::sizeFFromString(variant.toString()).toSize()); + break; + } + case QVariant::SizeF: { + variant.setValue(QDeclarativeStringConverters::sizeFFromString(variant.toString())); + break; + } + case QVariant::Color: { + variant.setValue(QDeclarativeStringConverters::colorFromString(variant.toString())); + break; + } + case QVariant::Vector3D: { + variant.setValue(QDeclarativeStringConverters::vector3DFromString(variant.toString())); + break; + } + default: + if (QDeclarativeValueTypeFactory::isValueType((uint)type)) { + variant.convert((QVariant::Type)type); + } else { + QDeclarativeMetaType::StringConverter converter = QDeclarativeMetaType::customStringConverter(type); + if (converter) + variant = converter(variant.toString()); + } + break; + } +} + +/*! + \qmlclass PropertyAnimation QDeclarativePropertyAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The PropertyAnimation element animates changes in property values. + + PropertyAnimation provides a way to animate changes to a property's value. + + It can be used to define animations in a number of ways: + + \list + \o In a \l Transition + + For example, to animate any objects that have changed their \c x or \c y properties + as a result of a state change, using an \c InOutQuad easing curve: + + \snippet doc/src/snippets/declarative/propertyanimation.qml transition + + + \o In a \l Behavior + + For example, to animate all changes to a rectangle's \c x property: + + \snippet doc/src/snippets/declarative/propertyanimation.qml behavior + + + \o As a property value source + + For example, to repeatedly animate the rectangle's \c x property: + + \snippet doc/src/snippets/declarative/propertyanimation.qml propertyvaluesource + + + \o In a signal handler + + For example, to fade out \c theObject when clicked: + \qml + MouseArea { + anchors.fill: theObject + onClicked: PropertyAnimation { target: theObject; property: "opacity"; to: 0 } + } + \endqml + + \o Standalone + + For example, to animate \c rect's \c width property over 500ms, from its current width to 30: + + \snippet doc/src/snippets/declarative/propertyanimation.qml standalone + + \endlist + + Depending on how the animation is used, the set of properties normally used will be + different. For more information see the individual property documentation, as well + as the \l{QML Animation and Transitions} introduction. + + Note that PropertyAnimation inherits the abstract \l Animation element. + This includes additional properties and methods for controlling the animation. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ + +QDeclarativePropertyAnimation::QDeclarativePropertyAnimation(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QDeclarativePropertyAnimationPrivate), parent) +{ + Q_D(QDeclarativePropertyAnimation); + d->init(); +} + +QDeclarativePropertyAnimation::QDeclarativePropertyAnimation(QDeclarativePropertyAnimationPrivate &dd, QObject *parent) +: QDeclarativeAbstractAnimation(dd, parent) +{ + Q_D(QDeclarativePropertyAnimation); + d->init(); +} + +QDeclarativePropertyAnimation::~QDeclarativePropertyAnimation() +{ +} + +void QDeclarativePropertyAnimationPrivate::init() +{ + Q_Q(QDeclarativePropertyAnimation); + va = new QDeclarativeBulkValueAnimator; + QDeclarative_setParent_noEvent(va, q); +} + +/*! + \qmlproperty int PropertyAnimation::duration + This property holds the duration of the animation, in milliseconds. + + The default value is 250. +*/ +int QDeclarativePropertyAnimation::duration() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->va->duration(); +} + +void QDeclarativePropertyAnimation::setDuration(int duration) +{ + if (duration < 0) { + qmlInfo(this) << tr("Cannot set a duration of < 0"); + return; + } + + Q_D(QDeclarativePropertyAnimation); + if (d->va->duration() == duration) + return; + d->va->setDuration(duration); + emit durationChanged(duration); +} + +/*! + \qmlproperty real PropertyAnimation::from + This property holds the starting value for the animation. + + If the PropertyAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ +QVariant QDeclarativePropertyAnimation::from() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->from; +} + +void QDeclarativePropertyAnimation::setFrom(const QVariant &f) +{ + Q_D(QDeclarativePropertyAnimation); + 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 end value for the animation. + + If the PropertyAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +QVariant QDeclarativePropertyAnimation::to() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->to; +} + +void QDeclarativePropertyAnimation::setTo(const QVariant &t) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->toIsDefined && t == d->to) + return; + d->to = t; + d->toIsDefined = t.isValid(); + emit toChanged(t); +} + +/*! + \qmlproperty enumeration PropertyAnimation::easing.type + \qmlproperty real PropertyAnimation::easing.amplitude + \qmlproperty real PropertyAnimation::easing.overshoot + \qmlproperty real PropertyAnimation::easing.period + \brief the easing curve used for the animation. + + To specify an easing curve you need to specify at least the type. For some curves you can also specify + amplitude, period and/or overshoot (more details provided after the table). The default easing curve is + \c Easing.Linear. + + \qml + PropertyAnimation { properties: "y"; easing.type: Easing.InOutElastic; easing.amplitude: 2.0; easing.period: 1.5 } + \endqml + + Available types are: + + \table + \row + \o \c Easing.Linear + \o Easing curve for a linear (t) function: velocity is constant. + \o \inlineimage qeasingcurve-linear.png + \row + \o \c Easing.InQuad + \o Easing curve for a quadratic (t^2) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inquad.png + \row + \o \c Easing.OutQuad + \o Easing curve for a quadratic (t^2) function: decelerating to zero velocity. + \o \inlineimage qeasingcurve-outquad.png + \row + \o \c Easing.InOutQuad + \o Easing curve for a quadratic (t^2) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutquad.png + \row + \o \c Easing.OutInQuad + \o Easing curve for a quadratic (t^2) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinquad.png + \row + \o \c Easing.InCubic + \o Easing curve for a cubic (t^3) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-incubic.png + \row + \o \c Easing.OutCubic + \o Easing curve for a cubic (t^3) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outcubic.png + \row + \o \c Easing.InOutCubic + \o Easing curve for a cubic (t^3) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutcubic.png + \row + \o \c Easing.OutInCubic + \o Easing curve for a cubic (t^3) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outincubic.png + \row + \o \c Easing.InQuart + \o Easing curve for a quartic (t^4) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inquart.png + \row + \o \c Easing.OutQuart + \o Easing curve for a quartic (t^4) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outquart.png + \row + \o \c Easing.InOutQuart + \o Easing curve for a quartic (t^4) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutquart.png + \row + \o \c Easing.OutInQuart + \o Easing curve for a quartic (t^4) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinquart.png + \row + \o \c Easing.InQuint + \o Easing curve for a quintic (t^5) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inquint.png + \row + \o \c Easing.OutQuint + \o Easing curve for a quintic (t^5) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outquint.png + \row + \o \c Easing.InOutQuint + \o Easing curve for a quintic (t^5) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutquint.png + \row + \o \c Easing.OutInQuint + \o Easing curve for a quintic (t^5) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinquint.png + \row + \o \c Easing.InSine + \o Easing curve for a sinusoidal (sin(t)) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-insine.png + \row + \o \c Easing.OutSine + \o Easing curve for a sinusoidal (sin(t)) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outsine.png + \row + \o \c Easing.InOutSine + \o Easing curve for a sinusoidal (sin(t)) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutsine.png + \row + \o \c Easing.OutInSine + \o Easing curve for a sinusoidal (sin(t)) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinsine.png + \row + \o \c Easing.InExpo + \o Easing curve for an exponential (2^t) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inexpo.png + \row + \o \c Easing.OutExpo + \o Easing curve for an exponential (2^t) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outexpo.png + \row + \o \c Easing.InOutExpo + \o Easing curve for an exponential (2^t) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutexpo.png + \row + \o \c Easing.OutInExpo + \o Easing curve for an exponential (2^t) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinexpo.png + \row + \o \c Easing.InCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-incirc.png + \row + \o \c Easing.OutCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outcirc.png + \row + \o \c Easing.InOutCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutcirc.png + \row + \o \c Easing.OutInCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outincirc.png + \row + \o \c Easing.InElastic + \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 Easing.OutElastic + \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 Easing.InOutElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutelastic.png + \row + \o \c Easing.OutInElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinelastic.png + \row + \o \c Easing.InBack + \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 Easing.OutBack + \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 Easing.InOutBack + \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 Easing.OutInBack + \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 Easing.InBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inbounce.png + \row + \o \c Easing.OutBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outbounce.png + \row + \o \c Easing.InOutBounce + \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 Easing.OutInBounce + \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 + + \c easing.amplitude is only applicable for bounce and elastic curves (curves of type + \c Easing.InBounce, \c Easing.OutBounce, \c Easing.InOutBounce, \c Easing.OutInBounce, \c Easing.InElastic, + \c Easing.OutElastic, \c Easing.InOutElastic or \c Easing.OutInElastic). + + \c easing.overshoot is only applicable if \c easing.type is: \c Easing.InBack, \c Easing.OutBack, + \c Easing.InOutBack or \c Easing.OutInBack. + + \c easing.period is only applicable if easing.type is: \c Easing.InElastic, \c Easing.OutElastic, + \c Easing.InOutElastic or \c Easing.OutInElastic. + + See the \l {declarative/animation/easing}{easing} example for a demonstration of + the different easing settings. +*/ +QEasingCurve QDeclarativePropertyAnimation::easing() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->va->easingCurve(); +} + +void QDeclarativePropertyAnimation::setEasing(const QEasingCurve &e) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->va->easingCurve() == e) + return; + + d->va->setEasingCurve(e); + emit easingChanged(e); +} + +QObject *QDeclarativePropertyAnimation::target() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->target; +} + +void QDeclarativePropertyAnimation::setTarget(QObject *o) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->target == o) + return; + d->target = o; + emit targetChanged(); +} + +QString QDeclarativePropertyAnimation::property() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->propertyName; +} + +void QDeclarativePropertyAnimation::setProperty(const QString &n) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->propertyName == n) + return; + d->propertyName = n; + emit propertyChanged(); +} + +QString QDeclarativePropertyAnimation::properties() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->properties; +} + +void QDeclarativePropertyAnimation::setProperties(const QString &prop) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->properties == prop) + return; + + d->properties = prop; + emit propertiesChanged(prop); +} + +/*! + \qmlproperty string PropertyAnimation::properties + \qmlproperty list PropertyAnimation::targets + \qmlproperty string PropertyAnimation::property + \qmlproperty Object PropertyAnimation::target + + These properties are used as a set to determine which properties should be animated. + The singular and plural forms are functionally identical, e.g. + \qml + NumberAnimation { target: theItem; property: "x"; to: 500 } + \endqml + has the same meaning as + \qml + NumberAnimation { targets: theItem; properties: "x"; to: 500 } + \endqml + The singular forms are slightly optimized, so if you do have only a single target/property + to animate you should try to use them. + + The \c targets property allows multiple targets to be set. For example, this animates the + \c x property of both \c itemA and \c itemB: + + \qml + NumberAnimation { targets: [itemA, itemB]; properties: "x"; to: 500 } + \endqml + + In many cases these properties do not need to be explicitly specified, as they can be + inferred from the animation framework: + + \table 80% + \row + \o Value Source / Behavior + \o When an animation is used as a value source or in a Behavior, the default target and property + name to be animated can both be inferred. + \qml + Rectangle { + id: theRect + width: 100; height: 100 + color: Qt.rgba(0,0,1) + NumberAnimation on x { to: 500; loops: Animation.Infinite } //animate theRect's x property + Behavior on y { NumberAnimation {} } //animate theRect's y property + } + \endqml + \row + \o Transition + \o When used in a transition, a property animation is assumed to match \e all targets + but \e no properties. In practice, that means you need to specify at least the properties + in order for the animation to do anything. + \qml + Rectangle { + id: theRect + width: 100; height: 100 + color: Qt.rgba(0,0,1) + Item { id: uselessItem } + states: State { + name: "state1" + PropertyChanges { target: theRect; x: 200; y: 200; z: 4 } + PropertyChanges { target: uselessItem; x: 10; y: 10; z: 2 } + } + transitions: Transition { + //animate both theRect's and uselessItem's x and y to their final values + NumberAnimation { properties: "x,y" } + + //animate theRect's z to its final value + NumberAnimation { target: theRect; property: "z" } + } + } + \endqml + \row + \o Standalone + \o When an animation is used standalone, both the target and property need to be + explicitly specified. + \qml + Rectangle { + id: theRect + width: 100; height: 100 + color: Qt.rgba(0,0,1) + //need to explicitly specify target and property + NumberAnimation { id: theAnim; target: theRect; property: "x"; to: 500 } + MouseArea { + anchors.fill: parent + onClicked: theAnim.start() + } + } + \endqml + \endtable + + As seen in the above example, properties is specified as a comma-separated string of property names to animate. + + \sa exclude, {QML Animation and Transitions} +*/ +QDeclarativeListProperty QDeclarativePropertyAnimation::targets() +{ + Q_D(QDeclarativePropertyAnimation); + return QDeclarativeListProperty(this, d->targets); +} + +/*! + \qmlproperty list PropertyAnimation::exclude + This property holds the items not to be affected by this animation. + \sa PropertyAnimation::targets +*/ +QDeclarativeListProperty QDeclarativePropertyAnimation::exclude() +{ + Q_D(QDeclarativePropertyAnimation); + return QDeclarativeListProperty(this, d->exclude); +} + +QAbstractAnimation *QDeclarativePropertyAnimation::qtAnimation() +{ + Q_D(QDeclarativePropertyAnimation); + return d->va; +} + +void QDeclarativeAnimationPropertyUpdater::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; + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + + if (v == 1.) + QDeclarativePropertyPrivate::write(action.property, action.toValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + else { + if (!fromSourced && !fromDefined) { + action.fromValue = action.property.read(); + if (interpolatorType) + QDeclarativePropertyAnimationPrivate::convertVariant(action.fromValue, interpolatorType); + } + if (!interpolatorType) { + int propType = action.property.propertyType(); + if (!prevInterpolatorType || prevInterpolatorType != propType) { + prevInterpolatorType = propType; + interpolator = QVariantAnimationPrivate::getInterpolator(prevInterpolatorType); + } + } + if (interpolator) + QDeclarativePropertyPrivate::write(action.property, interpolator(action.fromValue.constData(), action.toValue.constData(), v), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } + if (deleted) + return; + } + wasDeleted = 0; + fromSourced = true; +} + +void QDeclarativePropertyAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativePropertyAnimation); + + QStringList props = d->properties.isEmpty() ? QStringList() : d->properties.split(QLatin1Char(',')); + for (int ii = 0; ii < props.count(); ++ii) + props[ii] = props.at(ii).trimmed(); + if (!d->propertyName.isEmpty()) + props << d->propertyName; + + QList targets = d->targets; + if (d->target) + targets.append(d->target); + + bool hasSelectors = !props.isEmpty() || !targets.isEmpty() || !d->exclude.isEmpty(); + bool useType = (props.isEmpty() && d->defaultToInterpolatorType) ? true : false; + + if (d->defaultProperty.isValid() && !hasSelectors) { + props << d->defaultProperty.name(); + targets << d->defaultProperty.object(); + } + + if (props.isEmpty() && !d->defaultProperties.isEmpty()) { + props << d->defaultProperties.split(QLatin1Char(',')); + } + + QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater; + data->interpolatorType = d->interpolatorType; + data->interpolator = d->interpolator; + data->reverse = direction == Backward ? true : false; + data->fromSourced = false; + data->fromDefined = d->fromIsDefined; + + bool hasExplicit = false; + //an explicit animation has been specified + if (d->toIsDefined) { + for (int i = 0; i < props.count(); ++i) { + for (int j = 0; j < targets.count(); ++j) { + QDeclarativeAction myAction; + myAction.property = d->createProperty(targets.at(j), props.at(i), this); + if (myAction.property.isValid()) { + if (d->fromIsDefined) { + myAction.fromValue = d->from; + d->convertVariant(myAction.fromValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + } + myAction.toValue = d->to; + d->convertVariant(myAction.toValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + data->actions << myAction; + hasExplicit = true; + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &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) { + QDeclarativeAction &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 ((targets.isEmpty() || targets.contains(obj) || (!same && 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))) { + QDeclarativeAction 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); + d->va->setFromSourcedValue(&data->fromSourced); + d->actions = &data->actions; + } else { + delete data; + d->va->setFromSourcedValue(0); //clear previous data + d->va->setAnimValue(0, QAbstractAnimation::DeleteWhenStopped); //clear previous data + d->actions = 0; + } +} + +/*! + \qmlclass ParentAnimation QDeclarativeParentAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The ParentAnimation element animates changes in parent values. + + ParentAnimation is used to animate a parent change for an \l Item. + + For example, the following ParentChange changes \c blueRect to become + a child of \c redRect when it is clicked. The inclusion of the + ParentAnimation, which defines a NumberAnimation to be applied during + the transition, ensures the item animates smoothly as it moves to + its new parent: + + \snippet doc/src/snippets/declarative/parentanimation.qml 0 + + A ParentAnimation can contain any number of animations. These animations will + be run in parallel; to run them sequentially, define them within a + SequentialAnimation. + + In some cases, such as when reparenting between items with clipping enabled, it is useful + to animate the parent change via another item that does not have clipping + enabled. Such an item can be set using the \l via property. + + For convenience, when a ParentAnimation is used in a \l Transition, it will + animate any ParentChange that has occurred during the state change. + This can be overridden by setting a specific target item using the + \l target property. + + Like any other animation element, a ParentAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativeParentAnimation::QDeclarativeParentAnimation(QObject *parent) + : QDeclarativeAnimationGroup(*(new QDeclarativeParentAnimationPrivate), parent) +{ + Q_D(QDeclarativeParentAnimation); + d->topLevelGroup = new QSequentialAnimationGroup; + QDeclarative_setParent_noEvent(d->topLevelGroup, this); + + d->startAction = new QActionAnimation; + QDeclarative_setParent_noEvent(d->startAction, d->topLevelGroup); + d->topLevelGroup->addAnimation(d->startAction); + + d->ag = new QParallelAnimationGroup; + QDeclarative_setParent_noEvent(d->ag, d->topLevelGroup); + d->topLevelGroup->addAnimation(d->ag); + + d->endAction = new QActionAnimation; + QDeclarative_setParent_noEvent(d->endAction, d->topLevelGroup); + d->topLevelGroup->addAnimation(d->endAction); +} + +QDeclarativeParentAnimation::~QDeclarativeParentAnimation() +{ +} + +/*! + \qmlproperty Item ParentAnimation::target + The item to reparent. + + When used in a transition, if no target is specified, all + ParentChange occurrences are animated by the ParentAnimation. +*/ +QDeclarativeItem *QDeclarativeParentAnimation::target() const +{ + Q_D(const QDeclarativeParentAnimation); + return d->target; +} + +void QDeclarativeParentAnimation::setTarget(QDeclarativeItem *target) +{ + Q_D(QDeclarativeParentAnimation); + if (target == d->target) + return; + + d->target = target; + emit targetChanged(); +} + +/*! + \qmlproperty Item ParentAnimation::newParent + The new parent to animate to. + + If the ParentAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. +*/ +QDeclarativeItem *QDeclarativeParentAnimation::newParent() const +{ + Q_D(const QDeclarativeParentAnimation); + return d->newParent; +} + +void QDeclarativeParentAnimation::setNewParent(QDeclarativeItem *newParent) +{ + Q_D(QDeclarativeParentAnimation); + if (newParent == d->newParent) + return; + + d->newParent = newParent; + emit newParentChanged(); +} + +/*! + \qmlproperty Item ParentAnimation::via + The item to reparent via. This provides a way to do an unclipped animation + when both the old parent and new parent are clipped. + + \qml + ParentAnimation { + target: myItem + via: topLevelItem + // ... + } + \endqml +*/ +QDeclarativeItem *QDeclarativeParentAnimation::via() const +{ + Q_D(const QDeclarativeParentAnimation); + return d->via; +} + +void QDeclarativeParentAnimation::setVia(QDeclarativeItem *via) +{ + Q_D(QDeclarativeParentAnimation); + if (via == d->via) + return; + + d->via = via; + emit viaChanged(); +} + +//### mirrors same-named function in QDeclarativeItem +QPointF QDeclarativeParentAnimationPrivate::computeTransformOrigin(QDeclarativeItem::TransformOrigin origin, qreal width, qreal height) const +{ + switch(origin) { + default: + case QDeclarativeItem::TopLeft: + return QPointF(0, 0); + case QDeclarativeItem::Top: + return QPointF(width / 2., 0); + case QDeclarativeItem::TopRight: + return QPointF(width, 0); + case QDeclarativeItem::Left: + return QPointF(0, height / 2.); + case QDeclarativeItem::Center: + return QPointF(width / 2., height / 2.); + case QDeclarativeItem::Right: + return QPointF(width, height / 2.); + case QDeclarativeItem::BottomLeft: + return QPointF(0, height); + case QDeclarativeItem::Bottom: + return QPointF(width / 2., height); + case QDeclarativeItem::BottomRight: + return QPointF(width, height); + } +} + +void QDeclarativeParentAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeParentAnimation); + + struct QDeclarativeParentAnimationData : public QAbstractAnimationAction + { + QDeclarativeParentAnimationData() {} + ~QDeclarativeParentAnimationData() { qDeleteAll(pc); } + + QDeclarativeStateActions actions; + //### reverse should probably apply on a per-action basis + bool reverse; + QList pc; + virtual void doAction() + { + for (int ii = 0; ii < actions.count(); ++ii) { + const QDeclarativeAction &action = actions.at(ii); + if (reverse) + action.event->reverse(); + else + action.event->execute(); + } + } + }; + + QDeclarativeParentAnimationData *data = new QDeclarativeParentAnimationData; + QDeclarativeParentAnimationData *viaData = new QDeclarativeParentAnimationData; + + bool hasExplicit = false; + if (d->target && d->newParent) { + data->reverse = false; + QDeclarativeAction myAction; + QDeclarativeParentChange *pc = new QDeclarativeParentChange; + pc->setObject(d->target); + pc->setParent(d->newParent); + myAction.event = pc; + data->pc << pc; + data->actions << myAction; + hasExplicit = true; + if (d->via) { + viaData->reverse = false; + QDeclarativeAction myVAction; + QDeclarativeParentChange *vpc = new QDeclarativeParentChange; + vpc->setObject(d->target); + vpc->setParent(d->via); + myVAction.event = vpc; + viaData->pc << vpc; + viaData->actions << myVAction; + } + //### once actions have concept of modified, + // loop to match appropriate ParentChanges and mark as modified + } + + if (!hasExplicit) + for (int i = 0; i < actions.size(); ++i) { + QDeclarativeAction &action = actions[i]; + if (action.event && action.event->typeName() == QLatin1String("ParentChange") + && (!d->target || static_cast(action.event)->object() == d->target)) { + + QDeclarativeParentChange *pc = static_cast(action.event); + QDeclarativeAction myAction = action; + data->reverse = action.reverseEvent; + + //### this logic differs from PropertyAnimation + // (probably a result of modified vs. done) + if (d->newParent) { + QDeclarativeParentChange *epc = new QDeclarativeParentChange; + epc->setObject(static_cast(action.event)->object()); + epc->setParent(d->newParent); + myAction.event = epc; + data->pc << epc; + data->actions << myAction; + pc = epc; + } else { + action.actionDone = true; + data->actions << myAction; + } + + if (d->via) { + viaData->reverse = false; + QDeclarativeAction myAction; + QDeclarativeParentChange *vpc = new QDeclarativeParentChange; + vpc->setObject(pc->object()); + vpc->setParent(d->via); + myAction.event = vpc; + viaData->pc << vpc; + viaData->actions << myAction; + QDeclarativeAction dummyAction; + QDeclarativeAction &xAction = pc->xIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; + QDeclarativeAction &yAction = pc->yIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; + QDeclarativeAction &sAction = pc->scaleIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; + QDeclarativeAction &rAction = pc->rotationIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; + QDeclarativeItem *target = pc->object(); + QDeclarativeItem *targetParent = action.reverseEvent ? pc->originalParent() : pc->parent(); + + //### this mirrors the logic in QDeclarativeParentChange. + bool ok; + const QTransform &transform = targetParent->itemTransform(d->via, &ok); + if (transform.type() >= QTransform::TxShear || !ok) { + qmlInfo(this) << QDeclarativeParentAnimation::tr("Unable to preserve appearance under complex transform"); + ok = false; + } + + qreal scale = 1; + qreal rotation = 0; + bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0); + if (ok && !isRotate) { + if (transform.m11() == transform.m22()) + scale = transform.m11(); + else { + qmlInfo(this) << QDeclarativeParentAnimation::tr("Unable to preserve appearance under non-uniform scale"); + ok = false; + } + } else if (ok && isRotate) { + if (transform.m11() == transform.m22()) + scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12()); + else { + qmlInfo(this) << QDeclarativeParentAnimation::tr("Unable to preserve appearance under non-uniform scale"); + ok = false; + } + + if (scale != 0) + rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/qreal(M_PI); + else { + qmlInfo(this) << QDeclarativeParentAnimation::tr("Unable to preserve appearance under scale of 0"); + ok = false; + } + } + + const QPointF &point = transform.map(QPointF(xAction.toValue.toReal(),yAction.toValue.toReal())); + qreal x = point.x(); + qreal y = point.y(); + if (ok && target->transformOrigin() != QDeclarativeItem::TopLeft) { + qreal w = target->width(); + qreal h = target->height(); + if (pc->widthIsSet() && i < actions.size() - 1) + w = actions[++i].toValue.toReal(); + if (pc->heightIsSet() && i < actions.size() - 1) + h = actions[++i].toValue.toReal(); + const QPointF &transformOrigin + = d->computeTransformOrigin(target->transformOrigin(), w,h); + qreal tempxt = transformOrigin.x(); + qreal tempyt = transformOrigin.y(); + QTransform t; + t.translate(-tempxt, -tempyt); + t.rotate(rotation); + t.scale(scale, scale); + t.translate(tempxt, tempyt); + const QPointF &offset = t.map(QPointF(0,0)); + x += offset.x(); + y += offset.y(); + } + + if (ok) { + //qDebug() << x << y << rotation << scale; + xAction.toValue = x; + yAction.toValue = y; + sAction.toValue = sAction.toValue.toReal() * scale; + rAction.toValue = rAction.toValue.toReal() + rotation; + } + } + } + } + + if (data->actions.count()) { + if (direction == QDeclarativeAbstractAnimation::Forward) { + d->startAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped); + d->endAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped); + } else { + d->endAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped); + d->startAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped); + } + if (!d->via) + delete viaData; + } else { + delete data; + delete viaData; + } + + //take care of any child animations + bool valid = d->defaultProperty.isValid(); + for (int ii = 0; ii < d->animations.count(); ++ii) { + if (valid) + d->animations.at(ii)->setDefaultTarget(d->defaultProperty); + d->animations.at(ii)->transition(actions, modified, direction); + } + +} + +QAbstractAnimation *QDeclarativeParentAnimation::qtAnimation() +{ + Q_D(QDeclarativeParentAnimation); + return d->topLevelGroup; +} + +/*! + \qmlclass AnchorAnimation QDeclarativeAnchorAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The AnchorAnimation element animates changes in anchor values. + + AnchorAnimation is used to animate an anchor change. + + In the following snippet we animate the addition of a right anchor to a \l Rectangle: + + \snippet doc/src/snippets/declarative/anchoranimation.qml 0 + + For convenience, when an AnchorAnimation is used in a \l Transition, it will + animate any AnchorChanges that have occurred during the state change. + This can be overridden by setting a specific target item using the + \l target property. + + Like any other animation element, an AnchorAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa {QML Animation and Transitions}, AnchorChanges +*/ + +QDeclarativeAnchorAnimation::QDeclarativeAnchorAnimation(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QDeclarativeAnchorAnimationPrivate), parent) +{ + Q_D(QDeclarativeAnchorAnimation); + d->va = new QDeclarativeBulkValueAnimator; + QDeclarative_setParent_noEvent(d->va, this); +} + +QDeclarativeAnchorAnimation::~QDeclarativeAnchorAnimation() +{ +} + +QAbstractAnimation *QDeclarativeAnchorAnimation::qtAnimation() +{ + Q_D(QDeclarativeAnchorAnimation); + return d->va; +} + +/*! + \qmlproperty list AnchorAnimation::targets + The items to reanchor. + + If no targets are specified all AnchorChanges will be + animated by the AnchorAnimation. +*/ +QDeclarativeListProperty QDeclarativeAnchorAnimation::targets() +{ + Q_D(QDeclarativeAnchorAnimation); + return QDeclarativeListProperty(this, d->targets); +} + +/*! + \qmlproperty int AnchorAnimation::duration + This property holds the duration of the animation, in milliseconds. + + The default value is 250. +*/ +int QDeclarativeAnchorAnimation::duration() const +{ + Q_D(const QDeclarativeAnchorAnimation); + return d->va->duration(); +} + +void QDeclarativeAnchorAnimation::setDuration(int duration) +{ + if (duration < 0) { + qmlInfo(this) << tr("Cannot set a duration of < 0"); + return; + } + + Q_D(QDeclarativeAnchorAnimation); + if (d->va->duration() == duration) + return; + d->va->setDuration(duration); + emit durationChanged(duration); +} + +/*! + \qmlproperty enumeration AnchorAnimation::easing.type + \qmlproperty real AnchorAnimation::easing.amplitude + \qmlproperty real AnchorAnimation::easing.overshoot + \qmlproperty real AnchorAnimation::easing.period + \brief the easing curve used for the animation. + + To specify an easing curve you need to specify at least the type. For some curves you can also specify + amplitude, period and/or overshoot. The default easing curve is + Linear. + + \qml + AnchorAnimation { easing.type: Easing.InOutQuad } + \endqml + + See the \l{PropertyAnimation::easing.type} documentation for information + about the different types of easing curves. +*/ + +QEasingCurve QDeclarativeAnchorAnimation::easing() const +{ + Q_D(const QDeclarativeAnchorAnimation); + return d->va->easingCurve(); +} + +void QDeclarativeAnchorAnimation::setEasing(const QEasingCurve &e) +{ + Q_D(QDeclarativeAnchorAnimation); + if (d->va->easingCurve() == e) + return; + + d->va->setEasingCurve(e); + emit easingChanged(e); +} + +void QDeclarativeAnchorAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_UNUSED(modified); + Q_D(QDeclarativeAnchorAnimation); + QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater; + data->interpolatorType = QMetaType::QReal; + data->interpolator = d->interpolator; + + data->reverse = direction == Backward ? true : false; + data->fromSourced = false; + data->fromDefined = false; + + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + if (action.event && action.event->typeName() == QLatin1String("AnchorChanges") + && (d->targets.isEmpty() || d->targets.contains(static_cast(action.event)->object()))) { + data->actions << static_cast(action.event)->additionalActions(); + } + } + + 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); + d->va->setFromSourcedValue(&data->fromSourced); + } else { + delete data; + } +} + +QDeclarativeScriptActionPrivate::QDeclarativeScriptActionPrivate() + : QDeclarativeAbstractAnimationPrivate(), hasRunScriptScript(false), reversing(false), proxy(this), rsa(0) {} + + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativeanimation_p.h b/src/declarative/util/qdeclarativeanimation_p.h new file mode 100644 index 00000000..e80df259 --- /dev/null +++ b/src/declarative/util/qdeclarativeanimation_p.h @@ -0,0 +1,528 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEANIMATION_H +#define QDECLARATIVEANIMATION_H + +#include "private/qdeclarativetransition_p.h" +#include "private/qdeclarativestate_p.h" +#include + +#include +#include +#include + +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeAbstractAnimationPrivate; +class QDeclarativeAnimationGroup; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeAbstractAnimation : public QObject, public QDeclarativePropertyValueSource, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAbstractAnimation) + + Q_INTERFACES(QDeclarativeParserStatus) + Q_INTERFACES(QDeclarativePropertyValueSource) + Q_ENUMS(Loops) + 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(int loops READ loops WRITE setLoops NOTIFY loopCountChanged) + Q_CLASSINFO("DefaultMethod", "start()") + +public: + QDeclarativeAbstractAnimation(QObject *parent=0); + virtual ~QDeclarativeAbstractAnimation(); + + enum Loops { Infinite = -2 }; + + bool isRunning() const; + void setRunning(bool); + bool isPaused() const; + void setPaused(bool); + bool alwaysRunToEnd() const; + void setAlwaysRunToEnd(bool); + + int loops() const; + void setLoops(int); + + int currentTime(); + void setCurrentTime(int); + + QDeclarativeAnimationGroup *group() const; + void setGroup(QDeclarativeAnimationGroup *); + + void setDefaultTarget(const QDeclarativeProperty &); + void setDisableUserControl(); + + void classBegin(); + void componentComplete(); + +Q_SIGNALS: + void started(); + void completed(); + void runningChanged(bool); + void pausedChanged(bool); + void alwaysRunToEndChanged(bool); + void loopCountChanged(int); + +public Q_SLOTS: + void restart(); + void start(); + void pause(); + void resume(); + void stop(); + void complete(); + +protected: + QDeclarativeAbstractAnimation(QDeclarativeAbstractAnimationPrivate &dd, QObject *parent); + +public: + enum TransitionDirection { Forward, Backward }; + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation() = 0; + +private Q_SLOTS: + void timelineComplete(); + void componentFinalized(); +private: + virtual void setTarget(const QDeclarativeProperty &); + void notifyRunningChanged(bool running); + friend class QDeclarativeBehavior; + + +}; + +class QDeclarativePauseAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarativePauseAnimation : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePauseAnimation) + + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) + +public: + QDeclarativePauseAnimation(QObject *parent=0); + virtual ~QDeclarativePauseAnimation(); + + int duration() const; + void setDuration(int); + +Q_SIGNALS: + void durationChanged(int); + +protected: + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarativeScriptActionPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeScriptAction : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeScriptAction) + + Q_PROPERTY(QDeclarativeScriptString script READ script WRITE setScript) + Q_PROPERTY(QString scriptName READ stateChangeScriptName WRITE setStateChangeScriptName) + +public: + QDeclarativeScriptAction(QObject *parent=0); + virtual ~QDeclarativeScriptAction(); + + QDeclarativeScriptString script() const; + void setScript(const QDeclarativeScriptString &); + + QString stateChangeScriptName() const; + void setStateChangeScriptName(const QString &); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarativePropertyActionPrivate; +class QDeclarativePropertyAction : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyAction) + + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged) + Q_PROPERTY(QString properties READ properties WRITE setProperties NOTIFY propertiesChanged) + Q_PROPERTY(QDeclarativeListProperty targets READ targets) + Q_PROPERTY(QDeclarativeListProperty exclude READ exclude) + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) + +public: + QDeclarativePropertyAction(QObject *parent=0); + virtual ~QDeclarativePropertyAction(); + + QObject *target() const; + void setTarget(QObject *); + + QString property() const; + void setProperty(const QString &); + + QString properties() const; + void setProperties(const QString &); + + QDeclarativeListProperty targets(); + QDeclarativeListProperty exclude(); + + QVariant value() const; + void setValue(const QVariant &); + +Q_SIGNALS: + void valueChanged(const QVariant &); + void propertiesChanged(const QString &); + void targetChanged(); + void propertyChanged(); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarativeItem; +class QDeclarativePropertyAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarativePropertyAnimation : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation) + + 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(QEasingCurve 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 propertyChanged) + Q_PROPERTY(QString properties READ properties WRITE setProperties NOTIFY propertiesChanged) + Q_PROPERTY(QDeclarativeListProperty targets READ targets) + Q_PROPERTY(QDeclarativeListProperty exclude READ exclude) + +public: + QDeclarativePropertyAnimation(QObject *parent=0); + virtual ~QDeclarativePropertyAnimation(); + + virtual int duration() const; + virtual void setDuration(int); + + QVariant from() const; + void setFrom(const QVariant &); + + QVariant to() const; + void setTo(const QVariant &); + + QEasingCurve easing() const; + void setEasing(const QEasingCurve &); + + QObject *target() const; + void setTarget(QObject *); + + QString property() const; + void setProperty(const QString &); + + QString properties() const; + void setProperties(const QString &); + + QDeclarativeListProperty targets(); + QDeclarativeListProperty exclude(); + +protected: + QDeclarativePropertyAnimation(QDeclarativePropertyAnimationPrivate &dd, QObject *parent); + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); + +Q_SIGNALS: + void durationChanged(int); + void fromChanged(QVariant); + void toChanged(QVariant); + void easingChanged(const QEasingCurve &); + void propertiesChanged(const QString &); + void targetChanged(); + void propertyChanged(); +}; + +class Q_AUTOTEST_EXPORT QDeclarativeColorAnimation : public QDeclarativePropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation) + Q_PROPERTY(QColor from READ from WRITE setFrom) + Q_PROPERTY(QColor to READ to WRITE setTo) + +public: + QDeclarativeColorAnimation(QObject *parent=0); + virtual ~QDeclarativeColorAnimation(); + + QColor from() const; + void setFrom(const QColor &); + + QColor to() const; + void setTo(const QColor &); +}; + +class Q_AUTOTEST_EXPORT QDeclarativeNumberAnimation : public QDeclarativePropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation) + + Q_PROPERTY(qreal from READ from WRITE setFrom) + Q_PROPERTY(qreal to READ to WRITE setTo) + +public: + QDeclarativeNumberAnimation(QObject *parent=0); + virtual ~QDeclarativeNumberAnimation(); + + qreal from() const; + void setFrom(qreal); + + qreal to() const; + void setTo(qreal); + +protected: + QDeclarativeNumberAnimation(QDeclarativePropertyAnimationPrivate &dd, QObject *parent); + +private: + void init(); +}; + +class Q_AUTOTEST_EXPORT QDeclarativeVector3dAnimation : public QDeclarativePropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation) + + Q_PROPERTY(QVector3D from READ from WRITE setFrom) + Q_PROPERTY(QVector3D to READ to WRITE setTo) + +public: + QDeclarativeVector3dAnimation(QObject *parent=0); + virtual ~QDeclarativeVector3dAnimation(); + + QVector3D from() const; + void setFrom(QVector3D); + + QVector3D to() const; + void setTo(QVector3D); +}; + +class QDeclarativeRotationAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeRotationAnimation : public QDeclarativePropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeRotationAnimation) + Q_ENUMS(RotationDirection) + + Q_PROPERTY(qreal from READ from WRITE setFrom) + Q_PROPERTY(qreal to READ to WRITE setTo) + Q_PROPERTY(RotationDirection direction READ direction WRITE setDirection NOTIFY directionChanged) + +public: + QDeclarativeRotationAnimation(QObject *parent=0); + virtual ~QDeclarativeRotationAnimation(); + + qreal from() const; + void setFrom(qreal); + + qreal to() const; + void setTo(qreal); + + enum RotationDirection { Numerical, Shortest, Clockwise, Counterclockwise }; + RotationDirection direction() const; + void setDirection(RotationDirection direction); + +Q_SIGNALS: + void directionChanged(); +}; + +class QDeclarativeAnimationGroupPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeAnimationGroup : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAnimationGroup) + + Q_CLASSINFO("DefaultProperty", "animations") + Q_PROPERTY(QDeclarativeListProperty animations READ animations) + +public: + QDeclarativeAnimationGroup(QObject *parent); + virtual ~QDeclarativeAnimationGroup(); + + QDeclarativeListProperty animations(); + friend class QDeclarativeAbstractAnimation; + +protected: + QDeclarativeAnimationGroup(QDeclarativeAnimationGroupPrivate &dd, QObject *parent); +}; + +class QDeclarativeSequentialAnimation : public QDeclarativeAnimationGroup +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAnimationGroup) + +public: + QDeclarativeSequentialAnimation(QObject *parent=0); + virtual ~QDeclarativeSequentialAnimation(); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarativeParallelAnimation : public QDeclarativeAnimationGroup +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAnimationGroup) + +public: + QDeclarativeParallelAnimation(QObject *parent=0); + virtual ~QDeclarativeParallelAnimation(); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarativeParentAnimationPrivate; +class QDeclarativeParentAnimation : public QDeclarativeAnimationGroup +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeParentAnimation) + + Q_PROPERTY(QDeclarativeItem *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QDeclarativeItem *newParent READ newParent WRITE setNewParent NOTIFY newParentChanged) + Q_PROPERTY(QDeclarativeItem *via READ via WRITE setVia NOTIFY viaChanged) + +public: + QDeclarativeParentAnimation(QObject *parent=0); + virtual ~QDeclarativeParentAnimation(); + + QDeclarativeItem *target() const; + void setTarget(QDeclarativeItem *); + + QDeclarativeItem *newParent() const; + void setNewParent(QDeclarativeItem *); + + QDeclarativeItem *via() const; + void setVia(QDeclarativeItem *); + +Q_SIGNALS: + void targetChanged(); + void newParentChanged(); + void viaChanged(); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarativeAnchorAnimationPrivate; +class QDeclarativeAnchorAnimation : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAnchorAnimation) + Q_PROPERTY(QDeclarativeListProperty targets READ targets) + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) + Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged) + +public: + QDeclarativeAnchorAnimation(QObject *parent=0); + virtual ~QDeclarativeAnchorAnimation(); + + QDeclarativeListProperty targets(); + + int duration() const; + void setDuration(int); + + QEasingCurve easing() const; + void setEasing(const QEasingCurve &); + +Q_SIGNALS: + void durationChanged(int); + void easingChanged(const QEasingCurve&); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeAbstractAnimation) +QML_DECLARE_TYPE(QDeclarativePauseAnimation) +QML_DECLARE_TYPE(QDeclarativeScriptAction) +QML_DECLARE_TYPE(QDeclarativePropertyAction) +QML_DECLARE_TYPE(QDeclarativePropertyAnimation) +QML_DECLARE_TYPE(QDeclarativeColorAnimation) +QML_DECLARE_TYPE(QDeclarativeNumberAnimation) +QML_DECLARE_TYPE(QDeclarativeSequentialAnimation) +QML_DECLARE_TYPE(QDeclarativeParallelAnimation) +QML_DECLARE_TYPE(QDeclarativeVector3dAnimation) +QML_DECLARE_TYPE(QDeclarativeRotationAnimation) +QML_DECLARE_TYPE(QDeclarativeParentAnimation) +QML_DECLARE_TYPE(QDeclarativeAnchorAnimation) + +QT_END_HEADER + +#endif // QDECLARATIVEANIMATION_H diff --git a/src/declarative/util/qdeclarativeanimation_p_p.h b/src/declarative/util/qdeclarativeanimation_p_p.h new file mode 100644 index 00000000..24e10884 --- /dev/null +++ b/src/declarative/util/qdeclarativeanimation_p_p.h @@ -0,0 +1,397 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEANIMATION_P_H +#define QDECLARATIVEANIMATION_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 "private/qdeclarativeanimation_p.h" + +#include "private/qdeclarativenullablevalue_p_p.h" +#include "private/qdeclarativetimeline_p_p.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +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 QDeclarativeAbstractAnimationAction) +template +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 Q_AUTOTEST_EXPORT QActionAnimation : public QAbstractAnimation +{ + Q_OBJECT +public: + QActionAnimation(QObject *parent = 0) : QAbstractAnimation(parent), animAction(0), policy(KeepWhenStopped) {} + QActionAnimation(QAbstractAnimationAction *action, QObject *parent = 0) + : QAbstractAnimation(parent), animAction(action), policy(KeepWhenStopped) {} + ~QActionAnimation() { if (policy == DeleteWhenStopped) { delete animAction; animAction = 0; } } + virtual int duration() const { return 0; } + void setAnimAction(QAbstractAnimationAction *action, DeletionPolicy p) + { + if (state() == Running) + stop(); + if (policy == DeleteWhenStopped) + delete animAction; + animAction = action; + policy = p; + } +protected: + virtual void updateCurrentTime(int) {} + + virtual void updateState(State newState, State /*oldState*/) + { + if (newState == Running) { + if (animAction) { + animAction->doAction(); + if (state() == Stopped && policy == DeleteWhenStopped) { + delete animAction; + animAction = 0; + } + } + } + } + +private: + QAbstractAnimationAction *animAction; + DeletionPolicy policy; +}; + +class QDeclarativeBulkValueUpdater +{ +public: + virtual ~QDeclarativeBulkValueUpdater() {} + virtual void setValue(qreal value) = 0; +}; + +//animates QDeclarativeBulkValueUpdater (assumes start and end values will be reals or compatible) +class Q_AUTOTEST_EXPORT QDeclarativeBulkValueAnimator : public QVariantAnimation +{ + Q_OBJECT +public: + QDeclarativeBulkValueAnimator(QObject *parent = 0) : QVariantAnimation(parent), animValue(0), fromSourced(0), policy(KeepWhenStopped) {} + ~QDeclarativeBulkValueAnimator() { if (policy == DeleteWhenStopped) { delete animValue; animValue = 0; } } + void setAnimValue(QDeclarativeBulkValueUpdater *value, DeletionPolicy p) + { + if (state() == Running) + stop(); + if (policy == DeleteWhenStopped) + delete animValue; + animValue = value; + policy = p; + } + void setFromSourcedValue(bool *value) + { + fromSourced = value; + } +protected: + virtual void updateCurrentValue(const QVariant &value) + { + if (state() == QAbstractAnimation::Stopped) + return; + + 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; + } + } + +private: + QDeclarativeBulkValueUpdater *animValue; + bool *fromSourced; + DeletionPolicy policy; +}; + +//an animation that just gives a tick +template +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 QDeclarativeAbstractAnimationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeAbstractAnimation) +public: + QDeclarativeAbstractAnimationPrivate() + : running(false), paused(false), alwaysRunToEnd(false), + connectedTimeLine(false), componentComplete(true), + avoidPropertyValueSourceStart(false), disableUserControl(false), + registered(false), loopCount(1), group(0) {} + + bool running:1; + bool paused:1; + bool alwaysRunToEnd:1; + bool connectedTimeLine:1; + bool componentComplete:1; + bool avoidPropertyValueSourceStart:1; + bool disableUserControl:1; + bool registered:1; + + int loopCount; + + void commence(); + + QDeclarativeProperty defaultProperty; + + QDeclarativeAnimationGroup *group; + + static QDeclarativeProperty createProperty(QObject *obj, const QString &str, QObject *infoObj); +}; + +class QDeclarativePauseAnimationPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePauseAnimation) +public: + QDeclarativePauseAnimationPrivate() + : QDeclarativeAbstractAnimationPrivate(), pa(0) {} + + void init(); + + QPauseAnimation *pa; +}; + +class QDeclarativeScriptActionPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeScriptAction) +public: + QDeclarativeScriptActionPrivate(); + + void init(); + + QDeclarativeScriptString script; + QString name; + QDeclarativeScriptString runScriptScript; + bool hasRunScriptScript; + bool reversing; + + void execute(); + + QAnimationActionProxy proxy; + QActionAnimation *rsa; +}; + +class QDeclarativePropertyActionPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePropertyAction) +public: + QDeclarativePropertyActionPrivate() + : QDeclarativeAbstractAnimationPrivate(), target(0), spa(0) {} + + void init(); + + QObject *target; + QString propertyName; + QString properties; + QList targets; + QList exclude; + + QDeclarativeNullableValue value; + + QActionAnimation *spa; +}; + +class QDeclarativeAnimationGroupPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeAnimationGroup) +public: + QDeclarativeAnimationGroupPrivate() + : QDeclarativeAbstractAnimationPrivate(), ag(0) {} + + static void append_animation(QDeclarativeListProperty *list, QDeclarativeAbstractAnimation *role); + static void clear_animation(QDeclarativeListProperty *list); + QList animations; + QAnimationGroup *ag; +}; + +class QDeclarativePropertyAnimationPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePropertyAnimation) +public: + QDeclarativePropertyAnimationPrivate() + : QDeclarativeAbstractAnimationPrivate(), target(0), fromSourced(false), fromIsDefined(false), toIsDefined(false), + rangeIsSet(false), defaultToInterpolatorType(0), interpolatorType(0), interpolator(0), va(0), actions(0) {} + + void init(); + + QVariant from; + QVariant to; + + QObject *target; + QString propertyName; + QString properties; + QList targets; + QList exclude; + QString defaultProperties; + + bool fromSourced; + bool fromIsDefined:1; + bool toIsDefined:1; + bool rangeIsSet:1; + bool defaultToInterpolatorType:1; + int interpolatorType; + QVariantAnimation::Interpolator interpolator; + + QDeclarativeBulkValueAnimator *va; + + // for animations that don't use the QDeclarativeBulkValueAnimator + QDeclarativeStateActions *actions; + + static QVariant interpolateVariant(const QVariant &from, const QVariant &to, qreal progress); + static void convertVariant(QVariant &variant, int type); +}; + +class QDeclarativeRotationAnimationPrivate : public QDeclarativePropertyAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeRotationAnimation) +public: + QDeclarativeRotationAnimationPrivate() : direction(QDeclarativeRotationAnimation::Numerical) {} + + QDeclarativeRotationAnimation::RotationDirection direction; +}; + +class QDeclarativeParentAnimationPrivate : public QDeclarativeAnimationGroupPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeParentAnimation) +public: + QDeclarativeParentAnimationPrivate() + : QDeclarativeAnimationGroupPrivate(), target(0), newParent(0), + via(0), topLevelGroup(0), startAction(0), endAction(0) {} + + QDeclarativeItem *target; + QDeclarativeItem *newParent; + QDeclarativeItem *via; + + QSequentialAnimationGroup *topLevelGroup; + QActionAnimation *startAction; + QActionAnimation *endAction; + + QPointF computeTransformOrigin(QDeclarativeItem::TransformOrigin origin, qreal width, qreal height) const; +}; + +class QDeclarativeAnchorAnimationPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeAnchorAnimation) +public: + QDeclarativeAnchorAnimationPrivate() : rangeIsSet(false), va(0), + interpolator(QVariantAnimationPrivate::getInterpolator(QMetaType::QReal)) {} + + bool rangeIsSet; + QDeclarativeBulkValueAnimator *va; + QVariantAnimation::Interpolator interpolator; + QList targets; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeAnimationPropertyUpdater : public QDeclarativeBulkValueUpdater +{ +public: + QDeclarativeStateActions actions; + int interpolatorType; //for Number/ColorAnimation + int prevInterpolatorType; //for generic + QVariantAnimation::Interpolator interpolator; + bool reverse; + bool fromSourced; + bool fromDefined; + bool *wasDeleted; + QDeclarativeAnimationPropertyUpdater() : prevInterpolatorType(0), wasDeleted(0) {} + ~QDeclarativeAnimationPropertyUpdater() { if (wasDeleted) *wasDeleted = true; } + void setValue(qreal v); +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEANIMATION_P_H diff --git a/src/declarative/util/qdeclarativeapplication.cpp b/src/declarative/util/qdeclarativeapplication.cpp new file mode 100644 index 00000000..af80aede --- /dev/null +++ b/src/declarative/util/qdeclarativeapplication.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeapplication_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeApplicationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeApplication) +public: + QDeclarativeApplicationPrivate() : active(QApplication::activeWindow() != 0), + layoutDirection(QApplication::layoutDirection()) {} + bool active; + Qt::LayoutDirection layoutDirection; +}; + +/* + This object and its properties are documented as part of the Qt object, + in qdeclarativengine.cpp +*/ + +QDeclarativeApplication::QDeclarativeApplication(QObject *parent) : QObject(*new QDeclarativeApplicationPrivate(), parent) +{ + if (qApp) + qApp->installEventFilter(this); +} + +QDeclarativeApplication::~QDeclarativeApplication() +{ +} + +bool QDeclarativeApplication::active() const +{ + Q_D(const QDeclarativeApplication); + return d->active; +} + +Qt::LayoutDirection QDeclarativeApplication::layoutDirection() const +{ + Q_D(const QDeclarativeApplication); + return d->layoutDirection; +} + +bool QDeclarativeApplication::eventFilter(QObject *obj, QEvent *event) +{ + Q_UNUSED(obj) + Q_D(QDeclarativeApplication); + if (event->type() == QEvent::ApplicationActivate + || event->type() == QEvent::ApplicationDeactivate) { + bool active = d->active; + if (event->type() == QEvent::ApplicationActivate) + active = true; + else if (event->type() == QEvent::ApplicationDeactivate) + active = false; + + if (d->active != active) { + d->active = active; + emit activeChanged(); + } + } + if (event->type() == QEvent::LayoutDirectionChange) { + Qt::LayoutDirection direction = QApplication::layoutDirection(); + if (d->layoutDirection != direction) { + d->layoutDirection = direction; + emit layoutDirectionChanged(); + } + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativeapplication_p.h b/src/declarative/util/qdeclarativeapplication_p.h new file mode 100644 index 00000000..70852ed8 --- /dev/null +++ b/src/declarative/util/qdeclarativeapplication_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEAPPLICATION_P_H +#define QDECLARATIVEAPPLICATION_P_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeApplicationPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeApplication : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool active READ active NOTIFY activeChanged) + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection NOTIFY layoutDirectionChanged) + +public: + explicit QDeclarativeApplication(QObject *parent = 0); + virtual ~QDeclarativeApplication(); + bool active() const; + Qt::LayoutDirection layoutDirection() const; + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +Q_SIGNALS: + void activeChanged(); + void layoutDirectionChanged(); + +private: + Q_DISABLE_COPY(QDeclarativeApplication) + Q_DECLARE_PRIVATE(QDeclarativeApplication) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeApplication) + +QT_END_HEADER + +#endif // QDECLARATIVEAPPLICATION_P_H diff --git a/src/declarative/util/qdeclarativebehavior.cpp b/src/declarative/util/qdeclarativebehavior.cpp new file mode 100644 index 00000000..8921bee6 --- /dev/null +++ b/src/declarative/util/qdeclarativebehavior.cpp @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativebehavior_p.h" + +#include "private/qdeclarativeanimation_p.h" +#include "private/qdeclarativetransition_p.h" + +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeBehaviorPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeBehavior) +public: + QDeclarativeBehaviorPrivate() : animation(0), enabled(true), finalized(false) + , blockRunningChanged(false) {} + + QDeclarativeProperty property; + QVariant currentValue; + QVariant targetValue; + QDeclarativeGuard animation; + bool enabled; + bool finalized; + bool blockRunningChanged; +}; + +/*! + \qmlclass Behavior QDeclarativeBehavior + \ingroup qml-animation-transition + \since 4.7 + \brief The Behavior element allows you to specify a default animation for a property change. + + A Behavior defines the default animation to be applied whenever a + particular property value changes. + + For example, the following Behavior defines a NumberAnimation to be run + whenever the \l Rectangle's \c width value changes. When the MouseArea + is clicked, the \c width is changed, triggering the behavior's animation: + + \snippet doc/src/snippets/declarative/behavior.qml 0 + + Note that a property cannot have more than one assigned Behavior. To provide + multiple animations within a Behavior, use ParallelAnimation or + SequentialAnimation. + + If a \l{QML States}{state change} has a \l Transition that matches the same property as a + Behavior, the \l Transition animation overrides the Behavior for that + state change. For general advice on using Behaviors to animate state changes, see + \l{Using QML Behaviors with States}. + + \sa {QML Animation and Transitions}, {declarative/animation/behaviors}{Behavior example}, QtDeclarative +*/ + + +QDeclarativeBehavior::QDeclarativeBehavior(QObject *parent) + : QObject(*(new QDeclarativeBehaviorPrivate), parent) +{ +} + +QDeclarativeBehavior::~QDeclarativeBehavior() +{ +} + +/*! + \qmlproperty Animation Behavior::animation + \default + + This property holds the animation to run when the behavior is triggered. +*/ + +QDeclarativeAbstractAnimation *QDeclarativeBehavior::animation() +{ + Q_D(QDeclarativeBehavior); + return d->animation; +} + +void QDeclarativeBehavior::setAnimation(QDeclarativeAbstractAnimation *animation) +{ + Q_D(QDeclarativeBehavior); + if (d->animation) { + qmlInfo(this) << tr("Cannot change the animation assigned to a Behavior."); + return; + } + + d->animation = animation; + if (d->animation) { + d->animation->setDefaultTarget(d->property); + d->animation->setDisableUserControl(); + connect(d->animation->qtAnimation(), + SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)), + this, + SLOT(qtAnimationStateChanged(QAbstractAnimation::State,QAbstractAnimation::State))); + } +} + + +void QDeclarativeBehavior::qtAnimationStateChanged(QAbstractAnimation::State newState,QAbstractAnimation::State) +{ + Q_D(QDeclarativeBehavior); + if (!d->blockRunningChanged) + d->animation->notifyRunningChanged(newState == QAbstractAnimation::Running); +} + + +/*! + \qmlproperty bool Behavior::enabled + + This property holds whether the behavior will be triggered when the tracked + property changes value. + + By default a Behavior is enabled. +*/ + +bool QDeclarativeBehavior::enabled() const +{ + Q_D(const QDeclarativeBehavior); + return d->enabled; +} + +void QDeclarativeBehavior::setEnabled(bool enabled) +{ + Q_D(QDeclarativeBehavior); + if (d->enabled == enabled) + return; + d->enabled = enabled; + emit enabledChanged(); +} + +void QDeclarativeBehavior::write(const QVariant &value) +{ + Q_D(QDeclarativeBehavior); + qmlExecuteDeferred(this); + if (!d->animation || !d->enabled || !d->finalized) { + QDeclarativePropertyPrivate::write(d->property, value, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + d->targetValue = value; + return; + } + + if (d->animation->isRunning() && value == d->targetValue) + return; + + d->currentValue = d->property.read(); + d->targetValue = value; + + if (d->animation->qtAnimation()->duration() != -1 + && d->animation->qtAnimation()->state() != QAbstractAnimation::Stopped) { + d->blockRunningChanged = true; + d->animation->qtAnimation()->stop(); + } + + QDeclarativeStateOperation::ActionList actions; + QDeclarativeAction action; + action.property = d->property; + action.fromValue = d->currentValue; + action.toValue = value; + actions << action; + + QList after; + d->animation->transition(actions, after, QDeclarativeAbstractAnimation::Forward); + d->animation->qtAnimation()->start(); + d->blockRunningChanged = false; + if (!after.contains(d->property)) + QDeclarativePropertyPrivate::write(d->property, value, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); +} + +void QDeclarativeBehavior::setTarget(const QDeclarativeProperty &property) +{ + Q_D(QDeclarativeBehavior); + d->property = property; + d->currentValue = property.read(); + if (d->animation) + d->animation->setDefaultTarget(property); + + QDeclarativeEnginePrivate *engPriv = QDeclarativeEnginePrivate::get(qmlEngine(this)); + engPriv->registerFinalizedParserStatusObject(this, this->metaObject()->indexOfSlot("componentFinalized()")); +} + +void QDeclarativeBehavior::componentFinalized() +{ + Q_D(QDeclarativeBehavior); + d->finalized = true; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativebehavior_p.h b/src/declarative/util/qdeclarativebehavior_p.h new file mode 100644 index 00000000..d9294678 --- /dev/null +++ b/src/declarative/util/qdeclarativebehavior_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEBEHAVIOR_H +#define QDECLARATIVEBEHAVIOR_H + +#include "private/qdeclarativestate_p.h" + +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeAbstractAnimation; +class QDeclarativeBehaviorPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeBehavior : public QObject, public QDeclarativePropertyValueInterceptor +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeBehavior) + + Q_INTERFACES(QDeclarativePropertyValueInterceptor) + Q_CLASSINFO("DefaultProperty", "animation") + Q_PROPERTY(QDeclarativeAbstractAnimation *animation READ animation WRITE setAnimation) + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_CLASSINFO("DeferredPropertyNames", "animation") + +public: + QDeclarativeBehavior(QObject *parent=0); + ~QDeclarativeBehavior(); + + virtual void setTarget(const QDeclarativeProperty &); + virtual void write(const QVariant &value); + + QDeclarativeAbstractAnimation *animation(); + void setAnimation(QDeclarativeAbstractAnimation *); + + bool enabled() const; + void setEnabled(bool enabled); + +Q_SIGNALS: + void enabledChanged(); + +private Q_SLOTS: + void componentFinalized(); + void qtAnimationStateChanged(QAbstractAnimation::State,QAbstractAnimation::State); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeBehavior) + +QT_END_HEADER + +#endif // QDECLARATIVEBEHAVIOR_H diff --git a/src/declarative/util/qdeclarativebind.cpp b/src/declarative/util/qdeclarativebind.cpp new file mode 100644 index 00000000..bd077a81 --- /dev/null +++ b/src/declarative/util/qdeclarativebind.cpp @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativebind_p.h" + +#include "private/qdeclarativenullablevalue_p_p.h" +#include "private/qdeclarativeguard_p.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeBindPrivate : public QObjectPrivate +{ +public: + QDeclarativeBindPrivate() : when(true), componentComplete(true), obj(0) {} + + bool when : 1; + bool componentComplete : 1; + QDeclarativeGuard obj; + QString prop; + QDeclarativeNullableValue value; +}; + + +/*! + \qmlclass Binding QDeclarativeBind + \ingroup qml-working-with-data + \since 4.7 + \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. + + \sa QtDeclarative +*/ +QDeclarativeBind::QDeclarativeBind(QObject *parent) + : QObject(*(new QDeclarativeBindPrivate), parent) +{ +} + +QDeclarativeBind::~QDeclarativeBind() +{ +} + +/*! + \qmlproperty bool Binding::when + + This property holds when the binding is active. + This should be set to an expression that evaluates to true when you want the binding to be active. + + \code + Binding { + target: contactName; property: 'text' + value: name; when: list.ListView.isCurrentItem + } + \endcode +*/ +bool QDeclarativeBind::when() const +{ + Q_D(const QDeclarativeBind); + return d->when; +} + +void QDeclarativeBind::setWhen(bool v) +{ + Q_D(QDeclarativeBind); + d->when = v; + eval(); +} + +/*! + \qmlproperty Object Binding::target + + The object to be updated. +*/ +QObject *QDeclarativeBind::object() +{ + Q_D(const QDeclarativeBind); + return d->obj; +} + +void QDeclarativeBind::setObject(QObject *obj) +{ + Q_D(QDeclarativeBind); + d->obj = obj; + eval(); +} + +/*! + \qmlproperty string Binding::property + + The property to be updated. +*/ +QString QDeclarativeBind::property() const +{ + Q_D(const QDeclarativeBind); + return d->prop; +} + +void QDeclarativeBind::setProperty(const QString &p) +{ + Q_D(QDeclarativeBind); + 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 QDeclarativeBind::value() const +{ + Q_D(const QDeclarativeBind); + return d->value.value; +} + +void QDeclarativeBind::setValue(const QVariant &v) +{ + Q_D(QDeclarativeBind); + d->value.value = v; + d->value.isNull = false; + eval(); +} + +void QDeclarativeBind::classBegin() +{ + Q_D(QDeclarativeBind); + d->componentComplete = false; +} + +void QDeclarativeBind::componentComplete() +{ + Q_D(QDeclarativeBind); + d->componentComplete = true; + eval(); +} + +void QDeclarativeBind::eval() +{ + Q_D(QDeclarativeBind); + if (!d->obj || d->value.isNull || !d->when || !d->componentComplete) + return; + + QDeclarativeProperty prop(d->obj, d->prop); + prop.write(d->value.value); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativebind_p.h b/src/declarative/util/qdeclarativebind_p.h new file mode 100644 index 00000000..eb607a71 --- /dev/null +++ b/src/declarative/util/qdeclarativebind_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEBIND_H +#define QDECLARATIVEBIND_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeBindPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeBind : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeBind) + Q_INTERFACES(QDeclarativeParserStatus) + 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: + QDeclarativeBind(QObject *parent=0); + ~QDeclarativeBind(); + + 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 classBegin(); + virtual void componentComplete(); + +private: + void eval(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeBind) + +QT_END_HEADER + +#endif diff --git a/src/declarative/util/qdeclarativeconnections.cpp b/src/declarative/util/qdeclarativeconnections.cpp new file mode 100644 index 00000000..7c9c9336 --- /dev/null +++ b/src/declarative/util/qdeclarativeconnections.cpp @@ -0,0 +1,289 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeconnections_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeConnectionsPrivate : public QObjectPrivate +{ +public: + QDeclarativeConnectionsPrivate() : target(0), targetSet(false), ignoreUnknownSignals(false), componentcomplete(true) {} + + QList boundsignals; + QObject *target; + + bool targetSet; + bool ignoreUnknownSignals; + bool componentcomplete; + + QByteArray data; +}; + +/*! + \qmlclass Connections QDeclarativeConnections + \ingroup qml-utility-elements + \since 4.7 + \brief A Connections element describes generalized connections to signals. + + A Connections object creates a connection to a QML signal. + + When connecting to signals in QML, the usual way is to create an + "on" handler that reacts when a signal is received, like this: + + \qml + MouseArea { + onClicked: { foo(parameters) } + } + \endqml + + However, it is not possible to connect to a signal in this way in some + cases, such as when: + + \list + \i Multiple connections to the same signal are required + \i Creating connections outside the scope of the signal sender + \i Connecting to targets not defined in QML + \endlist + + When any of these are needed, the Connections element can be used instead. + + For example, the above code can be changed to use a Connections object, + like this: + + \qml + MouseArea { + Connections { + onClicked: foo(parameters) + } + } + \endqml + + More generally, the Connections object can be a child of some object other than + the sender of the signal: + + \qml + MouseArea { + id: area + } + // ... + \endqml + \qml + Connections { + target: area + onClicked: foo(parameters) + } + \endqml + + \sa QtDeclarative +*/ +QDeclarativeConnections::QDeclarativeConnections(QObject *parent) : + QObject(*(new QDeclarativeConnectionsPrivate), parent) +{ +} + +QDeclarativeConnections::~QDeclarativeConnections() +{ +} + +/*! + \qmlproperty Object Connections::target + This property holds the object that sends the signal. + + If this property is not set, the \c target defaults to the parent of the Connection. + + If set to null, no connection is made and any signal handlers are ignored + until the target is not null. +*/ +QObject *QDeclarativeConnections::target() const +{ + Q_D(const QDeclarativeConnections); + return d->targetSet ? d->target : parent(); +} + +void QDeclarativeConnections::setTarget(QObject *obj) +{ + Q_D(QDeclarativeConnections); + d->targetSet = true; // even if setting to 0, it is *set* + if (d->target == obj) + return; + foreach (QDeclarativeBoundSignal *s, d->boundsignals) { + // It is possible that target is being changed due to one of our signal + // handlers -> use deleteLater(). + if (s->isEvaluating()) + s->deleteLater(); + else + delete s; + } + d->boundsignals.clear(); + d->target = obj; + connectSignals(); + emit targetChanged(); +} + +/*! + \qmlproperty bool Connections::ignoreUnknownSignals + + Normally, a connection to a non-existent signal produces runtime errors. + + If this property is set to \c true, such errors are ignored. + This is useful if you intend to connect to different types of objects, handling + a different set of signals for each object. +*/ +bool QDeclarativeConnections::ignoreUnknownSignals() const +{ + Q_D(const QDeclarativeConnections); + return d->ignoreUnknownSignals; +} + +void QDeclarativeConnections::setIgnoreUnknownSignals(bool ignore) +{ + Q_D(QDeclarativeConnections); + d->ignoreUnknownSignals = ignore; +} + + + +QByteArray +QDeclarativeConnectionsParser::compile(const QList &props) +{ + QByteArray rv; + QDataStream ds(&rv, QIODevice::WriteOnly); + + for(int ii = 0; ii < props.count(); ++ii) + { + QString propName = QString::fromUtf8(props.at(ii).name()); + if (!propName.startsWith(QLatin1String("on")) || !propName.at(2).isUpper()) { + error(props.at(ii), QDeclarativeConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); + return QByteArray(); + } + + QList values = props.at(ii).assignedValues(); + + for (int i = 0; i < values.count(); ++i) { + const QVariant &value = values.at(i); + + if (value.userType() == qMetaTypeId()) { + error(props.at(ii), QDeclarativeConnections::tr("Connections: nested objects not allowed")); + return QByteArray(); + } else if (value.userType() == qMetaTypeId()) { + error(props.at(ii), QDeclarativeConnections::tr("Connections: syntax error")); + return QByteArray(); + } else { + QDeclarativeParser::Variant v = qvariant_cast(value); + if (v.isScript()) { + ds << propName; + ds << v.asScript(); + } else { + error(props.at(ii), QDeclarativeConnections::tr("Connections: script expected")); + return QByteArray(); + } + } + } + } + + return rv; +} + +void QDeclarativeConnectionsParser::setCustomData(QObject *object, + const QByteArray &data) +{ + QDeclarativeConnectionsPrivate *p = + static_cast(QObjectPrivate::get(object)); + p->data = data; +} + + +void QDeclarativeConnections::connectSignals() +{ + Q_D(QDeclarativeConnections); + if (!d->componentcomplete || (d->targetSet && !target())) + return; + + QDataStream ds(d->data); + while (!ds.atEnd()) { + QString propName; + ds >> propName; + QString script; + ds >> script; + QDeclarativeProperty prop(target(), propName); + if (prop.isValid() && (prop.type() & QDeclarativeProperty::SignalProperty)) { + QDeclarativeBoundSignal *signal = + new QDeclarativeBoundSignal(target(), prop.method(), this); + QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(this), 0, script); + QDeclarativeData *ddata = QDeclarativeData::get(this); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + signal->setExpression(expression); + d->boundsignals += signal; + } else { + if (!d->ignoreUnknownSignals) + qmlInfo(this) << tr("Cannot assign to non-existent property \"%1\"").arg(propName); + } + } +} + +void QDeclarativeConnections::classBegin() +{ + Q_D(QDeclarativeConnections); + d->componentcomplete=false; +} + +void QDeclarativeConnections::componentComplete() +{ + Q_D(QDeclarativeConnections); + d->componentcomplete=true; + connectSignals(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativeconnections_p.h b/src/declarative/util/qdeclarativeconnections_p.h new file mode 100644 index 00000000..2cb34f2f --- /dev/null +++ b/src/declarative/util/qdeclarativeconnections_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECONNECTIONS_H +#define QDECLARATIVECONNECTIONS_H + +#include +#include +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeBoundSignal; +class QDeclarativeContext; +class QDeclarativeConnectionsPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeConnections : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeConnections) + + Q_INTERFACES(QDeclarativeParserStatus) + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(bool ignoreUnknownSignals READ ignoreUnknownSignals WRITE setIgnoreUnknownSignals) + +public: + QDeclarativeConnections(QObject *parent=0); + ~QDeclarativeConnections(); + + QObject *target() const; + void setTarget(QObject *); + + bool ignoreUnknownSignals() const; + void setIgnoreUnknownSignals(bool ignore); + +Q_SIGNALS: + void targetChanged(); + +private: + void connectSignals(); + void classBegin(); + void componentComplete(); +}; + +class QDeclarativeConnectionsParser : public QDeclarativeCustomParser +{ +public: + virtual QByteArray compile(const QList &); + virtual void setCustomData(QObject *, const QByteArray &); +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeConnections) + +QT_END_HEADER + +#endif diff --git a/src/declarative/util/qdeclarativefontloader.cpp b/src/declarative/util/qdeclarativefontloader.cpp new file mode 100644 index 00000000..d1fd49a1 --- /dev/null +++ b/src/declarative/util/qdeclarativefontloader.cpp @@ -0,0 +1,338 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativefontloader_p.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#define FONTLOADER_MAXIMUM_REDIRECT_RECURSION 16 + +class QDeclarativeFontObject : public QObject +{ +Q_OBJECT + +public: + QDeclarativeFontObject(int _id); + + void download(const QUrl &url, QNetworkAccessManager *manager); + +Q_SIGNALS: + void fontDownloaded(const QString&, QDeclarativeFontLoader::Status); + +private Q_SLOTS: + void replyFinished(); + +public: + int id; + +private: + QNetworkReply *reply; + int redirectCount; + + Q_DISABLE_COPY(QDeclarativeFontObject) +}; + +QDeclarativeFontObject::QDeclarativeFontObject(int _id = -1) + : QObject(0), id(_id), reply(0), redirectCount(0) {} + + +void QDeclarativeFontObject::download(const QUrl &url, QNetworkAccessManager *manager) +{ + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + reply = manager->get(req); + QObject::connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); +} + +void QDeclarativeFontObject::replyFinished() +{ + if (reply) { + redirectCount++; + if (redirectCount < FONTLOADER_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = reply->url().resolved(redirect.toUrl()); + QNetworkAccessManager *manager = reply->manager(); + reply->deleteLater(); + reply = 0; + download(url, manager); + return; + } + } + redirectCount = 0; + + if (!reply->error()) { + id = QFontDatabase::addApplicationFontFromData(reply->readAll()); + if (id != -1) + emit fontDownloaded(QFontDatabase::applicationFontFamilies(id).at(0), QDeclarativeFontLoader::Ready); + else + emit fontDownloaded(QString(), QDeclarativeFontLoader::Error); + } else { + emit fontDownloaded(QString(), QDeclarativeFontLoader::Error); + } + reply->deleteLater(); + reply = 0; + } +} + + +class QDeclarativeFontLoaderPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeFontLoader) + +public: + QDeclarativeFontLoaderPrivate() : status(QDeclarativeFontLoader::Null) {} + + QUrl url; + QString name; + QDeclarativeFontLoader::Status status; + static QHash fonts; +}; + +QHash QDeclarativeFontLoaderPrivate::fonts; + +/*! + \qmlclass FontLoader QDeclarativeFontLoader + \ingroup qml-utility-elements + \since 4.7 + \brief The FontLoader element allows fonts to be loaded by name or URL. + + The FontLoader element is used to load fonts by name or URL. + + The \l status indicates when the font has been loaded, which is useful + for fonts loaded from remote sources. + + For example: + \qml + import QtQuick 1.0 + + Column { + 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 + + \sa {declarative/text/fonts}{Fonts example} +*/ +QDeclarativeFontLoader::QDeclarativeFontLoader(QObject *parent) + : QObject(*(new QDeclarativeFontLoaderPrivate), parent) +{ +} + +QDeclarativeFontLoader::~QDeclarativeFontLoader() +{ +} + +/*! + \qmlproperty url FontLoader::source + The url of the font to load. +*/ +QUrl QDeclarativeFontLoader::source() const +{ + Q_D(const QDeclarativeFontLoader); + return d->url; +} + +void QDeclarativeFontLoader::setSource(const QUrl &url) +{ + Q_D(QDeclarativeFontLoader); + if (url == d->url) + return; + d->url = qmlContext(this)->resolvedUrl(url); + emit sourceChanged(); + +#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML + QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url); + if (!localFile.isEmpty()) { + if (!d->fonts.contains(d->url)) { + int id = QFontDatabase::addApplicationFont(localFile); + if (id != -1) { + updateFontInfo(QFontDatabase::applicationFontFamilies(id).at(0), Ready); + QDeclarativeFontObject *fo = new QDeclarativeFontObject(id); + d->fonts[d->url] = fo; + } else { + updateFontInfo(QString(), Error); + } + } else { + updateFontInfo(QFontDatabase::applicationFontFamilies(d->fonts[d->url]->id).at(0), Ready); + } + } else +#endif + { + if (!d->fonts.contains(d->url)) { + QDeclarativeFontObject *fo = new QDeclarativeFontObject; + d->fonts[d->url] = fo; + fo->download(d->url, qmlEngine(this)->networkAccessManager()); + d->status = Loading; + emit statusChanged(); + QObject::connect(fo, SIGNAL(fontDownloaded(QString,QDeclarativeFontLoader::Status)), + this, SLOT(updateFontInfo(QString,QDeclarativeFontLoader::Status))); + } else { + QDeclarativeFontObject *fo = d->fonts[d->url]; + if (fo->id == -1) { + d->status = Loading; + emit statusChanged(); + QObject::connect(fo, SIGNAL(fontDownloaded(QString,QDeclarativeFontLoader::Status)), + this, SLOT(updateFontInfo(QString,QDeclarativeFontLoader::Status))); + } + else + updateFontInfo(QFontDatabase::applicationFontFamilies(fo->id).at(0), Ready); + } + } +} + +void QDeclarativeFontLoader::updateFontInfo(const QString& name, QDeclarativeFontLoader::Status status) +{ + Q_D(QDeclarativeFontLoader); + + if (name != d->name) { + d->name = name; + emit nameChanged(); + } + if (status != d->status) { + if (status == Error) + qmlInfo(this) << "Cannot load font: \"" << d->url.toString() << "\""; + d->status = status; + emit statusChanged(); + } +} + +/*! + \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 + Item { + width: 200; height: 50 + + FontLoader { + id: webFont + source: "http://www.mysite.com/myfont.ttf" + } + Text { + text: "Fancy font" + font.family: webFont.name + } + } + \endqml +*/ +QString QDeclarativeFontLoader::name() const +{ + Q_D(const QDeclarativeFontLoader); + return d->name; +} + +void QDeclarativeFontLoader::setName(const QString &name) +{ + Q_D(QDeclarativeFontLoader); + if (d->name == name) + return; + d->name = name; + emit nameChanged(); + d->status = Ready; + emit statusChanged(); +} + +/*! + \qmlproperty enumeration FontLoader::status + + This property holds the status of font loading. It can be one of: + \list + \o FontLoader.Null - no font has been set + \o FontLoader.Ready - the font has been loaded + \o FontLoader.Loading - the font is currently being loaded + \o FontLoader.Error - an error occurred while loading the font + \endlist + + Use this status to provide an update or respond to the status change in some way. + For example, you could: + + \list + \o Trigger a state change: + \qml + State { name: 'loaded'; when: loader.status == FontLoader.Ready } + \endqml + + \o Implement an \c onStatusChanged signal handler: + \qml + FontLoader { + id: loader + onStatusChanged: if (loader.status == FontLoader.Ready) console.log('Loaded') + } + \endqml + + \o Bind to the status value: + \qml + Text { text: loader.status == FontLoader.Ready ? 'Loaded' : 'Not loaded' } + \endqml + \endlist +*/ +QDeclarativeFontLoader::Status QDeclarativeFontLoader::status() const +{ + Q_D(const QDeclarativeFontLoader); + return d->status; +} + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/util/qdeclarativefontloader_p.h b/src/declarative/util/qdeclarativefontloader_p.h new file mode 100644 index 00000000..23927224 --- /dev/null +++ b/src/declarative/util/qdeclarativefontloader_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFONTLOADER_H +#define QDECLARATIVEFONTLOADER_H + +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeFontLoaderPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeFontLoader : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeFontLoader) + 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 }; + + QDeclarativeFontLoader(QObject *parent = 0); + ~QDeclarativeFontLoader(); + + QUrl source() const; + void setSource(const QUrl &url); + + QString name() const; + void setName(const QString &name); + + Status status() const; + +private Q_SLOTS: + void updateFontInfo(const QString&, QDeclarativeFontLoader::Status); + +Q_SIGNALS: + void sourceChanged(); + void nameChanged(); + void statusChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeFontLoader) + +QT_END_HEADER + +#endif // QDECLARATIVEFONTLOADER_H + diff --git a/src/declarative/util/qdeclarativelistaccessor.cpp b/src/declarative/util/qdeclarativelistaccessor.cpp new file mode 100644 index 00000000..5a0e2fde --- /dev/null +++ b/src/declarative/util/qdeclarativelistaccessor.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativelistaccessor_p.h" + +#include + +#include +#include + +// ### Remove me +#include + +QT_BEGIN_NAMESPACE + +QDeclarativeListAccessor::QDeclarativeListAccessor() +: m_type(Invalid) +{ +} + +QDeclarativeListAccessor::~QDeclarativeListAccessor() +{ +} + +QVariant QDeclarativeListAccessor::list() const +{ + return d; +} + +void QDeclarativeListAccessor::setList(const QVariant &v, QDeclarativeEngine *engine) +{ + d = v; + + QDeclarativeEnginePrivate *enginePrivate = engine?QDeclarativeEnginePrivate::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 && QDeclarativeMetaType::isQObject(d.userType())) || + (enginePrivate && enginePrivate->isQObject(d.userType()))) { + QObject *data = enginePrivate?enginePrivate->toQObject(v):QDeclarativeMetaType::toQObject(v); + d = QVariant::fromValue(data); + m_type = Instance; + } else if (d.userType() == qMetaTypeId()) { + m_type = ListProperty; + } else { + m_type = Instance; + } +} + +int QDeclarativeListAccessor::count() const +{ + switch(m_type) { + case StringList: + return qvariant_cast(d).count(); + case VariantList: + return qvariant_cast(d).count(); + case ListProperty: + return ((QDeclarativeListReference *)d.constData())->count(); + case Instance: + return 1; + case Integer: + return d.toInt(); + default: + case Invalid: + return 0; + } +} + +QVariant QDeclarativeListAccessor::at(int idx) const +{ + Q_ASSERT(idx >= 0 && idx < count()); + switch(m_type) { + case StringList: + return QVariant::fromValue(qvariant_cast(d).at(idx)); + case VariantList: + return qvariant_cast(d).at(idx); + case ListProperty: + return QVariant::fromValue(((QDeclarativeListReference *)d.constData())->at(idx)); + case Instance: + return d; + case Integer: + return QVariant(idx); + default: + case Invalid: + return QVariant(); + } +} + +bool QDeclarativeListAccessor::isValid() const +{ + return m_type != Invalid; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativelistaccessor_p.h b/src/declarative/util/qdeclarativelistaccessor_p.h new file mode 100644 index 00000000..8686a9d2 --- /dev/null +++ b/src/declarative/util/qdeclarativelistaccessor_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTACCESSOR_H +#define QDECLARATIVELISTACCESSOR_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeEngine; +class Q_AUTOTEST_EXPORT QDeclarativeListAccessor +{ +public: + QDeclarativeListAccessor(); + ~QDeclarativeListAccessor(); + + QVariant list() const; + void setList(const QVariant &, QDeclarativeEngine * = 0); + + bool isValid() const; + + int count() const; + QVariant at(int) const; + + enum Type { Invalid, StringList, VariantList, ListProperty, Instance, Integer }; + Type type() const { return m_type; } + +private: + Type m_type; + QVariant d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVELISTACCESSOR_H diff --git a/src/declarative/util/qdeclarativelistmodel.cpp b/src/declarative/util/qdeclarativelistmodel.cpp new file mode 100644 index 00000000..b87f9144 --- /dev/null +++ b/src/declarative/util/qdeclarativelistmodel.cpp @@ -0,0 +1,1628 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativelistmodel_p_p.h" +#include "private/qdeclarativelistmodelworkeragent_p.h" +#include "private/qdeclarativeopenmetaobject_p.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QListModelInterface *) + +QT_BEGIN_NAMESPACE + +template +void qdeclarativelistmodel_move(int from, int to, int n, T *items) +{ + if (n == 1) { + items->move(from, to); + } else { + T replaced; + int i=0; + typename T::ConstIterator it=items->begin(); it += from+n; + for (; ibegin(); it += from; + for (; ibegin(); t += from; + for (; f != replaced.end(); ++f, ++t) + *t = *f; + } +} + +QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListModelData::instructions() const +{ + return (QDeclarativeListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData)); +} + +/*! + \qmlclass ListModel QDeclarativeListModel + \ingroup qml-working-with-data + \since 4.7 + \brief The ListModel element defines a free-form list data source. + + The ListModel is a simple container of ListElement definitions, each containing data roles. + The contents can be defined dynamically, or explicitly in QML. + + The number of elements in the model can be obtained from its \l count property. + A number of familiar methods are also provided to manipulate the contents of the + model, including append(), insert(), move(), remove() and set(). These methods + accept dictionaries as their arguments; these are translated to ListElement objects + by the model. + + Elements can be manipulated via the model using the setProperty() method, which + allows the roles of the specified element to be set and changed. + + \section1 Example Usage + + The following example shows a ListModel containing three elements, with the roles + "name" and "cost". + + \div {class="float-right"} + \inlineimage listmodel.png + \enddiv + + \snippet doc/src/snippets/declarative/listmodel.qml 0 + + \clearfloat + Roles (properties) in each element must begin with a lower-case letter and + should be common to all elements in a model. The ListElement documentation + provides more guidelines for how elements should be defined. + + Since the example model contains an \c id property, it can be referenced + by views, such as the ListView in this example: + + \snippet doc/src/snippets/declarative/listmodel-simple.qml 0 + \dots 8 + \snippet doc/src/snippets/declarative/listmodel-simple.qml 1 + + It is possible for roles to contain list data. In the following example we + create a list of fruit attributes: + + \snippet doc/src/snippets/declarative/listmodel-nested.qml model + + The delegate displays all the fruit attributes: + + \div {class="float-right"} + \inlineimage listmodel-nested.png + \enddiv + + \snippet doc/src/snippets/declarative/listmodel-nested.qml delegate + + \clearfloat + \section1 Modifying List Models + + The content of a ListModel may be created and modified using the clear(), + append(), set(), insert() and setProperty() methods. For example: + + \snippet doc/src/snippets/declarative/listmodel-modify.qml delegate + + Note that when creating content dynamically the set of available properties + cannot be changed once set. Whatever properties are first added to the model + are the only permitted properties in the model. + + \section1 Using Threaded List Models with WorkerScript + + ListModel can be used together with WorkerScript access a list model + from multiple threads. This is useful if list modifications are + synchronous and take some time: the list operations can be moved to a + different thread to avoid blocking of the main GUI thread. + + Here is an example that uses WorkerScript to periodically append the + current time to a list model: + + \snippet examples/declarative/threading/threadedlistmodel/timedisplay.qml 0 + + The included file, \tt dataloader.js, looks like this: + + \snippet examples/declarative/threading/threadedlistmodel/dataloader.js 0 + + The timer in the main example sends messages to the worker script by calling + \l WorkerScript::sendMessage(). When this message is received, + \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js, + which appends the current time to the list model. + + Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()} + handler. You must call sync() or else the changes made to the list from the external + thread will not be reflected in the list model in the main thread. + + \section1 Restrictions + + If a list model is to be accessed from a WorkerScript, it cannot + contain list-type data. So, the following model cannot be used from a WorkerScript + because of the list contained in the "attributes" property: + + \code + ListModel { + id: fruitModel + ListElement { + name: "Apple" + cost: 2.45 + attributes: [ + ListElement { description: "Core" }, + ListElement { description: "Deciduous" } + ] + } + } + \endcode + + In addition, the WorkerScript cannot add list-type data to the model. + + \sa {qmlmodels}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtDeclarative +*/ + + +/* + A ListModel internally uses either a NestedListModel or FlatListModel. + + A NestedListModel can contain lists of ListElements (which + when retrieved from get() is accessible as a list model within the list + model) whereas a FlatListModel cannot. + + ListModel uses a NestedListModel to begin with, and if the model is later + used from a WorkerScript, it changes to use a FlatListModel instead. This + is because ModelNode (which abstracts the nested list model data) needs + access to the declarative engine and script engine, which cannot be + safely used from outside of the main thread. +*/ + +QDeclarativeListModel::QDeclarativeListModel(QObject *parent) +: QListModelInterface(parent), m_agent(0), m_nested(new NestedListModel(this)), m_flat(0) +{ +} + +QDeclarativeListModel::QDeclarativeListModel(const QDeclarativeListModel *orig, QDeclarativeListModelWorkerAgent *parent) +: QListModelInterface(parent), m_agent(0), m_nested(0), m_flat(0) +{ + m_flat = new FlatListModel(this); + m_flat->m_parentAgent = parent; + + if (orig->m_flat) { + m_flat->m_roles = orig->m_flat->m_roles; + m_flat->m_strings = orig->m_flat->m_strings; + m_flat->m_values = orig->m_flat->m_values; + + m_flat->m_nodeData.reserve(m_flat->m_values.count()); + for (int i=0; im_values.count(); i++) + m_flat->m_nodeData << 0; + } +} + +QDeclarativeListModel::~QDeclarativeListModel() +{ + if (m_agent) + m_agent->release(); + + delete m_nested; + delete m_flat; +} + +bool QDeclarativeListModel::flatten() +{ + if (m_flat) + return true; + + QList roles = m_nested->roles(); + + QList > values; + bool hasNested = false; + for (int i=0; icount(); i++) { + values.append(m_nested->data(i, roles, &hasNested)); + if (hasNested) + return false; + } + + FlatListModel *flat = new FlatListModel(this); + flat->m_values = values; + + for (int i=0; itoString(roles[i]); + flat->m_roles.insert(roles[i], s); + flat->m_strings.insert(s, roles[i]); + } + + flat->m_nodeData.reserve(flat->m_values.count()); + for (int i=0; im_values.count(); i++) + flat->m_nodeData << 0; + + m_flat = flat; + delete m_nested; + m_nested = 0; + return true; +} + +bool QDeclarativeListModel::inWorkerThread() const +{ + return m_flat && m_flat->m_parentAgent; +} + +QDeclarativeListModelWorkerAgent *QDeclarativeListModel::agent() +{ + if (m_agent) + return m_agent; + + if (!flatten()) { + qmlInfo(this) << "List contains list-type data and cannot be used from a worker script"; + return 0; + } + + m_agent = new QDeclarativeListModelWorkerAgent(this); + return m_agent; +} + +QList QDeclarativeListModel::roles() const +{ + return m_flat ? m_flat->roles() : m_nested->roles(); +} + +QString QDeclarativeListModel::toString(int role) const +{ + return m_flat ? m_flat->toString(role) : m_nested->toString(role); +} + +QVariant QDeclarativeListModel::data(int index, int role) const +{ + if (index >= count() || index < 0) + return QVariant(); + + return m_flat ? m_flat->data(index, role) : m_nested->data(index, role); +} + +/*! + \qmlproperty int ListModel::count + The number of data entries in the model. +*/ +int QDeclarativeListModel::count() const +{ + return m_flat ? m_flat->count() : m_nested->count(); +} + +/*! + \qmlmethod ListModel::clear() + + Deletes all content from the model. + + \sa append() remove() +*/ +void QDeclarativeListModel::clear() +{ + int cleared = count(); + if (m_flat) + m_flat->clear(); + else + m_nested->clear(); + + if (!inWorkerThread()) { + emit itemsRemoved(0, cleared); + emit countChanged(); + } +} + +QDeclarativeListModel *ModelNode::model(const NestedListModel *model) +{ + if (!modelCache) { + modelCache = new QDeclarativeListModel; + QDeclarativeEngine::setContextForObject(modelCache,QDeclarativeEngine::contextForObject(model->m_listModel)); + modelCache->m_nested->_root = this; // ListModel defaults to nestable model + + for (int i=0; i(values.at(i)); + if (subNode) + subNode->m_model = modelCache->m_nested; + } + } + return modelCache; +} + +ModelObject *ModelNode::object(const NestedListModel *model) +{ + if (!objectCache) { + objectCache = new ModelObject(this, + const_cast(model), + QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(model->m_listModel))); + QHash::iterator it; + for (it = properties.begin(); it != properties.end(); ++it) { + objectCache->setValue(it.key().toUtf8(), model->valueForNode(*it)); + } + objectCache->setNodeUpdatesEnabled(true); + } + return objectCache; +} + +/*! + \qmlmethod ListModel::remove(int index) + + Deletes the content at \a index from the model. + + \sa clear() +*/ +void QDeclarativeListModel::remove(int index) +{ + if (index < 0 || index >= count()) { + qmlInfo(this) << tr("remove: index %1 out of range").arg(index); + return; + } + + if (m_flat) + m_flat->remove(index); + else + m_nested->remove(index); + + if (!inWorkerThread()) { + emit itemsRemoved(index, 1); + emit countChanged(); + } +} + +/*! + \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 QDeclarativeListModel::insert(int index, const QScriptValue& valuemap) +{ + if (!valuemap.isObject() || valuemap.isArray()) { + qmlInfo(this) << tr("insert: value is not an object"); + return; + } + + if (index < 0 || index > count()) { + qmlInfo(this) << tr("insert: index %1 out of range").arg(index); + return; + } + + bool ok = m_flat ? m_flat->insert(index, valuemap) : m_nested->insert(index, valuemap); + if (ok && !inWorkerThread()) { + emit itemsInserted(index, 1); + emit countChanged(); + } +} + +/*! + \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 QDeclarativeListModel::move(int from, int to, int n) +{ + if (n==0 || from==to) + return; + if (!canMove(from, to, n)) { + qmlInfo(this) << tr("move: out of range"); + return; + } + + int origfrom = from; + 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 (m_flat) + m_flat->move(from, to, n); + else + m_nested->move(from, to, n); + + if (!inWorkerThread()) + 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 QDeclarativeListModel::append(const QScriptValue& valuemap) +{ + if (!valuemap.isObject() || valuemap.isArray()) { + qmlInfo(this) << tr("append: value is not an object"); + return; + } + + insert(count(), valuemap); +} + +/*! + \qmlmethod object ListModel::get(int index) + + Returns the item at \a index in the list model. This allows the item + data to be accessed or modified from JavaScript: + + \code + Component.onCompleted: { + fruitModel.append({"cost": 5.95, "name":"Jackfruit"}); + console.log(fruitModel.get(0).cost); + fruitModel.get(0).cost = 10.95; + } + \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 + + \warning The returned object is not guaranteed to remain valid. It + should not be used in \l{Property Binding}{property bindings}. + + \sa append() +*/ +QScriptValue QDeclarativeListModel::get(int index) const +{ + // the internal flat/nested class checks for bad index + return m_flat ? m_flat->get(index) : m_nested->get(index); +} + +/*! + \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 dict + are left unchanged. + + \code + fruitModel.set(3, {"cost": 5.95, "name":"Pizza"}) + \endcode + + If \a index is equal to count() then a new item is appended to the + list. Otherwise, \a index must be an element in the list. + + \sa append() +*/ +void QDeclarativeListModel::set(int index, const QScriptValue& valuemap) +{ + QList roles; + set(index, valuemap, &roles); + if (!roles.isEmpty() && !inWorkerThread()) + emit itemsChanged(index, 1, roles); +} + +void QDeclarativeListModel::set(int index, const QScriptValue& valuemap, QList *roles) +{ + if (!valuemap.isObject() || valuemap.isArray()) { + qmlInfo(this) << tr("set: value is not an object"); + return; + } + if (index > count() || index < 0) { + qmlInfo(this) << tr("set: index %1 out of range").arg(index); + return; + } + + if (index == count()) { + append(valuemap); + } else { + if (m_flat) + m_flat->set(index, valuemap, roles); + else + m_nested->set(index, valuemap, 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.setProperty(3, "cost", 5.95) + \endcode + + The \a index must be an element in the list. + + \sa append() +*/ +void QDeclarativeListModel::setProperty(int index, const QString& property, const QVariant& value) +{ + QList roles; + setProperty(index, property, value, &roles); + if (!roles.isEmpty() && !inWorkerThread()) + emit itemsChanged(index, 1, roles); +} + +void QDeclarativeListModel::setProperty(int index, const QString& property, const QVariant& value, QList *roles) +{ + if (count() == 0 || index >= count() || index < 0) { + qmlInfo(this) << tr("set: index %1 out of range").arg(index); + return; + } + + if (m_flat) + m_flat->setProperty(index, property, value, roles); + else + m_nested->setProperty(index, property, value, roles); +} + +/*! + \qmlmethod ListModel::sync() + + Writes any unsaved changes to the list model after it has been modified + from a worker script. +*/ +void QDeclarativeListModel::sync() +{ + // This is just a dummy method to make it look like sync() exists in + // ListModel (and not just QDeclarativeListModelWorkerAgent) and to let + // us document sync(). + qmlInfo(this) << "List sync() can only be called from a WorkerScript"; +} + +bool QDeclarativeListModelParser::compileProperty(const QDeclarativeCustomParserProperty &prop, QList &instr, QByteArray &data) +{ + QList values = prop.assignedValues(); + for(int ii = 0; ii < values.count(); ++ii) { + const QVariant &value = values.at(ii); + + if(value.userType() == qMetaTypeId()) { + QDeclarativeCustomParserNode node = + qvariant_cast(value); + + if (node.name() != listElementTypeName) { + const QMetaObject *mo = resolveType(node.name()); + if (mo != &QDeclarativeListElement::staticMetaObject) { + error(node, QDeclarativeListModel::tr("ListElement: cannot contain nested elements")); + return false; + } + listElementTypeName = node.name(); // cache right name for next time + } + + { + ListInstruction li; + li.type = ListInstruction::Push; + li.dataIdx = -1; + instr << li; + } + + QList props = node.properties(); + for(int jj = 0; jj < props.count(); ++jj) { + const QDeclarativeCustomParserProperty &nodeProp = props.at(jj); + if (nodeProp.name().isEmpty()) { + error(nodeProp, QDeclarativeListModel::tr("ListElement: cannot contain nested elements")); + return false; + } + if (nodeProp.name() == "id") { + error(nodeProp, QDeclarativeListModel::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 { + + QDeclarativeParser::Variant variant = + qvariant_cast(value); + + int ref = data.count(); + + QByteArray d; + d += char(variant.type()); // type tag + if (variant.isString()) { + d += variant.asString().toUtf8(); + } else if (variant.isNumber()) { + d += QByteArray::number(variant.asNumber(),'g',20); + } else if (variant.isBoolean()) { + d += char(variant.asBoolean()); + } else if (variant.isScript()) { + if (definesEmptyList(variant.asScript())) { + d[0] = char(QDeclarativeParser::Variant::Invalid); // marks empty list + } else { + QByteArray script = variant.asScript().toUtf8(); + int v = evaluateEnum(script); + if (v<0) { + if (script.startsWith("QT_TR_NOOP(\"") && script.endsWith("\")")) { + d[0] = char(QDeclarativeParser::Variant::String); + d += script.mid(12,script.length()-14); + } else { + error(prop, QDeclarativeListModel::tr("ListElement: cannot use script for property value")); + return false; + } + } else { + d[0] = char(QDeclarativeParser::Variant::Number); + d += QByteArray::number(v); + } + } + } + d.append('\0'); + data.append(d); + + ListInstruction li; + li.type = ListInstruction::Value; + li.dataIdx = ref; + instr << li; + } + } + + return true; +} + +QByteArray QDeclarativeListModelParser::compile(const QList &customProps) +{ + QList instr; + QByteArray data; + listElementTypeName = QByteArray(); // unknown + + for(int ii = 0; ii < customProps.count(); ++ii) { + const QDeclarativeCustomParserProperty &prop = customProps.at(ii); + if(!prop.name().isEmpty()) { // isn't default property + error(prop, QDeclarativeListModel::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 QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray &d) +{ + QDeclarativeListModel *rv = static_cast(obj); + + ModelNode *root = new ModelNode(rv->m_nested); + rv->m_nested->m_ownsRoot = true; + rv->m_nested->_root = root; + QStack 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(rv->m_nested); + n->values << QVariant::fromValue(n2); + nodes.push(n2); + if (processingSet) + n->isArray = true; + } + break; + + case ListInstruction::Pop: + nodes.pop(); + break; + + case ListInstruction::Value: + { + ModelNode *n = nodes.top(); + switch (QDeclarativeParser::Variant::Type(data[instr.dataIdx])) { + case QDeclarativeParser::Variant::Invalid: + n->isArray = true; + break; + case QDeclarativeParser::Variant::Boolean: + n->values.append(bool(data[1 + instr.dataIdx])); + break; + case QDeclarativeParser::Variant::Number: + n->values.append(QByteArray(data + 1 + instr.dataIdx).toDouble()); + break; + case QDeclarativeParser::Variant::String: + n->values.append(QString::fromUtf8(data + 1 + instr.dataIdx)); + break; + default: + Q_ASSERT("Format error in ListInstruction"); + } + + processingSet = false; + } + break; + + case ListInstruction::Set: + { + ModelNode *n = nodes.top(); + ModelNode *n2 = new ModelNode(rv->m_nested); + n->properties.insert(QString::fromUtf8(data + instr.dataIdx), n2); + nodes.push(n2); + processingSet = true; + } + break; + } + } + + ModelNode *rootNode = rv->m_nested->_root; + for (int i=0; ivalues.count(); ++i) { + ModelNode *node = qvariant_cast(rootNode->values[i]); + node->listIndex = i; + node->updateListIndexes(); + } +} + +bool QDeclarativeListModelParser::definesEmptyList(const QString &s) +{ + if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) { + for (int i=1; i= 0 && index < m_values.count()); + if (m_values[index].contains(role)) + return m_values[index][role]; + return QVariant(); +} + +QList FlatListModel::roles() const +{ + return m_roles.keys(); +} + +QString FlatListModel::toString(int role) const +{ + if (m_roles.contains(role)) + return m_roles[role]; + return QString(); +} + +int FlatListModel::count() const +{ + return m_values.count(); +} + +void FlatListModel::clear() +{ + m_values.clear(); + + qDeleteAll(m_nodeData); + m_nodeData.clear(); +} + +void FlatListModel::remove(int index) +{ + m_values.removeAt(index); + removedNode(index); +} + +bool FlatListModel::insert(int index, const QScriptValue &value) +{ + Q_ASSERT(index >= 0 && index <= m_values.count()); + + QHash row; + if (!addValue(value, &row, 0)) + return false; + + m_values.insert(index, row); + insertedNode(index); + + return true; +} + +QScriptValue FlatListModel::get(int index) const +{ + QScriptEngine *scriptEngine = m_scriptEngine ? m_scriptEngine : QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(m_listModel)); + + if (!scriptEngine) + return 0; + + if (index < 0 || index >= m_values.count()) + return scriptEngine->undefinedValue(); + + FlatListModel *that = const_cast(this); + if (!m_scriptClass) + that->m_scriptClass = new FlatListScriptClass(that, scriptEngine); + + FlatNodeData *data = m_nodeData.value(index); + if (!data) { + data = new FlatNodeData(index); + that->m_nodeData.replace(index, data); + } + + return QScriptDeclarativeClass::newObject(scriptEngine, m_scriptClass, new FlatNodeObjectData(data)); +} + +void FlatListModel::set(int index, const QScriptValue &value, QList *roles) +{ + Q_ASSERT(index >= 0 && index < m_values.count()); + + QHash row = m_values[index]; + if (addValue(value, &row, roles)) + m_values[index] = row; +} + +void FlatListModel::setProperty(int index, const QString& property, const QVariant& value, QList *roles) +{ + Q_ASSERT(index >= 0 && index < m_values.count()); + + QHash::Iterator iter = m_strings.find(property); + int role; + if (iter == m_strings.end()) { + role = m_roles.count(); + m_roles.insert(role, property); + m_strings.insert(property, role); + } else { + role = iter.value(); + } + + if (m_values[index][role] != value) { + roles->append(role); + m_values[index][role] = value; + } +} + +void FlatListModel::move(int from, int to, int n) +{ + qdeclarativelistmodel_move > >(from, to, n, &m_values); + moveNodes(from, to, n); +} + +bool FlatListModel::addValue(const QScriptValue &value, QHash *row, QList *roles) +{ + QScriptValueIterator it(value); + while (it.hasNext()) { + it.next(); + QScriptValue value = it.value(); + if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) { + qmlInfo(m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script"; + return false; + } + + QString name = it.name(); + QVariant v = it.value().toVariant(); + + QHash::Iterator iter = m_strings.find(name); + if (iter == m_strings.end()) { + int role = m_roles.count(); + m_roles.insert(role, name); + iter = m_strings.insert(name, role); + if (roles) + roles->append(role); + } else { + int role = iter.value(); + if (roles && row->contains(role) && row->value(role) != v) + roles->append(role); + } + row->insert(*iter, v); + } + return true; +} + +void FlatListModel::insertedNode(int index) +{ + if (index >= 0 && index <= m_values.count()) { + m_nodeData.insert(index, 0); + + for (int i=index + 1; iindex = i; + } + } +} + +void FlatListModel::removedNode(int index) +{ + if (index >= 0 && index < m_nodeData.count()) { + delete m_nodeData.takeAt(index); + + for (int i=index; iindex = i; + } + } +} + +void FlatListModel::moveNodes(int from, int to, int n) +{ + if (!m_listModel->canMove(from, to, n)) + return; + + qdeclarativelistmodel_move >(from, to, n, &m_nodeData); + + for (int i=from; iindex = i; + } +} + + + +FlatNodeData::~FlatNodeData() +{ + for (QSet::Iterator iter = objects.begin(); iter != objects.end(); ++iter) { + FlatNodeObjectData *data = *iter; + data->nodeData = 0; + } +} + +void FlatNodeData::addData(FlatNodeObjectData *data) +{ + objects.insert(data); +} + +void FlatNodeData::removeData(FlatNodeObjectData *data) +{ + objects.remove(data); +} + + +FlatListScriptClass::FlatListScriptClass(FlatListModel *model, QScriptEngine *seng) + : QScriptDeclarativeClass(seng), + m_model(model) +{ +} + +QScriptDeclarativeClass::Value FlatListScriptClass::property(Object *obj, const Identifier &name) +{ + FlatNodeObjectData *objData = static_cast(obj); + if (!objData->nodeData) // item at this index has been deleted + return QScriptDeclarativeClass::Value(engine(), engine()->undefinedValue()); + + int index = objData->nodeData->index; + QString propName = toString(name); + int role = m_model->m_strings.value(propName, -1); + + if (role >= 0 && index >=0 ) { + const QHash &row = m_model->m_values[index]; + QScriptValue sv = engine()->toScriptValue(row[role]); + return QScriptDeclarativeClass::Value(engine(), sv); + } + + return QScriptDeclarativeClass::Value(engine(), engine()->undefinedValue()); +} + +void FlatListScriptClass::setProperty(Object *obj, const Identifier &name, const QScriptValue &value) +{ + if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) { + qmlInfo(m_model->m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script"; + return; + } + + FlatNodeObjectData *objData = static_cast(obj); + if (!objData->nodeData) // item at this index has been deleted + return; + + int index = objData->nodeData->index; + QString propName = toString(name); + + int role = m_model->m_strings.value(propName, -1); + if (role >= 0 && index >= 0) { + QHash &row = m_model->m_values[index]; + row[role] = value.toVariant(); + + QList roles; + roles << role; + if (m_model->m_parentAgent) { + // This is the list in the worker thread, so tell the agent to + // emit itemsChanged() later + m_model->m_parentAgent->changedData(index, 1, roles); + } else { + // This is the list in the main thread, so emit itemsChanged() + emit m_model->m_listModel->itemsChanged(index, 1, roles); + } + } +} + +QScriptClass::QueryFlags FlatListScriptClass::queryProperty(Object *, const Identifier &, QScriptClass::QueryFlags) +{ + return (QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess); +} + +bool FlatListScriptClass::compare(Object *obj1, Object *obj2) +{ + FlatNodeObjectData *data1 = static_cast(obj1); + FlatNodeObjectData *data2 = static_cast(obj2); + + if (!data1->nodeData || !data2->nodeData) + return false; + + return data1->nodeData->index == data2->nodeData->index; +} + + + +NestedListModel::NestedListModel(QDeclarativeListModel *base) + : _root(0), m_ownsRoot(false), m_listModel(base), _rolesOk(false) +{ +} + +NestedListModel::~NestedListModel() +{ + if (m_ownsRoot) + delete _root; +} + +QVariant NestedListModel::valueForNode(ModelNode *node, bool *hasNested) const +{ + QObject *rv = 0; + if (hasNested) + *hasNested = false; + + if (node->isArray) { + // List + rv = node->model(this); + if (hasNested) + *hasNested = true; + } 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(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 NestedListModel::data(int index, const QList &roles, bool *hasNested) const +{ + Q_ASSERT(_root && index >= 0 && index < _root->values.count()); + checkRoles(); + QHash rv; + + ModelNode *node = qvariant_cast(_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::ConstIterator iter = node->properties.find(roleString); + if (iter != node->properties.end()) { + ModelNode *row = *iter; + rv.insert(roles.at(ii), valueForNode(row, hasNested)); + } + } + + return rv; +} + +QVariant NestedListModel::data(int index, int role) const +{ + Q_ASSERT(_root && index >= 0 && index < _root->values.count()); + checkRoles(); + QVariant rv; + if (roleStrings.count() < role) + return rv; + + ModelNode *node = qvariant_cast(_root->values.at(index)); + if (!node) + return rv; + + const QString &roleString = roleStrings.at(role); + + QHash::ConstIterator iter = node->properties.find(roleString); + if (iter != node->properties.end()) { + ModelNode *row = *iter; + rv = valueForNode(row); + } + + return rv; +} + +int NestedListModel::count() const +{ + if (!_root) return 0; + return _root->values.count(); +} + +void NestedListModel::clear() +{ + if (_root) + _root->clear(); +} + +void NestedListModel::remove(int index) +{ + if (!_root) + return; + ModelNode *node = qvariant_cast(_root->values.at(index)); + _root->values.removeAt(index); + if (node) + delete node; +} + +bool NestedListModel::insert(int index, const QScriptValue& valuemap) +{ + if (!_root) { + _root = new ModelNode(this); + m_ownsRoot = true; + } + + ModelNode *mn = new ModelNode(this); + mn->listIndex = index; + mn->setObjectValue(valuemap); + _root->values.insert(index,QVariant::fromValue(mn)); + return true; +} + +void NestedListModel::move(int from, int to, int n) +{ + if (!_root) + return; + qdeclarativelistmodel_move(from, to, n, &_root->values); +} + +QScriptValue NestedListModel::get(int index) const +{ + QDeclarativeEngine *eng = qmlEngine(m_listModel); + if (!eng) + return 0; + + if (index < 0 || index >= count()) { + QScriptEngine *seng = QDeclarativeEnginePrivate::getScriptEngine(eng); + if (seng) + return seng->undefinedValue(); + return 0; + } + + ModelNode *node = qvariant_cast(_root->values.at(index)); + if (!node) + return 0; + + return QDeclarativeEnginePrivate::qmlScriptObject(node->object(this), eng); +} + +void NestedListModel::set(int index, const QScriptValue& valuemap, QList *roles) +{ + Q_ASSERT(index >=0 && index < count()); + + ModelNode *node = qvariant_cast(_root->values.at(index)); + bool emitItemsChanged = node->setObjectValue(valuemap); + if (!emitItemsChanged) + return; + + 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); + } +} + +void NestedListModel::setProperty(int index, const QString& property, const QVariant& value, QList *roles) +{ + Q_ASSERT(index >=0 && index < count()); + + ModelNode *node = qvariant_cast(_root->values.at(index)); + bool emitItemsChanged = node->setProperty(property, value); + if (!emitItemsChanged) + return; + + int r = roleStrings.indexOf(property); + if (r < 0) { + r = roleStrings.count(); + roleStrings << property; + } + roles->append(r); +} + +void NestedListModel::checkRoles() const +{ + if (_rolesOk || !_root) + return; + + for (int i = 0; i<_root->values.count(); ++i) { + ModelNode *node = qvariant_cast(_root->values.at(i)); + if (node) { + foreach (const QString &role, node->properties.keys()) { + if (!roleStrings.contains(role)) + roleStrings.append(role); + } + } + } + + _rolesOk = true; +} + +QList NestedListModel::roles() const +{ + checkRoles(); + QList rv; + for (int ii = 0; ii < roleStrings.count(); ++ii) + rv << ii; + return rv; +} + +QString NestedListModel::toString(int role) const +{ + checkRoles(); + if (role < roleStrings.count()) + return roleStrings.at(role); + else + return QString(); +} + + +ModelNode::ModelNode(NestedListModel *model) +: modelCache(0), objectCache(0), isArray(false), m_model(model), listIndex(-1) +{ +} + +ModelNode::~ModelNode() +{ + clear(); + if (modelCache) { modelCache->m_nested->_root = 0/* ==this */; delete modelCache; modelCache = 0; } + if (objectCache) { delete objectCache; objectCache = 0; } +} + +void ModelNode::clear() +{ + ModelNode *node; + for (int ii = 0; ii < values.count(); ++ii) { + node = qvariant_cast(values.at(ii)); + if (node) { delete node; node = 0; } + } + values.clear(); + + qDeleteAll(properties.values()); + properties.clear(); +} + +bool ModelNode::setObjectValue(const QScriptValue& valuemap, bool writeToCache) +{ + bool emitItemsChanged = false; + + QScriptValueIterator it(valuemap); + while (it.hasNext()) { + it.next(); + ModelNode *prev = properties.value(it.name()); + ModelNode *value = new ModelNode(m_model); + QScriptValue v = it.value(); + + if (v.isArray()) { + value->isArray = true; + value->setListValue(v); + if (writeToCache && objectCache) + objectCache->setValue(it.name().toUtf8(), QVariant::fromValue(value->model(m_model))); + emitItemsChanged = true; // for now, too inefficient to check whether list and sublists have changed + } else { + value->values << v.toVariant(); + if (writeToCache && objectCache) + objectCache->setValue(it.name().toUtf8(), value->values.last()); + if (!emitItemsChanged && prev && prev->values.count() == 1 + && prev->values[0] != value->values.last()) { + emitItemsChanged = true; + } + } + if (properties.contains(it.name())) + delete properties[it.name()]; + properties.insert(it.name(), value); + } + return emitItemsChanged; +} + +void ModelNode::setListValue(const QScriptValue& valuelist) { + values.clear(); + int size = valuelist.property(QLatin1String("length")).toInt32(); + for (int i=0; iisArray = true; + value->setListValue(v); + } else if (v.isObject()) { + value->listIndex = i; + value->setObjectValue(v); + } else { + value->listIndex = i; + value->values << v.toVariant(); + } + values.append(QVariant::fromValue(value)); + } +} + +bool ModelNode::setProperty(const QString& prop, const QVariant& val) { + QHash::const_iterator it = properties.find(prop); + bool emitItemsChanged = false; + if (it != properties.end()) { + if (val != (*it)->values[0]) + emitItemsChanged = true; + (*it)->values[0] = val; + } else { + ModelNode *n = new ModelNode(m_model); + n->values << val; + properties.insert(prop,n); + } + if (objectCache) + objectCache->setValue(prop.toUtf8(), val); + return emitItemsChanged; +} + +void ModelNode::updateListIndexes() +{ + for (QHash::ConstIterator iter = properties.begin(); iter != properties.end(); ++iter) { + ModelNode *node = iter.value(); + if (node->isArray) { + for (int i=0; ivalues.count(); ++i) { + ModelNode *subNode = qvariant_cast(node->values.at(i)); + if (subNode) + subNode->listIndex = i; + } + } + node->updateListIndexes(); + } +} + +/* + Need to call this to emit itemsChanged() for modifications outside of set() + and setProperty(), i.e. if an item returned from get() is modified +*/ +void ModelNode::changedProperty(const QString &name) const +{ + if (listIndex < 0) + return; + + m_model->checkRoles(); + QList roles; + int role = m_model->roleStrings.indexOf(name); + if (role < 0) + roles = m_model->roles(); + else + roles << role; + emit m_model->m_listModel->itemsChanged(listIndex, 1, roles); +} + +void ModelNode::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(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::ConstIterator iter = node->properties.begin(); iter != node->properties.end(); ++iter) { + qWarning().nospace() << indent << "Property " << iter.key() << ':'; + dump(iter.value(), ind + 1); + } +} + +ModelObject::ModelObject(ModelNode *node, NestedListModel *model, QScriptEngine *seng) + : m_model(model), + m_node(node), + m_meta(new ModelNodeMetaObject(seng, this)) +{ +} + +void ModelObject::setValue(const QByteArray &name, const QVariant &val) +{ + m_meta->setValue(name, val); + //setProperty(name.constData(), val); +} + +void ModelObject::setNodeUpdatesEnabled(bool enable) +{ + m_meta->m_enabled = enable; +} + + +ModelNodeMetaObject::ModelNodeMetaObject(QScriptEngine *seng, ModelObject *object) + : QDeclarativeOpenMetaObject(object), + m_enabled(false), + m_seng(seng), + m_obj(object) +{ +} + +void ModelNodeMetaObject::propertyWritten(int index) +{ + if (!m_enabled) + return; + + QString propName = QString::fromUtf8(name(index)); + QVariant value = operator[](index); + + QScriptValue sv = m_seng->newObject(); + sv.setProperty(propName, m_seng->newVariant(value)); + bool changed = m_obj->m_node->setObjectValue(sv, false); + if (changed) + m_obj->m_node->changedProperty(propName); +} + + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativelistmodel_p.h b/src/declarative/util/qdeclarativelistmodel_p.h new file mode 100644 index 00000000..716837f0 --- /dev/null +++ b/src/declarative/util/qdeclarativelistmodel_p.h @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTMODEL_H +#define QDECLARATIVELISTMODEL_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class FlatListModel; +class NestedListModel; +class QDeclarativeListModelWorkerAgent; +struct ModelNode; +class FlatListScriptClass; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeListModel : public QListModelInterface +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + +public: + QDeclarativeListModel(QObject *parent=0); + ~QDeclarativeListModel(); + + virtual QList roles() const; + virtual QString toString(int role) const; + virtual int count() 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_INVOKABLE void sync(); + + QDeclarativeListModelWorkerAgent *agent(); + +Q_SIGNALS: + void countChanged(); + +private: + friend class QDeclarativeListModelParser; + friend class QDeclarativeListModelWorkerAgent; + friend class FlatListModel; + friend class FlatListScriptClass; + friend struct ModelNode; + + // Constructs a flat list model for a worker agent + QDeclarativeListModel(const QDeclarativeListModel *orig, QDeclarativeListModelWorkerAgent *parent); + + void set(int index, const QScriptValue&, QList *roles); + void setProperty(int index, const QString& property, const QVariant& value, QList *roles); + + bool flatten(); + bool inWorkerThread() const; + + inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); } + + QDeclarativeListModelWorkerAgent *m_agent; + NestedListModel *m_nested; + FlatListModel *m_flat; +}; + +// ### FIXME +class QDeclarativeListElement : public QObject +{ +Q_OBJECT +}; + +class QDeclarativeListModelParser : public QDeclarativeCustomParser +{ +public: + QByteArray compile(const QList &); + void setCustomData(QObject *, const QByteArray &); + +private: + struct ListInstruction + { + enum { Push, Pop, Value, Set } type; + int dataIdx; + }; + struct ListModelData + { + int dataOffset; + int instrCount; + ListInstruction *instructions() const; + }; + bool compileProperty(const QDeclarativeCustomParserProperty &prop, QList &instr, QByteArray &data); + + bool definesEmptyList(const QString &); + + QByteArray listElementTypeName; +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeListModel) +QML_DECLARE_TYPE(QDeclarativeListElement) + +QT_END_HEADER + +#endif // QDECLARATIVELISTMODEL_H diff --git a/src/declarative/util/qdeclarativelistmodel_p_p.h b/src/declarative/util/qdeclarativelistmodel_p_p.h new file mode 100644 index 00000000..c5a897c3 --- /dev/null +++ b/src/declarative/util/qdeclarativelistmodel_p_p.h @@ -0,0 +1,281 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTMODEL_P_P_H +#define QDECLARATIVELISTMODEL_P_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 "private/qdeclarativelistmodel_p.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativeopenmetaobject_p.h" +#include "qdeclarative.h" + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeOpenMetaObject; +class QScriptEngine; +class QDeclarativeListModelWorkerAgent; +struct ModelNode; +class FlatListScriptClass; +class FlatNodeData; + +class FlatListModel +{ +public: + FlatListModel(QDeclarativeListModel *base); + ~FlatListModel(); + + QVariant data(int index, int role) const; + + QList roles() const; + QString toString(int role) const; + + int count() const; + void clear(); + void remove(int index); + bool insert(int index, const QScriptValue&); + QScriptValue get(int index) const; + void set(int index, const QScriptValue&, QList *roles); + void setProperty(int index, const QString& property, const QVariant& value, QList *roles); + void move(int from, int to, int count); + +private: + friend class QDeclarativeListModelWorkerAgent; + friend class QDeclarativeListModel; + friend class FlatListScriptClass; + friend class FlatNodeData; + + bool addValue(const QScriptValue &value, QHash *row, QList *roles); + void insertedNode(int index); + void removedNode(int index); + void moveNodes(int from, int to, int n); + + QScriptEngine *m_scriptEngine; + QHash m_roles; + QHash m_strings; + QList > m_values; + QDeclarativeListModel *m_listModel; + + FlatListScriptClass *m_scriptClass; + QList m_nodeData; + QDeclarativeListModelWorkerAgent *m_parentAgent; +}; + + +/* + Created when get() is called on a FlatListModel. This allows changes to the + object returned by get() to be tracked, and passed onto the model. +*/ +class FlatListScriptClass : public QScriptDeclarativeClass +{ +public: + FlatListScriptClass(FlatListModel *model, QScriptEngine *seng); + + Value property(Object *, const Identifier &); + void setProperty(Object *, const Identifier &name, const QScriptValue &); + QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, QScriptClass::QueryFlags flags); + bool compare(Object *, Object *); + +private: + FlatListModel *m_model; +}; + +/* + FlatNodeData and FlatNodeObjectData allow objects returned by get() to still + point to the correct list index if move(), insert() or remove() are called. +*/ +struct FlatNodeObjectData; +class FlatNodeData +{ +public: + FlatNodeData(int i) + : index(i) {} + + ~FlatNodeData(); + + void addData(FlatNodeObjectData *data); + void removeData(FlatNodeObjectData *data); + + int index; + +private: + QSet objects; +}; + +struct FlatNodeObjectData : public QScriptDeclarativeClass::Object +{ + FlatNodeObjectData(FlatNodeData *data) : nodeData(data) { + nodeData->addData(this); + } + + ~FlatNodeObjectData() { + if (nodeData) + nodeData->removeData(this); + } + + FlatNodeData *nodeData; +}; + + + +class NestedListModel +{ +public: + NestedListModel(QDeclarativeListModel *base); + ~NestedListModel(); + + QHash data(int index, const QList &roles, bool *hasNested = 0) const; + QVariant data(int index, int role) const; + + QList roles() const; + QString toString(int role) const; + + int count() const; + void clear(); + void remove(int index); + bool insert(int index, const QScriptValue&); + QScriptValue get(int index) const; + void set(int index, const QScriptValue&, QList *roles); + void setProperty(int index, const QString& property, const QVariant& value, QList *roles); + void move(int from, int to, int count); + + QVariant valueForNode(ModelNode *, bool *hasNested = 0) const; + void checkRoles() const; + + ModelNode *_root; + bool m_ownsRoot; + QDeclarativeListModel *m_listModel; + +private: + friend struct ModelNode; + mutable QStringList roleStrings; + mutable bool _rolesOk; +}; + + +class ModelNodeMetaObject; +class ModelObject : public QObject +{ + Q_OBJECT +public: + ModelObject(ModelNode *node, NestedListModel *model, QScriptEngine *seng); + void setValue(const QByteArray &name, const QVariant &val); + void setNodeUpdatesEnabled(bool enable); + + NestedListModel *m_model; + ModelNode *m_node; + +private: + ModelNodeMetaObject *m_meta; +}; + +class ModelNodeMetaObject : public QDeclarativeOpenMetaObject +{ +public: + ModelNodeMetaObject(QScriptEngine *seng, ModelObject *object); + + bool m_enabled; + +protected: + void propertyWritten(int index); + +private: + QScriptEngine *m_seng; + ModelObject *m_obj; +}; + + +/* + A ModelNode is created for each item in a NestedListModel. +*/ +struct ModelNode +{ + ModelNode(NestedListModel *model); + ~ModelNode(); + + QList values; + QHash properties; + + void clear(); + + QDeclarativeListModel *model(const NestedListModel *model); + ModelObject *object(const NestedListModel *model); + + bool setObjectValue(const QScriptValue& valuemap, bool writeToCache = true); + void setListValue(const QScriptValue& valuelist); + bool setProperty(const QString& prop, const QVariant& val); + void changedProperty(const QString &name) const; + void updateListIndexes(); + static void dump(ModelNode *node, int ind); + + QDeclarativeListModel *modelCache; + ModelObject *objectCache; + bool isArray; + + NestedListModel *m_model; + int listIndex; // only used for top-level nodes within a list +}; + + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(ModelNode *) + +QT_END_HEADER + +#endif // QDECLARATIVELISTMODEL_P_P_H + diff --git a/src/declarative/util/qdeclarativelistmodelworkeragent.cpp b/src/declarative/util/qdeclarativelistmodelworkeragent.cpp new file mode 100644 index 00000000..5422eb64 --- /dev/null +++ b/src/declarative/util/qdeclarativelistmodelworkeragent.cpp @@ -0,0 +1,278 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativelistmodelworkeragent_p.h" +#include "private/qdeclarativelistmodel_p_p.h" +#include "private/qdeclarativedata_p.h" +#include "private/qdeclarativeengine_p.h" +#include "qdeclarativeinfo.h" + +#include +#include +#include + + +QT_BEGIN_NAMESPACE + + +void QDeclarativeListModelWorkerAgent::Data::clearChange() +{ + changes.clear(); +} + +void QDeclarativeListModelWorkerAgent::Data::insertChange(int index, int count) +{ + Change c = { Change::Inserted, index, count, 0, QList() }; + changes << c; +} + +void QDeclarativeListModelWorkerAgent::Data::removeChange(int index, int count) +{ + Change c = { Change::Removed, index, count, 0, QList() }; + changes << c; +} + +void QDeclarativeListModelWorkerAgent::Data::moveChange(int index, int count, int to) +{ + Change c = { Change::Moved, index, count, to, QList() }; + changes << c; +} + +void QDeclarativeListModelWorkerAgent::Data::changedChange(int index, int count, const QList &roles) +{ + Change c = { Change::Changed, index, count, 0, roles }; + changes << c; +} + +QDeclarativeListModelWorkerAgent::QDeclarativeListModelWorkerAgent(QDeclarativeListModel *model) + : m_engine(0), + m_ref(1), + m_orig(model), + m_copy(new QDeclarativeListModel(model, this)) +{ +} + +QDeclarativeListModelWorkerAgent::~QDeclarativeListModelWorkerAgent() +{ +} + +void QDeclarativeListModelWorkerAgent::setScriptEngine(QScriptEngine *eng) +{ + m_engine = eng; + if (m_copy->m_flat) + m_copy->m_flat->m_scriptEngine = eng; +} + +QScriptEngine *QDeclarativeListModelWorkerAgent::scriptEngine() const +{ + return m_engine; +} + +void QDeclarativeListModelWorkerAgent::addref() +{ + m_ref.ref(); +} + +void QDeclarativeListModelWorkerAgent::release() +{ + bool del = !m_ref.deref(); + + if (del) + delete this; +} + +int QDeclarativeListModelWorkerAgent::count() const +{ + return m_copy->count(); +} + +void QDeclarativeListModelWorkerAgent::clear() +{ + data.clearChange(); + data.removeChange(0, m_copy->count()); + m_copy->clear(); +} + +void QDeclarativeListModelWorkerAgent::remove(int index) +{ + int count = m_copy->count(); + m_copy->remove(index); + + if (m_copy->count() != count) + data.removeChange(index, 1); +} + +void QDeclarativeListModelWorkerAgent::append(const QScriptValue &value) +{ + int count = m_copy->count(); + m_copy->append(value); + + if (m_copy->count() != count) + data.insertChange(m_copy->count() - 1, 1); +} + +void QDeclarativeListModelWorkerAgent::insert(int index, const QScriptValue &value) +{ + int count = m_copy->count(); + m_copy->insert(index, value); + + if (m_copy->count() != count) + data.insertChange(index, 1); +} + +QScriptValue QDeclarativeListModelWorkerAgent::get(int index) const +{ + return m_copy->get(index); +} + +void QDeclarativeListModelWorkerAgent::set(int index, const QScriptValue &value) +{ + QList roles; + m_copy->set(index, value, &roles); + if (!roles.isEmpty()) + data.changedChange(index, 1, roles); +} + +void QDeclarativeListModelWorkerAgent::setProperty(int index, const QString& property, const QVariant& value) +{ + QList roles; + m_copy->setProperty(index, property, value, &roles); + if (!roles.isEmpty()) + data.changedChange(index, 1, roles); +} + +void QDeclarativeListModelWorkerAgent::move(int from, int to, int count) +{ + m_copy->move(from, to, count); + data.moveChange(from, to, count); +} + +void QDeclarativeListModelWorkerAgent::sync() +{ + Sync *s = new Sync; + s->data = data; + s->list = m_copy; + data.changes.clear(); + + mutex.lock(); + QCoreApplication::postEvent(this, s); + syncDone.wait(&mutex); + mutex.unlock(); +} + +void QDeclarativeListModelWorkerAgent::changedData(int index, int count, const QList &roles) +{ + data.changedChange(index, count, roles); +} + +bool QDeclarativeListModelWorkerAgent::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + QMutexLocker locker(&mutex); + Sync *s = static_cast(e); + + const QList &changes = s->data.changes; + + if (m_copy) { + bool cc = m_orig->count() != s->list->count(); + + FlatListModel *orig = m_orig->m_flat; + FlatListModel *copy = s->list->m_flat; + if (!orig || !copy) { + syncDone.wakeAll(); + return QObject::event(e); + } + + orig->m_roles = copy->m_roles; + orig->m_strings = copy->m_strings; + orig->m_values = copy->m_values; + + // update the orig->m_nodeData list + for (int ii = 0; ii < changes.count(); ++ii) { + const Change &change = changes.at(ii); + switch (change.type) { + case Change::Inserted: + orig->insertedNode(change.index); + break; + case Change::Removed: + orig->removedNode(change.index); + break; + case Change::Moved: + orig->moveNodes(change.index, change.to, change.count); + break; + case Change::Changed: + break; + } + } + + syncDone.wakeAll(); + locker.unlock(); + + for (int ii = 0; ii < changes.count(); ++ii) { + const Change &change = changes.at(ii); + switch (change.type) { + case Change::Inserted: + emit m_orig->itemsInserted(change.index, change.count); + break; + case Change::Removed: + emit m_orig->itemsRemoved(change.index, change.count); + break; + case Change::Moved: + emit m_orig->itemsMoved(change.index, change.to, change.count); + break; + case Change::Changed: + emit m_orig->itemsChanged(change.index, change.count, change.roles); + break; + } + } + + if (cc) + emit m_orig->countChanged(); + } else { + syncDone.wakeAll(); + } + } + + return QObject::event(e); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/util/qdeclarativelistmodelworkeragent_p.h b/src/declarative/util/qdeclarativelistmodelworkeragent_p.h new file mode 100644 index 00000000..3c1181d8 --- /dev/null +++ b/src/declarative/util/qdeclarativelistmodelworkeragent_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTMODELWORKERAGENT_P_H +#define QDECLARATIVELISTMODELWORKERAGENT_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 "qdeclarative.h" + +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeListModel; +class FlatListScriptClass; + +class QDeclarativeListModelWorkerAgent : public QObject +{ + Q_OBJECT + Q_PROPERTY(int count READ count) + +public: + QDeclarativeListModelWorkerAgent(QDeclarativeListModel *); + ~QDeclarativeListModelWorkerAgent(); + + void setScriptEngine(QScriptEngine *eng); + QScriptEngine *scriptEngine() const; + + void addref(); + void release(); + + int count() 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_INVOKABLE void sync(); + + struct VariantRef + { + VariantRef() : a(0) {} + VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); } + VariantRef(QDeclarativeListModelWorkerAgent *_a) : a(_a) { if (a) a->addref(); } + ~VariantRef() { if (a) a->release(); } + + VariantRef &operator=(const VariantRef &o) { + if (o.a) o.a->addref(); + if (a) a->release(); a = o.a; + return *this; + } + + QDeclarativeListModelWorkerAgent *a; + }; +protected: + virtual bool event(QEvent *); + +private: + friend class QDeclarativeWorkerScriptEnginePrivate; + friend class FlatListScriptClass; + QScriptEngine *m_engine; + + struct Change { + enum { Inserted, Removed, Moved, Changed } type; + int index; // Inserted/Removed/Moved/Changed + int count; // Inserted/Removed/Moved/Changed + int to; // Moved + QList roles; + }; + + struct Data { + QList changes; + + void clearChange(); + void insertChange(int index, int count); + void removeChange(int index, int count); + void moveChange(int index, int count, int to); + void changedChange(int index, int count, const QList &roles); + }; + Data data; + + struct Sync : public QEvent { + Sync() : QEvent(QEvent::User) {} + Data data; + QDeclarativeListModel *list; + }; + + void changedData(int index, int count, const QList &roles); + + QAtomicInt m_ref; + QDeclarativeListModel *m_orig; + QDeclarativeListModel *m_copy; + QMutex mutex; + QWaitCondition syncDone; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDeclarativeListModelWorkerAgent::VariantRef) + +QT_END_HEADER + +#endif // QDECLARATIVEWORKERSCRIPT_P_H + diff --git a/src/declarative/util/qdeclarativenullablevalue_p_p.h b/src/declarative/util/qdeclarativenullablevalue_p_p.h new file mode 100644 index 00000000..5d04f53b --- /dev/null +++ b/src/declarative/util/qdeclarativenullablevalue_p_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVENULLABLEVALUE_P_H +#define QDECLARATIVENULLABLEVALUE_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 +struct QDeclarativeNullableValue +{ + QDeclarativeNullableValue() + : isNull(true), value(T()) {} + QDeclarativeNullableValue(const QDeclarativeNullableValue &o) + : isNull(o.isNull), value(o.value) {} + QDeclarativeNullableValue(const T &t) + : isNull(false), value(t) {} + QDeclarativeNullableValue &operator=(const T &t) + { isNull = false; value = t; return *this; } + QDeclarativeNullableValue &operator=(const QDeclarativeNullableValue &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 // QDECLARATIVENULLABLEVALUE_P_H diff --git a/src/declarative/util/qdeclarativeopenmetaobject.cpp b/src/declarative/util/qdeclarativeopenmetaobject.cpp new file mode 100644 index 00000000..6290bf93 --- /dev/null +++ b/src/declarative/util/qdeclarativeopenmetaobject.cpp @@ -0,0 +1,380 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeopenmetaobject_p.h" +#include "private/qdeclarativepropertycache_p.h" +#include "private/qdeclarativedata_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + + +class QDeclarativeOpenMetaObjectTypePrivate +{ +public: + QDeclarativeOpenMetaObjectTypePrivate() : mem(0), cache(0), engine(0) {} + + void init(const QMetaObject *metaObj); + + int propertyOffset; + int signalOffset; + QHash names; + QMetaObjectBuilder mob; + QMetaObject *mem; + QDeclarativePropertyCache *cache; + QDeclarativeEngine *engine; + QSet referers; +}; + +QDeclarativeOpenMetaObjectType::QDeclarativeOpenMetaObjectType(const QMetaObject *base, QDeclarativeEngine *engine) + : d(new QDeclarativeOpenMetaObjectTypePrivate) +{ + d->engine = engine; + d->init(base); +} + +QDeclarativeOpenMetaObjectType::~QDeclarativeOpenMetaObjectType() +{ + if (d->mem) + qFree(d->mem); + if (d->cache) + d->cache->release(); + delete d; +} + + +int QDeclarativeOpenMetaObjectType::propertyOffset() const +{ + return d->propertyOffset; +} + +int QDeclarativeOpenMetaObjectType::signalOffset() const +{ + return d->signalOffset; +} + +int QDeclarativeOpenMetaObjectType::createProperty(const QByteArray &name) +{ + int id = d->mob.propertyCount(); + d->mob.addSignal("__" + QByteArray::number(id) + "()"); + QMetaPropertyBuilder build = d->mob.addProperty(name, "QVariant", id); + propertyCreated(id, build); + qFree(d->mem); + d->mem = d->mob.toMetaObject(); + d->names.insert(name, id); + QSet::iterator it = d->referers.begin(); + while (it != d->referers.end()) { + QDeclarativeOpenMetaObject *omo = *it; + *static_cast(omo) = *d->mem; + if (d->cache) + d->cache->update(d->engine, omo); + ++it; + } + + return d->propertyOffset + id; +} + +void QDeclarativeOpenMetaObjectType::propertyCreated(int id, QMetaPropertyBuilder &builder) +{ + if (d->referers.count()) + (*d->referers.begin())->propertyCreated(id, builder); +} + +void QDeclarativeOpenMetaObjectTypePrivate::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 QDeclarativeOpenMetaObjectPrivate +{ +public: + QDeclarativeOpenMetaObjectPrivate(QDeclarativeOpenMetaObject *_q) + : q(_q), parent(0), type(0), cacheProperties(false) {} + + inline QVariant &getData(int idx) { + while (data.count() <= idx) + data << QPair(QVariant(), false); + QPair &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(), false); + QPair &prop = data[idx]; + prop.first = value; + prop.second = true; + } + + inline bool hasData(int idx) const { + if (idx >= data.count()) + return false; + return data[idx].second; + } + + bool autoCreate; + QDeclarativeOpenMetaObject *q; + QAbstractDynamicMetaObject *parent; + QList > data; + QObject *object; + QDeclarativeOpenMetaObjectType *type; + bool cacheProperties; +}; + +QDeclarativeOpenMetaObject::QDeclarativeOpenMetaObject(QObject *obj, bool automatic) +: d(new QDeclarativeOpenMetaObjectPrivate(this)) +{ + d->autoCreate = automatic; + d->object = obj; + + d->type = new QDeclarativeOpenMetaObjectType(obj->metaObject(), 0); + d->type->d->referers.insert(this); + + QObjectPrivate *op = QObjectPrivate::get(obj); + d->parent = static_cast(op->metaObject); + *static_cast(this) = *d->type->d->mem; + op->metaObject = this; +} + +QDeclarativeOpenMetaObject::QDeclarativeOpenMetaObject(QObject *obj, QDeclarativeOpenMetaObjectType *type, bool automatic) +: d(new QDeclarativeOpenMetaObjectPrivate(this)) +{ + d->autoCreate = automatic; + d->object = obj; + + d->type = type; + d->type->addref(); + d->type->d->referers.insert(this); + + QObjectPrivate *op = QObjectPrivate::get(obj); + d->parent = static_cast(op->metaObject); + *static_cast(this) = *d->type->d->mem; + op->metaObject = this; +} + +QDeclarativeOpenMetaObject::~QDeclarativeOpenMetaObject() +{ + if (d->parent) + delete d->parent; + d->type->d->referers.remove(this); + d->type->release(); + delete d; +} + +QDeclarativeOpenMetaObjectType *QDeclarativeOpenMetaObject::type() const +{ + return d->type; +} + +int QDeclarativeOpenMetaObject::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(a[0]) = d->getData(propId); + } else if (c == QMetaObject::WriteProperty) { + if (propId <= d->data.count() || d->data[propId].first != *reinterpret_cast(a[0])) { + propertyWrite(propId); + d->writeData(propId, *reinterpret_cast(a[0])); + propertyWritten(propId); + 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 *QDeclarativeOpenMetaObject::parent() const +{ + return d->parent; +} + +QVariant QDeclarativeOpenMetaObject::value(int id) const +{ + return d->getData(id); +} + +void QDeclarativeOpenMetaObject::setValue(int id, const QVariant &value) +{ + d->writeData(id, value); + activate(d->object, id + d->type->d->signalOffset, 0); +} + +QVariant QDeclarativeOpenMetaObject::value(const QByteArray &name) const +{ + QHash::ConstIterator iter = d->type->d->names.find(name); + if (iter == d->type->d->names.end()) + return QVariant(); + + return d->getData(*iter); +} + +QVariant &QDeclarativeOpenMetaObject::operator[](const QByteArray &name) +{ + QHash::ConstIterator iter = d->type->d->names.find(name); + Q_ASSERT(iter != d->type->d->names.end()); + + return d->getData(*iter); +} + +QVariant &QDeclarativeOpenMetaObject::operator[](int id) +{ + return d->getData(id); +} + +void QDeclarativeOpenMetaObject::setValue(const QByteArray &name, const QVariant &val) +{ + QHash::ConstIterator iter = d->type->d->names.find(name); + + int id = -1; + if (iter == d->type->d->names.end()) { + id = createProperty(name.constData(), "") - d->type->d->propertyOffset; + } else { + id = *iter; + } + + if (id >= 0) { + QVariant &dataVal = d->getData(id); + if (dataVal == val) + return; + + dataVal = val; + activate(d->object, id + d->type->d->signalOffset, 0); + } +} + +// returns true if this value has been initialized by a call to either value() or setValue() +bool QDeclarativeOpenMetaObject::hasValue(int id) const +{ + return d->hasData(id); +} + +void QDeclarativeOpenMetaObject::setCached(bool c) +{ + if (c == d->cacheProperties || !d->type->d->engine) + return; + + d->cacheProperties = c; + + QDeclarativeData *qmldata = QDeclarativeData::get(d->object, true); + if (d->cacheProperties) { + if (!d->type->d->cache) + d->type->d->cache = new QDeclarativePropertyCache(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 QDeclarativeOpenMetaObject::createProperty(const char *name, const char *) +{ + if (d->autoCreate) + return d->type->createProperty(name); + else + return -1; +} + +void QDeclarativeOpenMetaObject::propertyRead(int) +{ +} + +void QDeclarativeOpenMetaObject::propertyWrite(int) +{ +} + +void QDeclarativeOpenMetaObject::propertyWritten(int) +{ +} + +void QDeclarativeOpenMetaObject::propertyCreated(int, QMetaPropertyBuilder &) +{ +} + +QVariant QDeclarativeOpenMetaObject::initialValue(int) +{ + return QVariant(); +} + +int QDeclarativeOpenMetaObject::count() const +{ + return d->type->d->names.count(); +} + +QByteArray QDeclarativeOpenMetaObject::name(int idx) const +{ + Q_ASSERT(idx >= 0 && idx < d->type->d->names.count()); + + return d->type->d->mob.property(idx).name(); +} + +QObject *QDeclarativeOpenMetaObject::object() const +{ + return d->object; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativeopenmetaobject_p.h b/src/declarative/util/qdeclarativeopenmetaobject_p.h new file mode 100644 index 00000000..74f27bbb --- /dev/null +++ b/src/declarative/util/qdeclarativeopenmetaobject_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEOPENMETAOBJECT_H +#define QDECLARATIVEOPENMETAOBJECT_H + +#include +#include + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeEngine; +class QMetaPropertyBuilder; +class QDeclarativeOpenMetaObjectTypePrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeOpenMetaObjectType : public QDeclarativeRefCount +{ +public: + QDeclarativeOpenMetaObjectType(const QMetaObject *base, QDeclarativeEngine *engine); + ~QDeclarativeOpenMetaObjectType(); + + int createProperty(const QByteArray &name); + + int propertyOffset() const; + int signalOffset() const; + +protected: + virtual void propertyCreated(int, QMetaPropertyBuilder &); + +private: + QDeclarativeOpenMetaObjectTypePrivate *d; + friend class QDeclarativeOpenMetaObject; + friend class QDeclarativeOpenMetaObjectPrivate; +}; + +class QDeclarativeOpenMetaObjectPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeOpenMetaObject : public QAbstractDynamicMetaObject +{ +public: + QDeclarativeOpenMetaObject(QObject *, bool = true); + QDeclarativeOpenMetaObject(QObject *, QDeclarativeOpenMetaObjectType *, bool = true); + ~QDeclarativeOpenMetaObject(); + + QVariant value(const QByteArray &) const; + void setValue(const QByteArray &, const QVariant &); + QVariant value(int) const; + void setValue(int, const QVariant &); + QVariant &operator[](const QByteArray &); + QVariant &operator[](int); + bool hasValue(int) const; + + 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); + + QDeclarativeOpenMetaObjectType *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 propertyWritten(int); + virtual void propertyCreated(int, QMetaPropertyBuilder &); + + QAbstractDynamicMetaObject *parent() const; + +private: + QDeclarativeOpenMetaObjectPrivate *d; + friend class QDeclarativeOpenMetaObjectType; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEOPENMETAOBJECT_H diff --git a/src/declarative/util/qdeclarativepackage.cpp b/src/declarative/util/qdeclarativepackage.cpp new file mode 100644 index 00000000..f138bef9 --- /dev/null +++ b/src/declarative/util/qdeclarativepackage.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativepackage_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Package QDeclarativePackage + \ingroup qml-working-with-data + \brief Package provides a collection of named items. + + The Package class is used in conjunction with + VisualDataModel to enable delegates with a shared context + to be provided to multiple views. + + Any item within a Package may be assigned a name via the + \l{Package::name}{Package.name} attached property. + + The example below creates a Package containing two named items; + \e list and \e grid. The third element in the package (the \l Rectangle) is parented to whichever + delegate it should appear in. This allows an item to move + between views. + + \snippet examples/declarative/modelviews/package/Delegate.qml 0 + + These named items are used as the delegates by the two views who + reference the special \l{VisualDataModel::parts} property to select + a model which provides the chosen delegate. + + \snippet examples/declarative/modelviews/package/view.qml 0 + + \sa {declarative/modelviews/package}{Package example}, {demos/declarative/photoviewer}{Photo Viewer demo}, QtDeclarative +*/ + +/*! + \qmlattachedproperty string Package::name + This attached property holds the name of an item within a Package. +*/ + + +class QDeclarativePackagePrivate : public QObjectPrivate +{ +public: + QDeclarativePackagePrivate() {} + + struct DataGuard : public QDeclarativeGuard + { + DataGuard(QObject *obj, QList *l) : list(l) { (QDeclarativeGuard&)*this = obj; } + QList *list; + void objectDestroyed(QObject *) { + // we assume priv will always be destroyed after objectDestroyed calls + list->removeOne(*this); + } + }; + + QList dataList; + static void data_append(QDeclarativeListProperty *prop, QObject *o) { + QList *list = static_cast *>(prop->data); + list->append(DataGuard(o, list)); + } + static void data_clear(QDeclarativeListProperty *prop) { + QList *list = static_cast *>(prop->data); + list->clear(); + } + static QObject *data_at(QDeclarativeListProperty *prop, int index) { + QList *list = static_cast *>(prop->data); + return list->at(index); + } + static int data_count(QDeclarativeListProperty *prop) { + QList *list = static_cast *>(prop->data); + return list->count(); + } +}; + +QHash QDeclarativePackageAttached::attached; + +QDeclarativePackageAttached::QDeclarativePackageAttached(QObject *parent) +: QObject(parent) +{ + attached.insert(parent, this); +} + +QDeclarativePackageAttached::~QDeclarativePackageAttached() +{ + attached.remove(parent()); +} + +QString QDeclarativePackageAttached::name() const +{ + return _name; +} + +void QDeclarativePackageAttached::setName(const QString &n) +{ + _name = n; +} + +QDeclarativePackage::QDeclarativePackage(QObject *parent) + : QObject(*(new QDeclarativePackagePrivate), parent) +{ +} + +QDeclarativePackage::~QDeclarativePackage() +{ + Q_D(QDeclarativePackage); + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + obj->setParent(this); + } +} + +QDeclarativeListProperty QDeclarativePackage::data() +{ + Q_D(QDeclarativePackage); + return QDeclarativeListProperty(this, &d->dataList, QDeclarativePackagePrivate::data_append, + QDeclarativePackagePrivate::data_count, + QDeclarativePackagePrivate::data_at, + QDeclarativePackagePrivate::data_clear); +} + +bool QDeclarativePackage::hasPart(const QString &name) +{ + Q_D(QDeclarativePackage); + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QDeclarativePackageAttached *a = QDeclarativePackageAttached::attached.value(obj); + if (a && a->name() == name) + return true; + } + return false; +} + +QObject *QDeclarativePackage::part(const QString &name) +{ + Q_D(QDeclarativePackage); + 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); + QDeclarativePackageAttached *a = QDeclarativePackageAttached::attached.value(obj); + if (a && a->name() == name) + return obj; + } + + if (name == QLatin1String("default") && !d->dataList.isEmpty()) + return d->dataList.at(0); + + return 0; +} + +QDeclarativePackageAttached *QDeclarativePackage::qmlAttachedProperties(QObject *o) +{ + return new QDeclarativePackageAttached(o); +} + + + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativepackage_p.h b/src/declarative/util/qdeclarativepackage_p.h new file mode 100644 index 00000000..27efbe2c --- /dev/null +++ b/src/declarative/util/qdeclarativepackage_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPACKAGE_H +#define QDECLARATIVEPACKAGE_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativePackagePrivate; +class QDeclarativePackageAttached; +class Q_AUTOTEST_EXPORT QDeclarativePackage : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePackage) + + Q_CLASSINFO("DefaultProperty", "data") + Q_PROPERTY(QDeclarativeListProperty data READ data SCRIPTABLE false) + +public: + QDeclarativePackage(QObject *parent=0); + virtual ~QDeclarativePackage(); + + QDeclarativeListProperty data(); + + QObject *part(const QString & = QString()); + bool hasPart(const QString &); + + static QDeclarativePackageAttached *qmlAttachedProperties(QObject *); +}; + +class QDeclarativePackageAttached : public QObject +{ +Q_OBJECT +Q_PROPERTY(QString name READ name WRITE setName) +public: + QDeclarativePackageAttached(QObject *parent); + virtual ~QDeclarativePackageAttached(); + + QString name() const; + void setName(const QString &n); + + static QHash attached; +private: + QString _name; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePackage) +QML_DECLARE_TYPEINFO(QDeclarativePackage, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER + +#endif // QDECLARATIVEPACKAGE_H diff --git a/src/declarative/util/qdeclarativepixmapcache.cpp b/src/declarative/util/qdeclarativepixmapcache.cpp new file mode 100644 index 00000000..03f878f9 --- /dev/null +++ b/src/declarative/util/qdeclarativepixmapcache.cpp @@ -0,0 +1,1119 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativepixmapcache_p.h" +#include "qdeclarativenetworkaccessmanagerfactory.h" +#include "qdeclarativeimageprovider.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IMAGEREQUEST_MAX_REQUEST_COUNT 8 +#define IMAGEREQUEST_MAX_REDIRECT_RECURSION 16 +#define CACHE_EXPIRE_TIME 30 +#define CACHE_REMOVAL_FRACTION 4 + +QT_BEGIN_NAMESPACE + +// The cache limit describes the maximum "junk" in the cache. +// These are the same defaults as QPixmapCache +#if defined(Q_WS_QWS) || defined(Q_WS_WINCE) +static int cache_limit = 2048 * 1024; // 2048 KB cache limit for embedded +#else +static int cache_limit = 10240 * 1024; // 10 MB cache limit for desktop +#endif + +class QDeclarativePixmapReader; +class QDeclarativePixmapData; +class QDeclarativePixmapReply : public QObject +{ + Q_OBJECT +public: + enum ReadError { NoError, Loading, Decoding }; + + QDeclarativePixmapReply(QDeclarativePixmapData *); + ~QDeclarativePixmapReply(); + + QDeclarativePixmapData *data; + QDeclarativeEngine *engineForReader; // always access reader inside readerMutex. + QSize requestSize; + QUrl url; + + bool loading; + int redirectCount; + + class Event : public QEvent { + public: + Event(ReadError, const QString &, const QSize &, const QImage &); + + ReadError error; + QString errorString; + QSize implicitSize; + QImage image; + }; + void postReply(ReadError, const QString &, const QSize &, const QImage &); + + +Q_SIGNALS: + void finished(); + void downloadProgress(qint64, qint64); + +protected: + bool event(QEvent *event); + +private: + Q_DISABLE_COPY(QDeclarativePixmapReply) + +public: + static int finishedIndex; + static int downloadProgressIndex; +}; + +class QDeclarativePixmapReaderThreadObject : public QObject { + Q_OBJECT +public: + QDeclarativePixmapReaderThreadObject(QDeclarativePixmapReader *); + void processJobs(); + virtual bool event(QEvent *e); +private slots: + void networkRequestDone(); +private: + QDeclarativePixmapReader *reader; +}; + +class QDeclarativePixmapData; +class QDeclarativePixmapReader : public QThread +{ + Q_OBJECT +public: + QDeclarativePixmapReader(QDeclarativeEngine *eng); + ~QDeclarativePixmapReader(); + + QDeclarativePixmapReply *getImage(QDeclarativePixmapData *); + void cancel(QDeclarativePixmapReply *rep); + + static QDeclarativePixmapReader *instance(QDeclarativeEngine *engine); + static QDeclarativePixmapReader *existingInstance(QDeclarativeEngine *engine); + +protected: + void run(); + +private: + friend class QDeclarativePixmapReaderThreadObject; + void processJobs(); + void processJob(QDeclarativePixmapReply *, const QUrl &, const QSize &); + void networkRequestDone(QNetworkReply *); + + QList jobs; + QList cancelled; + QDeclarativeEngine *engine; + QObject *eventLoopQuitHack; + + QMutex mutex; + QDeclarativePixmapReaderThreadObject *threadObject; + QWaitCondition waitCondition; + + QNetworkAccessManager *networkAccessManager(); + QNetworkAccessManager *accessManager; + + QHash replies; + + static int replyDownloadProgress; + static int replyFinished; + static int downloadProgress; + static int threadNetworkRequestDone; + static QHash readers; +public: + static QMutex readerMutex; +}; + +class QDeclarativePixmapData +{ +public: + QDeclarativePixmapData(const QUrl &u, const QSize &s, const QString &e) + : refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Error), + url(u), errorString(e), requestSize(s), reply(0), prevUnreferenced(0), + prevUnreferencedPtr(0), nextUnreferenced(0) + { + } + + QDeclarativePixmapData(const QUrl &u, const QSize &r) + : refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Loading), + url(u), requestSize(r), reply(0), prevUnreferenced(0), prevUnreferencedPtr(0), + nextUnreferenced(0) + { + } + + QDeclarativePixmapData(const QUrl &u, const QPixmap &p, const QSize &s, const QSize &r) + : refCount(1), inCache(false), privatePixmap(false), pixmapStatus(QDeclarativePixmap::Ready), + url(u), pixmap(p), implicitSize(s), requestSize(r), reply(0), prevUnreferenced(0), + prevUnreferencedPtr(0), nextUnreferenced(0) + { + } + + QDeclarativePixmapData(const QPixmap &p) + : refCount(1), inCache(false), privatePixmap(true), pixmapStatus(QDeclarativePixmap::Ready), + pixmap(p), implicitSize(p.size()), requestSize(p.size()), reply(0), prevUnreferenced(0), + prevUnreferencedPtr(0), nextUnreferenced(0) + { + } + + int cost() const; + void addref(); + void release(); + void addToCache(); + void removeFromCache(); + + uint refCount; + + bool inCache:1; + bool privatePixmap:1; + + QDeclarativePixmap::Status pixmapStatus; + QUrl url; + QString errorString; + QPixmap pixmap; + QSize implicitSize; + QSize requestSize; + + QDeclarativePixmapReply *reply; + + QDeclarativePixmapData *prevUnreferenced; + QDeclarativePixmapData**prevUnreferencedPtr; + QDeclarativePixmapData *nextUnreferenced; +}; + +int QDeclarativePixmapReply::finishedIndex = -1; +int QDeclarativePixmapReply::downloadProgressIndex = -1; + +// XXX +QHash QDeclarativePixmapReader::readers; +QMutex QDeclarativePixmapReader::readerMutex; + +int QDeclarativePixmapReader::replyDownloadProgress = -1; +int QDeclarativePixmapReader::replyFinished = -1; +int QDeclarativePixmapReader::downloadProgress = -1; +int QDeclarativePixmapReader::threadNetworkRequestDone = -1; + + +void QDeclarativePixmapReply::postReply(ReadError error, const QString &errorString, + const QSize &implicitSize, const QImage &image) +{ + loading = false; + QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, image)); +} + +QDeclarativePixmapReply::Event::Event(ReadError e, const QString &s, const QSize &iSize, const QImage &i) +: QEvent(QEvent::User), error(e), errorString(s), implicitSize(iSize), image(i) +{ +} + +QNetworkAccessManager *QDeclarativePixmapReader::networkAccessManager() +{ + if (!accessManager) { + Q_ASSERT(threadObject); + accessManager = QDeclarativeEnginePrivate::get(engine)->createNetworkAccessManager(threadObject); + } + return accessManager; +} + +static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize, + const QSize &requestSize) +{ + QImageReader imgio(dev); + + bool force_scale = false; + if (url.path().endsWith(QLatin1String(".svg"),Qt::CaseInsensitive)) { + imgio.setFormat("svg"); // QSvgPlugin::capabilities bug QTBUG-9053 + force_scale = true; + } + + bool scaled = false; + if (requestSize.width() > 0 || requestSize.height() > 0) { + QSize s = imgio.size(); + if (requestSize.width() && (force_scale || requestSize.width() < s.width())) { + if (requestSize.height() <= 0) + s.setHeight(s.height()*requestSize.width()/s.width()); + s.setWidth(requestSize.width()); scaled = true; + } + if (requestSize.height() && (force_scale || requestSize.height() < s.height())) { + if (requestSize.width() <= 0) + s.setWidth(s.width()*requestSize.height()/s.height()); + s.setHeight(requestSize.height()); scaled = true; + } + if (scaled) { imgio.setScaledSize(s); } + } + + if (impsize) + *impsize = imgio.size(); + + if (imgio.read(image)) { + if (impsize && impsize->width() < 0) + *impsize = image->size(); + return true; + } else { + if (errorString) + *errorString = QDeclarativePixmap::tr("Error decoding: %1: %2").arg(url.toString()) + .arg(imgio.errorString()); + return false; + } +} + +QDeclarativePixmapReader::QDeclarativePixmapReader(QDeclarativeEngine *eng) +: QThread(eng), engine(eng), threadObject(0), accessManager(0) +{ + eventLoopQuitHack = new QObject; + eventLoopQuitHack->moveToThread(this); + connect(eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection); + start(QThread::IdlePriority); +} + +QDeclarativePixmapReader::~QDeclarativePixmapReader() +{ + readerMutex.lock(); + readers.remove(engine); + readerMutex.unlock(); + + mutex.lock(); + // manually cancel all outstanding jobs. + foreach (QDeclarativePixmapReply *reply, jobs) { + delete reply; + } + jobs.clear(); + QList activeJobs = replies.values(); + foreach (QDeclarativePixmapReply *reply, activeJobs) { + if (reply->loading) { + cancelled.append(reply); + reply->data = 0; + } + } + if (threadObject) threadObject->processJobs(); + mutex.unlock(); + + eventLoopQuitHack->deleteLater(); + wait(); +} + +void QDeclarativePixmapReader::networkRequestDone(QNetworkReply *reply) +{ + QDeclarativePixmapReply *job = replies.take(reply); + + if (job) { + job->redirectCount++; + if (job->redirectCount < IMAGEREQUEST_MAX_REDIRECT_RECURSION) { + QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = reply->url().resolved(redirect.toUrl()); + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + + reply->deleteLater(); + reply = networkAccessManager()->get(req); + + QMetaObject::connect(reply, replyDownloadProgress, job, downloadProgress); + QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone); + + replies.insert(reply, job); + return; + } + } + + QImage image; + QDeclarativePixmapReply::ReadError error = QDeclarativePixmapReply::NoError; + QString errorString; + QSize readSize; + if (reply->error()) { + error = QDeclarativePixmapReply::Loading; + errorString = reply->errorString(); + } else { + QByteArray all = reply->readAll(); + QBuffer buff(&all); + buff.open(QIODevice::ReadOnly); + if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize)) { + error = QDeclarativePixmapReply::Decoding; + } + } + // send completion event to the QDeclarativePixmapReply + mutex.lock(); + if (!cancelled.contains(job)) job->postReply(error, errorString, readSize, image); + mutex.unlock(); + } + reply->deleteLater(); + + // kick off event loop again incase we have dropped below max request count + threadObject->processJobs(); +} + +QDeclarativePixmapReaderThreadObject::QDeclarativePixmapReaderThreadObject(QDeclarativePixmapReader *i) +: reader(i) +{ +} + +void QDeclarativePixmapReaderThreadObject::processJobs() +{ + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); +} + +bool QDeclarativePixmapReaderThreadObject::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + reader->processJobs(); + return true; + } else { + return QObject::event(e); + } +} + +void QDeclarativePixmapReaderThreadObject::networkRequestDone() +{ + QNetworkReply *reply = static_cast(sender()); + reader->networkRequestDone(reply); +} + +void QDeclarativePixmapReader::processJobs() +{ + QMutexLocker locker(&mutex); + + while (true) { + if (cancelled.isEmpty() && (jobs.isEmpty() || replies.count() >= IMAGEREQUEST_MAX_REQUEST_COUNT)) + return; // Nothing else to do + + // Clean cancelled jobs + if (cancelled.count()) { + for (int i = 0; i < cancelled.count(); ++i) { + QDeclarativePixmapReply *job = cancelled.at(i); + QNetworkReply *reply = replies.key(job, 0); + if (reply && reply->isRunning()) { + // cancel any jobs already started + replies.remove(reply); + reply->close(); + } + // deleteLater, since not owned by this thread + job->deleteLater(); + } + cancelled.clear(); + } + + if (!jobs.isEmpty() && replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT) { + QDeclarativePixmapReply *runningJob = jobs.takeLast(); + runningJob->loading = true; + QUrl url = runningJob->url; + QSize requestSize = runningJob->requestSize; + locker.unlock(); + processJob(runningJob, url, requestSize); + locker.relock(); + } + } +} + +void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, const QUrl &url, + const QSize &requestSize) +{ + // fetch + if (url.scheme() == QLatin1String("image")) { + // Use QmlImageProvider + QSize readSize; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QImage image = ep->getImageFromProvider(url, &readSize, requestSize); + + QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError; + QString errorStr; + if (image.isNull()) { + errorCode = QDeclarativePixmapReply::Loading; + errorStr = QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString()); + } + mutex.lock(); + if (!cancelled.contains(runningJob)) runningJob->postReply(errorCode, errorStr, readSize, image); + mutex.unlock(); + } else { + QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); + if (!lf.isEmpty()) { + // Image is local - load/decode immediately + QImage image; + QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError; + QString errorStr; + QFile f(lf); + QSize readSize; + if (f.open(QIODevice::ReadOnly)) { + if (!readImage(url, &f, &image, &errorStr, &readSize, requestSize)) + errorCode = QDeclarativePixmapReply::Loading; + } else { + errorStr = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString()); + errorCode = QDeclarativePixmapReply::Loading; + } + mutex.lock(); + if (!cancelled.contains(runningJob)) runningJob->postReply(errorCode, errorStr, readSize, image); + mutex.unlock(); + } else { + // Network resource + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + QNetworkReply *reply = networkAccessManager()->get(req); + QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress); + QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone); + replies.insert(reply, runningJob); + } + } +} + +QDeclarativePixmapReader *QDeclarativePixmapReader::instance(QDeclarativeEngine *engine) +{ + // XXX NOTE: must be called within readerMutex locking. + QDeclarativePixmapReader *reader = readers.value(engine); + if (!reader) { + reader = new QDeclarativePixmapReader(engine); + readers.insert(engine, reader); + } + + return reader; +} + +QDeclarativePixmapReader *QDeclarativePixmapReader::existingInstance(QDeclarativeEngine *engine) +{ + // XXX NOTE: must be called within readerMutex locking. + return readers.value(engine, 0); +} + +QDeclarativePixmapReply *QDeclarativePixmapReader::getImage(QDeclarativePixmapData *data) +{ + mutex.lock(); + QDeclarativePixmapReply *reply = new QDeclarativePixmapReply(data); + reply->engineForReader = engine; + jobs.append(reply); + // XXX + if (threadObject) threadObject->processJobs(); + mutex.unlock(); + return reply; +} + +void QDeclarativePixmapReader::cancel(QDeclarativePixmapReply *reply) +{ + mutex.lock(); + if (reply->loading) { + cancelled.append(reply); + reply->data = 0; + // XXX + if (threadObject) threadObject->processJobs(); + } else { + jobs.removeAll(reply); + delete reply; + } + mutex.unlock(); +} + +void QDeclarativePixmapReader::run() +{ + if (replyDownloadProgress == -1) { + const QMetaObject *nr = &QNetworkReply::staticMetaObject; + const QMetaObject *pr = &QDeclarativePixmapReply::staticMetaObject; + const QMetaObject *ir = &QDeclarativePixmapReaderThreadObject::staticMetaObject; + replyDownloadProgress = nr->indexOfSignal("downloadProgress(qint64,qint64)"); + replyFinished = nr->indexOfSignal("finished()"); + downloadProgress = pr->indexOfSignal("downloadProgress(qint64,qint64)"); + threadNetworkRequestDone = ir->indexOfSlot("networkRequestDone()"); + } + + mutex.lock(); + threadObject = new QDeclarativePixmapReaderThreadObject(this); + mutex.unlock(); + + processJobs(); + exec(); + + delete threadObject; + threadObject = 0; +} + +class QDeclarativePixmapKey +{ +public: + const QUrl *url; + const QSize *size; +}; + +inline bool operator==(const QDeclarativePixmapKey &lhs, const QDeclarativePixmapKey &rhs) +{ + return *lhs.size == *rhs.size && *lhs.url == *rhs.url; +} + +inline uint qHash(const QDeclarativePixmapKey &key) +{ + return qHash(*key.url) ^ key.size->width() ^ key.size->height(); +} + +class QDeclarativePixmapStore : public QObject +{ + Q_OBJECT +public: + QDeclarativePixmapStore(); + + void unreferencePixmap(QDeclarativePixmapData *); + void referencePixmap(QDeclarativePixmapData *); + void flushCache(); + +protected: + virtual void timerEvent(QTimerEvent *); + +public: + QHash m_cache; + +private: + void shrinkCache(int remove); + + QDeclarativePixmapData *m_unreferencedPixmaps; + QDeclarativePixmapData *m_lastUnreferencedPixmap; + + int m_unreferencedCost; + int m_timerId; +}; +Q_GLOBAL_STATIC(QDeclarativePixmapStore, pixmapStore); + +QDeclarativePixmapStore::QDeclarativePixmapStore() +: m_unreferencedPixmaps(0), m_lastUnreferencedPixmap(0), m_unreferencedCost(0), m_timerId(-1) +{ +} + +void QDeclarativePixmapStore::unreferencePixmap(QDeclarativePixmapData *data) +{ + Q_ASSERT(data->prevUnreferenced == 0); + Q_ASSERT(data->prevUnreferencedPtr == 0); + Q_ASSERT(data->nextUnreferenced == 0); + + data->nextUnreferenced = m_unreferencedPixmaps; + data->prevUnreferencedPtr = &m_unreferencedPixmaps; + + m_unreferencedPixmaps = data; + if (m_unreferencedPixmaps->nextUnreferenced) { + m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps; + m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced; + } + + if (!m_lastUnreferencedPixmap) + m_lastUnreferencedPixmap = data; + + m_unreferencedCost += data->cost(); + + shrinkCache(-1); // Shrink the cache incase it has become larger than cache_limit + + if (m_timerId == -1 && m_unreferencedPixmaps) + m_timerId = startTimer(CACHE_EXPIRE_TIME * 1000); +} + +void QDeclarativePixmapStore::referencePixmap(QDeclarativePixmapData *data) +{ + Q_ASSERT(data->prevUnreferencedPtr); + + *data->prevUnreferencedPtr = data->nextUnreferenced; + if (data->nextUnreferenced) { + data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr; + data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced; + } + if (m_lastUnreferencedPixmap == data) + m_lastUnreferencedPixmap = data->prevUnreferenced; + + data->nextUnreferenced = 0; + data->prevUnreferencedPtr = 0; + data->prevUnreferenced = 0; + + m_unreferencedCost -= data->cost(); +} + +void QDeclarativePixmapStore::shrinkCache(int remove) +{ + while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) { + QDeclarativePixmapData *data = m_lastUnreferencedPixmap; + Q_ASSERT(data->nextUnreferenced == 0); + + *data->prevUnreferencedPtr = 0; + m_lastUnreferencedPixmap = data->prevUnreferenced; + data->prevUnreferencedPtr = 0; + data->prevUnreferenced = 0; + + remove -= data->cost(); + m_unreferencedCost -= data->cost(); + data->removeFromCache(); + delete data; + } +} + +void QDeclarativePixmapStore::timerEvent(QTimerEvent *) +{ + int removalCost = m_unreferencedCost / CACHE_REMOVAL_FRACTION; + + shrinkCache(removalCost); + + if (m_unreferencedPixmaps == 0) { + killTimer(m_timerId); + m_timerId = -1; + } +} + +/* + Remove all unreferenced pixmaps from the cache. +*/ +void QDeclarativePixmapStore::flushCache() +{ + shrinkCache(m_unreferencedCost); +} + +QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativePixmapData *d) +: data(d), engineForReader(0), requestSize(d->requestSize), url(d->url), loading(false), redirectCount(0) +{ + if (finishedIndex == -1) { + finishedIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("finished()"); + downloadProgressIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); + } +} + +QDeclarativePixmapReply::~QDeclarativePixmapReply() +{ +} + +bool QDeclarativePixmapReply::event(QEvent *event) +{ + if (event->type() == QEvent::User) { + + if (data) { + Event *de = static_cast(event); + data->pixmapStatus = (de->error == NoError) ? QDeclarativePixmap::Ready : QDeclarativePixmap::Error; + + if (data->pixmapStatus == QDeclarativePixmap::Ready) { + data->pixmap = QPixmap::fromImage(de->image); + data->implicitSize = de->implicitSize; + } else { + data->errorString = de->errorString; + data->removeFromCache(); // We don't continue to cache error'd pixmaps + } + + data->reply = 0; + emit finished(); + } + + delete this; + return true; + } else { + return QObject::event(event); + } +} + +int QDeclarativePixmapData::cost() const +{ + return (pixmap.width() * pixmap.height() * pixmap.depth()) / 8; +} + +void QDeclarativePixmapData::addref() +{ + ++refCount; + if (prevUnreferencedPtr) + pixmapStore()->referencePixmap(this); +} + +void QDeclarativePixmapData::release() +{ + Q_ASSERT(refCount > 0); + --refCount; + + if (refCount == 0) { + if (reply) { + QDeclarativePixmapReply *cancelReply = reply; + reply->data = 0; + reply = 0; + QDeclarativePixmapReader::readerMutex.lock(); + QDeclarativePixmapReader *reader = QDeclarativePixmapReader::existingInstance(cancelReply->engineForReader); + if (reader) + reader->cancel(cancelReply); + QDeclarativePixmapReader::readerMutex.unlock(); + } + + if (pixmapStatus == QDeclarativePixmap::Ready) { + pixmapStore()->unreferencePixmap(this); + } else { + removeFromCache(); + delete this; + } + } +} + +void QDeclarativePixmapData::addToCache() +{ + if (!inCache) { + QDeclarativePixmapKey key = { &url, &requestSize }; + pixmapStore()->m_cache.insert(key, this); + inCache = true; + } +} + +void QDeclarativePixmapData::removeFromCache() +{ + if (inCache) { + QDeclarativePixmapKey key = { &url, &requestSize }; + pixmapStore()->m_cache.remove(key); + inCache = false; + } +} + +static QDeclarativePixmapData* createPixmapDataSync(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, bool *ok) +{ + if (url.scheme() == QLatin1String("image")) { + QSize readSize; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url); + + switch (imageType) { + case QDeclarativeImageProvider::Image: + { + QImage image = ep->getImageFromProvider(url, &readSize, requestSize); + if (!image.isNull()) { + *ok = true; + return new QDeclarativePixmapData(url, QPixmap::fromImage(image), readSize, requestSize); + } + } + case QDeclarativeImageProvider::Pixmap: + { + QPixmap pixmap = ep->getPixmapFromProvider(url, &readSize, requestSize); + if (!pixmap.isNull()) { + *ok = true; + return new QDeclarativePixmapData(url, pixmap, readSize, requestSize); + } + } + } + + // no matching provider, or provider has bad image type, or provider returned null image + return new QDeclarativePixmapData(url, requestSize, + QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString())); + } + + QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); + if (localFile.isEmpty()) + return 0; + + QFile f(localFile); + QSize readSize; + QString errorString; + + if (f.open(QIODevice::ReadOnly)) { + QImage image; + if (readImage(url, &f, &image, &errorString, &readSize, requestSize)) { + *ok = true; + return new QDeclarativePixmapData(url, QPixmap::fromImage(image), readSize, requestSize); + } + } else { + errorString = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString()); + } + return new QDeclarativePixmapData(url, requestSize, errorString); +} + + +struct QDeclarativePixmapNull { + QUrl url; + QPixmap pixmap; + QSize size; +}; +Q_GLOBAL_STATIC(QDeclarativePixmapNull, nullPixmap); + +QDeclarativePixmap::QDeclarativePixmap() +: d(0) +{ +} + +QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url) +: d(0) +{ + load(engine, url); +} + +QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url, const QSize &size) +: d(0) +{ + load(engine, url, size); +} + +QDeclarativePixmap::~QDeclarativePixmap() +{ + if (d) { + d->release(); + d = 0; + } +} + +bool QDeclarativePixmap::isNull() const +{ + return d == 0; +} + +bool QDeclarativePixmap::isReady() const +{ + return status() == Ready; +} + +bool QDeclarativePixmap::isError() const +{ + return status() == Error; +} + +bool QDeclarativePixmap::isLoading() const +{ + return status() == Loading; +} + +QString QDeclarativePixmap::error() const +{ + if (d) + return d->errorString; + else + return QString(); +} + +QDeclarativePixmap::Status QDeclarativePixmap::status() const +{ + if (d) + return d->pixmapStatus; + else + return Null; +} + +const QUrl &QDeclarativePixmap::url() const +{ + if (d) + return d->url; + else + return nullPixmap()->url; +} + +const QSize &QDeclarativePixmap::implicitSize() const +{ + if (d) + return d->implicitSize; + else + return nullPixmap()->size; +} + +const QSize &QDeclarativePixmap::requestSize() const +{ + if (d) + return d->requestSize; + else + return nullPixmap()->size; +} + +const QPixmap &QDeclarativePixmap::pixmap() const +{ + if (d) + return d->pixmap; + else + return nullPixmap()->pixmap; +} + +void QDeclarativePixmap::setPixmap(const QPixmap &p) +{ + clear(); + + if (!p.isNull()) + d = new QDeclarativePixmapData(p); +} + +int QDeclarativePixmap::width() const +{ + if (d) + return d->pixmap.width(); + else + return 0; +} + +int QDeclarativePixmap::height() const +{ + if (d) + return d->pixmap.height(); + else + return 0; +} + +QRect QDeclarativePixmap::rect() const +{ + if (d) + return d->pixmap.rect(); + else + return QRect(); +} + +void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url) +{ + load(engine, url, QSize(), QDeclarativePixmap::Cache); +} + +void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, QDeclarativePixmap::Options options) +{ + load(engine, url, QSize(), options); +} + +void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &size) +{ + load(engine, url, size, QDeclarativePixmap::Cache); +} + +void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, QDeclarativePixmap::Options options) +{ + if (d) { d->release(); d = 0; } + + QDeclarativePixmapKey key = { &url, &requestSize }; + QDeclarativePixmapStore *store = pixmapStore(); + + QHash::Iterator iter = store->m_cache.find(key); + + if (iter == store->m_cache.end()) { + if (options & QDeclarativePixmap::Asynchronous) { + // pixmaps can only be loaded synchronously + if (url.scheme() == QLatin1String("image") + && QDeclarativeEnginePrivate::get(engine)->getImageProviderType(url) == QDeclarativeImageProvider::Pixmap) { + options &= ~QDeclarativePixmap::Asynchronous; + } + } + + if (!(options & QDeclarativePixmap::Asynchronous)) { + bool ok = false; + d = createPixmapDataSync(engine, url, requestSize, &ok); + if (ok) { + if (options & QDeclarativePixmap::Cache) + d->addToCache(); + return; + } + if (d) // loadable, but encountered error while loading + return; + } + + if (!engine) + return; + + d = new QDeclarativePixmapData(url, requestSize); + if (options & QDeclarativePixmap::Cache) + d->addToCache(); + QDeclarativePixmapReader::readerMutex.lock(); + d->reply = QDeclarativePixmapReader::instance(engine)->getImage(d); + QDeclarativePixmapReader::readerMutex.unlock(); + } else { + d = *iter; + d->addref(); + } +} + +void QDeclarativePixmap::clear() +{ + if (d) { + d->release(); + d = 0; + } +} + +void QDeclarativePixmap::clear(QObject *obj) +{ + if (d) { + if (d->reply) + QObject::disconnect(d->reply, 0, obj, 0); + d->release(); + d = 0; + } +} + +bool QDeclarativePixmap::connectFinished(QObject *object, const char *method) +{ + if (!d || !d->reply) { + qWarning("QDeclarativePixmap: connectFinished() called when not loading."); + return false; + } + + return QObject::connect(d->reply, SIGNAL(finished()), object, method); +} + +bool QDeclarativePixmap::connectFinished(QObject *object, int method) +{ + if (!d || !d->reply) { + qWarning("QDeclarativePixmap: connectFinished() called when not loading."); + return false; + } + + return QMetaObject::connect(d->reply, QDeclarativePixmapReply::finishedIndex, object, method); +} + +bool QDeclarativePixmap::connectDownloadProgress(QObject *object, const char *method) +{ + if (!d || !d->reply) { + qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading."); + return false; + } + + return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object, method); +} + +bool QDeclarativePixmap::connectDownloadProgress(QObject *object, int method) +{ + if (!d || !d->reply) { + qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading."); + return false; + } + + return QMetaObject::connect(d->reply, QDeclarativePixmapReply::downloadProgressIndex, object, method); +} + +void QDeclarativePixmap::flushCache() +{ + pixmapStore()->flushCache(); +} + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/util/qdeclarativepixmapcache_p.h b/src/declarative/util/qdeclarativepixmapcache_p.h new file mode 100644 index 00000000..33c3a55a --- /dev/null +++ b/src/declarative/util/qdeclarativepixmapcache_p.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPIXMAPCACHE_H +#define QDECLARATIVEPIXMAPCACHE_H + +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeEngine; +class QDeclarativePixmapData; +class Q_DECLARATIVE_EXPORT QDeclarativePixmap +{ + Q_DECLARE_TR_FUNCTIONS(QDeclarativePixmap) +public: + QDeclarativePixmap(); + QDeclarativePixmap(QDeclarativeEngine *, const QUrl &); + QDeclarativePixmap(QDeclarativeEngine *, const QUrl &, const QSize &); + ~QDeclarativePixmap(); + + enum Status { Null, Ready, Error, Loading }; + + enum Option { + Asynchronous = 0x00000001, + Cache = 0x00000002 + }; + Q_DECLARE_FLAGS(Options, Option) + + bool isNull() const; + bool isReady() const; + bool isError() const; + bool isLoading() const; + + Status status() const; + QString error() const; + const QUrl &url() const; + const QSize &implicitSize() const; + const QSize &requestSize() const; + const QPixmap &pixmap() const; + void setPixmap(const QPixmap &); + + QRect rect() const; + int width() const; + int height() const; + inline operator const QPixmap &() const; + + void load(QDeclarativeEngine *, const QUrl &); + void load(QDeclarativeEngine *, const QUrl &, QDeclarativePixmap::Options options); + void load(QDeclarativeEngine *, const QUrl &, const QSize &); + void load(QDeclarativeEngine *, const QUrl &, const QSize &, QDeclarativePixmap::Options options); + + void clear(); + void clear(QObject *); + + bool connectFinished(QObject *, const char *); + bool connectFinished(QObject *, int); + bool connectDownloadProgress(QObject *, const char *); + bool connectDownloadProgress(QObject *, int); + + static void flushCache(); + +private: + Q_DISABLE_COPY(QDeclarativePixmap) + QDeclarativePixmapData *d; +}; + +inline QDeclarativePixmap::operator const QPixmap &() const +{ + return pixmap(); +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarativePixmap::Options) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEPIXMAPCACHE_H diff --git a/src/declarative/util/qdeclarativepropertychanges.cpp b/src/declarative/util/qdeclarativepropertychanges.cpp new file mode 100644 index 00000000..97c6a1a1 --- /dev/null +++ b/src/declarative/util/qdeclarativepropertychanges.cpp @@ -0,0 +1,798 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativepropertychanges_p.h" + +#include "private/qdeclarativeopenmetaobject_p.h" +#include "private/qdeclarativerewrite_p.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativecompiler_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass PropertyChanges QDeclarativePropertyChanges + \ingroup qml-state-elements + \since 4.7 + \brief The PropertyChanges element describes new property bindings or values for a state. + + PropertyChanges is used to define the property values or bindings in a + \l State. This enables an item's property values to be changed when it + \l {QML States}{changes between states}. + + To create a PropertyChanges object, specify the \l target item whose + properties are to be modified, and define the new property values or + bindings. For example: + + \snippet doc/src/snippets/declarative/propertychanges.qml import + \codeline + \snippet doc/src/snippets/declarative/propertychanges.qml 0 + + When the mouse is pressed, the \l Rectangle changes to the \e resized + state. In this state, the PropertyChanges object sets the rectangle's + color to blue and the \c height value to that of \c container.height. + + Note this automatically binds \c rect.height to \c container.height + in the \e resized state. If a property binding should not be + established, and the height should just be set to the value of + \c container.height at the time of the state change, set the \l explicit + property to \c true. + + A PropertyChanges object can also override the default signal handler + for an object to implement a signal handler specific to the new state: + + \qml + PropertyChanges { + target: myMouseArea + onClicked: doSomethingDifferent() + } + \endqml + + \note PropertyChanges can be used to change anchor margins, but not other anchor + values; use AnchorChanges for this instead. Similarly, to change an \l Item's + \l {Item::}{parent} value, use ParentChanges instead. + + + \section2 Resetting property values + + The \c undefined value can be used to reset the property value for a state. + In the following example, when \c theText changes to the \e widerText + state, its \c width property is reset, giving the text its natural width + and displaying the whole string on a single line. + + \snippet doc/src/snippets/declarative/propertychanges.qml reset + + + \section2 Immediate property changes in transitions + + When \l{QML Animation and Transitions}{Transitions} are used to animate + state changes, they animate properties from their values in the current + state to those defined in the new state (as defined by PropertyChanges + objects). However, it is sometimes desirable to set a property value + \e immediately during a \l Transition, without animation; in these cases, + the PropertyAction element can be used to force an immediate property + change. + + See the PropertyAction documentation for more details. + + \sa {declarative/animation/states}{states example}, {qmlstate}{States}, QtDeclarative +*/ + +/*! + \qmlproperty Object PropertyChanges::target + This property holds the object which contains the properties to be changed. +*/ + +class QDeclarativeReplaceSignalHandler : public QDeclarativeActionEvent +{ +public: + QDeclarativeReplaceSignalHandler() : expression(0), reverseExpression(0), + rewindExpression(0), ownedExpression(0) {} + ~QDeclarativeReplaceSignalHandler() { + delete ownedExpression; + } + + virtual QString typeName() const { return QLatin1String("ReplaceSignalHandler"); } + + QDeclarativeProperty property; + QDeclarativeExpression *expression; + QDeclarativeExpression *reverseExpression; + QDeclarativeExpression *rewindExpression; + QDeclarativeGuard ownedExpression; + + virtual void execute(Reason) { + ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, expression); + if (ownedExpression == expression) + ownedExpression = 0; + } + + virtual bool isReversable() { return true; } + virtual void reverse(Reason) { + ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, reverseExpression); + if (ownedExpression == reverseExpression) + ownedExpression = 0; + } + + virtual void saveOriginals() { + saveCurrentValues(); + reverseExpression = rewindExpression; + } + + virtual bool needsCopy() { return true; } + virtual void copyOriginals(QDeclarativeActionEvent *other) + { + QDeclarativeReplaceSignalHandler *rsh = static_cast(other); + saveCurrentValues(); + if (rsh == this) + return; + reverseExpression = rsh->reverseExpression; + if (rsh->ownedExpression == reverseExpression) { + ownedExpression = rsh->ownedExpression; + rsh->ownedExpression = 0; + } + } + + virtual void rewind() { + ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, rewindExpression); + if (ownedExpression == rewindExpression) + ownedExpression = 0; + } + virtual void saveCurrentValues() { + rewindExpression = QDeclarativePropertyPrivate::signalExpression(property); + } + + virtual bool override(QDeclarativeActionEvent*other) { + if (other == this) + return true; + if (other->typeName() != typeName()) + return false; + if (static_cast(other)->property == property) + return true; + return false; + } +}; + + +class QDeclarativePropertyChangesPrivate : public QDeclarativeStateOperationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePropertyChanges) +public: + QDeclarativePropertyChangesPrivate() : decoded(true), restore(true), + isExplicit(false) {} + + QDeclarativeGuard object; + QByteArray data; + + bool decoded : 1; + bool restore : 1; + bool isExplicit : 1; + + void decode(); + + class ExpressionChange { + public: + ExpressionChange(const QString &_name, + QDeclarativeBinding::Identifier _id, + QDeclarativeExpression *_expr) + : name(_name), id(_id), expression(_expr) {} + QString name; + QDeclarativeBinding::Identifier id; + QDeclarativeExpression *expression; + }; + + QList > properties; + QList expressions; + QList signalReplacements; + + QDeclarativeProperty property(const QString &); +}; + +void +QDeclarativePropertyChangesParser::compileList(QList > &list, + const QByteArray &pre, + const QDeclarativeCustomParserProperty &prop) +{ + QByteArray propName = pre + prop.name(); + + QList values = prop.assignedValues(); + for (int ii = 0; ii < values.count(); ++ii) { + const QVariant &value = values.at(ii); + + if (value.userType() == qMetaTypeId()) { + error(qvariant_cast(value), + QDeclarativePropertyChanges::tr("PropertyChanges does not support creating state-specific objects.")); + continue; + } else if(value.userType() == qMetaTypeId()) { + + QDeclarativeCustomParserProperty prop = + qvariant_cast(value); + QByteArray pre = propName + '.'; + compileList(list, pre, prop); + + } else { + list << qMakePair(propName, value); + } + } +} + +QByteArray +QDeclarativePropertyChangesParser::compile(const QList &props) +{ + QList > 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) { + QDeclarativeParser::Variant v = qvariant_cast(data.at(ii).second); + QVariant var; + bool isScript = v.isScript(); + QDeclarativeBinding::Identifier id = 0; + switch(v.type()) { + case QDeclarativeParser::Variant::Boolean: + var = QVariant(v.asBoolean()); + break; + case QDeclarativeParser::Variant::Number: + var = QVariant(v.asNumber()); + break; + case QDeclarativeParser::Variant::String: + var = QVariant(v.asString()); + break; + case QDeclarativeParser::Variant::Invalid: + case QDeclarativeParser::Variant::Script: + var = QVariant(v.asScript()); + { + // Pre-rewrite the expression + QString expression = v.asScript(); + id = rewriteBinding(expression, data.at(ii).first); //### recreates the AST, which is slow + } + break; + } + + ds << QString::fromUtf8(data.at(ii).first) << isScript << var; + if (isScript) + ds << id; + } + + return rv; +} + +void QDeclarativePropertyChangesPrivate::decode() +{ + Q_Q(QDeclarativePropertyChanges); + if (decoded) + return; + + QDataStream ds(&data, QIODevice::ReadOnly); + + int count; + ds >> count; + for (int ii = 0; ii < count; ++ii) { + QString name; + bool isScript; + QVariant data; + QDeclarativeBinding::Identifier id = QDeclarativeBinding::Invalid; + ds >> name; + ds >> isScript; + ds >> data; + if (isScript) + ds >> id; + + QDeclarativeProperty prop = property(name); //### better way to check for signal property? + if (prop.type() & QDeclarativeProperty::SignalProperty) { + QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(q), object, data.toString()); + QDeclarativeData *ddata = QDeclarativeData::get(q); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + QDeclarativeReplaceSignalHandler *handler = new QDeclarativeReplaceSignalHandler; + handler->property = prop; + handler->expression = expression; + signalReplacements << handler; + } else if (isScript) { + QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(q), object, data.toString()); + QDeclarativeData *ddata = QDeclarativeData::get(q); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + expressions << ExpressionChange(name, id, expression); + } else { + properties << qMakePair(name, data); + } + } + + decoded = true; + data.clear(); +} + +void QDeclarativePropertyChangesParser::setCustomData(QObject *object, + const QByteArray &data) +{ + QDeclarativePropertyChangesPrivate *p = + static_cast(QObjectPrivate::get(object)); + p->data = data; + p->decoded = false; +} + +QDeclarativePropertyChanges::QDeclarativePropertyChanges() +: QDeclarativeStateOperation(*(new QDeclarativePropertyChangesPrivate)) +{ +} + +QDeclarativePropertyChanges::~QDeclarativePropertyChanges() +{ + Q_D(QDeclarativePropertyChanges); + for(int ii = 0; ii < d->expressions.count(); ++ii) + delete d->expressions.at(ii).expression; + for(int ii = 0; ii < d->signalReplacements.count(); ++ii) + delete d->signalReplacements.at(ii); +} + +QObject *QDeclarativePropertyChanges::object() const +{ + Q_D(const QDeclarativePropertyChanges); + return d->object; +} + +void QDeclarativePropertyChanges::setObject(QObject *o) +{ + Q_D(QDeclarativePropertyChanges); + d->object = o; +} + +/*! + \qmlproperty bool PropertyChanges::restoreEntryValues + + This property holds whether the previous values should be restored when + leaving the state. + + The default value is \c true. Setting this value to \c false creates a + temporary state that has permanent effects on property values. +*/ +bool QDeclarativePropertyChanges::restoreEntryValues() const +{ + Q_D(const QDeclarativePropertyChanges); + return d->restore; +} + +void QDeclarativePropertyChanges::setRestoreEntryValues(bool v) +{ + Q_D(QDeclarativePropertyChanges); + d->restore = v; +} + +QDeclarativeProperty +QDeclarativePropertyChangesPrivate::property(const QString &property) +{ + Q_Q(QDeclarativePropertyChanges); + QDeclarativeProperty prop(object, property, qmlContext(q)); + if (!prop.isValid()) { + qmlInfo(q) << QDeclarativePropertyChanges::tr("Cannot assign to non-existent property \"%1\"").arg(property); + return QDeclarativeProperty(); + } else if (!(prop.type() & QDeclarativeProperty::SignalProperty) && !prop.isWritable()) { + qmlInfo(q) << QDeclarativePropertyChanges::tr("Cannot assign to read-only property \"%1\"").arg(property); + return QDeclarativeProperty(); + } + return prop; +} + +QDeclarativePropertyChanges::ActionList QDeclarativePropertyChanges::actions() +{ + Q_D(QDeclarativePropertyChanges); + + d->decode(); + + ActionList list; + + for (int ii = 0; ii < d->properties.count(); ++ii) { + + QDeclarativeAction a(d->object, d->properties.at(ii).first, + qmlContext(this), d->properties.at(ii).second); + + if (a.property.isValid()) { + a.restore = restoreEntryValues(); + list << a; + } + } + + for (int ii = 0; ii < d->signalReplacements.count(); ++ii) { + + QDeclarativeReplaceSignalHandler *handler = d->signalReplacements.at(ii); + + if (handler->property.isValid()) { + QDeclarativeAction a; + a.event = handler; + list << a; + } + } + + for (int ii = 0; ii < d->expressions.count(); ++ii) { + + const QString &property = d->expressions.at(ii).name; + QDeclarativeProperty prop = d->property(property); + + if (prop.isValid()) { + QDeclarativeAction a; + a.restore = restoreEntryValues(); + a.property = prop; + a.fromValue = a.property.read(); + a.specifiedObject = d->object; + a.specifiedProperty = property; + + if (d->isExplicit) { + a.toValue = d->expressions.at(ii).expression->evaluate(); + } else { + QDeclarativeExpression *e = d->expressions.at(ii).expression; + + QDeclarativeBinding::Identifier id = d->expressions.at(ii).id; + QDeclarativeBinding *newBinding = id != QDeclarativeBinding::Invalid ? QDeclarativeBinding::createBinding(id, object(), qmlContext(this), e->sourceFile(), e->lineNumber()) : 0; + if (!newBinding) { + newBinding = new QDeclarativeBinding(e->expression(), object(), qmlContext(this)); + newBinding->setSourceLocation(e->sourceFile(), e->lineNumber()); + } + 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 \c myItem.width from + being bound to \c parent.width. Instead, it is assigned the value of \c 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 QDeclarativePropertyChanges::isExplicit() const +{ + Q_D(const QDeclarativePropertyChanges); + return d->isExplicit; +} + +void QDeclarativePropertyChanges::setIsExplicit(bool e) +{ + Q_D(QDeclarativePropertyChanges); + d->isExplicit = e; +} + +bool QDeclarativePropertyChanges::containsValue(const QString &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QPair PropertyEntry; + + QListIterator propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + return true; + } + } + + return false; +} + +bool QDeclarativePropertyChanges::containsExpression(const QString &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QListIterator expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + return true; + } + } + + return false; +} + +bool QDeclarativePropertyChanges::containsProperty(const QString &name) const +{ + return containsValue(name) || containsExpression(name); +} + +void QDeclarativePropertyChanges::changeValue(const QString &name, const QVariant &value) +{ + Q_D(QDeclarativePropertyChanges); + typedef QPair PropertyEntry; + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QMutableListIterator expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + expressionIterator.remove(); + if (state() && state()->isStateActive()) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name)); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(d->property(name), 0); + oldBinding->destroy(); + } + d->property(name).write(value); + } + + d->properties.append(PropertyEntry(name, value)); + return; + } + } + + QMutableListIterator propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + entry.second = value; + if (state() && state()->isStateActive()) + d->property(name).write(value); + return; + } + } + + QDeclarativeAction action; + action.restore = restoreEntryValues(); + action.property = d->property(name); + action.fromValue = action.property.read(); + action.specifiedObject = object(); + action.specifiedProperty = name; + action.toValue = value; + + propertyIterator.insert(PropertyEntry(name, value)); + if (state() && state()->isStateActive()) { + state()->addEntryToRevertList(action); + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(action.property); + if (oldBinding) + oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + d->property(name).write(value); + } +} + +void QDeclarativePropertyChanges::changeExpression(const QString &name, const QString &expression) +{ + Q_D(QDeclarativePropertyChanges); + typedef QPair PropertyEntry; + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + bool hadValue = false; + + QMutableListIterator propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + propertyIterator.remove(); + hadValue = true; + break; + } + } + + QMutableListIterator expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + entry.expression->setExpression(expression); + if (state() && state()->isStateActive()) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name)); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(d->property(name), 0); + oldBinding->destroy(); + } + + QDeclarativeBinding *newBinding = new QDeclarativeBinding(expression, object(), qmlContext(this)); + newBinding->setTarget(d->property(name)); + QDeclarativePropertyPrivate::setBinding(d->property(name), newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + } + return; + } + } + + QDeclarativeExpression *newExpression = new QDeclarativeExpression(qmlContext(this), d->object, expression); + expressionIterator.insert(ExpressionEntry(name, QDeclarativeBinding::Invalid, newExpression)); + + if (state() && state()->isStateActive()) { + if (hadValue) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name)); + if (oldBinding) { + oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + state()->changeBindingInRevertList(object(), name, oldBinding); + } + + QDeclarativeBinding *newBinding = new QDeclarativeBinding(expression, object(), qmlContext(this)); + newBinding->setTarget(d->property(name)); + QDeclarativePropertyPrivate::setBinding(d->property(name), newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + } else { + QDeclarativeAction action; + action.restore = restoreEntryValues(); + action.property = d->property(name); + action.fromValue = action.property.read(); + action.specifiedObject = object(); + action.specifiedProperty = name; + + + if (d->isExplicit) { + action.toValue = newExpression->evaluate(); + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(newExpression->expression(), object(), qmlContext(this)); + newBinding->setTarget(d->property(name)); + action.toBinding = newBinding; + action.deletableToBinding = true; + + state()->addEntryToRevertList(action); + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(action.property); + if (oldBinding) + oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + + QDeclarativePropertyPrivate::setBinding(action.property, newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + } + } + } + // what about the signal handler? +} + +QVariant QDeclarativePropertyChanges::property(const QString &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QPair PropertyEntry; + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QListIterator propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + return entry.second; + } + } + + QListIterator expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + return QVariant(entry.expression->expression()); + } + } + + return QVariant(); +} + +void QDeclarativePropertyChanges::removeProperty(const QString &name) +{ + Q_D(QDeclarativePropertyChanges); + typedef QPair PropertyEntry; + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QMutableListIterator expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + expressionIterator.remove(); + state()->removeEntryFromRevertList(object(), name); + return; + } + } + + QMutableListIterator propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + propertyIterator.remove(); + state()->removeEntryFromRevertList(object(), name); + return; + } + } +} + +QVariant QDeclarativePropertyChanges::value(const QString &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QPair PropertyEntry; + + QListIterator propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + return entry.second; + } + } + + return QVariant(); +} + +QString QDeclarativePropertyChanges::expression(const QString &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QListIterator expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + return entry.expression->expression(); + } + } + + return QString(); +} + +void QDeclarativePropertyChanges::detachFromState() +{ + if (state()) + state()->removeAllEntriesFromRevertList(object()); +} + +void QDeclarativePropertyChanges::attachToState() +{ + if (state()) + state()->addEntriesToRevertList(actions()); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativepropertychanges_p.h b/src/declarative/util/qdeclarativepropertychanges_p.h new file mode 100644 index 00000000..7b03c3f0 --- /dev/null +++ b/src/declarative/util/qdeclarativepropertychanges_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPROPERTYCHANGES_H +#define QDECLARATIVEPROPERTYCHANGES_H + +#include "private/qdeclarativestateoperations_p.h" +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativePropertyChangesPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativePropertyChanges : public QDeclarativeStateOperation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyChanges) + + 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: + QDeclarativePropertyChanges(); + ~QDeclarativePropertyChanges(); + + QObject *object() const; + void setObject(QObject *); + + bool restoreEntryValues() const; + void setRestoreEntryValues(bool); + + bool isExplicit() const; + void setIsExplicit(bool); + + virtual ActionList actions(); + + bool containsProperty(const QString &name) const; + bool containsValue(const QString &name) const; + bool containsExpression(const QString &name) const; + void changeValue(const QString &name, const QVariant &value); + void changeExpression(const QString &name, const QString &expression); + void removeProperty(const QString &name); + QVariant value(const QString &name) const; + QString expression(const QString &name) const; + + void detachFromState(); + void attachToState(); + + QVariant property(const QString &name) const; +}; + +class QDeclarativePropertyChangesParser : public QDeclarativeCustomParser +{ +public: + QDeclarativePropertyChangesParser() + : QDeclarativeCustomParser(AcceptsAttachedProperties) {} + + void compileList(QList > &list, const QByteArray &pre, const QDeclarativeCustomParserProperty &prop); + + virtual QByteArray compile(const QList &); + virtual void setCustomData(QObject *, const QByteArray &); +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePropertyChanges) + +QT_END_HEADER + +#endif // QDECLARATIVEPROPERTYCHANGES_H diff --git a/src/declarative/util/qdeclarativepropertymap.cpp b/src/declarative/util/qdeclarativepropertymap.cpp new file mode 100644 index 00000000..6e2886ce --- /dev/null +++ b/src/declarative/util/qdeclarativepropertymap.cpp @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativepropertymap.h" + +#include +#include "private/qdeclarativeopenmetaobject_p.h" + +#include + +QT_BEGIN_NAMESPACE + +//QDeclarativePropertyMapMetaObject lets us listen for changes coming from QML +//so we can emit the changed signal. +class QDeclarativePropertyMapMetaObject : public QDeclarativeOpenMetaObject +{ +public: + QDeclarativePropertyMapMetaObject(QDeclarativePropertyMap *obj, QDeclarativePropertyMapPrivate *objPriv); + +protected: + virtual void propertyWritten(int index); + virtual void propertyCreated(int, QMetaPropertyBuilder &); + +private: + QDeclarativePropertyMap *map; + QDeclarativePropertyMapPrivate *priv; +}; + +class QDeclarativePropertyMapPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePropertyMap) +public: + QDeclarativePropertyMapMetaObject *mo; + QStringList keys; + void emitChanged(const QString &key, const QVariant &value); +}; + +void QDeclarativePropertyMapPrivate::emitChanged(const QString &key, const QVariant &value) +{ + Q_Q(QDeclarativePropertyMap); + emit q->valueChanged(key, value); +} + +QDeclarativePropertyMapMetaObject::QDeclarativePropertyMapMetaObject(QDeclarativePropertyMap *obj, QDeclarativePropertyMapPrivate *objPriv) : QDeclarativeOpenMetaObject(obj) +{ + map = obj; + priv = objPriv; +} + +void QDeclarativePropertyMapMetaObject::propertyWritten(int index) +{ + priv->emitChanged(QString::fromUtf8(name(index)), operator[](index)); +} + +void QDeclarativePropertyMapMetaObject::propertyCreated(int, QMetaPropertyBuilder &b) +{ + priv->keys.append(QString::fromUtf8(b.name())); +} + +/*! + \class QDeclarativePropertyMap + \since 4.7 + \brief The QDeclarativePropertyMap class allows you to set key-value pairs that can be used in QML bindings. + + QDeclarativePropertyMap 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. + + In the C++ file: + \code + // create our data + QDeclarativePropertyMap ownerData; + ownerData.insert("name", QVariant(QString("John Smith"))); + ownerData.insert("phone", QVariant(QString("555-5555"))); + + // expose it to the UI layer + QDeclarativeView view; + QDeclarativeContext *ctxt = view.rootContext(); + ctxt->setContextProperty("owner", &ownerData); + + view.setSource(QUrl::fromLocalFile("main.qml")); + view.show(); + \endcode + + Then, in \c main.qml: + \code + Text { text: owner.name + " " + 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. +*/ +QDeclarativePropertyMap::QDeclarativePropertyMap(QObject *parent) +: QObject(*(new QDeclarativePropertyMapPrivate), parent) +{ + Q_D(QDeclarativePropertyMap); + d->mo = new QDeclarativePropertyMapMetaObject(this, d); +} + +/*! + Destroys the bindable map. +*/ +QDeclarativePropertyMap::~QDeclarativePropertyMap() +{ +} + +/*! + Clears the value (if any) associated with \a key. +*/ +void QDeclarativePropertyMap::clear(const QString &key) +{ + Q_D(QDeclarativePropertyMap); + 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 QDeclarativePropertyMap::value(const QString &key) const +{ + Q_D(const QDeclarativePropertyMap); + 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 QDeclarativePropertyMap::insert(const QString &key, const QVariant &value) +{ + Q_D(QDeclarativePropertyMap); + //The following strings shouldn't be used as property names + if (key != QLatin1String("keys") + && key != QLatin1String("valueChanged") + && key != QLatin1String("QObject") + && key != QLatin1String("destroyed") + && key != QLatin1String("deleteLater")) { + d->mo->setValue(key.toUtf8(), value); + } else { + qWarning() << "Creating property with name" + << key + << "is not permitted, conflicts with internal symbols."; + } +} + +/*! + 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 QDeclarativePropertyMap::keys() const +{ + Q_D(const QDeclarativePropertyMap); + return d->keys; +} + +/*! + \overload + + Same as size(). +*/ +int QDeclarativePropertyMap::count() const +{ + Q_D(const QDeclarativePropertyMap); + return d->keys.count(); +} + +/*! + Returns the number of keys in the map. + + \sa isEmpty(), count() +*/ +int QDeclarativePropertyMap::size() const +{ + Q_D(const QDeclarativePropertyMap); + return d->keys.size(); +} + +/*! + Returns true if the map contains no keys; otherwise returns + false. + + \sa size() +*/ +bool QDeclarativePropertyMap::isEmpty() const +{ + Q_D(const QDeclarativePropertyMap); + return d->keys.isEmpty(); +} + +/*! + Returns true if the map contains \a key. + + \sa size() +*/ +bool QDeclarativePropertyMap::contains(const QString &key) const +{ + Q_D(const QDeclarativePropertyMap); + 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 &QDeclarativePropertyMap::operator[](const QString &key) +{ + //### optimize + Q_D(QDeclarativePropertyMap); + QByteArray utf8key = key.toUtf8(); + if (!d->keys.contains(key)) + insert(key, QVariant());//force creation -- needed below + + return (*(d->mo))[utf8key]; +} + +/*! + \overload + + Same as value(). +*/ +QVariant QDeclarativePropertyMap::operator[](const QString &key) const +{ + return value(key); +} + +/*! + \fn void QDeclarativePropertyMap::valueChanged(const QString &key, const QVariant &value) + This signal is emitted whenever one of the values in the map is changed. \a key + is the key corresponding to the \a 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/qdeclarativepropertymap.h b/src/declarative/util/qdeclarativepropertymap.h new file mode 100644 index 00000000..0f217253 --- /dev/null +++ b/src/declarative/util/qdeclarativepropertymap.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPROPERTYMAP_H +#define QDECLARATIVEPROPERTYMAP_H + +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativePropertyMapPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativePropertyMap : public QObject +{ + Q_OBJECT +public: + QDeclarativePropertyMap(QObject *parent = 0); + virtual ~QDeclarativePropertyMap(); + + 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); + QVariant operator[](const QString &key) const; + +Q_SIGNALS: + void valueChanged(const QString &key, const QVariant &value); + +private: + Q_DECLARE_PRIVATE(QDeclarativePropertyMap) + Q_DISABLE_COPY(QDeclarativePropertyMap) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/declarative/util/qdeclarativesmoothedanimation.cpp b/src/declarative/util/qdeclarativesmoothedanimation.cpp new file mode 100644 index 00000000..79b4eff4 --- /dev/null +++ b/src/declarative/util/qdeclarativesmoothedanimation.cpp @@ -0,0 +1,493 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativesmoothedanimation_p.h" +#include "private/qdeclarativesmoothedanimation_p_p.h" + +#include "private/qdeclarativeanimation_p_p.h" + +#include +#include "private/qdeclarativeproperty_p.h" + +#include "private/qdeclarativeglobal_p.h" + +#include + +#include + +#define DELAY_STOP_TIMER_INTERVAL 32 + +QT_BEGIN_NAMESPACE + +QSmoothedAnimation::QSmoothedAnimation(QObject *parent) + : QAbstractAnimation(parent), to(0), velocity(200), userDuration(-1), maximumEasingTime(-1), + reversingMode(QDeclarativeSmoothedAnimation::Eased), initialVelocity(0), + trackVelocity(0), initialValue(0), invert(false), finalDuration(-1), lastTime(0) +{ + delayedStopTimer.setInterval(DELAY_STOP_TIMER_INTERVAL); + delayedStopTimer.setSingleShot(true); + connect(&delayedStopTimer, SIGNAL(timeout()), this, SLOT(stop())); +} + +void QSmoothedAnimation::restart() +{ + initialVelocity = trackVelocity; + if (state() != QAbstractAnimation::Running) + start(); + else + init(); +} + +void QSmoothedAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State /*oldState*/) +{ + if (newState == QAbstractAnimation::Running) + init(); +} + +void QSmoothedAnimation::delayedStop() +{ + if (!delayedStopTimer.isActive()) + delayedStopTimer.start(); +} + +int QSmoothedAnimation::duration() const +{ + return -1; +} + +bool QSmoothedAnimation::recalc() +{ + s = to - initialValue; + vi = initialVelocity; + + s = (invert? qreal(-1.0): qreal(1.0)) * s; + + if (userDuration > 0 && velocity > 0) { + tf = s / velocity; + if (tf > (userDuration / qreal(1000.))) tf = (userDuration / qreal(1000.)); + } else if (userDuration > 0) { + tf = userDuration / qreal(1000.); + } else if (velocity > 0) { + tf = s / velocity; + } else { + return false; + } + + finalDuration = ceil(tf * qreal(1000.0)); + + if (maximumEasingTime == 0) { + a = 0; + d = 0; + tp = 0; + td = tf; + vp = velocity; + sp = 0; + sd = s; + } else if (maximumEasingTime != -1 && tf > (maximumEasingTime / qreal(1000.))) { + qreal met = maximumEasingTime / qreal(1000.); + td = tf - met; + + qreal c1 = td; + qreal c2 = (tf - td) * vi - tf * velocity; + qreal c3 = qreal(-0.5) * (tf - td) * vi * vi; + + qreal vp1 = (-c2 + qSqrt(c2 * c2 - 4 * c1 * c3)) / (qreal(2.) * c1); + + vp = vp1; + a = vp / met; + d = a; + tp = (vp - vi) / a; + sp = vi * tp + qreal(0.5) * a * tp * tp; + sd = sp + (td - tp) * vp; + } else { + qreal c1 = qreal(0.25) * tf * tf; + qreal c2 = qreal(0.5) * vi * tf - s; + qreal c3 = qreal(-0.25) * vi * vi; + + qreal a1 = (-c2 + qSqrt(c2 * c2 - 4 * c1 * c3)) / (qreal(2.) * c1); + + qreal tp1 = qreal(0.5) * tf - qreal(0.5) * vi / a1; + qreal vp1 = a1 * tp1 + vi; + + qreal sp1 = qreal(0.5) * a1 * tp1 * tp1 + vi * tp1; + + a = a1; + d = a1; + tp = tp1; + td = tp1; + vp = vp1; + sp = sp1; + sd = sp1; + } + return true; +} + +qreal QSmoothedAnimation::easeFollow(qreal time_seconds) +{ + qreal value; + if (time_seconds < tp) { + trackVelocity = vi + time_seconds * a; + value = qreal(0.5) * a * time_seconds * time_seconds + vi * time_seconds; + } else if (time_seconds < td) { + time_seconds -= tp; + trackVelocity = vp; + value = sp + time_seconds * vp; + } else if (time_seconds < tf) { + time_seconds -= td; + trackVelocity = vp - time_seconds * a; + value = sd - qreal(0.5) * d * time_seconds * time_seconds + vp * time_seconds; + } else { + trackVelocity = 0; + value = s; + delayedStop(); + } + + // to normalize 's' between [0..1], divide 'value' by 's' + return value; +} + +void QSmoothedAnimation::updateCurrentTime(int t) +{ + qreal time_seconds = qreal(t - lastTime) / qreal(1000.); + + qreal value = easeFollow(time_seconds); + value *= (invert? qreal(-1.0): qreal(1.0)); + QDeclarativePropertyPrivate::write(target, initialValue + value, + QDeclarativePropertyPrivate::BypassInterceptor + | QDeclarativePropertyPrivate::DontRemoveBinding); +} + +void QSmoothedAnimation::init() +{ + if (velocity == 0) { + stop(); + return; + } + + if (delayedStopTimer.isActive()) + delayedStopTimer.stop(); + + initialValue = target.read().toReal(); + lastTime = this->currentTime(); + + if (to == initialValue) { + stop(); + return; + } + + bool hasReversed = trackVelocity != qreal(0.) && + ((!invert) == ((initialValue - to) > 0)); + + if (hasReversed) { + switch (reversingMode) { + default: + case QDeclarativeSmoothedAnimation::Eased: + initialVelocity = -trackVelocity; + break; + case QDeclarativeSmoothedAnimation::Sync: + QDeclarativePropertyPrivate::write(target, to, + QDeclarativePropertyPrivate::BypassInterceptor + | QDeclarativePropertyPrivate::DontRemoveBinding); + trackVelocity = 0; + stop(); + return; + case QDeclarativeSmoothedAnimation::Immediate: + initialVelocity = 0; + break; + } + } + + trackVelocity = initialVelocity; + + invert = (to < initialValue); + + if (!recalc()) { + QDeclarativePropertyPrivate::write(target, to, + QDeclarativePropertyPrivate::BypassInterceptor + | QDeclarativePropertyPrivate::DontRemoveBinding); + stop(); + return; + } +} + +/*! + \qmlclass SmoothedAnimation QDeclarativeSmoothedAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits NumberAnimation + \brief The SmoothedAnimation element allows a property to smoothly track a value. + + A SmoothedAnimation animates a property's value to a set target value + using an ease in/out quad easing curve. When the target value changes, + the easing curves used to animate between the old and new target values + are smoothly spliced together to create a smooth movement to the new + target value that maintains the current velocity. + + The follow example shows one \l Rectangle tracking the position of another + using SmoothedAnimation. The green rectangle's \c x and \c y values are + bound to those of the red rectangle. Whenever these values change, the + green rectangle smoothly animates to its new position: + + \snippet doc/src/snippets/declarative/smoothedanimation.qml 0 + + A SmoothedAnimation can be configured by setting the \l velocity at which the + animation should occur, or the \l duration that the animation should take. + If both the \l velocity and \l 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 default velocity of SmoothedAnimation is 200 units/second. Note that if the range of the + value being animated is small, then the velocity will need to be adjusted + appropriately. For example, the opacity of an item ranges from 0 - 1.0. + To enable a smooth animation in this range the velocity will need to be + set to a value such as 0.5 units/second. Animating from 0 to 1.0 with a velocity + of 0.5 will take 2000 ms to complete. + + Like any other animation element, a SmoothedAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa SpringAnimation, NumberAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ + +QDeclarativeSmoothedAnimation::QDeclarativeSmoothedAnimation(QObject *parent) +: QDeclarativeNumberAnimation(*(new QDeclarativeSmoothedAnimationPrivate), parent) +{ +} + +QDeclarativeSmoothedAnimation::~QDeclarativeSmoothedAnimation() +{ +} + +QDeclarativeSmoothedAnimationPrivate::QDeclarativeSmoothedAnimationPrivate() + : wrapperGroup(new QParallelAnimationGroup), anim(new QSmoothedAnimation) +{ + Q_Q(QDeclarativeSmoothedAnimation); + QDeclarative_setParent_noEvent(wrapperGroup, q); + QDeclarative_setParent_noEvent(anim, q); +} + +void QDeclarativeSmoothedAnimationPrivate::updateRunningAnimations() +{ + foreach(QSmoothedAnimation* ease, activeAnimations.values()){ + ease->maximumEasingTime = anim->maximumEasingTime; + ease->reversingMode = anim->reversingMode; + ease->velocity = anim->velocity; + ease->userDuration = anim->userDuration; + ease->init(); + } +} + +QAbstractAnimation* QDeclarativeSmoothedAnimation::qtAnimation() +{ + Q_D(QDeclarativeSmoothedAnimation); + return d->wrapperGroup; +} + +void QDeclarativeSmoothedAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeSmoothedAnimation); + QDeclarativeNumberAnimation::transition(actions, modified, direction); + + if (!d->actions) + return; + + QSet anims; + for (int i = 0; i < d->actions->size(); i++) { + QSmoothedAnimation *ease; + bool needsRestart; + if (!d->activeAnimations.contains((*d->actions)[i].property)) { + ease = new QSmoothedAnimation(); + d->wrapperGroup->addAnimation(ease); + d->activeAnimations.insert((*d->actions)[i].property, ease); + needsRestart = false; + } else { + ease = d->activeAnimations.value((*d->actions)[i].property); + needsRestart = true; + } + ease->target = (*d->actions)[i].property; + ease->to = (*d->actions)[i].toValue.toReal(); + + // copying public members from main value holder animation + ease->maximumEasingTime = d->anim->maximumEasingTime; + ease->reversingMode = d->anim->reversingMode; + ease->velocity = d->anim->velocity; + ease->userDuration = d->anim->userDuration; + + ease->initialVelocity = ease->trackVelocity; + + if (needsRestart) + ease->init(); + anims.insert(ease); + } + + for (int i = d->wrapperGroup->animationCount() - 1; i >= 0 ; --i) { + if (!anims.contains(d->wrapperGroup->animationAt(i))) { + QSmoothedAnimation *ease = static_cast(d->wrapperGroup->animationAt(i)); + d->activeAnimations.remove(ease->target); + d->wrapperGroup->takeAnimation(i); + delete ease; + } + } +} + +/*! + \qmlproperty enumeration SmoothedAnimation::reversingMode + + Sets how the SmoothedAnimation behaves if an animation direction is reversed. + + Possible values are: + + \list + \o SmoothedAnimation.Eased (default) - the animation will smoothly decelerate, and then reverse direction + \o SmoothedAnimation.Immediate - the animation will immediately begin accelerating in the reverse direction, beginning with a velocity of 0 + \o SmoothedAnimation.Sync - the property is immediately set to the target value + \endlist +*/ +QDeclarativeSmoothedAnimation::ReversingMode QDeclarativeSmoothedAnimation::reversingMode() const +{ + Q_D(const QDeclarativeSmoothedAnimation); + return (QDeclarativeSmoothedAnimation::ReversingMode) d->anim->reversingMode; +} + +void QDeclarativeSmoothedAnimation::setReversingMode(ReversingMode m) +{ + Q_D(QDeclarativeSmoothedAnimation); + if (d->anim->reversingMode == m) + return; + + d->anim->reversingMode = m; + emit reversingModeChanged(); + d->updateRunningAnimations(); +} + +/*! + \qmlproperty int SmoothedAnimation::duration + + This property holds the animation duration, in msecs, used when tracking the source. + + Setting this to -1 (the default) disables the duration value. + + If the velocity value and the duration value are both enabled, then the animation will + use whichever gives the shorter duration. +*/ +int QDeclarativeSmoothedAnimation::duration() const +{ + Q_D(const QDeclarativeSmoothedAnimation); + return d->anim->userDuration; +} + +void QDeclarativeSmoothedAnimation::setDuration(int duration) +{ + Q_D(QDeclarativeSmoothedAnimation); + if (duration != -1) + QDeclarativeNumberAnimation::setDuration(duration); + if(duration == d->anim->userDuration) + return; + d->anim->userDuration = duration; + d->updateRunningAnimations(); +} + +qreal QDeclarativeSmoothedAnimation::velocity() const +{ + Q_D(const QDeclarativeSmoothedAnimation); + return d->anim->velocity; +} + +/*! + \qmlproperty real SmoothedAnimation::velocity + + This property holds the average velocity allowed when tracking the 'to' value. + + The default velocity of SmoothedAnimation is 200 units/second. + + Setting this to -1 disables the velocity value. + + If the velocity value and the duration value are both enabled, then the animation will + use whichever gives the shorter duration. +*/ +void QDeclarativeSmoothedAnimation::setVelocity(qreal v) +{ + Q_D(QDeclarativeSmoothedAnimation); + if (d->anim->velocity == v) + return; + + d->anim->velocity = v; + emit velocityChanged(); + d->updateRunningAnimations(); +} + +/*! + \qmlproperty int SmoothedAnimation::maximumEasingTime + + This property specifies the maximum time, in msecs, any "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. +*/ +int QDeclarativeSmoothedAnimation::maximumEasingTime() const +{ + Q_D(const QDeclarativeSmoothedAnimation); + return d->anim->maximumEasingTime; +} + +void QDeclarativeSmoothedAnimation::setMaximumEasingTime(int v) +{ + Q_D(QDeclarativeSmoothedAnimation); + if(v == d->anim->maximumEasingTime) + return; + d->anim->maximumEasingTime = v; + emit maximumEasingTimeChanged(); + d->updateRunningAnimations(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativesmoothedanimation_p.h b/src/declarative/util/qdeclarativesmoothedanimation_p.h new file mode 100644 index 00000000..dd030dcf --- /dev/null +++ b/src/declarative/util/qdeclarativesmoothedanimation_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESMOOTHEDANIMATION_H +#define QDECLARATIVESMOOTHEDANIMATION_H + +#include +#include "private/qdeclarativeanimation_p.h" + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeProperty; +class QDeclarativeSmoothedAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeSmoothedAnimation : public QDeclarativeNumberAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeSmoothedAnimation) + Q_ENUMS(ReversingMode) + + Q_PROPERTY(qreal velocity READ velocity WRITE setVelocity NOTIFY velocityChanged) + Q_PROPERTY(ReversingMode reversingMode READ reversingMode WRITE setReversingMode NOTIFY reversingModeChanged) + Q_PROPERTY(qreal maximumEasingTime READ maximumEasingTime WRITE setMaximumEasingTime NOTIFY maximumEasingTimeChanged) + +public: + enum ReversingMode { Eased, Immediate, Sync }; + + QDeclarativeSmoothedAnimation(QObject *parent = 0); + ~QDeclarativeSmoothedAnimation(); + + ReversingMode reversingMode() const; + void setReversingMode(ReversingMode); + + virtual int duration() const; + virtual void setDuration(int); + + qreal velocity() const; + void setVelocity(qreal); + + int maximumEasingTime() const; + void setMaximumEasingTime(int); + + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + QAbstractAnimation* qtAnimation(); + +Q_SIGNALS: + void velocityChanged(); + void reversingModeChanged(); + void maximumEasingTimeChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeSmoothedAnimation) + +QT_END_HEADER + +#endif // QDECLARATIVESMOOTHEDANIMATION_H diff --git a/src/declarative/util/qdeclarativesmoothedanimation_p_p.h b/src/declarative/util/qdeclarativesmoothedanimation_p_p.h new file mode 100644 index 00000000..8f8da8d3 --- /dev/null +++ b/src/declarative/util/qdeclarativesmoothedanimation_p_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESMOOTHEDANIMATION_P_H +#define QDECLARATIVESMOOTHEDANIMATION_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 "private/qdeclarativesmoothedanimation_p.h" +#include "private/qdeclarativeanimation_p.h" + +#include "private/qdeclarativeanimation_p_p.h" + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QSmoothedAnimation : public QAbstractAnimation +{ +public: + QSmoothedAnimation(QObject *parent=0); + + qreal to; + qreal velocity; + int userDuration; + + int maximumEasingTime; + QDeclarativeSmoothedAnimation::ReversingMode reversingMode; + + qreal initialVelocity; + qreal trackVelocity; + + QDeclarativeProperty target; + + int duration() const; + void restart(); + void init(); + +protected: + virtual void updateCurrentTime(int); + virtual void updateState(QAbstractAnimation::State, QAbstractAnimation::State); + +private: + qreal easeFollow(qreal); + qreal initialValue; + + bool invert; + + int finalDuration; + + // Parameters for use in updateCurrentTime() + 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 + qreal s; // Total s + + int lastTime; + + bool recalc(); + void delayedStop(); + + QTimer delayedStopTimer; +}; + +class QDeclarativeSmoothedAnimationPrivate : public QDeclarativePropertyAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeSmoothedAnimation) +public: + QDeclarativeSmoothedAnimationPrivate(); + void updateRunningAnimations(); + + QParallelAnimationGroup *wrapperGroup; + QSmoothedAnimation *anim; + QHash activeAnimations; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVESMOOTHEDANIMATION_P_H diff --git a/src/declarative/util/qdeclarativespringanimation.cpp b/src/declarative/util/qdeclarativespringanimation.cpp new file mode 100644 index 00000000..034c54b2 --- /dev/null +++ b/src/declarative/util/qdeclarativespringanimation.cpp @@ -0,0 +1,462 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativespringanimation_p.h" + +#include "private/qdeclarativeanimation_p_p.h" +#include + +#include + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + + +class QDeclarativeSpringAnimationPrivate : public QDeclarativePropertyAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeSpringAnimation) +public: + + + struct SpringAnimation { + SpringAnimation() + : currentValue(0), to(0), velocity(0), start(0), duration(0) {} + qreal currentValue; + qreal to; + qreal velocity; + int start; + int duration; + }; + QHash activeAnimations; + + qreal maxVelocity; + qreal velocityms; + int lastTime; + qreal mass; + qreal spring; + qreal damping; + qreal epsilon; + qreal modulus; + + bool useMass : 1; + bool haveModulus : 1; + + enum Mode { + Track, + Velocity, + Spring + }; + Mode mode; + + QDeclarativeSpringAnimationPrivate() + : maxVelocity(0), velocityms(0), lastTime(0) + , mass(1.0), spring(0.), damping(0.), epsilon(0.01) + , modulus(0.0), useMass(false), haveModulus(false) + , mode(Track), clock(0) + { } + + void tick(int time); + bool animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed); + void updateMode(); + + typedef QTickAnimationProxy Clock; + Clock *clock; +}; + +void QDeclarativeSpringAnimationPrivate::tick(int time) +{ + if (mode == Track) { + clock->stop(); + return; + } + int elapsed = time - lastTime; + if (!elapsed) + return; + + if (mode == Spring) { + if (elapsed < 16) // capped at 62fps. + return; + int count = elapsed / 16; + lastTime = time - (elapsed - count * 16); + } else { + lastTime = time; + } + + QMutableHashIterator it(activeAnimations); + while (it.hasNext()) { + it.next(); + if (animate(it.key(), it.value(), elapsed)) + it.remove(); + } + + if (activeAnimations.isEmpty()) + clock->stop(); +} + +bool QDeclarativeSpringAnimationPrivate::animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed) +{ + qreal srcVal = animation.to; + + bool stop = false; + + if (haveModulus) { + animation.currentValue = fmod(animation.currentValue, modulus); + srcVal = fmod(srcVal, modulus); + } + if (mode == Spring) { + // 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 - animation.currentValue; + if (haveModulus && qAbs(diff) > modulus / 2) { + if (diff < 0) + diff += modulus; + else + diff -= modulus; + } + if (useMass) + animation.velocity = animation.velocity + (spring * diff - damping * animation.velocity) / mass; + else + animation.velocity = animation.velocity + spring * diff - damping * animation.velocity; + if (maxVelocity > qreal(0.)) { + // limit velocity + if (animation.velocity > maxVelocity) + animation.velocity = maxVelocity; + else if (animation.velocity < -maxVelocity) + animation.velocity = -maxVelocity; + } + animation.currentValue += animation.velocity * qreal(16.0) / qreal(1000.0); + if (haveModulus) { + animation.currentValue = fmod(animation.currentValue, modulus); + if (animation.currentValue < qreal(0.0)) + animation.currentValue += modulus; + } + } + if (qAbs(animation.velocity) < epsilon && qAbs(srcVal - animation.currentValue) < epsilon) { + animation.velocity = 0.0; + animation.currentValue = srcVal; + stop = true; + } + } else { + qreal moveBy = elapsed * velocityms; + qreal diff = srcVal - animation.currentValue; + if (haveModulus && qAbs(diff) > modulus / 2) { + if (diff < 0) + diff += modulus; + else + diff -= modulus; + } + if (diff > 0) { + animation.currentValue += moveBy; + if (haveModulus) + animation.currentValue = fmod(animation.currentValue, modulus); + } else { + animation.currentValue -= moveBy; + if (haveModulus && animation.currentValue < qreal(0.0)) + animation.currentValue = fmod(animation.currentValue, modulus) + modulus; + } + if (lastTime - animation.start >= animation.duration) { + animation.currentValue = animation.to; + stop = true; + } + } + + qreal old_to = animation.to; + + QDeclarativePropertyPrivate::write(property, animation.currentValue, + QDeclarativePropertyPrivate::BypassInterceptor | + QDeclarativePropertyPrivate::DontRemoveBinding); + + return (stop && old_to == animation.to); // do not stop if we got restarted +} + +void QDeclarativeSpringAnimationPrivate::updateMode() +{ + if (spring == 0. && maxVelocity == 0.) + mode = Track; + else if (spring > 0.) + mode = Spring; + else { + mode = Velocity; + QHash::iterator it; + for (it = activeAnimations.begin(); it != activeAnimations.end(); ++it) { + SpringAnimation &animation = *it; + animation.start = lastTime; + qreal dist = qAbs(animation.currentValue - animation.to); + if (haveModulus && dist > modulus / 2) + dist = modulus - fmod(dist, modulus); + animation.duration = dist / velocityms; + } + } +} + +/*! + \qmlclass SpringAnimation QDeclarativeSpringAnimation + \ingroup qml-animation-transition + \inherits NumberAnimation + \since 4.7 + + \brief The SpringAnimation element allows a property to track a value in a spring-like motion. + + SpringAnimation mimics the oscillatory behavior of a spring, with the appropriate \l spring constant to + control the acceleration and the \l damping to control how quickly the effect dies away. + + You can also limit the maximum \l velocity of the animation. + + The following \l Rectangle moves to the position of the mouse using a + SpringAnimation when the mouse is clicked. The use of the \l Behavior + on the \c x and \c y values indicates that whenever these values are + changed, a SpringAnimation should be applied. + + \snippet doc/src/snippets/declarative/springanimation.qml 0 + + Like any other animation element, a SpringAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa SmoothedAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example}, {declarative/toys/clocks}{Clocks example} +*/ + +QDeclarativeSpringAnimation::QDeclarativeSpringAnimation(QObject *parent) +: QDeclarativeNumberAnimation(*(new QDeclarativeSpringAnimationPrivate),parent) +{ + Q_D(QDeclarativeSpringAnimation); + d->clock = new QDeclarativeSpringAnimationPrivate::Clock(d, this); +} + +QDeclarativeSpringAnimation::~QDeclarativeSpringAnimation() +{ +} + +/*! + \qmlproperty real SpringAnimation::velocity + + This property holds the maximum velocity allowed when tracking the source. + + The default value is 0 (no maximum velocity). +*/ + +qreal QDeclarativeSpringAnimation::velocity() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->maxVelocity; +} + +void QDeclarativeSpringAnimation::setVelocity(qreal velocity) +{ + Q_D(QDeclarativeSpringAnimation); + d->maxVelocity = velocity; + d->velocityms = velocity / 1000.0; + d->updateMode(); +} + +/*! + \qmlproperty real SpringAnimation::spring + + This property describes how strongly the target is pulled towards the + source. The default value is 0 (that is, the spring-like motion is disabled). + + The useful value range is 0 - 5.0. + + When this property is set and the \l velocity value is greater than 0, + the \l velocity limits the maximum speed. +*/ +qreal QDeclarativeSpringAnimation::spring() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->spring; +} + +void QDeclarativeSpringAnimation::setSpring(qreal spring) +{ + Q_D(QDeclarativeSpringAnimation); + d->spring = spring; + d->updateMode(); +} + +/*! + \qmlproperty real SpringAnimation::damping + This property holds the spring damping value. + + This value describes how quickly the spring-like motion comes to rest. + The default value is 0. + + The useful value range is 0 - 1.0. The lower the value, the faster it + comes to rest. +*/ +qreal QDeclarativeSpringAnimation::damping() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->damping; +} + +void QDeclarativeSpringAnimation::setDamping(qreal damping) +{ + Q_D(QDeclarativeSpringAnimation); + if (damping > 1.) + damping = 1.; + + d->damping = damping; +} + + +/*! + \qmlproperty real SpringAnimation::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 QDeclarativeSpringAnimation::epsilon() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->epsilon; +} + +void QDeclarativeSpringAnimation::setEpsilon(qreal epsilon) +{ + Q_D(QDeclarativeSpringAnimation); + d->epsilon = epsilon; +} + +/*! + \qmlproperty real SpringAnimation::modulus + This property holds the modulus value. The default value is 0. + + 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 QDeclarativeSpringAnimation::modulus() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->modulus; +} + +void QDeclarativeSpringAnimation::setModulus(qreal modulus) +{ + Q_D(QDeclarativeSpringAnimation); + if (d->modulus != modulus) { + d->haveModulus = modulus != 0.0; + d->modulus = modulus; + d->updateMode(); + emit modulusChanged(); + } +} + +/*! + \qmlproperty real SpringAnimation::mass + This property holds the "mass" of the property being moved. + + The value is 1.0 by default. + + A greater mass causes slower movement and a greater spring-like + motion when an item comes to rest. +*/ +qreal QDeclarativeSpringAnimation::mass() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->mass; +} + +void QDeclarativeSpringAnimation::setMass(qreal mass) +{ + Q_D(QDeclarativeSpringAnimation); + if (d->mass != mass && mass > 0.0) { + d->useMass = mass != 1.0; + d->mass = mass; + emit massChanged(); + } +} + +void QDeclarativeSpringAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeSpringAnimation); + Q_UNUSED(direction); + + if (d->clock->state() != QAbstractAnimation::Running) { + d->lastTime = 0; + } + + QDeclarativeNumberAnimation::transition(actions, modified, direction); + + if (!d->actions) + return; + + if (!d->actions->isEmpty()) { + for (int i = 0; i < d->actions->size(); ++i) { + const QDeclarativeProperty &property = d->actions->at(i).property; + QDeclarativeSpringAnimationPrivate::SpringAnimation &animation + = d->activeAnimations[property]; + animation.to = d->actions->at(i).toValue.toReal(); + animation.start = d->lastTime; + if (d->fromIsDefined) + animation.currentValue = d->actions->at(i).fromValue.toReal(); + else + animation.currentValue = property.read().toReal(); + if (d->mode == QDeclarativeSpringAnimationPrivate::Velocity) { + qreal dist = qAbs(animation.currentValue - animation.to); + if (d->haveModulus && dist > d->modulus / 2) + dist = d->modulus - fmod(dist, d->modulus); + animation.duration = dist / d->velocityms; + } + } + } +} + + +QAbstractAnimation *QDeclarativeSpringAnimation::qtAnimation() +{ + Q_D(QDeclarativeSpringAnimation); + return d->clock; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativespringanimation_p.h b/src/declarative/util/qdeclarativespringanimation_p.h new file mode 100644 index 00000000..0ae7dd4b --- /dev/null +++ b/src/declarative/util/qdeclarativespringanimation_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESPRINGANIMATION_H +#define QDECLARATIVESPRINGANIMATION_H + +#include +#include "private/qdeclarativeanimation_p.h" + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeSpringAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeSpringAnimation : public QDeclarativeNumberAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeSpringAnimation) + Q_INTERFACES(QDeclarativePropertyValueSource) + + 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(qreal modulus READ modulus WRITE setModulus NOTIFY modulusChanged) + Q_PROPERTY(qreal mass READ mass WRITE setMass NOTIFY massChanged) + +public: + QDeclarativeSpringAnimation(QObject *parent=0); + ~QDeclarativeSpringAnimation(); + + 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); + + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + +protected: + virtual QAbstractAnimation *qtAnimation(); + +Q_SIGNALS: + void modulusChanged(); + void massChanged(); + void syncChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeSpringAnimation) + +QT_END_HEADER + +#endif // QDECLARATIVESPRINGANIMATION_H diff --git a/src/declarative/util/qdeclarativestate.cpp b/src/declarative/util/qdeclarativestate.cpp new file mode 100644 index 00000000..23871088 --- /dev/null +++ b/src/declarative/util/qdeclarativestate.cpp @@ -0,0 +1,742 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativestate_p_p.h" +#include "private/qdeclarativestate_p.h" + +#include "private/qdeclarativetransition_p.h" +#include "private/qdeclarativestategroup_p.h" +#include "private/qdeclarativestateoperations_p.h" +#include "private/qdeclarativeanimation_p.h" +#include "private/qdeclarativeanimation_p_p.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +QDeclarativeAction::QDeclarativeAction() +: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), fromBinding(0), event(0), + specifiedObject(0) +{ +} + +QDeclarativeAction::QDeclarativeAction(QObject *target, const QString &propertyName, + const QVariant &value) +: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), + property(target, propertyName, qmlEngine(target)), toValue(value), + fromBinding(0), event(0), + specifiedObject(target), specifiedProperty(propertyName) +{ + if (property.isValid()) + fromValue = property.read(); +} + +QDeclarativeAction::QDeclarativeAction(QObject *target, const QString &propertyName, + QDeclarativeContext *context, const QVariant &value) +: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), + property(target, propertyName, context), toValue(value), + fromBinding(0), event(0), + specifiedObject(target), specifiedProperty(propertyName) +{ + if (property.isValid()) + fromValue = property.read(); +} + + +QDeclarativeActionEvent::~QDeclarativeActionEvent() +{ +} + +QString QDeclarativeActionEvent::typeName() const +{ + return QString(); +} + +void QDeclarativeActionEvent::execute(Reason) +{ +} + +bool QDeclarativeActionEvent::isReversable() +{ + return false; +} + +void QDeclarativeActionEvent::reverse(Reason) +{ +} + +bool QDeclarativeActionEvent::changesBindings() +{ + return false; +} + +void QDeclarativeActionEvent::clearBindings() +{ +} + +bool QDeclarativeActionEvent::override(QDeclarativeActionEvent *other) +{ + Q_UNUSED(other); + return false; +} + +QDeclarativeStateOperation::QDeclarativeStateOperation(QObjectPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + \qmlclass State QDeclarativeState + \ingroup qml-state-elements + \since 4.7 + \brief The State element defines configurations of objects and properties. + + A \e state is a set of batched changes from the default configuration. + + All items have a default state that defines the default configuration of objects + and property values. New states can be defined by adding State items to the \l {Item::states}{states} property to + allow items to switch between different configurations. These configurations + can, for example, be used to apply different sets of property values or execute + different scripts. + + The following example displays a single \l Rectangle. In the default state, the rectangle + is colored black. In the "clicked" state, a PropertyChanges element changes the + rectangle's color to red. Clicking within the MouseArea toggles the rectangle's state + between the default state and the "clicked" state, thus toggling the color of the + rectangle between black and red. + + \snippet doc/src/snippets/declarative/state.qml 0 + + Notice the default state is referred to using an empty string (""). + + States are commonly used together with \l{QML Animation and Transitions}{Transitions} to provide + animations when state changes occur. + + \note Setting the state of an object from within another state of the same object is + not allowed. + + \sa {declarative/animation/states}{states example}, {qmlstates}{States}, + {QML Animation and Transitions}{Transitions}, QtDeclarative +*/ +QDeclarativeState::QDeclarativeState(QObject *parent) +: QObject(*(new QDeclarativeStatePrivate), parent) +{ + Q_D(QDeclarativeState); + d->transitionManager.setState(this); +} + +QDeclarativeState::~QDeclarativeState() +{ + Q_D(QDeclarativeState); + if (d->group) + d->group->removeState(this); + + /* + destroying an active state does not return us to the + base state, so we need to clean up our revert list to + prevent leaks. In the future we may want to redconsider + this overall architecture. + */ + for (int i = 0; i < d->revertList.count(); ++i) { + if (d->revertList.at(i).binding()) { + d->revertList.at(i).binding()->destroy(); + } + } +} + +/*! + \qmlproperty string State::name + This property holds the name of the state. + + Each state should have a unique name within its item. +*/ +QString QDeclarativeState::name() const +{ + Q_D(const QDeclarativeState); + return d->name; +} + +void QDeclarativeState::setName(const QString &n) +{ + Q_D(QDeclarativeState); + d->name = n; + d->named = true; +} + +bool QDeclarativeState::isNamed() const +{ + Q_D(const QDeclarativeState); + return d->named; +} + +bool QDeclarativeState::isWhenKnown() const +{ + Q_D(const QDeclarativeState); + 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 \c true when you want the state to + be applied. For example, the following \l Rectangle changes in and out of the "hidden" + state when the \l MouseArea is pressed: + + \snippet doc/src/snippets/declarative/state-when.qml 0 + + If multiple states in a group have \c when clauses that evaluate to \c true + at the same time, the first matching state will be applied. For example, in + the following snippet \c state1 will always be selected rather than + \c state2 when sharedCondition becomes \c true. + \qml + Item { + states: [ + State { name: "state1"; when: sharedCondition }, + State { name: "state2"; when: sharedCondition } + ] + // ... + } + \endqml +*/ +QDeclarativeBinding *QDeclarativeState::when() const +{ + Q_D(const QDeclarativeState); + return d->when; +} + +void QDeclarativeState::setWhen(QDeclarativeBinding *when) +{ + Q_D(QDeclarativeState); + d->when = when; + if (d->group) + d->group->updateAutoState(); +} + +/*! + \qmlproperty string State::extend + This property holds the state that this state extends. + + When a state extends another state, it inherits all the changes of that state. + + The state being extended is treated as the base state in regards to + the changes specified by the extending state. +*/ +QString QDeclarativeState::extends() const +{ + Q_D(const QDeclarativeState); + return d->extends; +} + +void QDeclarativeState::setExtends(const QString &extends) +{ + Q_D(QDeclarativeState); + d->extends = extends; +} + +/*! + \qmlproperty list 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. +*/ +QDeclarativeListProperty QDeclarativeState::changes() +{ + Q_D(QDeclarativeState); + return QDeclarativeListProperty(this, &d->operations, QDeclarativeStatePrivate::operations_append, + QDeclarativeStatePrivate::operations_count, QDeclarativeStatePrivate::operations_at, + QDeclarativeStatePrivate::operations_clear); +} + +int QDeclarativeState::operationCount() const +{ + Q_D(const QDeclarativeState); + return d->operations.count(); +} + +QDeclarativeStateOperation *QDeclarativeState::operationAt(int index) const +{ + Q_D(const QDeclarativeState); + return d->operations.at(index); +} + +QDeclarativeState &QDeclarativeState::operator<<(QDeclarativeStateOperation *op) +{ + Q_D(QDeclarativeState); + d->operations.append(QDeclarativeStatePrivate::OperationGuard(op, &d->operations)); + return *this; +} + +void QDeclarativeStatePrivate::complete() +{ + Q_Q(QDeclarativeState); + + 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" +QDeclarativeStateOperation::ActionList +QDeclarativeStatePrivate::generateActionList(QDeclarativeStateGroup *group) const +{ + QDeclarativeStateOperation::ActionList applyList; + if (inState) + return applyList; + + // Prevent "extends" recursion + inState = true; + + if (!extends.isEmpty()) { + QList states = group->states(); + for (int ii = 0; ii < states.count(); ++ii) + if (states.at(ii)->name() == extends) { + qmlExecuteDeferred(states.at(ii)); + applyList = static_cast(states.at(ii)->d_func())->generateActionList(group); + } + } + + foreach(QDeclarativeStateOperation *op, operations) + applyList << op->actions(); + + inState = false; + return applyList; +} + +QDeclarativeStateGroup *QDeclarativeState::stateGroup() const +{ + Q_D(const QDeclarativeState); + return d->group; +} + +void QDeclarativeState::setStateGroup(QDeclarativeStateGroup *group) +{ + Q_D(QDeclarativeState); + d->group = group; +} + +void QDeclarativeState::cancel() +{ + Q_D(QDeclarativeState); + d->transitionManager.cancel(); +} + +void QDeclarativeAction::deleteFromBinding() +{ + if (fromBinding) { + QDeclarativePropertyPrivate::setBinding(property, 0); + fromBinding->destroy(); + fromBinding = 0; + } +} + +bool QDeclarativeState::containsPropertyInRevertList(QObject *target, const QString &name) const +{ + Q_D(const QDeclarativeState); + + if (isStateActive()) { + QListIterator revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + const QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) + return true; + } + } + + return false; +} + +bool QDeclarativeState::changeValueInRevertList(QObject *target, const QString &name, const QVariant &revertValue) +{ + Q_D(QDeclarativeState); + + if (isStateActive()) { + QMutableListIterator revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) { + simpleAction.setValue(revertValue); + return true; + } + } + } + + return false; +} + +bool QDeclarativeState::changeBindingInRevertList(QObject *target, const QString &name, QDeclarativeAbstractBinding *binding) +{ + Q_D(QDeclarativeState); + + if (isStateActive()) { + QMutableListIterator revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) { + if (simpleAction.binding()) + simpleAction.binding()->destroy(); + + simpleAction.setBinding(binding); + return true; + } + } + } + + return false; +} + +bool QDeclarativeState::removeEntryFromRevertList(QObject *target, const QString &name) +{ + Q_D(QDeclarativeState); + + if (isStateActive()) { + QMutableListIterator revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.property().object() == target && simpleAction.property().name() == name) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property()); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0); + oldBinding->destroy(); + } + + simpleAction.property().write(simpleAction.value()); + if (simpleAction.binding()) + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding()); + + revertListIterator.remove(); + return true; + } + } + } + + return false; +} + +void QDeclarativeState::addEntryToRevertList(const QDeclarativeAction &action) +{ + Q_D(QDeclarativeState); + + QDeclarativeSimpleAction simpleAction(action); + + d->revertList.append(simpleAction); +} + +void QDeclarativeState::removeAllEntriesFromRevertList(QObject *target) +{ + Q_D(QDeclarativeState); + + if (isStateActive()) { + QMutableListIterator revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.property().object() == target) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property()); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0); + oldBinding->destroy(); + } + + simpleAction.property().write(simpleAction.value()); + if (simpleAction.binding()) + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding()); + + revertListIterator.remove(); + } + } + } +} + +void QDeclarativeState::addEntriesToRevertList(const QList &actionList) +{ + Q_D(QDeclarativeState); + if (isStateActive()) { + QList simpleActionList; + + QListIterator actionListIterator(actionList); + while(actionListIterator.hasNext()) { + const QDeclarativeAction &action = actionListIterator.next(); + QDeclarativeSimpleAction simpleAction(action); + action.property.write(action.toValue); + if (!action.toBinding.isNull()) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property()); + if (oldBinding) + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0); + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), action.toBinding.data(), QDeclarativePropertyPrivate::DontRemoveBinding); + } + + simpleActionList.append(simpleAction); + } + + d->revertList.append(simpleActionList); + } +} + +QVariant QDeclarativeState::valueInRevertList(QObject *target, const QString &name) const +{ + Q_D(const QDeclarativeState); + + if (isStateActive()) { + QListIterator revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + const QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) + return simpleAction.value(); + } + } + + return QVariant(); +} + +QDeclarativeAbstractBinding *QDeclarativeState::bindingInRevertList(QObject *target, const QString &name) const +{ + Q_D(const QDeclarativeState); + + if (isStateActive()) { + QListIterator revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + const QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) + return simpleAction.binding(); + } + } + + return 0; +} + +bool QDeclarativeState::isStateActive() const +{ + return stateGroup() && stateGroup()->state() == name(); +} + +void QDeclarativeState::apply(QDeclarativeStateGroup *group, QDeclarativeTransition *trans, QDeclarativeState *revert) +{ + Q_D(QDeclarativeState); + + qmlExecuteDeferred(this); + + cancel(); + if (revert) + revert->cancel(); + d->revertList.clear(); + d->reverting.clear(); + + if (revert) { + QDeclarativeStatePrivate *revertPrivate = + static_cast(revert->d_func()); + d->revertList = revertPrivate->revertList; + revertPrivate->revertList.clear(); + } + + // List of actions caused by this state + QDeclarativeStateOperation::ActionList applyList = d->generateActionList(group); + + // List of actions that need to be reverted to roll back (just) this state + QDeclarativeStatePrivate::SimpleActionList additionalReverts; + // First add the reverse of all the applyList actions + for (int ii = 0; ii < applyList.count(); ++ii) { + QDeclarativeAction &action = applyList[ii]; + + if (action.event) { + if (!action.event->isReversable()) + continue; + bool found = false; + for (int jj = 0; jj < d->revertList.count(); ++jj) { + QDeclarativeActionEvent *event = d->revertList.at(jj).event(); + if (event && event->typeName() == action.event->typeName()) { + if (action.event->override(event)) { + found = true; + + if (action.event != d->revertList.at(jj).event() && action.event->needsCopy()) { + action.event->copyOriginals(d->revertList.at(jj).event()); + + QDeclarativeSimpleAction r(action); + additionalReverts << r; + d->revertList.removeAt(jj); + --jj; + } else if (action.event->isRewindable()) //###why needed? + action.event->saveCurrentValues(); + + break; + } + } + } + if (!found) { + action.event->saveOriginals(); + // Only need to revert the applyList action if the previous + // state doesn't have a higher priority revert already + QDeclarativeSimpleAction r(action); + additionalReverts << r; + } + } else { + bool found = false; + action.fromBinding = QDeclarativePropertyPrivate::binding(action.property); + + for (int jj = 0; jj < d->revertList.count(); ++jj) { + if (d->revertList.at(jj).property() == action.property) { + found = true; + if (d->revertList.at(jj).binding() != action.fromBinding) { + action.deleteFromBinding(); + } + 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 + QDeclarativeSimpleAction r(action); + additionalReverts << r; + } + } + } + } + + // 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()) { + QDeclarativeActionEvent *event = d->revertList.at(ii).event(); + if (!event->isReversable()) + continue; + for (int jj = 0; !found && jj < applyList.count(); ++jj) { + const QDeclarativeAction &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 QDeclarativeAction &action = applyList.at(jj); + if (action.property == d->revertList.at(ii).property()) + found = true; + } + } + if (!found) { + QVariant cur = d->revertList.at(ii).property().read(); + QDeclarativeAbstractBinding *delBinding = + QDeclarativePropertyPrivate::setBinding(d->revertList.at(ii).property(), 0); + if (delBinding) + delBinding->destroy(); + + QDeclarativeAction a; + a.property = d->revertList.at(ii).property(); + a.fromValue = cur; + a.toValue = d->revertList.at(ii).value(); + a.toBinding = QDeclarativeAbstractBinding::getPointer(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; + +#ifndef QT_NO_DEBUG_STREAM + // Output for debugging + if (stateChangeDebug()) { + foreach(const QDeclarativeAction &action, applyList) { + if (action.event) + qWarning() << " QDeclarativeAction event:" << action.event->typeName(); + else + qWarning() << " QDeclarativeAction:" << action.property.object() + << action.property.name() << "From:" << action.fromValue + << "To:" << action.toValue; + } + } +#endif + + d->transitionManager.transition(applyList, trans); +} + +QDeclarativeStateOperation::ActionList QDeclarativeStateOperation::actions() +{ + return ActionList(); +} + +QDeclarativeState *QDeclarativeStateOperation::state() const +{ + Q_D(const QDeclarativeStateOperation); + return d->m_state; +} + +void QDeclarativeStateOperation::setState(QDeclarativeState *state) +{ + Q_D(QDeclarativeStateOperation); + d->m_state = state; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativestate_p.h b/src/declarative/util/qdeclarativestate_p.h new file mode 100644 index 00000000..849361f5 --- /dev/null +++ b/src/declarative/util/qdeclarativestate_p.h @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTATE_H +#define QDECLARATIVESTATE_H + +#include +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeActionEvent; +class QDeclarativeAbstractBinding; +class QDeclarativeBinding; +class QDeclarativeExpression; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeAction +{ +public: + QDeclarativeAction(); + QDeclarativeAction(QObject *, const QString &, const QVariant &); + QDeclarativeAction(QObject *, const QString &, + QDeclarativeContext *, const QVariant &); + + bool restore:1; + bool actionDone:1; + bool reverseEvent:1; + bool deletableToBinding:1; + + QDeclarativeProperty property; + QVariant fromValue; + QVariant toValue; + + QDeclarativeAbstractBinding *fromBinding; + QWeakPointer toBinding; + QDeclarativeActionEvent *event; + + //strictly for matching + QObject *specifiedObject; + QString specifiedProperty; + + void deleteFromBinding(); +}; + +class Q_AUTOTEST_EXPORT QDeclarativeActionEvent +{ +public: + virtual ~QDeclarativeActionEvent(); + virtual QString typeName() const; + + enum Reason { ActualChange, FastForward }; + + virtual void execute(Reason reason = ActualChange); + virtual bool isReversable(); + virtual void reverse(Reason reason = ActualChange); + virtual void saveOriginals() {} + virtual bool needsCopy() { return false; } + virtual void copyOriginals(QDeclarativeActionEvent *) {} + + virtual bool isRewindable() { return isReversable(); } + virtual void rewind() {} + virtual void saveCurrentValues() {} + virtual void saveTargetValues() {} + + virtual bool changesBindings(); + virtual void clearBindings(); + virtual bool override(QDeclarativeActionEvent*other); +}; + +//### rename to QDeclarativeStateChange? +class QDeclarativeStateGroup; +class QDeclarativeState; +class QDeclarativeStateOperationPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeStateOperation : public QObject +{ + Q_OBJECT +public: + QDeclarativeStateOperation(QObject *parent = 0) + : QObject(parent) {} + typedef QList ActionList; + + virtual ActionList actions(); + + QDeclarativeState *state() const; + void setState(QDeclarativeState *state); + +protected: + QDeclarativeStateOperation(QObjectPrivate &dd, QObject *parent = 0); + +private: + Q_DECLARE_PRIVATE(QDeclarativeStateOperation) + Q_DISABLE_COPY(QDeclarativeStateOperation) +}; + +typedef QDeclarativeStateOperation::ActionList QDeclarativeStateActions; + +class QDeclarativeTransition; +class QDeclarativeStatePrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeState : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(QDeclarativeBinding *when READ when WRITE setWhen) + Q_PROPERTY(QString extend READ extends WRITE setExtends) + Q_PROPERTY(QDeclarativeListProperty changes READ changes) + Q_CLASSINFO("DefaultProperty", "changes") + Q_CLASSINFO("DeferredPropertyNames", "changes") + +public: + QDeclarativeState(QObject *parent=0); + virtual ~QDeclarativeState(); + + QString name() const; + void setName(const QString &); + bool isNamed() const; + + /*'when' is a QDeclarativeBinding to limit state changes oscillation + due to the unpredictable order of evaluation of bound expressions*/ + bool isWhenKnown() const; + QDeclarativeBinding *when() const; + void setWhen(QDeclarativeBinding *); + + QString extends() const; + void setExtends(const QString &); + + QDeclarativeListProperty changes(); + int operationCount() const; + QDeclarativeStateOperation *operationAt(int) const; + + QDeclarativeState &operator<<(QDeclarativeStateOperation *); + + void apply(QDeclarativeStateGroup *, QDeclarativeTransition *, QDeclarativeState *revert); + void cancel(); + + QDeclarativeStateGroup *stateGroup() const; + void setStateGroup(QDeclarativeStateGroup *); + + bool containsPropertyInRevertList(QObject *target, const QString &name) const; + bool changeValueInRevertList(QObject *target, const QString &name, const QVariant &revertValue); + bool changeBindingInRevertList(QObject *target, const QString &name, QDeclarativeAbstractBinding *binding); + bool removeEntryFromRevertList(QObject *target, const QString &name); + void addEntryToRevertList(const QDeclarativeAction &action); + void removeAllEntriesFromRevertList(QObject *target); + void addEntriesToRevertList(const QList &actions); + QVariant valueInRevertList(QObject *target, const QString &name) const; + QDeclarativeAbstractBinding *bindingInRevertList(QObject *target, const QString &name) const; + + bool isStateActive() const; + +Q_SIGNALS: + void completed(); + +private: + Q_DECLARE_PRIVATE(QDeclarativeState) + Q_DISABLE_COPY(QDeclarativeState) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeStateOperation) +QML_DECLARE_TYPE(QDeclarativeState) + +QT_END_HEADER + +#endif // QDECLARATIVESTATE_H diff --git a/src/declarative/util/qdeclarativestate_p_p.h b/src/declarative/util/qdeclarativestate_p_p.h new file mode 100644 index 00000000..192d02db --- /dev/null +++ b/src/declarative/util/qdeclarativestate_p_p.h @@ -0,0 +1,255 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTATE_P_H +#define QDECLARATIVESTATE_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 "private/qdeclarativestate_p.h" + +#include "private/qdeclarativeanimation_p_p.h" +#include "private/qdeclarativetransitionmanager_p_p.h" + +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeSimpleAction +{ +public: + enum State { StartState, EndState }; + QDeclarativeSimpleAction(const QDeclarativeAction &a, State state = StartState) + { + m_property = a.property; + m_specifiedObject = a.specifiedObject; + m_specifiedProperty = a.specifiedProperty; + m_event = a.event; + if (state == StartState) { + m_value = a.fromValue; + if (QDeclarativePropertyPrivate::binding(m_property)) { + m_binding = QDeclarativeAbstractBinding::getPointer(QDeclarativePropertyPrivate::binding(m_property)); + } + m_reverseEvent = true; + } else { + m_value = a.toValue; + m_binding = a.toBinding; + m_reverseEvent = false; + } + } + + ~QDeclarativeSimpleAction() + { + } + + QDeclarativeSimpleAction(const QDeclarativeSimpleAction &other) + : m_property(other.m_property), + m_value(other.m_value), + m_binding(QDeclarativeAbstractBinding::getPointer(other.binding())), + m_specifiedObject(other.m_specifiedObject), + m_specifiedProperty(other.m_specifiedProperty), + m_event(other.m_event), + m_reverseEvent(other.m_reverseEvent) + { + } + + QDeclarativeSimpleAction &operator =(const QDeclarativeSimpleAction &other) + { + m_property = other.m_property; + m_value = other.m_value; + m_binding = QDeclarativeAbstractBinding::getPointer(other.binding()); + m_specifiedObject = other.m_specifiedObject; + m_specifiedProperty = other.m_specifiedProperty; + m_event = other.m_event; + m_reverseEvent = other.m_reverseEvent; + + return *this; + } + + void setProperty(const QDeclarativeProperty &property) + { + m_property = property; + } + + const QDeclarativeProperty &property() const + { + return m_property; + } + + void setValue(const QVariant &value) + { + m_value = value; + } + + const QVariant &value() const + { + return m_value; + } + + void setBinding(QDeclarativeAbstractBinding *binding) + { + m_binding = QDeclarativeAbstractBinding::getPointer(binding); + } + + QDeclarativeAbstractBinding *binding() const + { + return m_binding.data(); + } + + QObject *specifiedObject() const + { + return m_specifiedObject; + } + + const QString &specifiedProperty() const + { + return m_specifiedProperty; + } + + QDeclarativeActionEvent *event() const + { + return m_event; + } + + bool reverseEvent() const + { + return m_reverseEvent; + } + +private: + QDeclarativeProperty m_property; + QVariant m_value; + QDeclarativeAbstractBinding::Pointer m_binding; + QObject *m_specifiedObject; + QString m_specifiedProperty; + QDeclarativeActionEvent *m_event; + bool m_reverseEvent; +}; + +class QDeclarativeStateOperationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeStateOperation) + +public: + + QDeclarativeStateOperationPrivate() + : m_state(0) {} + + QDeclarativeState *m_state; +}; + +class QDeclarativeStatePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeState) + +public: + QDeclarativeStatePrivate() + : when(0), named(false), inState(false), group(0) {} + + typedef QList SimpleActionList; + + QString name; + QDeclarativeBinding *when; + bool named; + + struct OperationGuard : public QDeclarativeGuard + { + OperationGuard(QObject *obj, QList *l) : list(l) { + setObject(static_cast(obj)); + } + QList *list; + void objectDestroyed(QDeclarativeStateOperation *) { + // we assume priv will always be destroyed after objectDestroyed calls + list->removeOne(*this); + } + }; + QList operations; + + static void operations_append(QDeclarativeListProperty *prop, QDeclarativeStateOperation *op) { + QList *list = static_cast *>(prop->data); + op->setState(qobject_cast(prop->object)); + list->append(OperationGuard(op, list)); + } + static void operations_clear(QDeclarativeListProperty *prop) { + QList *list = static_cast *>(prop->data); + QMutableListIterator listIterator(*list); + while(listIterator.hasNext()) + listIterator.next()->setState(0); + list->clear(); + } + static int operations_count(QDeclarativeListProperty *prop) { + QList *list = static_cast *>(prop->data); + return list->count(); + } + static QDeclarativeStateOperation *operations_at(QDeclarativeListProperty *prop, int index) { + QList *list = static_cast *>(prop->data); + return list->at(index); + } + + QDeclarativeTransitionManager transitionManager; + + SimpleActionList revertList; + QList reverting; + QString extends; + mutable bool inState; + QDeclarativeStateGroup *group; + + QDeclarativeStateOperation::ActionList generateActionList(QDeclarativeStateGroup *) const; + void complete(); +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVESTATE_P_H diff --git a/src/declarative/util/qdeclarativestategroup.cpp b/src/declarative/util/qdeclarativestategroup.cpp new file mode 100644 index 00000000..29be45e9 --- /dev/null +++ b/src/declarative/util/qdeclarativestategroup.cpp @@ -0,0 +1,502 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativestategroup_p.h" + +#include "private/qdeclarativetransition_p.h" +#include "private/qdeclarativestate_p_p.h" + +#include +#include + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +class QDeclarativeStateGroupPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeStateGroup) +public: + QDeclarativeStateGroupPrivate() + : nullState(0), componentComplete(true), + ignoreTrans(false), applyingState(false), unnamedCount(0) {} + + QString currentState; + QDeclarativeState *nullState; + + static void append_state(QDeclarativeListProperty *list, QDeclarativeState *state); + static int count_state(QDeclarativeListProperty *list); + static QDeclarativeState *at_state(QDeclarativeListProperty *list, int index); + static void clear_states(QDeclarativeListProperty *list); + + static void append_transition(QDeclarativeListProperty *list, QDeclarativeTransition *state); + static int count_transitions(QDeclarativeListProperty *list); + static QDeclarativeTransition *at_transition(QDeclarativeListProperty *list, int index); + static void clear_transitions(QDeclarativeListProperty *list); + + QList states; + QList transitions; + + bool componentComplete; + bool ignoreTrans; + bool applyingState; + int unnamedCount; + + QDeclarativeTransition *findTransition(const QString &from, const QString &to); + void setCurrentStateInternal(const QString &state, bool = false); + bool updateAutoState(); +}; + +/*! + \qmlclass StateGroup QDeclarativeStateGroup + \ingroup qml-state-elements + \since 4.7 + \brief The StateGroup element provides state support for non-Item elements. + + Item (and all derived elements) provides built in support for states and transitions + via its \l{Item::state}{state}, \l{Item::states}{states} and \l{Item::transitions}{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} {QML Animation and Transitions}{Transitions}, {QtDeclarative} +*/ + +QDeclarativeStateGroup::QDeclarativeStateGroup(QObject *parent) + : QObject(*(new QDeclarativeStateGroupPrivate), parent) +{ +} + +QDeclarativeStateGroup::~QDeclarativeStateGroup() +{ + Q_D(const QDeclarativeStateGroup); + for (int i = 0; i < d->states.count(); ++i) + d->states.at(i)->setStateGroup(0); +} + +QList QDeclarativeStateGroup::states() const +{ + Q_D(const QDeclarativeStateGroup); + return d->states; +} + +/*! + \qmlproperty list StateGroup::states + This property holds a list of states defined by the state group. + + \qml + StateGroup { + states: [ + State { + // State definition... + }, + State { + // ... + } + // Other states... + ] + } + \endqml + + \sa {qmlstate}{States} +*/ +QDeclarativeListProperty QDeclarativeStateGroup::statesProperty() +{ + Q_D(QDeclarativeStateGroup); + return QDeclarativeListProperty(this, &d->states, &QDeclarativeStateGroupPrivate::append_state, + &QDeclarativeStateGroupPrivate::count_state, + &QDeclarativeStateGroupPrivate::at_state, + &QDeclarativeStateGroupPrivate::clear_states); +} + +void QDeclarativeStateGroupPrivate::append_state(QDeclarativeListProperty *list, QDeclarativeState *state) +{ + QDeclarativeStateGroup *_this = static_cast(list->object); + if (state) { + _this->d_func()->states.append(state); + state->setStateGroup(_this); + } + +} + +int QDeclarativeStateGroupPrivate::count_state(QDeclarativeListProperty *list) +{ + QDeclarativeStateGroup *_this = static_cast(list->object); + return _this->d_func()->states.count(); +} + +QDeclarativeState *QDeclarativeStateGroupPrivate::at_state(QDeclarativeListProperty *list, int index) +{ + QDeclarativeStateGroup *_this = static_cast(list->object); + return _this->d_func()->states.at(index); +} + +void QDeclarativeStateGroupPrivate::clear_states(QDeclarativeListProperty *list) +{ + QDeclarativeStateGroup *_this = static_cast(list->object); + _this->d_func()->setCurrentStateInternal(QString(), true); + for (int i = 0; i < _this->d_func()->states.count(); ++i) { + _this->d_func()->states.at(i)->setStateGroup(0); + } + _this->d_func()->states.clear(); +} + +/*! + \qmlproperty list StateGroup::transitions + This property holds a list of transitions defined by the state group. + + \qml + StateGroup { + transitions: [ + Transition { + // ... + }, + Transition { + // ... + } + // ... + ] + } + \endqml + + \sa {QML Animation and Transitions}{Transitions} +*/ +QDeclarativeListProperty QDeclarativeStateGroup::transitionsProperty() +{ + Q_D(QDeclarativeStateGroup); + return QDeclarativeListProperty(this, &d->transitions, &QDeclarativeStateGroupPrivate::append_transition, + &QDeclarativeStateGroupPrivate::count_transitions, + &QDeclarativeStateGroupPrivate::at_transition, + &QDeclarativeStateGroupPrivate::clear_transitions); +} + +void QDeclarativeStateGroupPrivate::append_transition(QDeclarativeListProperty *list, QDeclarativeTransition *trans) +{ + QDeclarativeStateGroup *_this = static_cast(list->object); + if (trans) + _this->d_func()->transitions.append(trans); +} + +int QDeclarativeStateGroupPrivate::count_transitions(QDeclarativeListProperty *list) +{ + QDeclarativeStateGroup *_this = static_cast(list->object); + return _this->d_func()->transitions.count(); +} + +QDeclarativeTransition *QDeclarativeStateGroupPrivate::at_transition(QDeclarativeListProperty *list, int index) +{ + QDeclarativeStateGroup *_this = static_cast(list->object); + return _this->d_func()->transitions.at(index); +} + +void QDeclarativeStateGroupPrivate::clear_transitions(QDeclarativeListProperty *list) +{ + QDeclarativeStateGroup *_this = static_cast(list->object); + _this->d_func()->transitions.clear(); +} + +/*! + \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: + + \js + function toggle() { + if (button.state == 'On') + button.state = 'Off'; + else + button.state = 'On'; + } + \endjs + + 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 QDeclarativeStateGroup::state() const +{ + Q_D(const QDeclarativeStateGroup); + return d->currentState; +} + +void QDeclarativeStateGroup::setState(const QString &state) +{ + Q_D(QDeclarativeStateGroup); + if (d->currentState == state) + return; + + d->setCurrentStateInternal(state); +} + +void QDeclarativeStateGroup::classBegin() +{ + Q_D(QDeclarativeStateGroup); + d->componentComplete = false; +} + +void QDeclarativeStateGroup::componentComplete() +{ + Q_D(QDeclarativeStateGroup); + d->componentComplete = true; + + for (int ii = 0; ii < d->states.count(); ++ii) { + QDeclarativeState *state = d->states.at(ii); + if (!state->isNamed()) + state->setName(QLatin1String("anonymousState") % QString::number(++d->unnamedCount)); + } + + if (d->updateAutoState()) { + return; + } else if (!d->currentState.isEmpty()) { + QString cs = d->currentState; + d->currentState.clear(); + d->setCurrentStateInternal(cs, true); + } +} + +/*! + Returns true if the state was changed, otherwise false. +*/ +bool QDeclarativeStateGroup::updateAutoState() +{ + Q_D(QDeclarativeStateGroup); + return d->updateAutoState(); +} + +bool QDeclarativeStateGroupPrivate::updateAutoState() +{ + Q_Q(QDeclarativeStateGroup); + if (!componentComplete) + return false; + + bool revert = false; + for (int ii = 0; ii < states.count(); ++ii) { + QDeclarativeState *state = states.at(ii); + if (state->isWhenKnown()) { + if (state->isNamed()) { + if (state->when() && state->when()->evaluate().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.isEmpty(); + q->setState(QString()); + return rv; + } else { + return false; + } +} + +QDeclarativeTransition *QDeclarativeStateGroupPrivate::findTransition(const QString &from, const QString &to) +{ + QDeclarativeTransition *highest = 0; + int score = 0; + bool reversed = false; + bool done = false; + + for (int ii = 0; !done && ii < transitions.count(); ++ii) { + QDeclarativeTransition *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 QDeclarativeStateGroupPrivate::setCurrentStateInternal(const QString &state, + bool ignoreTrans) +{ + Q_Q(QDeclarativeStateGroup); + if (!componentComplete) { + currentState = state; + return; + } + + if (applyingState) { + qmlInfo(q) << "Can't apply a state change as part of a state definition."; + return; + } + + applyingState = true; + + QDeclarativeTransition *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(); + } + + QDeclarativeState *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); + + QDeclarativeState *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 QDeclarativeState; QDeclarative_setParent_noEvent(nullState, q); } + if (!oldState) oldState = nullState; + if (!newState) newState = nullState; + } + + newState->apply(q, transition, oldState); + applyingState = false; + if (!transition) + static_cast(QObjectPrivate::get(newState))->complete(); +} + +QDeclarativeState *QDeclarativeStateGroup::findState(const QString &name) const +{ + Q_D(const QDeclarativeStateGroup); + for (int i = 0; i < d->states.count(); ++i) { + QDeclarativeState *state = d->states.at(i); + if (state->name() == name) + return state; + } + + return 0; +} + +void QDeclarativeStateGroup::removeState(QDeclarativeState *state) +{ + Q_D(QDeclarativeStateGroup); + d->states.removeOne(state); +} + +QT_END_NAMESPACE + + diff --git a/src/declarative/util/qdeclarativestategroup_p.h b/src/declarative/util/qdeclarativestategroup_p.h new file mode 100644 index 00000000..c99d6361 --- /dev/null +++ b/src/declarative/util/qdeclarativestategroup_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTATEGROUP_H +#define QDECLARATIVESTATEGROUP_H + +#include "private/qdeclarativestate_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeStateGroupPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeStateGroup : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_INTERFACES(QDeclarativeParserStatus) + Q_DECLARE_PRIVATE(QDeclarativeStateGroup) + + Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged) + Q_PROPERTY(QDeclarativeListProperty states READ statesProperty DESIGNABLE false) + Q_PROPERTY(QDeclarativeListProperty transitions READ transitionsProperty DESIGNABLE false) + +public: + QDeclarativeStateGroup(QObject * = 0); + virtual ~QDeclarativeStateGroup(); + + QString state() const; + void setState(const QString &); + + QDeclarativeListProperty statesProperty(); + QList states() const; + + QDeclarativeListProperty transitionsProperty(); + + QDeclarativeState *findState(const QString &name) const; + + virtual void classBegin(); + virtual void componentComplete(); +Q_SIGNALS: + void stateChanged(const QString &); + +private: + friend class QDeclarativeState; + bool updateAutoState(); + void removeState(QDeclarativeState *state); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeStateGroup) + +QT_END_HEADER + +#endif // QDECLARATIVESTATEGROUP_H diff --git a/src/declarative/util/qdeclarativestateoperations.cpp b/src/declarative/util/qdeclarativestateoperations.cpp new file mode 100644 index 00000000..30c5730d --- /dev/null +++ b/src/declarative/util/qdeclarativestateoperations.cpp @@ -0,0 +1,1587 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativestateoperations_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativeproperty_p.h" +#include "private/qdeclarativebinding_p.h" +#include "private/qdeclarativestate_p_p.h" + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeParentChangePrivate : public QDeclarativeStateOperationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeParentChange) +public: + QDeclarativeParentChangePrivate() : target(0), parent(0), origParent(0), origStackBefore(0), + rewindParent(0), rewindStackBefore(0) {} + + QDeclarativeItem *target; + QDeclarativeGuard parent; + QDeclarativeGuard origParent; + QDeclarativeGuard origStackBefore; + QDeclarativeItem *rewindParent; + QDeclarativeItem *rewindStackBefore; + + QDeclarativeNullableValue xString; + QDeclarativeNullableValue yString; + QDeclarativeNullableValue widthString; + QDeclarativeNullableValue heightString; + QDeclarativeNullableValue scaleString; + QDeclarativeNullableValue rotationString; + + QDeclarativeNullableValue x; + QDeclarativeNullableValue y; + QDeclarativeNullableValue width; + QDeclarativeNullableValue height; + QDeclarativeNullableValue scale; + QDeclarativeNullableValue rotation; + + void doChange(QDeclarativeItem *targetParent, QDeclarativeItem *stackBefore = 0); +}; + +void QDeclarativeParentChangePrivate::doChange(QDeclarativeItem *targetParent, QDeclarativeItem *stackBefore) +{ + if (targetParent && target && target->parentItem()) { + Q_Q(QDeclarativeParentChange); + bool ok; + const QTransform &transform = target->parentItem()->itemTransform(targetParent, &ok); + if (transform.type() >= QTransform::TxShear || !ok) { + qmlInfo(q) << QDeclarativeParentChange::tr("Unable to preserve appearance under complex transform"); + ok = false; + } + + qreal scale = 1; + qreal rotation = 0; + bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0); + if (ok && !isRotate) { + if (transform.m11() == transform.m22()) + scale = transform.m11(); + else { + qmlInfo(q) << QDeclarativeParentChange::tr("Unable to preserve appearance under non-uniform scale"); + ok = false; + } + } else if (ok && isRotate) { + if (transform.m11() == transform.m22()) + scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12()); + else { + qmlInfo(q) << QDeclarativeParentChange::tr("Unable to preserve appearance under non-uniform scale"); + ok = false; + } + + if (scale != 0) + rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/qreal(M_PI); + else { + qmlInfo(q) << QDeclarativeParentChange::tr("Unable to preserve appearance under scale of 0"); + ok = false; + } + } + + const QPointF &point = transform.map(QPointF(target->x(),target->y())); + qreal x = point.x(); + qreal y = point.y(); + + // setParentItem will update the transformOriginPoint if needed + target->setParentItem(targetParent); + + if (ok && target->transformOrigin() != QDeclarativeItem::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); + const QPointF &offset = t.map(QPointF(0,0)); + x += offset.x(); + y += offset.y(); + } + + if (ok) { + //qDebug() << x << y << rotation << scale; + target->setX(x); + target->setY(y); + target->setRotation(target->rotation() + rotation); + target->setScale(target->scale() * 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 QDeclarativeParentChange + \ingroup qml-state-elements + \brief The ParentChange element allows you to reparent an Item in a state change. + + ParentChange reparents an item while preserving its visual appearance (position, size, + rotation, and scale) on screen. You can then specify a transition to move/resize/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 (i.e. items in the common ancestor tree + for the original and new parent). + + The example below displays a large red rectangle and a small blue rectangle, side by side. + When the \c blueRect is clicked, it changes to the "reparented" state: its parent is changed to \c redRect and it is + positioned at (10, 10) within the red rectangle, as specified in the ParentChange. + + \snippet doc/src/snippets/declarative/parentchange.qml 0 + + \image parentchange.png + + You can specify at which point in a transition you want a ParentChange to occur by + using a ParentAnimation. +*/ + + +QDeclarativeParentChange::QDeclarativeParentChange(QObject *parent) + : QDeclarativeStateOperation(*(new QDeclarativeParentChangePrivate), parent) +{ +} + +QDeclarativeParentChange::~QDeclarativeParentChange() +{ +} + +/*! + \qmlproperty real ParentChange::x + \qmlproperty real ParentChange::y + \qmlproperty real ParentChange::width + \qmlproperty real ParentChange::height + \qmlproperty real ParentChange::scale + \qmlproperty real ParentChange::rotation + These properties hold the new position, size, scale, and rotation + for the item in this state. +*/ +QDeclarativeScriptString QDeclarativeParentChange::x() const +{ + Q_D(const QDeclarativeParentChange); + return d->xString.value; +} + +void tryReal(QDeclarativeNullableValue &value, const QString &string) +{ + bool ok = false; + qreal realValue = string.toFloat(&ok); + if (ok) + value = realValue; + else + value.invalidate(); +} + +void QDeclarativeParentChange::setX(QDeclarativeScriptString x) +{ + Q_D(QDeclarativeParentChange); + d->xString = x; + tryReal(d->x, x.script()); +} + +bool QDeclarativeParentChange::xIsSet() const +{ + Q_D(const QDeclarativeParentChange); + return d->xString.isValid(); +} + +QDeclarativeScriptString QDeclarativeParentChange::y() const +{ + Q_D(const QDeclarativeParentChange); + return d->yString.value; +} + +void QDeclarativeParentChange::setY(QDeclarativeScriptString y) +{ + Q_D(QDeclarativeParentChange); + d->yString = y; + tryReal(d->y, y.script()); +} + +bool QDeclarativeParentChange::yIsSet() const +{ + Q_D(const QDeclarativeParentChange); + return d->yString.isValid(); +} + +QDeclarativeScriptString QDeclarativeParentChange::width() const +{ + Q_D(const QDeclarativeParentChange); + return d->widthString.value; +} + +void QDeclarativeParentChange::setWidth(QDeclarativeScriptString width) +{ + Q_D(QDeclarativeParentChange); + d->widthString = width; + tryReal(d->width, width.script()); +} + +bool QDeclarativeParentChange::widthIsSet() const +{ + Q_D(const QDeclarativeParentChange); + return d->widthString.isValid(); +} + +QDeclarativeScriptString QDeclarativeParentChange::height() const +{ + Q_D(const QDeclarativeParentChange); + return d->heightString.value; +} + +void QDeclarativeParentChange::setHeight(QDeclarativeScriptString height) +{ + Q_D(QDeclarativeParentChange); + d->heightString = height; + tryReal(d->height, height.script()); +} + +bool QDeclarativeParentChange::heightIsSet() const +{ + Q_D(const QDeclarativeParentChange); + return d->heightString.isValid(); +} + +QDeclarativeScriptString QDeclarativeParentChange::scale() const +{ + Q_D(const QDeclarativeParentChange); + return d->scaleString.value; +} + +void QDeclarativeParentChange::setScale(QDeclarativeScriptString scale) +{ + Q_D(QDeclarativeParentChange); + d->scaleString = scale; + tryReal(d->scale, scale.script()); +} + +bool QDeclarativeParentChange::scaleIsSet() const +{ + Q_D(const QDeclarativeParentChange); + return d->scaleString.isValid(); +} + +QDeclarativeScriptString QDeclarativeParentChange::rotation() const +{ + Q_D(const QDeclarativeParentChange); + return d->rotationString.value; +} + +void QDeclarativeParentChange::setRotation(QDeclarativeScriptString rotation) +{ + Q_D(QDeclarativeParentChange); + d->rotationString = rotation; + tryReal(d->rotation, rotation.script()); +} + +bool QDeclarativeParentChange::rotationIsSet() const +{ + Q_D(const QDeclarativeParentChange); + return d->rotationString.isValid(); +} + +QDeclarativeItem *QDeclarativeParentChange::originalParent() const +{ + Q_D(const QDeclarativeParentChange); + return d->origParent; +} + +/*! + \qmlproperty Item ParentChange::target + This property holds the item to be reparented +*/ + +QDeclarativeItem *QDeclarativeParentChange::object() const +{ + Q_D(const QDeclarativeParentChange); + return d->target; +} + +void QDeclarativeParentChange::setObject(QDeclarativeItem *target) +{ + Q_D(QDeclarativeParentChange); + d->target = target; +} + +/*! + \qmlproperty Item ParentChange::parent + This property holds the new parent for the item in this state. +*/ + +QDeclarativeItem *QDeclarativeParentChange::parent() const +{ + Q_D(const QDeclarativeParentChange); + return d->parent; +} + +void QDeclarativeParentChange::setParent(QDeclarativeItem *parent) +{ + Q_D(QDeclarativeParentChange); + d->parent = parent; +} + +QDeclarativeStateOperation::ActionList QDeclarativeParentChange::actions() +{ + Q_D(QDeclarativeParentChange); + if (!d->target || !d->parent) + return ActionList(); + + ActionList actions; + + QDeclarativeAction a; + a.event = this; + actions << a; + + QDeclarativeContext *ctxt = qmlContext(this); + + if (d->xString.isValid()) { + if (d->x.isValid()) { + QDeclarativeAction xa(d->target, QLatin1String("x"), ctxt, d->x.value); + actions << xa; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->xString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("x"), ctxt)); + QDeclarativeAction xa; + xa.property = newBinding->property(); + xa.toBinding = newBinding; + xa.fromValue = xa.property.read(); + xa.deletableToBinding = true; + actions << xa; + } + } + + if (d->yString.isValid()) { + if (d->y.isValid()) { + QDeclarativeAction ya(d->target, QLatin1String("y"), ctxt, d->y.value); + actions << ya; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->yString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("y"), ctxt)); + QDeclarativeAction ya; + ya.property = newBinding->property(); + ya.toBinding = newBinding; + ya.fromValue = ya.property.read(); + ya.deletableToBinding = true; + actions << ya; + } + } + + if (d->scaleString.isValid()) { + if (d->scale.isValid()) { + QDeclarativeAction sa(d->target, QLatin1String("scale"), ctxt, d->scale.value); + actions << sa; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->scaleString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("scale"), ctxt)); + QDeclarativeAction sa; + sa.property = newBinding->property(); + sa.toBinding = newBinding; + sa.fromValue = sa.property.read(); + sa.deletableToBinding = true; + actions << sa; + } + } + + if (d->rotationString.isValid()) { + if (d->rotation.isValid()) { + QDeclarativeAction ra(d->target, QLatin1String("rotation"), ctxt, d->rotation.value); + actions << ra; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->rotationString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("rotation"), ctxt)); + QDeclarativeAction ra; + ra.property = newBinding->property(); + ra.toBinding = newBinding; + ra.fromValue = ra.property.read(); + ra.deletableToBinding = true; + actions << ra; + } + } + + if (d->widthString.isValid()) { + if (d->width.isValid()) { + QDeclarativeAction wa(d->target, QLatin1String("width"), ctxt, d->width.value); + actions << wa; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->widthString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("width"), ctxt)); + QDeclarativeAction wa; + wa.property = newBinding->property(); + wa.toBinding = newBinding; + wa.fromValue = wa.property.read(); + wa.deletableToBinding = true; + actions << wa; + } + } + + if (d->heightString.isValid()) { + if (d->height.isValid()) { + QDeclarativeAction ha(d->target, QLatin1String("height"), ctxt, d->height.value); + actions << ha; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->heightString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("height"), ctxt)); + QDeclarativeAction ha; + ha.property = newBinding->property(); + ha.toBinding = newBinding; + ha.fromValue = ha.property.read(); + ha.deletableToBinding = true; + actions << ha; + } + } + + return actions; +} + +class AccessibleFxItem : public QDeclarativeItem +{ + Q_OBJECT + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeItem) +public: + int siblingIndex() { + Q_D(QDeclarativeItem); + return d->siblingIndex; + } +}; + +void QDeclarativeParentChange::saveOriginals() +{ + Q_D(QDeclarativeParentChange); + saveCurrentValues(); + d->origParent = d->rewindParent; + d->origStackBefore = d->rewindStackBefore; +} + +/*void QDeclarativeParentChange::copyOriginals(QDeclarativeActionEvent *other) +{ + Q_D(QDeclarativeParentChange); + QDeclarativeParentChange *pc = static_cast(other); + + d->origParent = pc->d_func()->rewindParent; + d->origStackBefore = pc->d_func()->rewindStackBefore; + + saveCurrentValues(); +}*/ + +void QDeclarativeParentChange::execute(Reason) +{ + Q_D(QDeclarativeParentChange); + d->doChange(d->parent); +} + +bool QDeclarativeParentChange::isReversable() +{ + return true; +} + +void QDeclarativeParentChange::reverse(Reason) +{ + Q_D(QDeclarativeParentChange); + d->doChange(d->origParent, d->origStackBefore); +} + +QString QDeclarativeParentChange::typeName() const +{ + return QLatin1String("ParentChange"); +} + +bool QDeclarativeParentChange::override(QDeclarativeActionEvent*other) +{ + Q_D(QDeclarativeParentChange); + if (other->typeName() != QLatin1String("ParentChange")) + return false; + if (QDeclarativeParentChange *otherPC = static_cast(other)) + return (d->target == otherPC->object()); + return false; +} + +void QDeclarativeParentChange::saveCurrentValues() +{ + Q_D(QDeclarativeParentChange); + if (!d->target) { + d->rewindParent = 0; + d->rewindStackBefore = 0; + return; + } + + d->rewindParent = d->target->parentItem(); + d->rewindStackBefore = 0; + + if (!d->rewindParent) + return; + + //try to determine the item's original stack position so we can restore it + int siblingIndex = ((AccessibleFxItem*)d->target)->siblingIndex() + 1; + QList children = d->rewindParent->childItems(); + for (int i = 0; i < children.count(); ++i) { + QDeclarativeItem *child = qobject_cast(children.at(i)); + if (!child) + continue; + if (((AccessibleFxItem*)child)->siblingIndex() == siblingIndex) { + d->rewindStackBefore = child; + break; + } + } +} + +void QDeclarativeParentChange::rewind() +{ + Q_D(QDeclarativeParentChange); + d->doChange(d->rewindParent, d->rewindStackBefore); +} + +class QDeclarativeStateChangeScriptPrivate : public QDeclarativeStateOperationPrivate +{ +public: + QDeclarativeStateChangeScriptPrivate() {} + + QDeclarativeScriptString script; + QString name; +}; + +/*! + \qmlclass StateChangeScript QDeclarativeStateChangeScript + \ingroup qml-state-elements + \brief The StateChangeScript element allows you to run a script in a state. + + A StateChangeScript is run upon entering a state. You can optionally use + ScriptAction to specify the point in the transition at which + the StateChangeScript should to be run. + + \snippet snippets/declarative/states/statechangescript.qml state and transition + + \sa ScriptAction +*/ + +QDeclarativeStateChangeScript::QDeclarativeStateChangeScript(QObject *parent) +: QDeclarativeStateOperation(*(new QDeclarativeStateChangeScriptPrivate), parent) +{ +} + +QDeclarativeStateChangeScript::~QDeclarativeStateChangeScript() +{ +} + +/*! + \qmlproperty script StateChangeScript::script + This property holds the script to run when the state is current. +*/ +QDeclarativeScriptString QDeclarativeStateChangeScript::script() const +{ + Q_D(const QDeclarativeStateChangeScript); + return d->script; +} + +void QDeclarativeStateChangeScript::setScript(const QDeclarativeScriptString &s) +{ + Q_D(QDeclarativeStateChangeScript); + d->script = s; +} + +/*! + \qmlproperty string StateChangeScript::name + This property holds the name of the script. This name can be used by a + ScriptAction to target a specific script. + + \sa ScriptAction::scriptName +*/ +QString QDeclarativeStateChangeScript::name() const +{ + Q_D(const QDeclarativeStateChangeScript); + return d->name; +} + +void QDeclarativeStateChangeScript::setName(const QString &n) +{ + Q_D(QDeclarativeStateChangeScript); + d->name = n; +} + +void QDeclarativeStateChangeScript::execute(Reason) +{ + Q_D(QDeclarativeStateChangeScript); + const QString &script = d->script.script(); + if (!script.isEmpty()) { + QDeclarativeExpression expr(d->script.context(), d->script.scopeObject(), script); + QDeclarativeData *ddata = QDeclarativeData::get(this); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expr.setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + expr.evaluate(); + if (expr.hasError()) + qmlInfo(this, expr.error()); + } +} + +QDeclarativeStateChangeScript::ActionList QDeclarativeStateChangeScript::actions() +{ + ActionList rv; + QDeclarativeAction a; + a.event = this; + rv << a; + return rv; +} + +QString QDeclarativeStateChangeScript::typeName() const +{ + return QLatin1String("StateChangeScript"); +} + +/*! + \qmlclass AnchorChanges QDeclarativeAnchorChanges + \ingroup qml-state-elements + \brief The AnchorChanges element allows you to change the anchors of an item in a state. + + The AnchorChanges element is used to modify the anchors of an item in a \l State. + + AnchorChanges cannot be used to modify the margins on an item. For this, use + PropertyChanges intead. + + In the following example we change the top and bottom anchors of an item + using AnchorChanges, and the top and bottom anchor margins using + PropertyChanges: + + \snippet doc/src/snippets/declarative/anchorchanges.qml 0 + + \image anchorchanges.png + + AnchorChanges can be animated using AnchorAnimation. + \qml + //animate our anchor changes + Transition { + AnchorAnimation {} + } + \endqml + + Margin animations can be animated using NumberAnimation. + + For more information on anchors see \l {anchor-layout}{Anchor Layouts}. +*/ + +class QDeclarativeAnchorSetPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeAnchorSet) +public: + QDeclarativeAnchorSetPrivate() + : usedAnchors(0), resetAnchors(0), fill(0), + centerIn(0)/*, leftMargin(0), rightMargin(0), topMargin(0), bottomMargin(0), + margins(0), vCenterOffset(0), hCenterOffset(0), baselineOffset(0)*/ + { + } + + QDeclarativeAnchors::Anchors usedAnchors; + QDeclarativeAnchors::Anchors resetAnchors; + + QDeclarativeItem *fill; + QDeclarativeItem *centerIn; + + QDeclarativeScriptString leftScript; + QDeclarativeScriptString rightScript; + QDeclarativeScriptString topScript; + QDeclarativeScriptString bottomScript; + QDeclarativeScriptString hCenterScript; + QDeclarativeScriptString vCenterScript; + QDeclarativeScriptString baselineScript; + + /*qreal leftMargin; + qreal rightMargin; + qreal topMargin; + qreal bottomMargin; + qreal margins; + qreal vCenterOffset; + qreal hCenterOffset; + qreal baselineOffset;*/ +}; + +QDeclarativeAnchorSet::QDeclarativeAnchorSet(QObject *parent) + : QObject(*new QDeclarativeAnchorSetPrivate, parent) +{ +} + +QDeclarativeAnchorSet::~QDeclarativeAnchorSet() +{ +} + +QDeclarativeScriptString QDeclarativeAnchorSet::top() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->topScript; +} + +void QDeclarativeAnchorSet::setTop(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors |= QDeclarativeAnchors::TopAnchor; + d->topScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetTop(); +} + +void QDeclarativeAnchorSet::resetTop() +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors &= ~QDeclarativeAnchors::TopAnchor; + d->topScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarativeAnchors::TopAnchor; +} + +QDeclarativeScriptString QDeclarativeAnchorSet::bottom() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->bottomScript; +} + +void QDeclarativeAnchorSet::setBottom(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors |= QDeclarativeAnchors::BottomAnchor; + d->bottomScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetBottom(); +} + +void QDeclarativeAnchorSet::resetBottom() +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors &= ~QDeclarativeAnchors::BottomAnchor; + d->bottomScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarativeAnchors::BottomAnchor; +} + +QDeclarativeScriptString QDeclarativeAnchorSet::verticalCenter() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->vCenterScript; +} + +void QDeclarativeAnchorSet::setVerticalCenter(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors |= QDeclarativeAnchors::VCenterAnchor; + d->vCenterScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetVerticalCenter(); +} + +void QDeclarativeAnchorSet::resetVerticalCenter() +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors &= ~QDeclarativeAnchors::VCenterAnchor; + d->vCenterScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarativeAnchors::VCenterAnchor; +} + +QDeclarativeScriptString QDeclarativeAnchorSet::baseline() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->baselineScript; +} + +void QDeclarativeAnchorSet::setBaseline(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors |= QDeclarativeAnchors::BaselineAnchor; + d->baselineScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetBaseline(); +} + +void QDeclarativeAnchorSet::resetBaseline() +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors &= ~QDeclarativeAnchors::BaselineAnchor; + d->baselineScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarativeAnchors::BaselineAnchor; +} + +QDeclarativeScriptString QDeclarativeAnchorSet::left() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->leftScript; +} + +void QDeclarativeAnchorSet::setLeft(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors |= QDeclarativeAnchors::LeftAnchor; + d->leftScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetLeft(); +} + +void QDeclarativeAnchorSet::resetLeft() +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors &= ~QDeclarativeAnchors::LeftAnchor; + d->leftScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarativeAnchors::LeftAnchor; +} + +QDeclarativeScriptString QDeclarativeAnchorSet::right() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->rightScript; +} + +void QDeclarativeAnchorSet::setRight(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors |= QDeclarativeAnchors::RightAnchor; + d->rightScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetRight(); +} + +void QDeclarativeAnchorSet::resetRight() +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors &= ~QDeclarativeAnchors::RightAnchor; + d->rightScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarativeAnchors::RightAnchor; +} + +QDeclarativeScriptString QDeclarativeAnchorSet::horizontalCenter() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->hCenterScript; +} + +void QDeclarativeAnchorSet::setHorizontalCenter(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors |= QDeclarativeAnchors::HCenterAnchor; + d->hCenterScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetHorizontalCenter(); +} + +void QDeclarativeAnchorSet::resetHorizontalCenter() +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors &= ~QDeclarativeAnchors::HCenterAnchor; + d->hCenterScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarativeAnchors::HCenterAnchor; +} + +QDeclarativeItem *QDeclarativeAnchorSet::fill() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->fill; +} + +void QDeclarativeAnchorSet::setFill(QDeclarativeItem *f) +{ + Q_D(QDeclarativeAnchorSet); + d->fill = f; +} + +void QDeclarativeAnchorSet::resetFill() +{ + setFill(0); +} + +QDeclarativeItem *QDeclarativeAnchorSet::centerIn() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->centerIn; +} + +void QDeclarativeAnchorSet::setCenterIn(QDeclarativeItem* c) +{ + Q_D(QDeclarativeAnchorSet); + d->centerIn = c; +} + +void QDeclarativeAnchorSet::resetCenterIn() +{ + setCenterIn(0); +} + + +class QDeclarativeAnchorChangesPrivate : public QDeclarativeStateOperationPrivate +{ +public: + QDeclarativeAnchorChangesPrivate() + : target(0), anchorSet(new QDeclarativeAnchorSet), + leftBinding(0), rightBinding(0), hCenterBinding(0), + topBinding(0), bottomBinding(0), vCenterBinding(0), baselineBinding(0), + origLeftBinding(0), origRightBinding(0), origHCenterBinding(0), + origTopBinding(0), origBottomBinding(0), origVCenterBinding(0), + origBaselineBinding(0) + { + + } + ~QDeclarativeAnchorChangesPrivate() { delete anchorSet; } + + QDeclarativeItem *target; + QDeclarativeAnchorSet *anchorSet; + + QDeclarativeBinding *leftBinding; + QDeclarativeBinding *rightBinding; + QDeclarativeBinding *hCenterBinding; + QDeclarativeBinding *topBinding; + QDeclarativeBinding *bottomBinding; + QDeclarativeBinding *vCenterBinding; + QDeclarativeBinding *baselineBinding; + + QDeclarativeAbstractBinding *origLeftBinding; + QDeclarativeAbstractBinding *origRightBinding; + QDeclarativeAbstractBinding *origHCenterBinding; + QDeclarativeAbstractBinding *origTopBinding; + QDeclarativeAbstractBinding *origBottomBinding; + QDeclarativeAbstractBinding *origVCenterBinding; + QDeclarativeAbstractBinding *origBaselineBinding; + + QDeclarativeAnchorLine rewindLeft; + QDeclarativeAnchorLine rewindRight; + QDeclarativeAnchorLine rewindHCenter; + QDeclarativeAnchorLine rewindTop; + QDeclarativeAnchorLine rewindBottom; + QDeclarativeAnchorLine rewindVCenter; + QDeclarativeAnchorLine rewindBaseline; + + qreal fromX; + qreal fromY; + qreal fromWidth; + qreal fromHeight; + + qreal toX; + qreal toY; + qreal toWidth; + qreal toHeight; + + qreal rewindX; + qreal rewindY; + qreal rewindWidth; + qreal rewindHeight; + + bool applyOrigLeft; + bool applyOrigRight; + bool applyOrigHCenter; + bool applyOrigTop; + bool applyOrigBottom; + bool applyOrigVCenter; + bool applyOrigBaseline; + + QDeclarativeNullableValue origWidth; + QDeclarativeNullableValue origHeight; + qreal origX; + qreal origY; + + QList oldBindings; + + QDeclarativeProperty leftProp; + QDeclarativeProperty rightProp; + QDeclarativeProperty hCenterProp; + QDeclarativeProperty topProp; + QDeclarativeProperty bottomProp; + QDeclarativeProperty vCenterProp; + QDeclarativeProperty baselineProp; +}; + +/*! + \qmlproperty Item AnchorChanges::target + This property holds the \l Item for which the anchor changes will be applied. +*/ + +QDeclarativeAnchorChanges::QDeclarativeAnchorChanges(QObject *parent) + : QDeclarativeStateOperation(*(new QDeclarativeAnchorChangesPrivate), parent) +{ +} + +QDeclarativeAnchorChanges::~QDeclarativeAnchorChanges() +{ +} + +QDeclarativeAnchorChanges::ActionList QDeclarativeAnchorChanges::actions() +{ + Q_D(QDeclarativeAnchorChanges); + d->leftBinding = d->rightBinding = d->hCenterBinding = d->topBinding + = d->bottomBinding = d->vCenterBinding = d->baselineBinding = 0; + + d->leftProp = QDeclarativeProperty(d->target, QLatin1String("anchors.left")); + d->rightProp = QDeclarativeProperty(d->target, QLatin1String("anchors.right")); + d->hCenterProp = QDeclarativeProperty(d->target, QLatin1String("anchors.horizontalCenter")); + d->topProp = QDeclarativeProperty(d->target, QLatin1String("anchors.top")); + d->bottomProp = QDeclarativeProperty(d->target, QLatin1String("anchors.bottom")); + d->vCenterProp = QDeclarativeProperty(d->target, QLatin1String("anchors.verticalCenter")); + d->baselineProp = QDeclarativeProperty(d->target, QLatin1String("anchors.baseline")); + + QDeclarativeContext *ctxt = qmlContext(this); + + if (d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::LeftAnchor) { + d->leftBinding = new QDeclarativeBinding(d->anchorSet->d_func()->leftScript.script(), d->target, ctxt); + d->leftBinding->setTarget(d->leftProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::RightAnchor) { + d->rightBinding = new QDeclarativeBinding(d->anchorSet->d_func()->rightScript.script(), d->target, ctxt); + d->rightBinding->setTarget(d->rightProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::HCenterAnchor) { + d->hCenterBinding = new QDeclarativeBinding(d->anchorSet->d_func()->hCenterScript.script(), d->target, ctxt); + d->hCenterBinding->setTarget(d->hCenterProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::TopAnchor) { + d->topBinding = new QDeclarativeBinding(d->anchorSet->d_func()->topScript.script(), d->target, ctxt); + d->topBinding->setTarget(d->topProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::BottomAnchor) { + d->bottomBinding = new QDeclarativeBinding(d->anchorSet->d_func()->bottomScript.script(), d->target, ctxt); + d->bottomBinding->setTarget(d->bottomProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::VCenterAnchor) { + d->vCenterBinding = new QDeclarativeBinding(d->anchorSet->d_func()->vCenterScript.script(), d->target, ctxt); + d->vCenterBinding->setTarget(d->vCenterProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::BaselineAnchor) { + d->baselineBinding = new QDeclarativeBinding(d->anchorSet->d_func()->baselineScript.script(), d->target, ctxt); + d->baselineBinding->setTarget(d->baselineProp); + } + + QDeclarativeAction a; + a.event = this; + return ActionList() << a; +} + +QDeclarativeAnchorSet *QDeclarativeAnchorChanges::anchors() +{ + Q_D(QDeclarativeAnchorChanges); + return d->anchorSet; +} + +QDeclarativeItem *QDeclarativeAnchorChanges::object() const +{ + Q_D(const QDeclarativeAnchorChanges); + return d->target; +} + +void QDeclarativeAnchorChanges::setObject(QDeclarativeItem *target) +{ + Q_D(QDeclarativeAnchorChanges); + d->target = target; +} + +/*! + \qmlproperty AnchorLine AnchorChanges::anchors.left + \qmlproperty AnchorLine AnchorChanges::anchors.right + \qmlproperty AnchorLine AnchorChanges::anchors.horizontalCenter + \qmlproperty AnchorLine AnchorChanges::anchors.top + \qmlproperty AnchorLine AnchorChanges::anchors.bottom + \qmlproperty AnchorLine AnchorChanges::anchors.verticalCenter + \qmlproperty AnchorLine AnchorChanges::anchors.baseline + + These properties change the respective anchors of the item. + + To reset an anchor you can assign \c undefined: + \qml + AnchorChanges { + target: myItem + anchors.left: undefined //remove myItem's left anchor + anchors.right: otherItem.right + } + \endqml +*/ + +void QDeclarativeAnchorChanges::execute(Reason reason) +{ + Q_D(QDeclarativeAnchorChanges); + if (!d->target) + return; + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::get(d->target); + //incorporate any needed "reverts" + if (d->applyOrigLeft) { + if (!d->origLeftBinding) + targetPrivate->anchors()->resetLeft(); + QDeclarativePropertyPrivate::setBinding(d->leftProp, d->origLeftBinding); + } + if (d->applyOrigRight) { + if (!d->origRightBinding) + targetPrivate->anchors()->resetRight(); + QDeclarativePropertyPrivate::setBinding(d->rightProp, d->origRightBinding); + } + if (d->applyOrigHCenter) { + if (!d->origHCenterBinding) + targetPrivate->anchors()->resetHorizontalCenter(); + QDeclarativePropertyPrivate::setBinding(d->hCenterProp, d->origHCenterBinding); + } + if (d->applyOrigTop) { + if (!d->origTopBinding) + targetPrivate->anchors()->resetTop(); + QDeclarativePropertyPrivate::setBinding(d->topProp, d->origTopBinding); + } + if (d->applyOrigBottom) { + if (!d->origBottomBinding) + targetPrivate->anchors()->resetBottom(); + QDeclarativePropertyPrivate::setBinding(d->bottomProp, d->origBottomBinding); + } + if (d->applyOrigVCenter) { + if (!d->origVCenterBinding) + targetPrivate->anchors()->resetVerticalCenter(); + QDeclarativePropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding); + } + if (d->applyOrigBaseline) { + if (!d->origBaselineBinding) + targetPrivate->anchors()->resetBaseline(); + QDeclarativePropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding); + } + + //destroy old bindings + if (reason == ActualChange) { + for (int i = 0; i < d->oldBindings.size(); ++i) { + QDeclarativeAbstractBinding *binding = d->oldBindings.at(i); + if (binding) + binding->destroy(); + } + d->oldBindings.clear(); + } + + //reset any anchors that have been specified as "undefined" + if (d->anchorSet->d_func()->resetAnchors & QDeclarativeAnchors::LeftAnchor) { + targetPrivate->anchors()->resetLeft(); + QDeclarativePropertyPrivate::setBinding(d->leftProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarativeAnchors::RightAnchor) { + targetPrivate->anchors()->resetRight(); + QDeclarativePropertyPrivate::setBinding(d->rightProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarativeAnchors::HCenterAnchor) { + targetPrivate->anchors()->resetHorizontalCenter(); + QDeclarativePropertyPrivate::setBinding(d->hCenterProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarativeAnchors::TopAnchor) { + targetPrivate->anchors()->resetTop(); + QDeclarativePropertyPrivate::setBinding(d->topProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarativeAnchors::BottomAnchor) { + targetPrivate->anchors()->resetBottom(); + QDeclarativePropertyPrivate::setBinding(d->bottomProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarativeAnchors::VCenterAnchor) { + targetPrivate->anchors()->resetVerticalCenter(); + QDeclarativePropertyPrivate::setBinding(d->vCenterProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarativeAnchors::BaselineAnchor) { + targetPrivate->anchors()->resetBaseline(); + QDeclarativePropertyPrivate::setBinding(d->baselineProp, 0); + } + + //set any anchors that have been specified + if (d->leftBinding) + QDeclarativePropertyPrivate::setBinding(d->leftBinding->property(), d->leftBinding); + if (d->rightBinding) + QDeclarativePropertyPrivate::setBinding(d->rightBinding->property(), d->rightBinding); + if (d->hCenterBinding) + QDeclarativePropertyPrivate::setBinding(d->hCenterBinding->property(), d->hCenterBinding); + if (d->topBinding) + QDeclarativePropertyPrivate::setBinding(d->topBinding->property(), d->topBinding); + if (d->bottomBinding) + QDeclarativePropertyPrivate::setBinding(d->bottomBinding->property(), d->bottomBinding); + if (d->vCenterBinding) + QDeclarativePropertyPrivate::setBinding(d->vCenterBinding->property(), d->vCenterBinding); + if (d->baselineBinding) + QDeclarativePropertyPrivate::setBinding(d->baselineBinding->property(), d->baselineBinding); +} + +bool QDeclarativeAnchorChanges::isReversable() +{ + return true; +} + +void QDeclarativeAnchorChanges::reverse(Reason reason) +{ + Q_D(QDeclarativeAnchorChanges); + if (!d->target) + return; + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::get(d->target); + //reset any anchors set by the state + if (d->leftBinding) { + targetPrivate->anchors()->resetLeft(); + QDeclarativePropertyPrivate::setBinding(d->leftBinding->property(), 0); + if (reason == ActualChange) { + d->leftBinding->destroy(); d->leftBinding = 0; + } + } + if (d->rightBinding) { + targetPrivate->anchors()->resetRight(); + QDeclarativePropertyPrivate::setBinding(d->rightBinding->property(), 0); + if (reason == ActualChange) { + d->rightBinding->destroy(); d->rightBinding = 0; + } + } + if (d->hCenterBinding) { + targetPrivate->anchors()->resetHorizontalCenter(); + QDeclarativePropertyPrivate::setBinding(d->hCenterBinding->property(), 0); + if (reason == ActualChange) { + d->hCenterBinding->destroy(); d->hCenterBinding = 0; + } + } + if (d->topBinding) { + targetPrivate->anchors()->resetTop(); + QDeclarativePropertyPrivate::setBinding(d->topBinding->property(), 0); + if (reason == ActualChange) { + d->topBinding->destroy(); d->topBinding = 0; + } + } + if (d->bottomBinding) { + targetPrivate->anchors()->resetBottom(); + QDeclarativePropertyPrivate::setBinding(d->bottomBinding->property(), 0); + if (reason == ActualChange) { + d->bottomBinding->destroy(); d->bottomBinding = 0; + } + } + if (d->vCenterBinding) { + targetPrivate->anchors()->resetVerticalCenter(); + QDeclarativePropertyPrivate::setBinding(d->vCenterBinding->property(), 0); + if (reason == ActualChange) { + d->vCenterBinding->destroy(); d->vCenterBinding = 0; + } + } + if (d->baselineBinding) { + targetPrivate->anchors()->resetBaseline(); + QDeclarativePropertyPrivate::setBinding(d->baselineBinding->property(), 0); + if (reason == ActualChange) { + d->baselineBinding->destroy(); d->baselineBinding = 0; + } + } + + //restore previous anchors + if (d->origLeftBinding) + QDeclarativePropertyPrivate::setBinding(d->leftProp, d->origLeftBinding); + if (d->origRightBinding) + QDeclarativePropertyPrivate::setBinding(d->rightProp, d->origRightBinding); + if (d->origHCenterBinding) + QDeclarativePropertyPrivate::setBinding(d->hCenterProp, d->origHCenterBinding); + if (d->origTopBinding) + QDeclarativePropertyPrivate::setBinding(d->topProp, d->origTopBinding); + if (d->origBottomBinding) + QDeclarativePropertyPrivate::setBinding(d->bottomProp, d->origBottomBinding); + if (d->origVCenterBinding) + QDeclarativePropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding); + if (d->origBaselineBinding) + QDeclarativePropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding); + + //restore any absolute geometry changed by the state's anchors + QDeclarativeAnchors::Anchors stateVAnchors = d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::Vertical_Mask; + QDeclarativeAnchors::Anchors origVAnchors = targetPrivate->anchors()->usedAnchors() & QDeclarativeAnchors::Vertical_Mask; + QDeclarativeAnchors::Anchors stateHAnchors = d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::Horizontal_Mask; + QDeclarativeAnchors::Anchors origHAnchors = targetPrivate->anchors()->usedAnchors() & QDeclarativeAnchors::Horizontal_Mask; + + bool stateSetWidth = (stateHAnchors && + stateHAnchors != QDeclarativeAnchors::LeftAnchor && + stateHAnchors != QDeclarativeAnchors::RightAnchor && + stateHAnchors != QDeclarativeAnchors::HCenterAnchor); + bool origSetWidth = (origHAnchors && + origHAnchors != QDeclarativeAnchors::LeftAnchor && + origHAnchors != QDeclarativeAnchors::RightAnchor && + origHAnchors != QDeclarativeAnchors::HCenterAnchor); + if (d->origWidth.isValid() && stateSetWidth && !origSetWidth) + d->target->setWidth(d->origWidth.value); + + bool stateSetHeight = (stateVAnchors && + stateVAnchors != QDeclarativeAnchors::TopAnchor && + stateVAnchors != QDeclarativeAnchors::BottomAnchor && + stateVAnchors != QDeclarativeAnchors::VCenterAnchor && + stateVAnchors != QDeclarativeAnchors::BaselineAnchor); + bool origSetHeight = (origVAnchors && + origVAnchors != QDeclarativeAnchors::TopAnchor && + origVAnchors != QDeclarativeAnchors::BottomAnchor && + origVAnchors != QDeclarativeAnchors::VCenterAnchor && + origVAnchors != QDeclarativeAnchors::BaselineAnchor); + if (d->origHeight.isValid() && stateSetHeight && !origSetHeight) + d->target->setHeight(d->origHeight.value); + + if (stateHAnchors && !origHAnchors) + d->target->setX(d->origX); + + if (stateVAnchors && !origVAnchors) + d->target->setY(d->origY); +} + +QString QDeclarativeAnchorChanges::typeName() const +{ + return QLatin1String("AnchorChanges"); +} + +QList QDeclarativeAnchorChanges::additionalActions() +{ + Q_D(QDeclarativeAnchorChanges); + QList extra; + + QDeclarativeAnchors::Anchors combined = d->anchorSet->d_func()->usedAnchors | d->anchorSet->d_func()->resetAnchors; + bool hChange = combined & QDeclarativeAnchors::Horizontal_Mask; + bool vChange = combined & QDeclarativeAnchors::Vertical_Mask; + + if (d->target) { + QDeclarativeContext *ctxt = qmlContext(this); + QDeclarativeAction a; + if (hChange && d->fromX != d->toX) { + a.property = QDeclarativeProperty(d->target, QLatin1String("x"), ctxt); + a.toValue = d->toX; + extra << a; + } + if (vChange && d->fromY != d->toY) { + a.property = QDeclarativeProperty(d->target, QLatin1String("y"), ctxt); + a.toValue = d->toY; + extra << a; + } + if (hChange && d->fromWidth != d->toWidth) { + a.property = QDeclarativeProperty(d->target, QLatin1String("width"), ctxt); + a.toValue = d->toWidth; + extra << a; + } + if (vChange && d->fromHeight != d->toHeight) { + a.property = QDeclarativeProperty(d->target, QLatin1String("height"), ctxt); + a.toValue = d->toHeight; + extra << a; + } + } + + return extra; +} + +bool QDeclarativeAnchorChanges::changesBindings() +{ + return true; +} + +void QDeclarativeAnchorChanges::saveOriginals() +{ + Q_D(QDeclarativeAnchorChanges); + if (!d->target) + return; + + d->origLeftBinding = QDeclarativePropertyPrivate::binding(d->leftProp); + d->origRightBinding = QDeclarativePropertyPrivate::binding(d->rightProp); + d->origHCenterBinding = QDeclarativePropertyPrivate::binding(d->hCenterProp); + d->origTopBinding = QDeclarativePropertyPrivate::binding(d->topProp); + d->origBottomBinding = QDeclarativePropertyPrivate::binding(d->bottomProp); + d->origVCenterBinding = QDeclarativePropertyPrivate::binding(d->vCenterProp); + d->origBaselineBinding = QDeclarativePropertyPrivate::binding(d->baselineProp); + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::get(d->target); + if (targetPrivate->widthValid) + d->origWidth = d->target->width(); + if (targetPrivate->heightValid) + d->origHeight = d->target->height(); + d->origX = d->target->x(); + d->origY = d->target->y(); + + d->applyOrigLeft = d->applyOrigRight = d->applyOrigHCenter = d->applyOrigTop + = d->applyOrigBottom = d->applyOrigVCenter = d->applyOrigBaseline = false; + + saveCurrentValues(); +} + +void QDeclarativeAnchorChanges::copyOriginals(QDeclarativeActionEvent *other) +{ + Q_D(QDeclarativeAnchorChanges); + QDeclarativeAnchorChanges *ac = static_cast(other); + QDeclarativeAnchorChangesPrivate *acp = ac->d_func(); + + QDeclarativeAnchors::Anchors combined = acp->anchorSet->d_func()->usedAnchors | + acp->anchorSet->d_func()->resetAnchors; + + //probably also need to revert some things + d->applyOrigLeft = (combined & QDeclarativeAnchors::LeftAnchor); + d->applyOrigRight = (combined & QDeclarativeAnchors::RightAnchor); + d->applyOrigHCenter = (combined & QDeclarativeAnchors::HCenterAnchor); + d->applyOrigTop = (combined & QDeclarativeAnchors::TopAnchor); + d->applyOrigBottom = (combined & QDeclarativeAnchors::BottomAnchor); + d->applyOrigVCenter = (combined & QDeclarativeAnchors::VCenterAnchor); + d->applyOrigBaseline = (combined & QDeclarativeAnchors::BaselineAnchor); + + d->origLeftBinding = acp->origLeftBinding; + d->origRightBinding = acp->origRightBinding; + d->origHCenterBinding = acp->origHCenterBinding; + d->origTopBinding = acp->origTopBinding; + d->origBottomBinding = acp->origBottomBinding; + d->origVCenterBinding = acp->origVCenterBinding; + d->origBaselineBinding = acp->origBaselineBinding; + + d->origWidth = acp->origWidth; + d->origHeight = acp->origHeight; + d->origX = acp->origX; + d->origY = acp->origY; + + d->oldBindings.clear(); + d->oldBindings << acp->leftBinding << acp->rightBinding << acp->hCenterBinding + << acp->topBinding << acp->bottomBinding << acp->baselineBinding; + + saveCurrentValues(); +} + +void QDeclarativeAnchorChanges::clearBindings() +{ + Q_D(QDeclarativeAnchorChanges); + if (!d->target) + return; + + //### should this (saving "from" values) be moved to saveCurrentValues()? + d->fromX = d->target->x(); + d->fromY = d->target->y(); + d->fromWidth = d->target->width(); + d->fromHeight = d->target->height(); + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::get(d->target); + //reset any anchors with corresponding reverts + //reset any anchors that have been specified as "undefined" + //reset any anchors that we'll be setting in the state + QDeclarativeAnchors::Anchors combined = d->anchorSet->d_func()->resetAnchors | + d->anchorSet->d_func()->usedAnchors; + if (d->applyOrigLeft || (combined & QDeclarativeAnchors::LeftAnchor)) { + targetPrivate->anchors()->resetLeft(); + QDeclarativePropertyPrivate::setBinding(d->leftProp, 0); + } + if (d->applyOrigRight || (combined & QDeclarativeAnchors::RightAnchor)) { + targetPrivate->anchors()->resetRight(); + QDeclarativePropertyPrivate::setBinding(d->rightProp, 0); + } + if (d->applyOrigHCenter || (combined & QDeclarativeAnchors::HCenterAnchor)) { + targetPrivate->anchors()->resetHorizontalCenter(); + QDeclarativePropertyPrivate::setBinding(d->hCenterProp, 0); + } + if (d->applyOrigTop || (combined & QDeclarativeAnchors::TopAnchor)) { + targetPrivate->anchors()->resetTop(); + QDeclarativePropertyPrivate::setBinding(d->topProp, 0); + } + if (d->applyOrigBottom || (combined & QDeclarativeAnchors::BottomAnchor)) { + targetPrivate->anchors()->resetBottom(); + QDeclarativePropertyPrivate::setBinding(d->bottomProp, 0); + } + if (d->applyOrigVCenter || (combined & QDeclarativeAnchors::VCenterAnchor)) { + targetPrivate->anchors()->resetVerticalCenter(); + QDeclarativePropertyPrivate::setBinding(d->vCenterProp, 0); + } + if (d->applyOrigBaseline || (combined & QDeclarativeAnchors::BaselineAnchor)) { + targetPrivate->anchors()->resetBaseline(); + QDeclarativePropertyPrivate::setBinding(d->baselineProp, 0); + } +} + +bool QDeclarativeAnchorChanges::override(QDeclarativeActionEvent*other) +{ + if (other->typeName() != QLatin1String("AnchorChanges")) + return false; + if (static_cast(this) == other) + return true; + if (static_cast(other)->object() == object()) + return true; + return false; +} + +void QDeclarativeAnchorChanges::rewind() +{ + Q_D(QDeclarativeAnchorChanges); + if (!d->target) + return; + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::get(d->target); + + //restore previous values (but not previous bindings, i.e. anchors) + d->target->setX(d->rewindX); + d->target->setY(d->rewindY); + if (targetPrivate->widthValid) { + d->target->setWidth(d->rewindWidth); + } + if (targetPrivate->heightValid) { + d->target->setHeight(d->rewindHeight); + } +} + +void QDeclarativeAnchorChanges::saveCurrentValues() +{ + Q_D(QDeclarativeAnchorChanges); + if (!d->target) + return; + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::get(d->target); + d->rewindLeft = targetPrivate->anchors()->left(); + d->rewindRight = targetPrivate->anchors()->right(); + d->rewindHCenter = targetPrivate->anchors()->horizontalCenter(); + d->rewindTop = targetPrivate->anchors()->top(); + d->rewindBottom = targetPrivate->anchors()->bottom(); + d->rewindVCenter = targetPrivate->anchors()->verticalCenter(); + d->rewindBaseline = targetPrivate->anchors()->baseline(); + + d->rewindX = d->target->x(); + d->rewindY = d->target->y(); + d->rewindWidth = d->target->width(); + d->rewindHeight = d->target->height(); +} + +void QDeclarativeAnchorChanges::saveTargetValues() +{ + Q_D(QDeclarativeAnchorChanges); + if (!d->target) + return; + + d->toX = d->target->x(); + d->toY = d->target->y(); + d->toWidth = d->target->width(); + d->toHeight = d->target->height(); +} + +#include +#include + +QT_END_NAMESPACE + diff --git a/src/declarative/util/qdeclarativestateoperations_p.h b/src/declarative/util/qdeclarativestateoperations_p.h new file mode 100644 index 00000000..6848b8a4 --- /dev/null +++ b/src/declarative/util/qdeclarativestateoperations_p.h @@ -0,0 +1,299 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTATEOPERATIONS_H +#define QDECLARATIVESTATEOPERATIONS_H + +#include "private/qdeclarativestate_p.h" + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeParentChangePrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeParentChange : public QDeclarativeStateOperation, public QDeclarativeActionEvent +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeParentChange) + + Q_PROPERTY(QDeclarativeItem *target READ object WRITE setObject) + Q_PROPERTY(QDeclarativeItem *parent READ parent WRITE setParent) + Q_PROPERTY(QDeclarativeScriptString x READ x WRITE setX) + Q_PROPERTY(QDeclarativeScriptString y READ y WRITE setY) + Q_PROPERTY(QDeclarativeScriptString width READ width WRITE setWidth) + Q_PROPERTY(QDeclarativeScriptString height READ height WRITE setHeight) + Q_PROPERTY(QDeclarativeScriptString scale READ scale WRITE setScale) + Q_PROPERTY(QDeclarativeScriptString rotation READ rotation WRITE setRotation) +public: + QDeclarativeParentChange(QObject *parent=0); + ~QDeclarativeParentChange(); + + QDeclarativeItem *object() const; + void setObject(QDeclarativeItem *); + + QDeclarativeItem *parent() const; + void setParent(QDeclarativeItem *); + + QDeclarativeItem *originalParent() const; + + QDeclarativeScriptString x() const; + void setX(QDeclarativeScriptString x); + bool xIsSet() const; + + QDeclarativeScriptString y() const; + void setY(QDeclarativeScriptString y); + bool yIsSet() const; + + QDeclarativeScriptString width() const; + void setWidth(QDeclarativeScriptString width); + bool widthIsSet() const; + + QDeclarativeScriptString height() const; + void setHeight(QDeclarativeScriptString height); + bool heightIsSet() const; + + QDeclarativeScriptString scale() const; + void setScale(QDeclarativeScriptString scale); + bool scaleIsSet() const; + + QDeclarativeScriptString rotation() const; + void setRotation(QDeclarativeScriptString rotation); + bool rotationIsSet() const; + + virtual ActionList actions(); + + virtual void saveOriginals(); + //virtual void copyOriginals(QDeclarativeActionEvent*); + virtual void execute(Reason reason = ActualChange); + virtual bool isReversable(); + virtual void reverse(Reason reason = ActualChange); + virtual QString typeName() const; + virtual bool override(QDeclarativeActionEvent*other); + virtual void rewind(); + virtual void saveCurrentValues(); +}; + +class QDeclarativeStateChangeScriptPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeStateChangeScript : public QDeclarativeStateOperation, public QDeclarativeActionEvent +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeStateChangeScript) + + Q_PROPERTY(QDeclarativeScriptString script READ script WRITE setScript) + Q_PROPERTY(QString name READ name WRITE setName) + +public: + QDeclarativeStateChangeScript(QObject *parent=0); + ~QDeclarativeStateChangeScript(); + + virtual ActionList actions(); + + virtual QString typeName() const; + + QDeclarativeScriptString script() const; + void setScript(const QDeclarativeScriptString &); + + QString name() const; + void setName(const QString &); + + virtual void execute(Reason reason = ActualChange); +}; + +class QDeclarativeAnchorChanges; +class QDeclarativeAnchorSetPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeAnchorSet : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativeScriptString left READ left WRITE setLeft RESET resetLeft) + Q_PROPERTY(QDeclarativeScriptString right READ right WRITE setRight RESET resetRight) + Q_PROPERTY(QDeclarativeScriptString horizontalCenter READ horizontalCenter WRITE setHorizontalCenter RESET resetHorizontalCenter) + Q_PROPERTY(QDeclarativeScriptString top READ top WRITE setTop RESET resetTop) + Q_PROPERTY(QDeclarativeScriptString bottom READ bottom WRITE setBottom RESET resetBottom) + Q_PROPERTY(QDeclarativeScriptString verticalCenter READ verticalCenter WRITE setVerticalCenter RESET resetVerticalCenter) + Q_PROPERTY(QDeclarativeScriptString baseline READ baseline WRITE setBaseline RESET resetBaseline) + //Q_PROPERTY(QDeclarativeItem *fill READ fill WRITE setFill RESET resetFill) + //Q_PROPERTY(QDeclarativeItem *centerIn READ centerIn WRITE setCenterIn RESET resetCenterIn) + + /*Q_PROPERTY(qreal margins READ margins WRITE setMargins NOTIFY marginsChanged) + Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged) + Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged) + Q_PROPERTY(qreal horizontalCenterOffset READ horizontalCenterOffset WRITE setHorizontalCenterOffset NOTIFY horizontalCenterOffsetChanged()) + Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged) + Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged) + Q_PROPERTY(qreal verticalCenterOffset READ verticalCenterOffset WRITE setVerticalCenterOffset NOTIFY verticalCenterOffsetChanged()) + Q_PROPERTY(qreal baselineOffset READ baselineOffset WRITE setBaselineOffset NOTIFY baselineOffsetChanged())*/ + +public: + QDeclarativeAnchorSet(QObject *parent=0); + virtual ~QDeclarativeAnchorSet(); + + QDeclarativeScriptString left() const; + void setLeft(const QDeclarativeScriptString &edge); + void resetLeft(); + + QDeclarativeScriptString right() const; + void setRight(const QDeclarativeScriptString &edge); + void resetRight(); + + QDeclarativeScriptString horizontalCenter() const; + void setHorizontalCenter(const QDeclarativeScriptString &edge); + void resetHorizontalCenter(); + + QDeclarativeScriptString top() const; + void setTop(const QDeclarativeScriptString &edge); + void resetTop(); + + QDeclarativeScriptString bottom() const; + void setBottom(const QDeclarativeScriptString &edge); + void resetBottom(); + + QDeclarativeScriptString verticalCenter() const; + void setVerticalCenter(const QDeclarativeScriptString &edge); + void resetVerticalCenter(); + + QDeclarativeScriptString baseline() const; + void setBaseline(const QDeclarativeScriptString &edge); + void resetBaseline(); + + QDeclarativeItem *fill() const; + void setFill(QDeclarativeItem *); + void resetFill(); + + QDeclarativeItem *centerIn() const; + void setCenterIn(QDeclarativeItem *); + void resetCenterIn(); + + /*qreal leftMargin() const; + void setLeftMargin(qreal); + + qreal rightMargin() const; + void setRightMargin(qreal); + + qreal horizontalCenterOffset() const; + void setHorizontalCenterOffset(qreal); + + qreal topMargin() const; + void setTopMargin(qreal); + + qreal bottomMargin() const; + void setBottomMargin(qreal); + + qreal margins() const; + void setMargins(qreal); + + qreal verticalCenterOffset() const; + void setVerticalCenterOffset(qreal); + + qreal baselineOffset() const; + void setBaselineOffset(qreal);*/ + + QDeclarativeAnchors::Anchors usedAnchors() const; + +/*Q_SIGNALS: + void leftMarginChanged(); + void rightMarginChanged(); + void topMarginChanged(); + void bottomMarginChanged(); + void marginsChanged(); + void verticalCenterOffsetChanged(); + void horizontalCenterOffsetChanged(); + void baselineOffsetChanged();*/ + +private: + friend class QDeclarativeAnchorChanges; + Q_DISABLE_COPY(QDeclarativeAnchorSet) + Q_DECLARE_PRIVATE(QDeclarativeAnchorSet) +}; + +class QDeclarativeAnchorChangesPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeAnchorChanges : public QDeclarativeStateOperation, public QDeclarativeActionEvent +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAnchorChanges) + + Q_PROPERTY(QDeclarativeItem *target READ object WRITE setObject) + Q_PROPERTY(QDeclarativeAnchorSet *anchors READ anchors CONSTANT) + +public: + QDeclarativeAnchorChanges(QObject *parent=0); + ~QDeclarativeAnchorChanges(); + + virtual ActionList actions(); + + QDeclarativeAnchorSet *anchors(); + + QDeclarativeItem *object() const; + void setObject(QDeclarativeItem *); + + virtual void execute(Reason reason = ActualChange); + virtual bool isReversable(); + virtual void reverse(Reason reason = ActualChange); + virtual QString typeName() const; + virtual bool override(QDeclarativeActionEvent*other); + virtual bool changesBindings(); + virtual void saveOriginals(); + virtual bool needsCopy() { return true; } + virtual void copyOriginals(QDeclarativeActionEvent*); + virtual void clearBindings(); + virtual void rewind(); + virtual void saveCurrentValues(); + + QList additionalActions(); + virtual void saveTargetValues(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeParentChange) +QML_DECLARE_TYPE(QDeclarativeStateChangeScript) +QML_DECLARE_TYPE(QDeclarativeAnchorSet) +QML_DECLARE_TYPE(QDeclarativeAnchorChanges) + +QT_END_HEADER + +#endif // QDECLARATIVESTATEOPERATIONS_H diff --git a/src/declarative/util/qdeclarativestyledtext.cpp b/src/declarative/util/qdeclarativestyledtext.cpp new file mode 100644 index 00000000..022a8220 --- /dev/null +++ b/src/declarative/util/qdeclarativestyledtext.cpp @@ -0,0 +1,347 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "private/qdeclarativestyledtext_p.h" + +/* + QDeclarativeStyledText supports few tags: + + - bold + - italic +
- new line + + + The opening and closing tags must be correctly nested. +*/ + +QT_BEGIN_NAMESPACE + +class QDeclarativeStyledTextPrivate +{ +public: + QDeclarativeStyledTextPrivate(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 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 QDeclarativeStyledTextPrivate::lessThan(QLatin1Char('<')); +const QChar QDeclarativeStyledTextPrivate::greaterThan(QLatin1Char('>')); +const QChar QDeclarativeStyledTextPrivate::equals(QLatin1Char('=')); +const QChar QDeclarativeStyledTextPrivate::singleQuote(QLatin1Char('\'')); +const QChar QDeclarativeStyledTextPrivate::doubleQuote(QLatin1Char('\"')); +const QChar QDeclarativeStyledTextPrivate::slash(QLatin1Char('/')); +const QChar QDeclarativeStyledTextPrivate::ampersand(QLatin1Char('&')); + +QDeclarativeStyledText::QDeclarativeStyledText(const QString &string, QTextLayout &layout) +: d(new QDeclarativeStyledTextPrivate(string, layout)) +{ +} + +QDeclarativeStyledText::~QDeclarativeStyledText() +{ + delete d; +} + +void QDeclarativeStyledText::parse(const QString &string, QTextLayout &layout) +{ + if (string.isEmpty()) + return; + QDeclarativeStyledText styledText(string, layout); + styledText.d->parse(); +} + +void QDeclarativeStyledTextPrivate::parse() +{ + QList ranges; + QStack 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)) { + if (formatStack.count()) + formatStack.pop(); + } + } else { + QTextCharFormat format; + if (formatStack.count()) + format = formatStack.top(); + 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; + } + if (!ch->isNull()) + ++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 QDeclarativeStyledTextPrivate::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); + else if (tagLength == 2 && tag.at(1) == QLatin1Char('r')) { + textOut.append(QChar(QChar::LineSeparator)); + return false; + } + } else if (char0 == QLatin1Char('i')) { + if (tagLength == 1) + format.setFontItalic(true); + } + return true; + } 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 QDeclarativeStyledTextPrivate::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 QDeclarativeStyledTextPrivate::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 QDeclarativeStyledTextPrivate::parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format) +{ + bool valid = false; + QPair 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 QDeclarativeStyledTextPrivate::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(attr,val); + break; + } else { + ++attrLength; + } + ++ch; + } + + return QPair(); +} + +QStringRef QDeclarativeStyledTextPrivate::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); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativestyledtext_p.h b/src/declarative/util/qdeclarativestyledtext_p.h new file mode 100644 index 00000000..da86d4b3 --- /dev/null +++ b/src/declarative/util/qdeclarativestyledtext_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTYLEDTEXT_H +#define QDECLARATIVESTYLEDTEXT_H + +#include + +QT_BEGIN_NAMESPACE + +class QPainter; +class QPointF; +class QString; +class QDeclarativeStyledTextPrivate; +class QTextLayout; + +class Q_AUTOTEST_EXPORT QDeclarativeStyledText +{ +public: + static void parse(const QString &string, QTextLayout &layout); + +private: + QDeclarativeStyledText(const QString &string, QTextLayout &layout); + ~QDeclarativeStyledText(); + + QDeclarativeStyledTextPrivate *d; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/util/qdeclarativesystempalette.cpp b/src/declarative/util/qdeclarativesystempalette.cpp new file mode 100644 index 00000000..984909e1 --- /dev/null +++ b/src/declarative/util/qdeclarativesystempalette.cpp @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativesystempalette_p.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeSystemPalettePrivate : public QObjectPrivate +{ +public: + QPalette palette; + QPalette::ColorGroup group; +}; + + + +/*! + \qmlclass SystemPalette QDeclarativeSystemPalette + \ingroup qml-utility-elements + \since 4.7 + \brief The SystemPalette element provides access to the Qt palettes. + + The SystemPalette element provides access to the Qt application + palettes. This provides information about the standard colors used + for application windows, buttons and other features. These colors + are grouped into three \e {color groups}: \c Active, \c Inactive, + and \c Disabled. See the QPalette documentation for details about + color groups and the properties provided by SystemPalette. + + This can be used to color items in a way that provides a more + native look and feel. + + The following example creates a palette from the \c Active color + group and uses this to color the window and text items + appropriately: + + \snippet doc/src/snippets/declarative/systempalette.qml 0 + + \sa QPalette +*/ +QDeclarativeSystemPalette::QDeclarativeSystemPalette(QObject *parent) + : QObject(*(new QDeclarativeSystemPalettePrivate), parent) +{ + Q_D(QDeclarativeSystemPalette); + d->palette = QApplication::palette(); + d->group = QPalette::Active; + qApp->installEventFilter(this); +} + +QDeclarativeSystemPalette::~QDeclarativeSystemPalette() +{ +} + +/*! + \qmlproperty color SystemPalette::window + The window (general background) color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::window() const +{ + Q_D(const QDeclarativeSystemPalette); + 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 QDeclarativeSystemPalette::windowText() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::WindowText); +} + +/*! + \qmlproperty color SystemPalette::base + The base color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::base() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Base); +} + +/*! + \qmlproperty color SystemPalette::text + The text color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::text() const +{ + Q_D(const QDeclarativeSystemPalette); + 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 QDeclarativeSystemPalette::alternateBase() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::AlternateBase); +} + +/*! + \qmlproperty color SystemPalette::button + The button color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::button() const +{ + Q_D(const QDeclarativeSystemPalette); + 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 QDeclarativeSystemPalette::buttonText() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::ButtonText); +} + +/*! + \qmlproperty color SystemPalette::light + The light color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::light() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Light); +} + +/*! + \qmlproperty color SystemPalette::midlight + The midlight color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::midlight() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Midlight); +} + +/*! + \qmlproperty color SystemPalette::dark + The dark color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::dark() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Dark); +} + +/*! + \qmlproperty color SystemPalette::mid + The mid color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::mid() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Mid); +} + +/*! + \qmlproperty color SystemPalette::shadow + The shadow color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::shadow() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Shadow); +} + +/*! + \qmlproperty color SystemPalette::highlight + The highlight color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::highlight() const +{ + Q_D(const QDeclarativeSystemPalette); + 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 QDeclarativeSystemPalette::highlightedText() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::HighlightedText); +} + +/*! + \qmlproperty enumeration SystemPalette::colorGroup + + The color group of the palette. This can be one of: + + \list + \o SystemPalette.Active (default) + \o SystemPalette.Inactive + \o SystemPalette.Disabled + \endlist + + \sa QPalette::ColorGroup +*/ +QDeclarativeSystemPalette::ColorGroup QDeclarativeSystemPalette::colorGroup() const +{ + Q_D(const QDeclarativeSystemPalette); + return (QDeclarativeSystemPalette::ColorGroup)d->group; +} + +void QDeclarativeSystemPalette::setColorGroup(QDeclarativeSystemPalette::ColorGroup colorGroup) +{ + Q_D(QDeclarativeSystemPalette); + d->group = (QPalette::ColorGroup)colorGroup; + emit paletteChanged(); +} + +bool QDeclarativeSystemPalette::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 QDeclarativeSystemPalette::event(QEvent *event) +{ + Q_D(QDeclarativeSystemPalette); + 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/qdeclarativesystempalette_p.h b/src/declarative/util/qdeclarativesystempalette_p.h new file mode 100644 index 00000000..780479b0 --- /dev/null +++ b/src/declarative/util/qdeclarativesystempalette_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESYSTEMPALETTE_H +#define QDECLARATIVESYSTEMPALETTE_H + +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeSystemPalettePrivate; +class Q_AUTOTEST_EXPORT QDeclarativeSystemPalette : public QObject +{ + Q_OBJECT + Q_ENUMS(ColorGroup) + Q_DECLARE_PRIVATE(QDeclarativeSystemPalette) + + Q_PROPERTY(QDeclarativeSystemPalette::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: + QDeclarativeSystemPalette(QObject *parent=0); + ~QDeclarativeSystemPalette(); + + 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; + + QDeclarativeSystemPalette::ColorGroup colorGroup() const; + void setColorGroup(QDeclarativeSystemPalette::ColorGroup); + +Q_SIGNALS: + void paletteChanged(); + +private: + bool eventFilter(QObject *watched, QEvent *event); + bool event(QEvent *event); + +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeSystemPalette) + +QT_END_HEADER + +#endif // QDECLARATIVESYSTEMPALETTE_H diff --git a/src/declarative/util/qdeclarativetimeline.cpp b/src/declarative/util/qdeclarativetimeline.cpp new file mode 100644 index 00000000..0c802092 --- /dev/null +++ b/src/declarative/util/qdeclarativetimeline.cpp @@ -0,0 +1,947 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativetimeline_p_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +struct Update { + Update(QDeclarativeTimeLineValue *_g, qreal _v) + : g(_g), v(_v) {} + Update(const QDeclarativeTimeLineCallback &_e) + : g(0), v(0), e(_e) {} + + QDeclarativeTimeLineValue *g; + qreal v; + QDeclarativeTimeLineCallback e; +}; + +struct QDeclarativeTimeLinePrivate +{ + QDeclarativeTimeLinePrivate(QDeclarativeTimeLine *); + + struct Op { + enum Type { + Pause, + Set, + Move, + MoveBy, + Accel, + AccelDistance, + Execute + }; + Op() {} + Op(Type t, int l, qreal v, qreal v2, int o, + const QDeclarativeTimeLineCallback &ev = QDeclarativeTimeLineCallback(), 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; + QDeclarativeTimeLineCallback event; + QEasingCurve easing; + }; + struct TimeLine + { + TimeLine() : length(0), consumedOpLength(0), base(0.) {} + QList ops; + int length; + int consumedOpLength; + qreal base; + }; + + int length; + int syncPoint; + typedef QHash Ops; + Ops ops; + QDeclarativeTimeLine *q; + + void add(QDeclarativeTimeLineObject &, const Op &); + qreal value(const Op &op, int time, qreal base, bool *) const; + + int advance(int); + + bool clockRunning; + int prevTime; + + int order; + + QDeclarativeTimeLine::SyncMode syncMode; + int syncAdj; + QList > *updateQueue; +}; + +QDeclarativeTimeLinePrivate::QDeclarativeTimeLinePrivate(QDeclarativeTimeLine *parent) +: length(0), syncPoint(0), q(parent), clockRunning(false), prevTime(0), order(0), syncMode(QDeclarativeTimeLine::LocalSync), syncAdj(0), updateQueue(0) +{ +} + +void QDeclarativeTimeLinePrivate::add(QDeclarativeTimeLineObject &g, const Op &o) +{ + if (g._t && g._t != q) { + qWarning() << "QDeclarativeTimeLine: Cannot modify a QDeclarativeTimeLineValue 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 == QDeclarativeTimeLine::LocalSync) { + syncAdj = -1; + } else { + syncAdj = 0; + } + q->start(); +/* q->tick(0); + if (syncMode == QDeclarativeTimeLine::LocalSync) { + syncAdj = -1; + } else { + syncAdj = 0; + } + */ + } +} + +qreal QDeclarativeTimeLinePrivate::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.d0(op.event.d1); + *changed = false; + return -1; + } + + return base; +} + +/*! + \internal + \class QDeclarativeTimeLine + \brief The QDeclarativeTimeLine class provides a timeline for controlling animations. + + QDeclarativeTimeLine is similar to QTimeLine except: + \list + \i It updates QDeclarativeTimeLineValue instances directly, rather than maintaining a single + current value. + + For example, the following animates a simple value over 200 milliseconds: + \code + QDeclarativeTimeLineValue v(); + QDeclarativeTimeLine 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 QDeclarativeTimeLine's updated() signal, or inherit from QDeclarativeTimeLineValue + and reimplement the QDeclarativeTimeLineValue::setValue() method. + + \i Supports multiple QDeclarativeTimeLineValue, 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 + QDeclarativeTimeLineValue x(); + QDeclarativeTimeLineValue y(); + + QDeclarativeTimeLine tl; + tl.start(); + + tl.move(x, 100., 50); + tl.move(y, 100., 50); + tl.move(y, 200., 50); + \endcode + + \i All QDeclarativeTimeLine 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 + QDeclarativeTimeLine and across separate QDeclarativeTimeLine's within the same process. + + \endlist + + Currently easing functions are not supported. +*/ + + +/*! + Construct a new QDeclarativeTimeLine with the specified \a parent. +*/ +QDeclarativeTimeLine::QDeclarativeTimeLine(QObject *parent) +: QAbstractAnimation(parent) +{ + d = new QDeclarativeTimeLinePrivate(this); +} + +/*! + Destroys the time line. Any inprogress animations are canceled, but not + completed. +*/ +QDeclarativeTimeLine::~QDeclarativeTimeLine() +{ + for (QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.begin(); + iter != d->ops.end(); + ++iter) + iter.key()->_t = 0; + + delete d; d = 0; +} + +/*! + \enum QDeclarativeTimeLine::SyncMode + */ + +/*! + Return the timeline's synchronization mode. + */ +QDeclarativeTimeLine::SyncMode QDeclarativeTimeLine::syncMode() const +{ + return d->syncMode; +} + +/*! + Set the timeline's synchronization mode to \a syncMode. + */ +void QDeclarativeTimeLine::setSyncMode(SyncMode syncMode) +{ + d->syncMode = syncMode; +} + +/*! + Pause \a obj for \a time milliseconds. +*/ +void QDeclarativeTimeLine::pause(QDeclarativeTimeLineObject &obj, int time) +{ + if (time <= 0) return; + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Pause, time, 0., 0., d->order++); + d->add(obj, op); +} + +/*! + Execute the \a event. + */ +void QDeclarativeTimeLine::callback(const QDeclarativeTimeLineCallback &callback) +{ + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Execute, 0, 0, 0., d->order++, callback); + d->add(*callback.callbackObject(), op); +} + +/*! + Set the \a value of \a timeLineValue. +*/ +void QDeclarativeTimeLine::set(QDeclarativeTimeLineValue &timeLineValue, qreal value) +{ + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::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 QDeclarativeTimeLine will ensure + that the deceleration is in the opposite direction to the initial velocity. +*/ +int QDeclarativeTimeLine::accel(QDeclarativeTimeLineValue &timeLineValue, qreal velocity, qreal acceleration) +{ + if (acceleration == 0.0f) + return -1; + + if ((velocity > 0.0f) == (acceleration > 0.0f)) + acceleration = acceleration * -1.0f; + + int time = static_cast(-1000 * velocity / acceleration); + + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::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, QDeclarativeTimeLine 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 QDeclarativeTimeLine::accel(QDeclarativeTimeLineValue &timeLineValue, qreal velocity, qreal acceleration, qreal maxDistance) +{ + if (maxDistance == 0.0f || acceleration == 0.0f) + return -1; + + 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(-1000 * velocity / acceleration); + + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::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 QDeclarativeTimeLine calculates the exact + deceleration to use. + + \a distance should be positive. +*/ +int QDeclarativeTimeLine::accelDistance(QDeclarativeTimeLineValue &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(1000 * (2.0f * distance) / velocity); + + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::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 QDeclarativeTimeLine::move(QDeclarativeTimeLineValue &timeLineValue, qreal destination, int time) +{ + if (time <= 0) return; + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::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 QDeclarativeTimeLine::move(QDeclarativeTimeLineValue &timeLineValue, qreal destination, const QEasingCurve &easing, int time) +{ + if (time <= 0) return; + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++, QDeclarativeTimeLineCallback(), easing); + d->add(timeLineValue, op); +} + +/*! + Linearly change the \a timeLineValue from its current value by the \a change amount + over \a time milliseconds. +*/ +void QDeclarativeTimeLine::moveBy(QDeclarativeTimeLineValue &timeLineValue, qreal change, int time) +{ + if (time <= 0) return; + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::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 QDeclarativeTimeLine::moveBy(QDeclarativeTimeLineValue &timeLineValue, qreal change, const QEasingCurve &easing, int time) +{ + if (time <= 0) return; + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++, QDeclarativeTimeLineCallback(), easing); + d->add(timeLineValue, op); +} + +/*! + Cancel (but don't complete) all scheduled actions for \a timeLineValue. +*/ +void QDeclarativeTimeLine::reset(QDeclarativeTimeLineValue &timeLineValue) +{ + if (!timeLineValue._t) + return; + if (timeLineValue._t != this) { + qWarning() << "QDeclarativeTimeLine: Cannot reset a QDeclarativeTimeLineValue owned by another timeline."; + return; + } + remove(&timeLineValue); + timeLineValue._t = 0; +} + +int QDeclarativeTimeLine::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 + pseudo-code this is equivalent to: + \code + QDeclarativeTimeLine::pause(timeLineValue, min(0, length_of(syncTo) - length_of(timeLineValue))) + \endcode +*/ +void QDeclarativeTimeLine::sync(QDeclarativeTimeLineValue &timeLineValue, QDeclarativeTimeLineValue &syncTo) +{ + QDeclarativeTimeLinePrivate::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 pseudo-code, this is equivalent to: + \code + QDeclarativeTimeLine::pause(timeLineValue, length_of(timeline) - length_of(timeLineValue)) + \endcode +*/ +void QDeclarativeTimeLine::sync(QDeclarativeTimeLineValue &timeLineValue) +{ + QDeclarativeTimeLinePrivate::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.); + QDeclarativeTimeLine 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 QDeclarativeTimeLine::sync() +{ + for (QDeclarativeTimeLinePrivate::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 QDeclarativeTimeLine::setSyncPoint(int sp) +{ + d->syncPoint = sp; +} + +/*! + \internal + + Temporary hack. + */ +int QDeclarativeTimeLine::syncPoint() const +{ + return d->syncPoint; +} + +/*! + Returns true if the timeline is active. An active timeline is one where + QDeclarativeTimeLineValue actions are still pending. +*/ +bool QDeclarativeTimeLine::isActive() const +{ + return !d->ops.isEmpty(); +} + +/*! + Completes the timeline. All queued actions are played to completion, and then discarded. For example, + \code + QDeclarativeTimeLineValue v(0.); + QDeclarativeTimeLine tl; + tl.move(v, 100., 1000.); + // 500 ms passes + // v.value() == 50. + tl.complete(); + // v.value() == 100. + \endcode +*/ +void QDeclarativeTimeLine::complete() +{ + d->advance(d->length); +} + +/*! + Resets the timeline. All queued actions are discarded and QDeclarativeTimeLineValue's retain their current value. For example, + \code + QDeclarativeTimeLineValue v(0.); + QDeclarativeTimeLine tl; + tl.move(v, 100., 1000.); + // 500 ms passes + // v.value() == 50. + tl.clear(); + // v.value() == 50. + \endcode +*/ +void QDeclarativeTimeLine::clear() +{ + for (QDeclarativeTimeLinePrivate::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 QDeclarativeTimeLine::time() const +{ + return d->prevTime; +} + +/*! + \fn void QDeclarativeTimeLine::updated() + + Emitted each time the timeline modifies QDeclarativeTimeLineValues. Even if multiple + QDeclarativeTimeLineValues are changed, this signal is only emitted once for each clock tick. +*/ + +void QDeclarativeTimeLine::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 &lhs, + const QPair &rhs) +{ + return lhs.first < rhs.first; +} + +int QDeclarativeTimeLinePrivate::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 > updates; + + for (Ops::Iterator iter = ops.begin(); iter != ops.end(); ) { + QDeclarativeTimeLineValue *v = static_cast(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.d0(v.e.d1); + } + } + updateQueue = 0; + } while(t); + + return pauseTime; +} + +void QDeclarativeTimeLine::remove(QDeclarativeTimeLineObject *v) +{ + QDeclarativeTimeLinePrivate::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 (QDeclarativeTimeLinePrivate::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 == QDeclarativeTimeLine::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.callbackObject() == v) { + d->updateQueue->removeAt(ii); + --ii; + } + } + } + + +} + +/*! + \internal + \class QDeclarativeTimeLineValue + \brief The QDeclarativeTimeLineValue class provides a value that can be modified by QDeclarativeTimeLine. +*/ + +/*! + \fn QDeclarativeTimeLineValue::QDeclarativeTimeLineValue(qreal value = 0) + + Construct a new QDeclarativeTimeLineValue with an initial \a value. +*/ + +/*! + \fn qreal QDeclarativeTimeLineValue::value() const + + Return the current value. +*/ + +/*! + \fn void QDeclarativeTimeLineValue::setValue(qreal value) + + Set the current \a value. +*/ + +/*! + \fn QDeclarativeTimeLine *QDeclarativeTimeLineValue::timeLine() const + + If a QDeclarativeTimeLine is operating on this value, return a pointer to it, + otherwise return null. +*/ + + +QDeclarativeTimeLineObject::QDeclarativeTimeLineObject() +: _t(0) +{ +} + +QDeclarativeTimeLineObject::~QDeclarativeTimeLineObject() +{ + if (_t) { + _t->remove(this); + _t = 0; + } +} + +QDeclarativeTimeLineCallback::QDeclarativeTimeLineCallback() +: d0(0), d1(0), d2(0) +{ +} + +QDeclarativeTimeLineCallback::QDeclarativeTimeLineCallback(QDeclarativeTimeLineObject *b, Callback f, void *d) +: d0(f), d1(d), d2(b) +{ +} + +QDeclarativeTimeLineCallback::QDeclarativeTimeLineCallback(const QDeclarativeTimeLineCallback &o) +: d0(o.d0), d1(o.d1), d2(o.d2) +{ +} + +QDeclarativeTimeLineCallback &QDeclarativeTimeLineCallback::operator=(const QDeclarativeTimeLineCallback &o) +{ + d0 = o.d0; + d1 = o.d1; + d2 = o.d2; + return *this; +} + +QDeclarativeTimeLineObject *QDeclarativeTimeLineCallback::callbackObject() const +{ + return d2; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativetimeline_p_p.h b/src/declarative/util/qdeclarativetimeline_p_p.h new file mode 100644 index 00000000..aa5ac211 --- /dev/null +++ b/src/declarative/util/qdeclarativetimeline_p_p.h @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETIMELINE_H +#define QDECLARATIVETIMELINE_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 +#include + +QT_BEGIN_NAMESPACE + +class QEasingCurve; +class QDeclarativeTimeLineValue; +class QDeclarativeTimeLineCallback; +struct QDeclarativeTimeLinePrivate; +class QDeclarativeTimeLineObject; +class Q_AUTOTEST_EXPORT QDeclarativeTimeLine : public QAbstractAnimation +{ +Q_OBJECT +public: + QDeclarativeTimeLine(QObject *parent = 0); + ~QDeclarativeTimeLine(); + + enum SyncMode { LocalSync, GlobalSync }; + SyncMode syncMode() const; + void setSyncMode(SyncMode); + + void pause(QDeclarativeTimeLineObject &, int); + void callback(const QDeclarativeTimeLineCallback &); + void set(QDeclarativeTimeLineValue &, qreal); + + int accel(QDeclarativeTimeLineValue &, qreal velocity, qreal accel); + int accel(QDeclarativeTimeLineValue &, qreal velocity, qreal accel, qreal maxDistance); + int accelDistance(QDeclarativeTimeLineValue &, qreal velocity, qreal distance); + + void move(QDeclarativeTimeLineValue &, qreal destination, int time = 500); + void move(QDeclarativeTimeLineValue &, qreal destination, const QEasingCurve &, int time = 500); + void moveBy(QDeclarativeTimeLineValue &, qreal change, int time = 500); + void moveBy(QDeclarativeTimeLineValue &, qreal change, const QEasingCurve &, int time = 500); + + void sync(); + void setSyncPoint(int); + int syncPoint() const; + + void sync(QDeclarativeTimeLineValue &); + void sync(QDeclarativeTimeLineValue &, QDeclarativeTimeLineValue &); + + void reset(QDeclarativeTimeLineValue &); + + 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(QDeclarativeTimeLineObject *); + friend class QDeclarativeTimeLineObject; + friend struct QDeclarativeTimeLinePrivate; + QDeclarativeTimeLinePrivate *d; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeTimeLineObject +{ +public: + QDeclarativeTimeLineObject(); + virtual ~QDeclarativeTimeLineObject(); + +protected: + friend class QDeclarativeTimeLine; + friend struct QDeclarativeTimeLinePrivate; + QDeclarativeTimeLine *_t; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeTimeLineValue : public QDeclarativeTimeLineObject +{ +public: + QDeclarativeTimeLineValue(qreal v = 0.) : _v(v) {} + + virtual qreal value() const { return _v; } + virtual void setValue(qreal v) { _v = v; } + + QDeclarativeTimeLine *timeLine() const { return _t; } + + operator qreal() const { return _v; } + QDeclarativeTimeLineValue &operator=(qreal v) { setValue(v); return *this; } +private: + friend class QDeclarativeTimeLine; + friend struct QDeclarativeTimeLinePrivate; + qreal _v; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeTimeLineCallback +{ +public: + typedef void (*Callback)(void *); + + QDeclarativeTimeLineCallback(); + QDeclarativeTimeLineCallback(QDeclarativeTimeLineObject *b, Callback, void * = 0); + QDeclarativeTimeLineCallback(const QDeclarativeTimeLineCallback &o); + + QDeclarativeTimeLineCallback &operator=(const QDeclarativeTimeLineCallback &o); + QDeclarativeTimeLineObject *callbackObject() const; + +private: + friend struct QDeclarativeTimeLinePrivate; + Callback d0; + void *d1; + QDeclarativeTimeLineObject *d2; +}; + +template +class QDeclarativeTimeLineValueProxy : public QDeclarativeTimeLineValue +{ +public: + QDeclarativeTimeLineValueProxy(T *cls, void (T::*func)(qreal), qreal v = 0.) + : QDeclarativeTimeLineValue(v), _class(cls), _setFunctionReal(func), _setFunctionInt(0) + { + Q_ASSERT(_class); + } + + QDeclarativeTimeLineValueProxy(T *cls, void (T::*func)(int), qreal v = 0.) + : QDeclarativeTimeLineValue(v), _class(cls), _setFunctionReal(0), _setFunctionInt(func) + { + Q_ASSERT(_class); + } + + virtual void setValue(qreal v) + { + QDeclarativeTimeLineValue::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/qdeclarativetimer.cpp b/src/declarative/util/qdeclarativetimer.cpp new file mode 100644 index 00000000..8250809c --- /dev/null +++ b/src/declarative/util/qdeclarativetimer.cpp @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativetimer_p.h" + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + + + +class QDeclarativeTimerPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeTimer) +public: + QDeclarativeTimerPrivate() + : 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 QDeclarativeTimer + \ingroup qml-utility-elements + \since 4.7 + \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. It uses the JavaScript \c Date + object to access the current time. + + \qml + import QtQuick 1.0 + + Item { + Timer { + interval: 500; running: true; repeat: true + onTriggered: time.text = Date().toString() + } + + Text { id: time } + } + \endqml + + The Timer element is synchronized with the animation timer. Since the animation + timer is usually set to 60fps, the resolution of Timer 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. + + \sa {declarative/toys/clocks}{Clocks example} +*/ + +QDeclarativeTimer::QDeclarativeTimer(QObject *parent) + : QObject(*(new QDeclarativeTimerPrivate), parent) +{ + Q_D(QDeclarativeTimer); + 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 QDeclarativeTimer::setInterval(int interval) +{ + Q_D(QDeclarativeTimer); + if (interval != d->interval) { + d->interval = interval; + update(); + emit intervalChanged(); + } +} + +int QDeclarativeTimer::interval() const +{ + Q_D(const QDeclarativeTimer); + 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 QDeclarativeTimer::isRunning() const +{ + Q_D(const QDeclarativeTimer); + return d->running; +} + +void QDeclarativeTimer::setRunning(bool running) +{ + Q_D(QDeclarativeTimer); + 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 QDeclarativeTimer::isRepeating() const +{ + Q_D(const QDeclarativeTimer); + return d->repeating; +} + +void QDeclarativeTimer::setRepeating(bool repeating) +{ + Q_D(QDeclarativeTimer); + if (repeating != d->repeating) { + d->repeating = repeating; + update(); + emit repeatChanged(); + } +} + +/*! + \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 QDeclarativeTimer::triggeredOnStart() const +{ + Q_D(const QDeclarativeTimer); + return d->triggeredOnStart; +} + +void QDeclarativeTimer::setTriggeredOnStart(bool triggeredOnStart) +{ + Q_D(QDeclarativeTimer); + if (d->triggeredOnStart != triggeredOnStart) { + d->triggeredOnStart = triggeredOnStart; + update(); + emit triggeredOnStartChanged(); + } +} + +/*! + \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 QDeclarativeTimer::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 QDeclarativeTimer::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 QDeclarativeTimer::restart() +{ + setRunning(false); + setRunning(true); +} + +void QDeclarativeTimer::update() +{ + Q_D(QDeclarativeTimer); + 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 QDeclarativeTimer::classBegin() +{ + Q_D(QDeclarativeTimer); + d->classBegun = true; +} + +void QDeclarativeTimer::componentComplete() +{ + Q_D(QDeclarativeTimer); + d->componentComplete = true; + update(); +} + +/*! + \qmlsignal Timer::onTriggered() + + This handler is called when the Timer is triggered. +*/ +void QDeclarativeTimer::ticked() +{ + Q_D(QDeclarativeTimer); + if (d->running && (d->pause.currentTime() > 0 || (d->triggeredOnStart && d->firstTick))) + emit triggered(); + d->firstTick = false; +} + +void QDeclarativeTimer::finished() +{ + Q_D(QDeclarativeTimer); + 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/qdeclarativetimer_p.h b/src/declarative/util/qdeclarativetimer_p.h new file mode 100644 index 00000000..f7b30a85 --- /dev/null +++ b/src/declarative/util/qdeclarativetimer_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETIMER_H +#define QDECLARATIVETIMER_H + +#include + +#include +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeTimerPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeTimer : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeTimer) + Q_INTERFACES(QDeclarativeParserStatus) + Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY intervalChanged) + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(bool repeat READ isRepeating WRITE setRepeating NOTIFY repeatChanged) + Q_PROPERTY(bool triggeredOnStart READ triggeredOnStart WRITE setTriggeredOnStart NOTIFY triggeredOnStartChanged) + Q_PROPERTY(QObject *parent READ parent CONSTANT) + +public: + QDeclarativeTimer(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(); + void intervalChanged(); + void repeatChanged(); + void triggeredOnStartChanged(); + +private: + void update(); + +private Q_SLOTS: + void ticked(); + void finished(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeTimer) + +QT_END_HEADER + +#endif diff --git a/src/declarative/util/qdeclarativetransition.cpp b/src/declarative/util/qdeclarativetransition.cpp new file mode 100644 index 00000000..c3003087 --- /dev/null +++ b/src/declarative/util/qdeclarativetransition.cpp @@ -0,0 +1,345 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativestate_p.h" +#include "private/qdeclarativestategroup_p.h" +#include "private/qdeclarativestate_p_p.h" +#include "private/qdeclarativestateoperations_p.h" +#include "private/qdeclarativeanimation_p.h" +#include "private/qdeclarativeanimation_p_p.h" +#include "private/qdeclarativetransitionmanager_p_p.h" + +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Transition QDeclarativeTransition + \ingroup qml-animation-transition + \since 4.7 + \brief The Transition element defines animated transitions that occur on state changes. + + A Transition defines the animations to be applied when a \l State change occurs. + + For example, the following \l Rectangle has two states: the default state, and + an added "moved" state. In the "moved state, the rectangle's position changes + to (50, 50). The added Transition specifies that when the rectangle + changes between the default and the "moved" state, any changes + to the \c x and \c y properties should be animated, using an \c Easing.InOutQuad. + + \snippet doc/src/snippets/declarative/transition.qml 0 + + Notice the example does not require \l{PropertyAnimation::}{to} and + \l{PropertyAnimation::}{from} values for the NumberAnimation. As a convenience, + these properties are automatically set to the values of \c x and \c y before + and after the state change; the \c from values are provided by + the current values of \c x and \c y, and the \c to values are provided by + the PropertyChanges object. If you wish, you can provide \l{PropertyAnimation::}{to} and + \l{PropertyAnimation::}{from} values anyway to override the default values. + + By default, a Transition's animations are applied for any state change in the + parent item. The Transition \l {Transition::}{from} and \l {Transition::}{to} + values can be set to restrict the animations to only be applied when changing + from one particular state to another. + + To define multiple transitions, specify \l Item::transitions as a list: + + \snippet doc/src/snippets/declarative/transitions-list.qml list of transitions + + If multiple Transitions are specified, only a single (best-matching) Transition will be applied for any particular + state change. In the example above, when changing to \c state1, the first transition will be used, rather + than the more generic second transition. + + If a state change has a Transition that matches the same property as a + \l Behavior, the Transition animation overrides the \l Behavior for that + state change. + + \sa {QML Animation and Transitions}, {declarative/animation/states}{states example}, {qmlstates}{States}, {QtDeclarative} +*/ + +//ParallelAnimationWrapper allows 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) {} + QDeclarativeTransitionPrivate *trans; +protected: + virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState); +}; + +class QDeclarativeTransitionPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeTransition) +public: + QDeclarativeTransitionPrivate() + : fromState(QLatin1String("*")), toState(QLatin1String("*")), + reversed(false), reversible(false), endState(0) + { + group.trans = this; + } + + QString fromState; + QString toState; + bool reversed; + bool reversible; + ParallelAnimationWrapper group; + QDeclarativeTransitionManager *endState; + + void complete() + { + endState->complete(); + } + static void append_animation(QDeclarativeListProperty *list, QDeclarativeAbstractAnimation *a); + static int animation_count(QDeclarativeListProperty *list); + static QDeclarativeAbstractAnimation* animation_at(QDeclarativeListProperty *list, int pos); + static void clear_animations(QDeclarativeListProperty *list); + QList animations; +}; + +void QDeclarativeTransitionPrivate::append_animation(QDeclarativeListProperty *list, QDeclarativeAbstractAnimation *a) +{ + QDeclarativeTransition *q = static_cast(list->object); + q->d_func()->animations.append(a); + q->d_func()->group.addAnimation(a->qtAnimation()); + a->setDisableUserControl(); +} + +int QDeclarativeTransitionPrivate::animation_count(QDeclarativeListProperty *list) +{ + QDeclarativeTransition *q = static_cast(list->object); + return q->d_func()->animations.count(); +} + +QDeclarativeAbstractAnimation* QDeclarativeTransitionPrivate::animation_at(QDeclarativeListProperty *list, int pos) +{ + QDeclarativeTransition *q = static_cast(list->object); + return q->d_func()->animations.at(pos); +} + +void QDeclarativeTransitionPrivate::clear_animations(QDeclarativeListProperty *list) +{ + QDeclarativeTransition *q = static_cast(list->object); + while (q->d_func()->animations.count()) { + QDeclarativeAbstractAnimation *firstAnim = q->d_func()->animations.at(0); + q->d_func()->group.removeAnimation(firstAnim->qtAnimation()); + q->d_func()->animations.removeAll(firstAnim); + } +} + +void ParallelAnimationWrapper::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) +{ + QParallelAnimationGroup::updateState(newState, oldState); + if (newState == Stopped && (duration() == -1 + || (direction() == QAbstractAnimation::Forward && currentLoopTime() == duration()) + || (direction() == QAbstractAnimation::Backward && currentLoopTime() == 0))) + { + trans->complete(); + } +} + + + +QDeclarativeTransition::QDeclarativeTransition(QObject *parent) + : QObject(*(new QDeclarativeTransitionPrivate), parent) +{ +} + +QDeclarativeTransition::~QDeclarativeTransition() +{ +} + +void QDeclarativeTransition::stop() +{ + Q_D(QDeclarativeTransition); + d->group.stop(); +} + +void QDeclarativeTransition::setReversed(bool r) +{ + Q_D(QDeclarativeTransition); + d->reversed = r; +} + +void QDeclarativeTransition::prepare(QDeclarativeStateOperation::ActionList &actions, + QList &after, + QDeclarativeTransitionManager *endState) +{ + Q_D(QDeclarativeTransition); + + qmlExecuteDeferred(this); + + if (d->reversed) { + for (int ii = d->animations.count() - 1; ii >= 0; --ii) { + d->animations.at(ii)->transition(actions, after, QDeclarativeAbstractAnimation::Backward); + } + } else { + for (int ii = 0; ii < d->animations.count(); ++ii) { + d->animations.at(ii)->transition(actions, after, QDeclarativeAbstractAnimation::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 indicate the state changes that trigger the transition. + + The default values for these properties is "*" (that is, any state). + + For example, the following transition has not set the \c to and \c from + properties, so the animation is always applied when changing between + the two states (i.e. when the mouse is pressed and released). + + \snippet doc/src/snippets/declarative/transition-from-to.qml 0 + + If the transition was changed to this: + + \snippet doc/src/snippets/declarative/transition-from-to-modified.qml modified transition + + The animation would only be applied when changing from the default state to + the "brighter" state (i.e. when the mouse is pressed, but not on release). + + \sa reversible +*/ +QString QDeclarativeTransition::fromState() const +{ + Q_D(const QDeclarativeTransition); + return d->fromState; +} + +void QDeclarativeTransition::setFromState(const QString &f) +{ + Q_D(QDeclarativeTransition); + if (f == d->fromState) + return; + + d->fromState = f; + emit fromChanged(); +} + +/*! + \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. + + By default, transitions run in parallel and are applied to all state + changes if the \l from and \l to states have not been set. In this + situation, the transition is automatically applied when a state change + is reversed, and it is not necessary to set this property to reverse + the transition. + + However, if a SequentialAnimation is used, or if the \l from or \l to + properties have been set, this property will need to be set to reverse + a transition when a state change is reverted. For example, the following + transition applies a sequential animation when the mouse is pressed, + and reverses the sequence of the animation when the mouse is released: + + \snippet doc/src/snippets/declarative/transition-reversible.qml 0 + + If the transition did not set the \c to and \c reversible values, then + on the mouse release, the transition would play the PropertyAnimation + before the ColorAnimation instead of reversing the sequence. +*/ +bool QDeclarativeTransition::reversible() const +{ + Q_D(const QDeclarativeTransition); + return d->reversible; +} + +void QDeclarativeTransition::setReversible(bool r) +{ + Q_D(QDeclarativeTransition); + if (r == d->reversible) + return; + + d->reversible = r; + emit reversibleChanged(); +} + +QString QDeclarativeTransition::toState() const +{ + Q_D(const QDeclarativeTransition); + return d->toState; +} + +void QDeclarativeTransition::setToState(const QString &t) +{ + Q_D(QDeclarativeTransition); + if (t == d->toState) + return; + + d->toState = t; + emit toChanged(); +} + +/*! + \qmlproperty list Transition::animations + \default + + This property holds a list of the animations to be run for this transition. + + \snippet examples/declarative/toys/dynamicscene/dynamicscene.qml top-level transitions + + The top-level animations are run in parallel. To run them sequentially, + define them within a SequentialAnimation: + + \snippet doc/src/snippets/declarative/transition-reversible.qml sequential animations +*/ +QDeclarativeListProperty QDeclarativeTransition::animations() +{ + Q_D(QDeclarativeTransition); + return QDeclarativeListProperty(this, &d->animations, QDeclarativeTransitionPrivate::append_animation, + QDeclarativeTransitionPrivate::animation_count, + QDeclarativeTransitionPrivate::animation_at, + QDeclarativeTransitionPrivate::clear_animations); +} + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/util/qdeclarativetransition_p.h b/src/declarative/util/qdeclarativetransition_p.h new file mode 100644 index 00000000..6b84c0fd --- /dev/null +++ b/src/declarative/util/qdeclarativetransition_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETRANSITION_H +#define QDECLARATIVETRANSITION_H + +#include "private/qdeclarativestate_p.h" + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeAbstractAnimation; +class QDeclarativeTransitionPrivate; +class QDeclarativeTransitionManager; +class Q_DECLARATIVE_EXPORT QDeclarativeTransition : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeTransition) + + Q_PROPERTY(QString from READ fromState WRITE setFromState NOTIFY fromChanged) + Q_PROPERTY(QString to READ toState WRITE setToState NOTIFY toChanged) + Q_PROPERTY(bool reversible READ reversible WRITE setReversible NOTIFY reversibleChanged) + Q_PROPERTY(QDeclarativeListProperty animations READ animations) + Q_CLASSINFO("DefaultProperty", "animations") + Q_CLASSINFO("DeferredPropertyNames", "animations") + +public: + QDeclarativeTransition(QObject *parent=0); + ~QDeclarativeTransition(); + + QString fromState() const; + void setFromState(const QString &); + + QString toState() const; + void setToState(const QString &); + + bool reversible() const; + void setReversible(bool); + + QDeclarativeListProperty animations(); + + void prepare(QDeclarativeStateOperation::ActionList &actions, + QList &after, + QDeclarativeTransitionManager *end); + + void setReversed(bool r); + void stop(); + +Q_SIGNALS: + void fromChanged(); + void toChanged(); + void reversibleChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeTransition) + +QT_END_HEADER + +#endif // QDECLARATIVETRANSITION_H diff --git a/src/declarative/util/qdeclarativetransitionmanager.cpp b/src/declarative/util/qdeclarativetransitionmanager.cpp new file mode 100644 index 00000000..0cd66c05 --- /dev/null +++ b/src/declarative/util/qdeclarativetransitionmanager.cpp @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativetransitionmanager_p_p.h" + +#include "private/qdeclarativestate_p_p.h" +#include "private/qdeclarativestate_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +class QDeclarativeTransitionManagerPrivate +{ +public: + QDeclarativeTransitionManagerPrivate() + : state(0) {} + + void applyBindings(); + typedef QList SimpleActionList; + QDeclarativeState *state; + QDeclarativeGuard transition; + QDeclarativeStateOperation::ActionList bindingsList; + SimpleActionList completeList; +}; + +QDeclarativeTransitionManager::QDeclarativeTransitionManager() +: d(new QDeclarativeTransitionManagerPrivate) +{ +} + +void QDeclarativeTransitionManager::setState(QDeclarativeState *s) +{ + d->state = s; +} + +QDeclarativeTransitionManager::~QDeclarativeTransitionManager() +{ + delete d; d = 0; +} + +void QDeclarativeTransitionManager::complete() +{ + d->applyBindings(); + + for (int ii = 0; ii < d->completeList.count(); ++ii) { + const QDeclarativeProperty &prop = d->completeList.at(ii).property(); + prop.write(d->completeList.at(ii).value()); + } + + d->completeList.clear(); + + if (d->state) + static_cast(QObjectPrivate::get(d->state))->complete(); +} + +void QDeclarativeTransitionManagerPrivate::applyBindings() +{ + foreach(const QDeclarativeAction &action, bindingsList) { + if (!action.toBinding.isNull()) { + QDeclarativePropertyPrivate::setBinding(action.property, action.toBinding.data()); + } else if (action.event) { + if (action.reverseEvent) + action.event->reverse(); + else + action.event->execute(); + } + + } + + bindingsList.clear(); +} + +void QDeclarativeTransitionManager::transition(const QList &list, + QDeclarativeTransition *transition) +{ + cancel(); + + QDeclarativeStateOperation::ActionList applyList = list; + // Determine which actions are binding changes. + foreach(const QDeclarativeAction &action, applyList) { + if (action.toBinding) + d->bindingsList << action; + if (action.fromBinding) + QDeclarativePropertyPrivate::setBinding(action.property, 0); // Disable current binding + if (action.event && action.event->changesBindings()) { //### assume isReversable()? + d->bindingsList << action; + action.event->clearBindings(); + } + } + + // 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()) { + + // Apply all the property and binding changes + for (int ii = 0; ii < applyList.size(); ++ii) { + const QDeclarativeAction &action = applyList.at(ii); + if (!action.toBinding.isNull()) { + QDeclarativePropertyPrivate::setBinding(action.property, action.toBinding.data(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } else if (!action.event) { + QDeclarativePropertyPrivate::write(action.property, action.toValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } else if (action.event->isReversable()) { + if (action.reverseEvent) + action.event->reverse(QDeclarativeActionEvent::FastForward); + else + action.event->execute(QDeclarativeActionEvent::FastForward); + } + } + + // Read all the end values for binding changes + for (int ii = 0; ii < applyList.size(); ++ii) { + QDeclarativeAction *action = &applyList[ii]; + if (action->event) { + action->event->saveTargetValues(); + continue; + } + const QDeclarativeProperty &prop = action->property; + if (!action->toBinding.isNull() || !action->toValue.isValid()) { + action->toValue = prop.read(); + } + } + + // Revert back to the original values + foreach(const QDeclarativeAction &action, applyList) { + if (action.event) { + if (action.event->isReversable()) { + action.event->clearBindings(); + action.event->rewind(); + action.event->clearBindings(); //### shouldn't be needed + } + continue; + } + + if (action.toBinding) + QDeclarativePropertyPrivate::setBinding(action.property, 0); // Make sure this is disabled during the transition + + QDeclarativePropertyPrivate::write(action.property, action.fromValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } + } + + if (transition) { + QList 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 QDeclarativeAction &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 << + QDeclarativeSimpleAction(action, QDeclarativeSimpleAction::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 QDeclarativeAction &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); + } + } +#ifndef QT_NO_DEBUG_STREAM + if (stateChangeDebug()) { + foreach(const QDeclarativeAction &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; + } + } +#endif + if (!transition) + d->applyBindings(); +} + +void QDeclarativeTransitionManager::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) { + QDeclarativeAction action = d->bindingsList[i]; + if (!action.toBinding.isNull() && action.deletableToBinding) { + QDeclarativePropertyPrivate::setBinding(action.property, 0); + action.toBinding.data()->destroy(); + action.toBinding.clear(); + 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/qdeclarativetransitionmanager_p_p.h b/src/declarative/util/qdeclarativetransitionmanager_p_p.h new file mode 100644 index 00000000..64948cdb --- /dev/null +++ b/src/declarative/util/qdeclarativetransitionmanager_p_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETRANSITIONMANAGER_P_H +#define QDECLARATIVETRANSITIONMANAGER_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 "private/qdeclarativestateoperations_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativeStatePrivate; +class QDeclarativeTransitionManagerPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeTransitionManager +{ +public: + QDeclarativeTransitionManager(); + ~QDeclarativeTransitionManager(); + + void transition(const QList &, QDeclarativeTransition *transition); + + void cancel(); + +private: + Q_DISABLE_COPY(QDeclarativeTransitionManager) + QDeclarativeTransitionManagerPrivate *d; + + void complete(); + void setState(QDeclarativeState *); + + friend class QDeclarativeState; + friend class QDeclarativeTransitionPrivate; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVETRANSITIONMANAGER_P_H diff --git a/src/declarative/util/qdeclarativeutilmodule.cpp b/src/declarative/util/qdeclarativeutilmodule.cpp new file mode 100644 index 00000000..c6590978 --- /dev/null +++ b/src/declarative/util/qdeclarativeutilmodule.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeutilmodule_p.h" +#include "private/qdeclarativeanimation_p.h" +#include "private/qdeclarativeanimation_p_p.h" +#include "private/qdeclarativebehavior_p.h" +#include "private/qdeclarativebind_p.h" +#include "private/qdeclarativeconnections_p.h" +#include "private/qdeclarativesmoothedanimation_p.h" +#include "private/qdeclarativefontloader_p.h" +#include "private/qdeclarativelistaccessor_p.h" +#include "private/qdeclarativelistmodel_p.h" +#include "private/qdeclarativenullablevalue_p_p.h" +#include "private/qdeclarativeopenmetaobject_p.h" +#include "private/qdeclarativepackage_p.h" +#include "private/qdeclarativepixmapcache_p.h" +#include "private/qdeclarativepropertychanges_p.h" +#include "qdeclarativepropertymap.h" +#include "private/qdeclarativespringanimation_p.h" +#include "private/qdeclarativestategroup_p.h" +#include "private/qdeclarativestateoperations_p.h" +#include "private/qdeclarativestate_p.h" +#include "private/qdeclarativestate_p_p.h" +#include "private/qdeclarativestyledtext_p.h" +#include "private/qdeclarativesystempalette_p.h" +#include "private/qdeclarativetimeline_p_p.h" +#include "private/qdeclarativetimer_p.h" +#include "private/qdeclarativetransitionmanager_p_p.h" +#include "private/qdeclarativetransition_p.h" +#include "private/qdeclarativeapplication_p.h" +#include "qdeclarativeview.h" +#include "qdeclarativeinfo.h" +#include "private/qdeclarativetypenotavailable_p.h" +#ifndef QT_NO_XMLPATTERNS +#include "private/qdeclarativexmllistmodel_p.h" +#endif + +void QDeclarativeUtilModule::defineModule() +{ + qmlRegisterUncreatableType("QtQuick",1,1,"Application", QDeclarativeApplication::tr("Application is an abstract class")); + + qmlRegisterType("QtQuick",1,0,"AnchorAnimation"); + qmlRegisterType("QtQuick",1,0,"AnchorChanges"); + qmlRegisterType("QtQuick",1,0,"Behavior"); + qmlRegisterType("QtQuick",1,0,"Binding"); + qmlRegisterType("QtQuick",1,0,"ColorAnimation"); + qmlRegisterType("QtQuick",1,0,"Connections"); + qmlRegisterType("QtQuick",1,0,"SmoothedAnimation"); + qmlRegisterType("QtQuick",1,0,"FontLoader"); + qmlRegisterType("QtQuick",1,0,"ListElement"); + qmlRegisterType("QtQuick",1,0,"NumberAnimation"); + qmlRegisterType("QtQuick",1,0,"Package"); + qmlRegisterType("QtQuick",1,0,"ParallelAnimation"); + qmlRegisterType("QtQuick",1,0,"ParentAnimation"); + qmlRegisterType("QtQuick",1,0,"ParentChange"); + qmlRegisterType("QtQuick",1,0,"PauseAnimation"); + qmlRegisterType("QtQuick",1,0,"PropertyAction"); + qmlRegisterType("QtQuick",1,0,"PropertyAnimation"); + qmlRegisterType("QtQuick",1,0,"RotationAnimation"); + qmlRegisterType("QtQuick",1,0,"ScriptAction"); + qmlRegisterType("QtQuick",1,0,"SequentialAnimation"); + qmlRegisterType("QtQuick",1,0,"SpringAnimation"); + qmlRegisterType("QtQuick",1,0,"StateChangeScript"); + qmlRegisterType("QtQuick",1,0,"StateGroup"); + qmlRegisterType("QtQuick",1,0,"State"); + qmlRegisterType("QtQuick",1,0,"SystemPalette"); + qmlRegisterType("QtQuick",1,0,"Timer"); + qmlRegisterType("QtQuick",1,0,"Transition"); + qmlRegisterType("QtQuick",1,0,"Vector3dAnimation"); +#ifdef QT_NO_XMLPATTERNS + qmlRegisterTypeNotAvailable("QtQuick",1,0,"XmlListModel", + qApp->translate("QDeclarativeXmlListModel","Qt was built without support for xmlpatterns")); + qmlRegisterTypeNotAvailable("QtQuick",1,0,"XmlRole", + qApp->translate("QDeclarativeXmlListModel","Qt was built without support for xmlpatterns")); +#else + qmlRegisterType("QtQuick",1,0,"XmlListModel"); + qmlRegisterType("QtQuick",1,0,"XmlRole"); +#endif + + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterType(); + + qmlRegisterUncreatableType("QtQuick",1,0,"Animation",QDeclarativeAbstractAnimation::tr("Animation is an abstract class")); + + qmlRegisterCustomType("QtQuick",1,0,"ListModel", new QDeclarativeListModelParser); + qmlRegisterCustomType("QtQuick",1,0,"PropertyChanges", new QDeclarativePropertyChangesParser); + qmlRegisterCustomType("QtQuick",1,0,"Connections", new QDeclarativeConnectionsParser); + +#ifndef QT_NO_IMPORT_QT47_QML + qmlRegisterType("Qt",4,7,"AnchorAnimation"); + qmlRegisterType("Qt",4,7,"AnchorChanges"); + qmlRegisterType("Qt",4,7,"Behavior"); + qmlRegisterType("Qt",4,7,"Binding"); + qmlRegisterType("Qt",4,7,"ColorAnimation"); + qmlRegisterType("Qt",4,7,"Connections"); + qmlRegisterType("Qt",4,7,"SmoothedAnimation"); + qmlRegisterType("Qt",4,7,"FontLoader"); + qmlRegisterType("Qt",4,7,"ListElement"); + qmlRegisterType("Qt",4,7,"NumberAnimation"); + qmlRegisterType("Qt",4,7,"Package"); + qmlRegisterType("Qt",4,7,"ParallelAnimation"); + qmlRegisterType("Qt",4,7,"ParentAnimation"); + qmlRegisterType("Qt",4,7,"ParentChange"); + qmlRegisterType("Qt",4,7,"PauseAnimation"); + qmlRegisterType("Qt",4,7,"PropertyAction"); + qmlRegisterType("Qt",4,7,"PropertyAnimation"); + qmlRegisterType("Qt",4,7,"RotationAnimation"); + qmlRegisterType("Qt",4,7,"ScriptAction"); + qmlRegisterType("Qt",4,7,"SequentialAnimation"); + qmlRegisterType("Qt",4,7,"SpringAnimation"); + qmlRegisterType("Qt",4,7,"StateChangeScript"); + qmlRegisterType("Qt",4,7,"StateGroup"); + qmlRegisterType("Qt",4,7,"State"); + qmlRegisterType("Qt",4,7,"SystemPalette"); + qmlRegisterType("Qt",4,7,"Timer"); + qmlRegisterType("Qt",4,7,"Transition"); + qmlRegisterType("Qt",4,7,"Vector3dAnimation"); +#ifdef QT_NO_XMLPATTERNS + qmlRegisterTypeNotAvailable("Qt",4,7,"XmlListModel", + qApp->translate("QDeclarativeXmlListModel","Qt was built without support for xmlpatterns")); + qmlRegisterTypeNotAvailable("Qt",4,7,"XmlRole", + qApp->translate("QDeclarativeXmlListModel","Qt was built without support for xmlpatterns")); +#else + qmlRegisterType("Qt",4,7,"XmlListModel"); + qmlRegisterType("Qt",4,7,"XmlRole"); +#endif + + qmlRegisterUncreatableType("Qt",4,7,"Animation",QDeclarativeAbstractAnimation::tr("Animation is an abstract class")); + + qmlRegisterCustomType("Qt", 4,7, "ListModel", new QDeclarativeListModelParser); + qmlRegisterCustomType("Qt", 4, 7, "PropertyChanges", new QDeclarativePropertyChangesParser); + qmlRegisterCustomType("Qt", 4, 7, "Connections", new QDeclarativeConnectionsParser); +#endif +} diff --git a/src/declarative/util/qdeclarativeutilmodule_p.h b/src/declarative/util/qdeclarativeutilmodule_p.h new file mode 100644 index 00000000..addfad6c --- /dev/null +++ b/src/declarative/util/qdeclarativeutilmodule_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEUTILMODULE_H +#define QDECLARATIVEUTILMODULE_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeUtilModule +{ +public: + static void defineModule(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEUTILMODULE_H diff --git a/src/declarative/util/qdeclarativeview.cpp b/src/declarative/util/qdeclarativeview.cpp new file mode 100644 index 00000000..ec357ffb --- /dev/null +++ b/src/declarative/util/qdeclarativeview.cpp @@ -0,0 +1,739 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeview.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(frameRateDebug, QML_SHOW_FRAMERATE) + +class QDeclarativeScene : public QGraphicsScene +{ +public: + QDeclarativeScene(QObject *parent = 0); + +protected: + virtual void keyPressEvent(QKeyEvent *); + virtual void keyReleaseEvent(QKeyEvent *); + + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *); + virtual void mousePressEvent(QGraphicsSceneMouseEvent *); + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *); +}; + +QDeclarativeScene::QDeclarativeScene(QObject *parent) : QGraphicsScene(parent) +{ +} + +void QDeclarativeScene::keyPressEvent(QKeyEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Key); + + QGraphicsScene::keyPressEvent(e); +} + +void QDeclarativeScene::keyReleaseEvent(QKeyEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Key); + + QGraphicsScene::keyReleaseEvent(e); +} + +void QDeclarativeScene::mouseMoveEvent(QGraphicsSceneMouseEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse); + + QGraphicsScene::mouseMoveEvent(e); +} + +void QDeclarativeScene::mousePressEvent(QGraphicsSceneMouseEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse); + + QGraphicsScene::mousePressEvent(e); +} + +void QDeclarativeScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse); + + QGraphicsScene::mouseReleaseEvent(e); +} + +class QDeclarativeViewPrivate : public QGraphicsViewPrivate, public QDeclarativeItemChangeListener +{ + Q_DECLARE_PUBLIC(QDeclarativeView) +public: + QDeclarativeViewPrivate() + : root(0), declarativeItemRoot(0), graphicsWidgetRoot(0), component(0), + resizeMode(QDeclarativeView::SizeViewToRootObject), initialSize(0,0) {} + ~QDeclarativeViewPrivate() { delete root; delete engine; } + void execute(); + void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); + void initResize(); + void updateSize(); + inline QSize rootObjectSize() const; + + QDeclarativeGuard root; + QDeclarativeGuard declarativeItemRoot; + QDeclarativeGuard graphicsWidgetRoot; + + QUrl source; + + QDeclarativeEngine* engine; + QDeclarativeComponent *component; + QBasicTimer resizetimer; + + QDeclarativeView::ResizeMode resizeMode; + QSize initialSize; + QElapsedTimer frameTimer; + + void init(); +}; + +void QDeclarativeViewPrivate::execute() +{ + Q_Q(QDeclarativeView); + if (root) { + delete root; + root = 0; + } + if (component) { + delete component; + component = 0; + } + if (!source.isEmpty()) { + component = new QDeclarativeComponent(engine, source, q); + if (!component->isLoading()) { + q->continueExecute(); + } else { + QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), q, SLOT(continueExecute())); + } + } +} + +void QDeclarativeViewPrivate::itemGeometryChanged(QDeclarativeItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_Q(QDeclarativeView); + if (resizeItem == root && resizeMode == QDeclarativeView::SizeViewToRootObject) { + // wait for both width and height to be changed + resizetimer.start(0,q); + } + QDeclarativeItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry); +} + +/*! + \class QDeclarativeView + \since 4.7 + \brief The QDeclarativeView class provides a widget for displaying a Qt Declarative user interface. + + QDeclarativeItem objects can be placed on a standard QGraphicsScene and + displayed with QGraphicsView. QDeclarativeView is a QGraphicsView subclass + provided as a convenience for displaying QML files, and connecting between + QML and C++ Qt objects. + + QDeclarativeView provides: + + \list + \o Management of QDeclarativeComponent loading and object creation + \o Initialization of QGraphicsView for optimal performance with QML using these settings: + \list + \o QGraphicsView::setOptimizationFlags(QGraphicsView::DontSavePainterState) + \o QGraphicsView::setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate) + \o QGraphicsScene::setItemIndexMethod(QGraphicsScene::NoIndex) + \endlist + \o Initialization of QGraphicsView for QML key handling using these settings: + \list + \o QGraphicsView::viewport()->setFocusPolicy(Qt::NoFocus) + \o QGraphicsView::setFocusPolicy(Qt::StrongFocus) + \o QGraphicsScene::setStickyFocus(true) + \endlist + \endlist + + Typical usage: + + \code + QDeclarativeView *view = new QDeclarativeView; + view->setSource(QUrl::fromLocalFile("myqmlfile.qml")); + view->show(); + \endcode + + Since QDeclarativeView is a QWidget-based class, it can be used to + display QML interfaces within QWidget-based GUI applications that do not + use the Graphics View framework. + + To receive errors related to loading and executing QML with QDeclarativeView, + you can connect to the statusChanged() signal and monitor for QDeclarativeView::Error. + The errors are available via QDeclarativeView::errors(). + + If you're using your own QGraphicsScene-based scene with QDeclarativeView, remember to + enable scene's sticky focus mode and to set itemIndexMethod to QGraphicsScene::NoIndex. + + \sa {Integrating QML Code with Existing Qt UI Code}, {Using QML Bindings in C++ Applications} +*/ + + +/*! \fn void QDeclarativeView::sceneResized(QSize size) + This signal is emitted when the view is resized to \a size. +*/ + +/*! \fn void QDeclarativeView::statusChanged(QDeclarativeView::Status status) + This signal is emitted when the component's current \a status changes. +*/ + +/*! + \fn QDeclarativeView::QDeclarativeView(QWidget *parent) + + Constructs a QDeclarativeView with the given \a parent. +*/ +QDeclarativeView::QDeclarativeView(QWidget *parent) + : QGraphicsView(*(new QDeclarativeViewPrivate), parent) +{ + Q_D(QDeclarativeView); + setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred); + d->init(); +} + +/*! + \fn QDeclarativeView::QDeclarativeView(const QUrl &source, QWidget *parent) + + Constructs a QDeclarativeView with the given QML \a source and \a parent. +*/ +QDeclarativeView::QDeclarativeView(const QUrl &source, QWidget *parent) + : QGraphicsView(*(new QDeclarativeViewPrivate), parent) +{ + Q_D(QDeclarativeView); + setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred); + d->init(); + setSource(source); +} + +void QDeclarativeViewPrivate::init() +{ + Q_Q(QDeclarativeView); + engine = new QDeclarativeEngine(); + q->setScene(new QDeclarativeScene(q)); + + 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); + q->scene()->setItemIndexMethod(QGraphicsScene::NoIndex); + q->viewport()->setFocusPolicy(Qt::NoFocus); + q->setFocusPolicy(Qt::StrongFocus); + + q->scene()->setStickyFocus(true); //### needed for correct focus handling + +#ifdef QDECLARATIVEVIEW_NOBACKGROUND + q->setAttribute(Qt::WA_OpaquePaintEvent); + q->setAttribute(Qt::WA_NoSystemBackground); + q->viewport()->setAttribute(Qt::WA_OpaquePaintEvent); + q->viewport()->setAttribute(Qt::WA_NoSystemBackground); +#endif + + QDeclarativeInspectorService::instance()->addView(q); +} + +/*! + Destroys the view. + */ +QDeclarativeView::~QDeclarativeView() +{ + QDeclarativeInspectorService::instance()->removeView(this); +} + +/*! \property QDeclarativeView::source + \brief The URL of the source of the QML component. + + Changing this property causes the QML component to be reloaded. + + Ensure that the URL provided is full and correct, in particular, use + \l QUrl::fromLocalFile() when loading a file from the local filesystem. + + \sa {Network Transparency}{Loading Resources in QML} + */ + +/*! + Sets the source to the \a url, loads the QML component and instantiates it. + + Ensure that the URL provided is full and correct, in particular, use + \l QUrl::fromLocalFile() when loading a file from the local filesystem. + + Calling this methods multiple times with the same url will result + in the QML being reloaded. + */ +void QDeclarativeView::setSource(const QUrl& url) +{ + Q_D(QDeclarativeView); + d->source = url; + d->execute(); +} + +/*! + Returns the source URL, if set. + + \sa setSource() + */ +QUrl QDeclarativeView::source() const +{ + Q_D(const QDeclarativeView); + return d->source; +} + +/*! + Returns a pointer to the QDeclarativeEngine used for instantiating + QML Components. + */ +QDeclarativeEngine* QDeclarativeView::engine() const +{ + Q_D(const QDeclarativeView); + return d->engine; +} + +/*! + This function returns the root of the context hierarchy. Each QML + component is instantiated in a QDeclarativeContext. QDeclarativeContext's are + essential for passing data to QML components. In QML, contexts are + arranged hierarchically and this hierarchy is managed by the + QDeclarativeEngine. + */ +QDeclarativeContext* QDeclarativeView::rootContext() const +{ + Q_D(const QDeclarativeView); + return d->engine->rootContext(); +} + +/*! + \enum QDeclarativeView::Status + Specifies the loading status of the QDeclarativeView. + + \value Null This QDeclarativeView has no source set. + \value Ready This QDeclarativeView has loaded and created the QML component. + \value Loading This QDeclarativeView is loading network data. + \value Error One or more errors has occurred. Call errors() to retrieve a list + of errors. +*/ + +/*! \enum QDeclarativeView::ResizeMode + + This enum specifies how to resize the view. + + \value SizeViewToRootObject The view resizes with the root item in the QML. + \value SizeRootObjectToView The view will automatically resize the root item to the size of the view. +*/ + +/*! + \property QDeclarativeView::status + The component's current \l{QDeclarativeView::Status} {status}. +*/ + +QDeclarativeView::Status QDeclarativeView::status() const +{ + Q_D(const QDeclarativeView); + if (!d->component) + return QDeclarativeView::Null; + + return QDeclarativeView::Status(d->component->status()); +} + +/*! + Return the list of errors that occurred during the last compile or create + operation. When the status is not Error, an empty list is returned. +*/ +QList QDeclarativeView::errors() const +{ + Q_D(const QDeclarativeView); + if (d->component) + return d->component->errors(); + return QList(); +} + +/*! + \property QDeclarativeView::resizeMode + \brief whether the view should resize the canvas contents + + If this property is set to SizeViewToRootObject (the default), the view + resizes with the root item in the QML. + + If this property is set to SizeRootObjectToView, 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. +*/ + +void QDeclarativeView::setResizeMode(ResizeMode mode) +{ + Q_D(QDeclarativeView); + if (d->resizeMode == mode) + return; + + if (d->declarativeItemRoot) { + if (d->resizeMode == SizeViewToRootObject) { + QDeclarativeItemPrivate *p = + static_cast(QGraphicsItemPrivate::get(d->declarativeItemRoot)); + p->removeItemChangeListener(d, QDeclarativeItemPrivate::Geometry); + } + } else if (d->graphicsWidgetRoot) { + if (d->resizeMode == QDeclarativeView::SizeViewToRootObject) { + d->graphicsWidgetRoot->removeEventFilter(this); + } + } + + d->resizeMode = mode; + if (d->root) { + d->initResize(); + } +} + +void QDeclarativeViewPrivate::initResize() +{ + Q_Q(QDeclarativeView); + if (declarativeItemRoot) { + if (resizeMode == QDeclarativeView::SizeViewToRootObject) { + QDeclarativeItemPrivate *p = + static_cast(QGraphicsItemPrivate::get(declarativeItemRoot)); + p->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + } + } else if (graphicsWidgetRoot) { + if (resizeMode == QDeclarativeView::SizeViewToRootObject) { + graphicsWidgetRoot->installEventFilter(q); + } + } + updateSize(); +} + +void QDeclarativeViewPrivate::updateSize() +{ + Q_Q(QDeclarativeView); + if (!root) + return; + if (declarativeItemRoot) { + if (resizeMode == QDeclarativeView::SizeViewToRootObject) { + QSize newSize = QSize(declarativeItemRoot->width(), declarativeItemRoot->height()); + if (newSize.isValid() && newSize != q->size()) { + q->resize(newSize); + } + } else if (resizeMode == QDeclarativeView::SizeRootObjectToView) { + if (!qFuzzyCompare(q->width(), declarativeItemRoot->width())) + declarativeItemRoot->setWidth(q->width()); + if (!qFuzzyCompare(q->height(), declarativeItemRoot->height())) + declarativeItemRoot->setHeight(q->height()); + } + } else if (graphicsWidgetRoot) { + if (resizeMode == QDeclarativeView::SizeViewToRootObject) { + QSize newSize = QSize(graphicsWidgetRoot->size().width(), graphicsWidgetRoot->size().height()); + if (newSize.isValid() && newSize != q->size()) { + q->resize(newSize); + } + } else if (resizeMode == QDeclarativeView::SizeRootObjectToView) { + QSizeF newSize = QSize(q->size().width(), q->size().height()); + if (newSize.isValid() && newSize != graphicsWidgetRoot->size()) { + graphicsWidgetRoot->resize(newSize); + } + } + } + q->updateGeometry(); +} + +QSize QDeclarativeViewPrivate::rootObjectSize() const +{ + QSize rootObjectSize(0,0); + int widthCandidate = -1; + int heightCandidate = -1; + if (root) { + QSizeF size = root->boundingRect().size(); + widthCandidate = size.width(); + heightCandidate = size.height(); + } + if (widthCandidate > 0) { + rootObjectSize.setWidth(widthCandidate); + } + if (heightCandidate > 0) { + rootObjectSize.setHeight(heightCandidate); + } + return rootObjectSize; +} + +QDeclarativeView::ResizeMode QDeclarativeView::resizeMode() const +{ + Q_D(const QDeclarativeView); + return d->resizeMode; +} + +/*! + \internal + */ +void QDeclarativeView::continueExecute() +{ + Q_D(QDeclarativeView); + disconnect(d->component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), this, SLOT(continueExecute())); + + if (d->component->isError()) { + QList errorList = d->component->errors(); + foreach (const QDeclarativeError &error, errorList) { + qWarning() << error; + } + emit statusChanged(status()); + return; + } + + QObject *obj = d->component->create(); + + if(d->component->isError()) { + QList errorList = d->component->errors(); + foreach (const QDeclarativeError &error, errorList) { + qWarning() << error; + } + emit statusChanged(status()); + return; + } + + setRootObject(obj); + emit statusChanged(status()); +} + +/*! + \internal +*/ +void QDeclarativeView::setRootObject(QObject *obj) +{ + Q_D(QDeclarativeView); + if (d->root == obj || !scene()) + return; + if (QDeclarativeItem *declarativeItem = qobject_cast(obj)) { + scene()->addItem(declarativeItem); + d->root = declarativeItem; + d->declarativeItemRoot = declarativeItem; + } else if (QGraphicsObject *graphicsObject = qobject_cast(obj)) { + scene()->addItem(graphicsObject); + d->root = graphicsObject; + if (graphicsObject->isWidget()) { + d->graphicsWidgetRoot = static_cast(graphicsObject); + } else { + qWarning() << "QDeclarativeView::resizeMode is not honored for components of type QGraphicsObject"; + } + } else if (obj) { + qWarning() << "QDeclarativeView only supports loading of root objects that derive from QGraphicsObject"; + if (QWidget* widget = qobject_cast(obj)) { + window()->setAttribute(Qt::WA_OpaquePaintEvent, false); + window()->setAttribute(Qt::WA_NoSystemBackground, false); + if (layout() && layout()->count()) { + // Hide the QGraphicsView in GV mode. + QLayoutItem *item = layout()->itemAt(0); + if (item->widget()) + item->widget()->hide(); + } + widget->setParent(this); + if (isVisible()) { + widget->setVisible(true); + } + resize(widget->size()); + } + } + + if (d->root) { + d->initialSize = d->rootObjectSize(); + if (d->initialSize != size()) { + if (!(parentWidget() && parentWidget()->layout())) { + resize(d->initialSize); + } + } + d->initResize(); + } +} + +/*! + \internal + If the \l {QTimerEvent} {timer event} \a e is this + view's resize timer, sceneResized() is emitted. + */ +void QDeclarativeView::timerEvent(QTimerEvent* e) +{ + Q_D(QDeclarativeView); + if (!e || e->timerId() == d->resizetimer.timerId()) { + d->updateSize(); + d->resizetimer.stop(); + } +} + +/*! \internal */ +bool QDeclarativeView::eventFilter(QObject *watched, QEvent *e) +{ + Q_D(QDeclarativeView); + if (watched == d->root && d->resizeMode == SizeViewToRootObject) { + if (d->graphicsWidgetRoot) { + if (e->type() == QEvent::GraphicsSceneResize) { + d->updateSize(); + } + } + } + return QGraphicsView::eventFilter(watched, e); +} + +/*! + \internal + Preferred size follows the root object geometry. +*/ +QSize QDeclarativeView::sizeHint() const +{ + Q_D(const QDeclarativeView); + QSize rootObjectSize = d->rootObjectSize(); + if (rootObjectSize.isEmpty()) { + return size(); + } else { + return rootObjectSize; + } +} + +/*! + Returns the initial size of the root object +*/ +QSize QDeclarativeView::initialSize() const +{ + Q_D(const QDeclarativeView); + return d->initialSize; +} + +/*! + Returns the view's root \l {QGraphicsObject} {item}. + */ +QGraphicsObject *QDeclarativeView::rootObject() const +{ + Q_D(const QDeclarativeView); + return d->root; +} + +/*! + \internal + This function handles the \l {QResizeEvent} {resize event} + \a e. + */ +void QDeclarativeView::resizeEvent(QResizeEvent *e) +{ + Q_D(QDeclarativeView); + if (d->resizeMode == SizeRootObjectToView) { + d->updateSize(); + } + if (d->declarativeItemRoot) { + setSceneRect(QRectF(0, 0, d->declarativeItemRoot->width(), d->declarativeItemRoot->height())); + } else if (d->root) { + setSceneRect(d->root->boundingRect()); + } else { + setSceneRect(rect()); + } + emit sceneResized(e->size()); + QGraphicsView::resizeEvent(e); +} + +/*! + \internal +*/ +void QDeclarativeView::paintEvent(QPaintEvent *event) +{ + Q_D(QDeclarativeView); + + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::FramePaint); + QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Painting); + + int time = 0; + if (frameRateDebug()) + time = d->frameTimer.restart(); + + QGraphicsView::paintEvent(event); + + QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Painting); + + if (frameRateDebug()) + qDebug() << "paintEvent:" << d->frameTimer.elapsed() << "time since last frame:" << time; + +#if QT_SHOW_DECLARATIVEVIEW_FPS + static QTime timer; + static int frames; + + if (frames == 0) { + timer.start(); + } else if (timer.elapsed() > 5000) { + qreal avgtime = timer.elapsed() / (qreal) frames; + qDebug("Average time per frame: %f ms (%i fps)", avgtime, int(1000 / avgtime)); + timer.start(); + frames = 0; + } + ++frames; + scene()->update(); +#endif + +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativeview.h b/src/declarative/util/qdeclarativeview.h new file mode 100644 index 00000000..043460c8 --- /dev/null +++ b/src/declarative/util/qdeclarativeview.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEVIEW_H +#define QDECLARATIVEVIEW_H + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QGraphicsObject; +class QDeclarativeEngine; +class QDeclarativeContext; +class QDeclarativeError; + +class QDeclarativeViewPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeView : public QGraphicsView +{ + Q_OBJECT + Q_PROPERTY(ResizeMode resizeMode READ resizeMode WRITE setResizeMode) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QUrl source READ source WRITE setSource DESIGNABLE true) + Q_ENUMS(ResizeMode Status) +public: + explicit QDeclarativeView(QWidget *parent = 0); + QDeclarativeView(const QUrl &source, QWidget *parent = 0); + virtual ~QDeclarativeView(); + + QUrl source() const; + void setSource(const QUrl&); + + QDeclarativeEngine* engine() const; + QDeclarativeContext* rootContext() const; + + QGraphicsObject *rootObject() const; + + enum ResizeMode { SizeViewToRootObject, SizeRootObjectToView }; + ResizeMode resizeMode() const; + void setResizeMode(ResizeMode); + + enum Status { Null, Ready, Loading, Error }; + Status status() const; + + QList errors() const; + + QSize sizeHint() const; + QSize initialSize() const; + +Q_SIGNALS: + void sceneResized(QSize size); // ??? + void statusChanged(QDeclarativeView::Status); + +private Q_SLOTS: + void continueExecute(); + +protected: + virtual void resizeEvent(QResizeEvent *); + virtual void paintEvent(QPaintEvent *event); + virtual void timerEvent(QTimerEvent*); + virtual void setRootObject(QObject *obj); + virtual bool eventFilter(QObject *watched, QEvent *e); + +private: + Q_DISABLE_COPY(QDeclarativeView) + Q_DECLARE_PRIVATE(QDeclarativeView) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEVIEW_H diff --git a/src/declarative/util/qdeclarativexmllistmodel.cpp b/src/declarative/util/qdeclarativexmllistmodel.cpp new file mode 100644 index 00000000..a789996e --- /dev/null +++ b/src/declarative/util/qdeclarativexmllistmodel.cpp @@ -0,0 +1,1157 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativexmllistmodel_p.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +Q_DECLARE_METATYPE(QDeclarativeXmlQueryResult) + +QT_BEGIN_NAMESPACE + + +typedef QPair QDeclarativeXmlListRange; + +#define XMLLISTMODEL_CLEAR_ID 0 + +/*! + \qmlclass XmlRole QDeclarativeXmlListModelRole + \ingroup qml-working-with-data + \since 4.7 + \brief The XmlRole element allows you to specify a role for an XmlListModel. + + \sa {QtDeclarative} +*/ + +/*! + \qmlproperty string XmlRole::name + + The name for the role. This name is used to access the model data for this role. + + For example, the following model has a role named "title", which can be accessed + from the view's delegate: + + \qml + XmlListModel { + id: xmlModel + // ... + XmlRole { + name: "title" + query: "title/string()" + } + } + \endqml + + \qml + ListView { + model: xmlModel + delegate: Text { text: title } + } + \endqml +*/ + +/*! + \qmlproperty string XmlRole::query + The relative XPath expression query for this role. The query must be relative; it cannot start + with a '/'. + + For example, if there is an XML document like this: + + \quotefile doc/src/snippets/declarative/xmlrole.xml + + Here are some valid XPath expressions for XmlRole queries on this document: + + \snippet doc/src/snippets/declarative/xmlrole.qml 0 + \dots 4 + \snippet doc/src/snippets/declarative/xmlrole.qml 1 + + See the \l{http://www.w3.org/TR/xpath20/}{W3C XPath 2.0 specification} for more information. +*/ + +/*! + \qmlproperty bool XmlRole::isKey + Defines whether this is a key role. + + Key roles are used to to determine whether a set of values should + be updated or added to the XML list model when XmlListModel::reload() + is called. + + \sa XmlListModel +*/ + +struct XmlQueryJob +{ + int queryId; + QByteArray data; + QString query; + QString namespaces; + QStringList roleQueries; + QList roleQueryErrorId; // the ptr to send back if there is an error + QStringList keyRoleQueries; + QStringList keyRoleResultsCache; + QString prefix; +}; + + +class QDeclarativeXmlQueryEngine; +class QDeclarativeXmlQueryThreadObject : public QObject +{ + Q_OBJECT +public: + QDeclarativeXmlQueryThreadObject(QDeclarativeXmlQueryEngine *); + + void processJobs(); + virtual bool event(QEvent *e); + +private: + QDeclarativeXmlQueryEngine *m_queryEngine; +}; + + +class QDeclarativeXmlQueryEngine : public QThread +{ + Q_OBJECT +public: + QDeclarativeXmlQueryEngine(QDeclarativeEngine *eng); + ~QDeclarativeXmlQueryEngine(); + + int doQuery(QString query, QString namespaces, QByteArray data, QList* roleObjects, QStringList keyRoleResultsCache); + void abort(int id); + + void processJobs(); + + static QDeclarativeXmlQueryEngine *instance(QDeclarativeEngine *engine); + +signals: + void queryCompleted(const QDeclarativeXmlQueryResult &); + void error(void*, const QString&); + +protected: + void run(); + +private: + void processQuery(XmlQueryJob *job); + void doQueryJob(XmlQueryJob *job, QDeclarativeXmlQueryResult *currentResult); + void doSubQueryJob(XmlQueryJob *job, QDeclarativeXmlQueryResult *currentResult); + void getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const; + void addIndexToRangeList(QList *ranges, int index) const; + + QMutex m_mutex; + QDeclarativeXmlQueryThreadObject *m_threadObject; + QList m_jobs; + QSet m_cancelledJobs; + QAtomicInt m_queryIds; + + QDeclarativeEngine *m_engine; + QObject *m_eventLoopQuitHack; + + static QHash queryEngines; + static QMutex queryEnginesMutex; +}; +QHash QDeclarativeXmlQueryEngine::queryEngines; +QMutex QDeclarativeXmlQueryEngine::queryEnginesMutex; + + +QDeclarativeXmlQueryThreadObject::QDeclarativeXmlQueryThreadObject(QDeclarativeXmlQueryEngine *e) + : m_queryEngine(e) +{ +} + +void QDeclarativeXmlQueryThreadObject::processJobs() +{ + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); +} + +bool QDeclarativeXmlQueryThreadObject::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + m_queryEngine->processJobs(); + return true; + } else { + return QObject::event(e); + } +} + + + +QDeclarativeXmlQueryEngine::QDeclarativeXmlQueryEngine(QDeclarativeEngine *eng) +: QThread(eng), m_threadObject(0), m_queryIds(XMLLISTMODEL_CLEAR_ID + 1), m_engine(eng), m_eventLoopQuitHack(0) +{ + qRegisterMetaType("QDeclarativeXmlQueryResult"); + + m_eventLoopQuitHack = new QObject; + m_eventLoopQuitHack->moveToThread(this); + connect(m_eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection); + start(QThread::IdlePriority); +} + +QDeclarativeXmlQueryEngine::~QDeclarativeXmlQueryEngine() +{ + queryEnginesMutex.lock(); + queryEngines.remove(m_engine); + queryEnginesMutex.unlock(); + + m_eventLoopQuitHack->deleteLater(); + wait(); +} + +int QDeclarativeXmlQueryEngine::doQuery(QString query, QString namespaces, QByteArray data, QList* roleObjects, QStringList keyRoleResultsCache) { + { + QMutexLocker m1(&m_mutex); + m_queryIds.ref(); + if (m_queryIds <= 0) + m_queryIds = 1; + } + + XmlQueryJob job; + job.queryId = m_queryIds; + job.data = data; + job.query = QLatin1String("doc($src)") + query; + job.namespaces = namespaces; + job.keyRoleResultsCache = keyRoleResultsCache; + + for (int i=0; icount(); i++) { + if (!roleObjects->at(i)->isValid()) { + job.roleQueries << QString(); + continue; + } + job.roleQueries << roleObjects->at(i)->query(); + job.roleQueryErrorId << static_cast(roleObjects->at(i)); + if (roleObjects->at(i)->isKey()) + job.keyRoleQueries << job.roleQueries.last(); + } + + { + QMutexLocker ml(&m_mutex); + m_jobs.append(job); + if (m_threadObject) + m_threadObject->processJobs(); + } + + return job.queryId; +} + +void QDeclarativeXmlQueryEngine::abort(int id) +{ + QMutexLocker ml(&m_mutex); + if (id != -1) + m_cancelledJobs.insert(id); +} + +void QDeclarativeXmlQueryEngine::run() +{ + m_mutex.lock(); + m_threadObject = new QDeclarativeXmlQueryThreadObject(this); + m_mutex.unlock(); + + processJobs(); + exec(); + + delete m_threadObject; + m_threadObject = 0; +} + +void QDeclarativeXmlQueryEngine::processJobs() +{ + QMutexLocker locker(&m_mutex); + + while (true) { + if (m_jobs.isEmpty()) + return; + + XmlQueryJob currentJob = m_jobs.takeLast(); + while (m_cancelledJobs.remove(currentJob.queryId)) { + if (m_jobs.isEmpty()) + return; + currentJob = m_jobs.takeLast(); + } + + locker.unlock(); + processQuery(¤tJob); + locker.relock(); + } +} + +QDeclarativeXmlQueryEngine *QDeclarativeXmlQueryEngine::instance(QDeclarativeEngine *engine) +{ + queryEnginesMutex.lock(); + QDeclarativeXmlQueryEngine *queryEng = queryEngines.value(engine); + if (!queryEng) { + queryEng = new QDeclarativeXmlQueryEngine(engine); + queryEngines.insert(engine, queryEng); + } + queryEnginesMutex.unlock(); + + return queryEng; +} + +void QDeclarativeXmlQueryEngine::processQuery(XmlQueryJob *job) +{ + QDeclarativeXmlQueryResult result; + result.queryId = job->queryId; + doQueryJob(job, &result); + doSubQueryJob(job, &result); + + { + QMutexLocker ml(&m_mutex); + if (m_cancelledJobs.contains(job->queryId)) { + m_cancelledJobs.remove(job->queryId); + } else { + emit queryCompleted(result); + } + } +} + +void QDeclarativeXmlQueryEngine::doQueryJob(XmlQueryJob *currentJob, QDeclarativeXmlQueryResult *currentResult) +{ + Q_ASSERT(currentJob->queryId != -1); + + QString r; + QXmlQuery query; + QBuffer buffer(¤tJob->data); + buffer.open(QIODevice::ReadOnly); + query.bindVariable(QLatin1String("src"), &buffer); + query.setQuery(currentJob->namespaces + currentJob->query); + query.evaluateTo(&r); + + //always need a single root element + QByteArray xml = "\n" + r.toUtf8() + ""; + QBuffer b(&xml); + b.open(QIODevice::ReadOnly); + + QString namespaces = QLatin1String("declare namespace dummy=\"http://qtsotware.com/dummy\";\n") + currentJob->namespaces; + QString prefix = QLatin1String("doc($inputDocument)/dummy:items") + + currentJob->query.mid(currentJob->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(); + } + + currentJob->data = xml; + currentJob->prefix = namespaces + prefix + QLatin1Char('/'); + currentResult->size = (count > 0 ? count : 0); +} + +void QDeclarativeXmlQueryEngine::getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const +{ + const QStringList &keysQueries = currentJob.keyRoleQueries; + QString keysQuery; + if (keysQueries.count() == 1) + keysQuery = currentJob.prefix + keysQueries[0]; + else if (keysQueries.count() > 1) + keysQuery = currentJob.prefix + QLatin1String("concat(") + keysQueries.join(QLatin1String(",")) + QLatin1String(")"); + + if (!keysQuery.isEmpty()) { + query->setQuery(keysQuery); + QXmlResultItems resultItems; + query->evaluateTo(&resultItems); + QXmlItem item(resultItems.next()); + while (!item.isNull()) { + values->append(item.toAtomicValue().toString()); + item = resultItems.next(); + } + } +} + +void QDeclarativeXmlQueryEngine::addIndexToRangeList(QList *ranges, int index) const { + if (ranges->isEmpty()) + ranges->append(qMakePair(index, 1)); + else if (ranges->last().first + ranges->last().second == index) + ranges->last().second += 1; + else + ranges->append(qMakePair(index, 1)); +} + +void QDeclarativeXmlQueryEngine::doSubQueryJob(XmlQueryJob *currentJob, QDeclarativeXmlQueryResult *currentResult) +{ + Q_ASSERT(currentJob->queryId != -1); + + QBuffer b(¤tJob->data); + b.open(QIODevice::ReadOnly); + + QXmlQuery subquery; + subquery.bindVariable(QLatin1String("inputDocument"), &b); + + QStringList keyRoleResults; + getValuesOfKeyRoles(*currentJob, &keyRoleResults, &subquery); + + // See if any values of key roles have been inserted or removed. + + if (currentJob->keyRoleResultsCache.isEmpty()) { + currentResult->inserted << qMakePair(0, currentResult->size); + } else { + if (keyRoleResults != currentJob->keyRoleResultsCache) { + QStringList temp; + for (int i=0; ikeyRoleResultsCache.count(); i++) { + if (!keyRoleResults.contains(currentJob->keyRoleResultsCache[i])) + addIndexToRangeList(¤tResult->removed, i); + else + temp << currentJob->keyRoleResultsCache[i]; + } + + for (int i=0; iinserted, i); + } + } + } + } + currentResult->keyRoleResultsCache = keyRoleResults; + + // Get the new values for each role. + //### we might be able to condense even further (query for everything in one go) + const QStringList &queries = currentJob->roleQueries; + for (int i = 0; i < queries.size(); ++i) { + QList resultList; + if (!queries[i].isEmpty()) { + subquery.setQuery(currentJob->prefix + QLatin1String("(let $v := string(") + queries[i] + QLatin1String(") return if ($v) then ") + queries[i] + QLatin1String(" else \"\")")); + if (subquery.isValid()) { + QXmlResultItems resultItems; + subquery.evaluateTo(&resultItems); + QXmlItem item(resultItems.next()); + while (!item.isNull()) { + resultList << item.toAtomicValue(); //### we used to trim strings + item = resultItems.next(); + } + } else { + emit error(currentJob->roleQueryErrorId.at(i), queries[i]); + } + } + //### should warn here if things have gone wrong. + while (resultList.count() < currentResult->size) + resultList << QVariant(); + currentResult->data << resultList; + b.seek(0); + } + + //this method is much slower, but works better for incremental loading + /*for (int j = 0; j < m_size; ++j) { + QList resultList; + for (int i = 0; i < m_roleObjects->size(); ++i) { + QDeclarativeXmlListModelRole *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 QDeclarativeXmlListModelPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeXmlListModel) +public: + QDeclarativeXmlListModelPrivate() + : isComponentComplete(true), size(-1), highestRole(Qt::UserRole) + , reply(0), status(QDeclarativeXmlListModel::Null), progress(0.0) + , queryId(-1), roleObjects(), redirectCount(0) {} + + + void notifyQueryStarted(bool remoteSource) { + Q_Q(QDeclarativeXmlListModel); + progress = remoteSource ? qreal(0.0) : qreal(1.0); + status = QDeclarativeXmlListModel::Loading; + errorString.clear(); + emit q->progressChanged(progress); + emit q->statusChanged(status); + } + + void deleteReply() { + Q_Q(QDeclarativeXmlListModel); + if (reply) { + QObject::disconnect(reply, 0, q, 0); + reply->deleteLater(); + reply = 0; + } + } + + bool isComponentComplete; + QUrl src; + QString xml; + QString query; + QString namespaces; + int size; + QList roles; + QStringList roleNames; + int highestRole; + + QNetworkReply *reply; + QDeclarativeXmlListModel::Status status; + QString errorString; + qreal progress; + int queryId; + QStringList keyRoleResultsCache; + QList roleObjects; + + static void append_role(QDeclarativeListProperty *list, QDeclarativeXmlListModelRole *role); + static void clear_role(QDeclarativeListProperty *list); + QList > data; + int redirectCount; +}; + + +void QDeclarativeXmlListModelPrivate::append_role(QDeclarativeListProperty *list, QDeclarativeXmlListModelRole *role) +{ + QDeclarativeXmlListModel *_this = qobject_cast(list->object); + if (_this && role) { + int i = _this->d_func()->roleObjects.count(); + _this->d_func()->roleObjects.append(role); + if (_this->d_func()->roleNames.contains(role->name())) { + qmlInfo(role) << QObject::tr("\"%1\" duplicates a previous role name and will be disabled.").arg(role->name()); + return; + } + _this->d_func()->roles.insert(i, _this->d_func()->highestRole); + _this->d_func()->roleNames.insert(i, role->name()); + ++_this->d_func()->highestRole; + } +} + +//### clear needs to invalidate any cached data (in data table) as well +// (and the model should emit the appropriate signals) +void QDeclarativeXmlListModelPrivate::clear_role(QDeclarativeListProperty *list) +{ + QDeclarativeXmlListModel *_this = static_cast(list->object); + _this->d_func()->roles.clear(); + _this->d_func()->roleNames.clear(); + _this->d_func()->roleObjects.clear(); +} + +/*! + \qmlclass XmlListModel QDeclarativeXmlListModel + \ingroup qml-working-with-data + \since 4.7 + \brief The XmlListModel element is used to specify a read-only model using XPath expressions. + + XmlListModel is used to create a read-only model from XML data. It can be used as a data source + for view elements (such as ListView, PathView, GridView) and other elements that interact with model + data (such as \l Repeater). + + For example, if there is a XML document at http://www.mysite.com/feed.xml like this: + + \code + + + ... + + + A blog post + Sat, 07 Sep 2010 10:00:01 GMT + + + Another blog post + Sat, 07 Sep 2010 15:35:01 GMT + + + + \endcode + + A XmlListModel could create a model from this data, like this: + + \qml + import QtQuick 1.0 + + XmlListModel { + id: xmlModel + source: "http://www.mysite.com/feed.xml" + query: "/rss/channel/item" + + XmlRole { name: "title"; query: "title/string()" } + XmlRole { name: "pubDate"; query: "pubDate/string()" } + } + \endqml + + The \l {XmlListModel::query}{query} value of "/rss/channel/item" specifies that the XmlListModel should generate + a model item for each \c in the XML document. + + The XmlRole objects define the + model item attributes. Here, each model item will have \c title and \c pubDate + attributes that match the \c title and \c pubDate values of its corresponding \c . + (See \l XmlRole::query for more examples of valid XPath expressions for XmlRole.) + + The model could be used in a ListView, like this: + + \qml + ListView { + width: 180; height: 300 + model: xmlModel + delegate: Text { text: title + ": " + pubDate } + } + \endqml + + \image qml-xmllistmodel-example.png + + The XmlListModel data is loaded asynchronously, and \l status + is set to \c XmlListModel.Ready when loading is complete. + Note this means when XmlListModel is used for a view, the view is not + populated until the model is loaded. + + + \section2 Using key XML roles + + You can define certain roles as "keys" so that when reload() is called, + the model will only add and refresh data that contains new values for + these keys. + + For example, if above role for "pubDate" was defined like this instead: + + \qml + XmlRole { name: "pubDate"; query: "pubDate/string()"; isKey: true } + \endqml + + Then when reload() is called, the model will only add and reload + items with a "pubDate" value that is not already + present in the model. + + This is useful when displaying the contents of XML documents that + are incrementally updated (such as RSS feeds) to avoid repainting the + entire contents of a model in a view. + + If multiple key roles are specified, the model only adds and reload items + with a combined value of all key roles that is not already present in + the model. + + \sa {RSS News} +*/ + +QDeclarativeXmlListModel::QDeclarativeXmlListModel(QObject *parent) + : QListModelInterface(*(new QDeclarativeXmlListModelPrivate), parent) +{ +} + +QDeclarativeXmlListModel::~QDeclarativeXmlListModel() +{ +} + +/*! + \qmlproperty list XmlListModel::roles + + The roles to make available for this model. +*/ +QDeclarativeListProperty QDeclarativeXmlListModel::roleObjects() +{ + Q_D(QDeclarativeXmlListModel); + QDeclarativeListProperty list(this, d->roleObjects); + list.append = &QDeclarativeXmlListModelPrivate::append_role; + list.clear = &QDeclarativeXmlListModelPrivate::clear_role; + return list; +} + +QHash QDeclarativeXmlListModel::data(int index, const QList &roles) const +{ + Q_D(const QDeclarativeXmlListModel); + QHash 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.value(roleIndex).value(index)); + } + return rv; +} + +QVariant QDeclarativeXmlListModel::data(int index, int role) const +{ + Q_D(const QDeclarativeXmlListModel); + int roleIndex = d->roles.indexOf(role); + return (roleIndex == -1) ? QVariant() : d->data.value(roleIndex).value(index); +} + +/*! + \qmlproperty int XmlListModel::count + The number of data entries in the model. +*/ +int QDeclarativeXmlListModel::count() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->size; +} + +QList QDeclarativeXmlListModel::roles() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->roles; +} + +QString QDeclarativeXmlListModel::toString(int role) const +{ + Q_D(const QDeclarativeXmlListModel); + 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 \c source and \l xml are set, \l xml is used. +*/ +QUrl QDeclarativeXmlListModel::source() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->src; +} + +void QDeclarativeXmlListModel::setSource(const QUrl &src) +{ + Q_D(QDeclarativeXmlListModel); + if (d->src != src) { + d->src = src; + if (d->xml.isEmpty()) // src is only used if d->xml is not set + reload(); + emit sourceChanged(); + } +} + +/*! + \qmlproperty string XmlListModel::xml + This property holds the XML data for this model, if set. + + The text is assumed to be UTF-8 encoded. + + If both \l source and \c xml are set, \c xml is used. +*/ +QString QDeclarativeXmlListModel::xml() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->xml; +} + +void QDeclarativeXmlListModel::setXml(const QString &xml) +{ + Q_D(QDeclarativeXmlListModel); + if (d->xml != xml) { + d->xml = xml; + reload(); + emit xmlChanged(); + } +} + +/*! + \qmlproperty string XmlListModel::query + An absolute XPath query representing the base query for creating model items + from this model's XmlRole objects. The query should start with '/' or '//'. +*/ +QString QDeclarativeXmlListModel::query() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->query; +} + +void QDeclarativeXmlListModel::setQuery(const QString &query) +{ + Q_D(QDeclarativeXmlListModel); + if (!query.startsWith(QLatin1Char('/'))) { + qmlInfo(this) << QCoreApplication::translate("QDeclarativeXmlRoleList", "An XmlListModel query must start with '/' or \"//\""); + return; + } + + if (d->query != query) { + d->query = query; + reload(); + emit queryChanged(); + } +} + +/*! + \qmlproperty string XmlListModel::namespaceDeclarations + The namespace declarations to be used in the XPath queries. + + The namespaces should be declared as in XQuery. For example, if a requested document + at http://mysite.com/feed.xml uses the namespace "http://www.w3.org/2005/Atom", + this can be declared as the default namespace: + + \qml + XmlListModel { + source: "http://mysite.com/feed.xml" + query: "/feed/entry" + namespaceDeclarations: "declare default element namespace 'http://www.w3.org/2005/Atom';" + + XmlRole { name: "title"; query: "title/string()" } + } + \endqml +*/ +QString QDeclarativeXmlListModel::namespaceDeclarations() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->namespaces; +} + +void QDeclarativeXmlListModel::setNamespaceDeclarations(const QString &declarations) +{ + Q_D(QDeclarativeXmlListModel); + if (d->namespaces != declarations) { + d->namespaces = declarations; + reload(); + emit namespaceDeclarationsChanged(); + } +} + +/*! + \qmlmethod object XmlListModel::get(int index) + + Returns the item at \a index in the model. + + For example, for a model like this: + + \qml + XmlListModel { + id: model + source: "http://mysite.com/feed.xml" + query: "/feed/entry" + XmlRole { name: "title"; query: "title/string()" } + } + \endqml + + This will access the \c title value for the first item in the model: + + \js + var title = model.get(0).title; + \endjs +*/ +QScriptValue QDeclarativeXmlListModel::get(int index) const +{ + Q_D(const QDeclarativeXmlListModel); + + QScriptEngine *sengine = QDeclarativeEnginePrivate::getScriptEngine(qmlContext(this)->engine()); + if (index < 0 || index >= count()) + return sengine->undefinedValue(); + + QScriptValue sv = sengine->newObject(); + for (int i=0; iroleObjects.count(); i++) + sv.setProperty(d->roleObjects[i]->name(), sengine->toScriptValue(d->data.value(i).value(index))); + return sv; +} + +/*! + \qmlproperty enumeration XmlListModel::status + Specifies the model loading status, which can be one of the following: + + \list + \o XmlListModel.Null - No XML data has been set for this model. + \o XmlListModel.Ready - The XML data has been loaded into the model. + \o XmlListModel.Loading - The model is in the process of reading and loading XML data. + \o XmlListModel.Error - An error occurred while the model was loading. See errorString() for details + about the error. + \endlist + + \sa progress + +*/ +QDeclarativeXmlListModel::Status QDeclarativeXmlListModel::status() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->status; +} + +/*! + \qmlproperty real XmlListModel::progress + + This indicates the current progress of the downloading of the XML data + source. This value ranges from 0.0 (no data downloaded) to + 1.0 (all data downloaded). If the XML data is not from a remote source, + the progress becomes 1.0 as soon as the data is read. + + Note that when the progress is 1.0, the XML data has been downloaded, but + it is yet to be loaded into the model at this point. Use the status + property to find out when the XML data has been read and loaded into + the model. + + \sa status, source +*/ +qreal QDeclarativeXmlListModel::progress() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->progress; +} + +/*! + \qmlmethod void XmlListModel::errorString() + + Returns a string description of the last error that occurred + if \l status is XmlListModel::Error. +*/ +QString QDeclarativeXmlListModel::errorString() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->errorString; +} + +void QDeclarativeXmlListModel::classBegin() +{ + Q_D(QDeclarativeXmlListModel); + d->isComponentComplete = false; + + QDeclarativeXmlQueryEngine *queryEngine = QDeclarativeXmlQueryEngine::instance(qmlEngine(this)); + connect(queryEngine, SIGNAL(queryCompleted(QDeclarativeXmlQueryResult)), + SLOT(queryCompleted(QDeclarativeXmlQueryResult))); + connect(queryEngine, SIGNAL(error(void*,QString)), + SLOT(queryError(void*,QString))); +} + +void QDeclarativeXmlListModel::componentComplete() +{ + Q_D(QDeclarativeXmlListModel); + d->isComponentComplete = true; + reload(); +} + +/*! + \qmlmethod XmlListModel::reload() + + Reloads the model. + + If no key roles have been specified, all existing model + data is removed, and the model is rebuilt from scratch. + + Otherwise, items are only added if the model does not already + contain items with matching key role values. + + \sa {Using key XML roles}, XmlRole::isKey +*/ +void QDeclarativeXmlListModel::reload() +{ + Q_D(QDeclarativeXmlListModel); + + if (!d->isComponentComplete) + return; + + QDeclarativeXmlQueryEngine::instance(qmlEngine(this))->abort(d->queryId); + d->queryId = -1; + + if (d->size < 0) + d->size = 0; + + if (d->reply) { + d->reply->abort(); + d->deleteReply(); + } + + if (!d->xml.isEmpty()) { + d->queryId = QDeclarativeXmlQueryEngine::instance(qmlEngine(this))->doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects, d->keyRoleResultsCache); + d->notifyQueryStarted(false); + + } else if (d->src.isEmpty()) { + d->queryId = XMLLISTMODEL_CLEAR_ID; + d->notifyQueryStarted(false); + QTimer::singleShot(0, this, SLOT(dataCleared())); + + } else { + d->notifyQueryStarted(true); + QNetworkRequest req(d->src); + req.setRawHeader("Accept", "application/xml,*/*"); + 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))); + } +} + +#define XMLLISTMODEL_MAX_REDIRECT 16 + +void QDeclarativeXmlListModel::requestFinished() +{ + Q_D(QDeclarativeXmlListModel); + + d->redirectCount++; + if (d->redirectCount < XMLLISTMODEL_MAX_REDIRECT) { + QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = d->reply->url().resolved(redirect.toUrl()); + d->deleteReply(); + setSource(url); + return; + } + } + d->redirectCount = 0; + + if (d->reply->error() != QNetworkReply::NoError) { + d->errorString = d->reply->errorString(); + d->deleteReply(); + + int count = this->count(); + d->data.clear(); + d->size = 0; + if (count > 0) { + emit itemsRemoved(0, count); + emit countChanged(); + } + + d->status = Error; + d->queryId = -1; + emit statusChanged(d->status); + } else { + QByteArray data = d->reply->readAll(); + if (data.isEmpty()) { + d->queryId = XMLLISTMODEL_CLEAR_ID; + QTimer::singleShot(0, this, SLOT(dataCleared())); + } else { + d->queryId = QDeclarativeXmlQueryEngine::instance(qmlEngine(this))->doQuery(d->query, d->namespaces, data, &d->roleObjects, d->keyRoleResultsCache); + } + d->deleteReply(); + + d->progress = 1.0; + emit progressChanged(d->progress); + } +} + +void QDeclarativeXmlListModel::requestProgress(qint64 received, qint64 total) +{ + Q_D(QDeclarativeXmlListModel); + if (d->status == Loading && total > 0) { + d->progress = qreal(received)/total; + emit progressChanged(d->progress); + } +} + +void QDeclarativeXmlListModel::dataCleared() +{ + Q_D(QDeclarativeXmlListModel); + QDeclarativeXmlQueryResult r; + r.queryId = XMLLISTMODEL_CLEAR_ID; + r.size = 0; + r.removed << qMakePair(0, count()); + r.keyRoleResultsCache = d->keyRoleResultsCache; + queryCompleted(r); +} + +void QDeclarativeXmlListModel::queryError(void* object, const QString& error) +{ + // Be extra careful, object may no longer exist, it's just an ID. + Q_D(QDeclarativeXmlListModel); + for (int i=0; iroleObjects.count(); i++) { + if (d->roleObjects.at(i) == static_cast(object)) { + qmlInfo(d->roleObjects.at(i)) << QObject::tr("invalid query: \"%1\"").arg(error); + return; + } + } + qmlInfo(this) << QObject::tr("invalid query: \"%1\"").arg(error); +} + +void QDeclarativeXmlListModel::queryCompleted(const QDeclarativeXmlQueryResult &result) +{ + Q_D(QDeclarativeXmlListModel); + if (result.queryId != d->queryId) + return; + + int origCount = d->size; + bool sizeChanged = result.size != d->size; + + d->size = result.size; + d->data = result.data; + d->keyRoleResultsCache = result.keyRoleResultsCache; + d->status = Ready; + d->errorString.clear(); + d->queryId = -1; + + bool hasKeys = false; + for (int i=0; iroleObjects.count(); i++) { + if (d->roleObjects[i]->isKey()) { + hasKeys = true; + break; + } + } + if (!hasKeys) { + if (!(origCount == 0 && d->size == 0)) { + emit itemsRemoved(0, origCount); + emit itemsInserted(0, d->size); + emit countChanged(); + } + + } else { + for (int i=0; istatus); +} + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/util/qdeclarativexmllistmodel_p.h b/src/declarative/util/qdeclarativexmllistmodel_p.h new file mode 100644 index 00000000..cb0e12b0 --- /dev/null +++ b/src/declarative/util/qdeclarativexmllistmodel_p.h @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEXMLLISTMODEL_H +#define QDECLARATIVEXMLLISTMODEL_H + +#include +#include + +#include +#include +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeContext; +class QDeclarativeXmlListModelRole; +class QDeclarativeXmlListModelPrivate; + +struct QDeclarativeXmlQueryResult { + int queryId; + int size; + QList > data; + QList > inserted; + QList > removed; + QStringList keyRoleResultsCache; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeXmlListModel : public QListModelInterface, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_INTERFACES(QDeclarativeParserStatus) + 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 NOTIFY sourceChanged) + Q_PROPERTY(QString xml READ xml WRITE setXml NOTIFY xmlChanged) + Q_PROPERTY(QString query READ query WRITE setQuery NOTIFY queryChanged) + Q_PROPERTY(QString namespaceDeclarations READ namespaceDeclarations WRITE setNamespaceDeclarations NOTIFY namespaceDeclarationsChanged) + Q_PROPERTY(QDeclarativeListProperty roles READ roleObjects) + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_CLASSINFO("DefaultProperty", "roles") + +public: + QDeclarativeXmlListModel(QObject *parent = 0); + ~QDeclarativeXmlListModel(); + + virtual QHash data(int index, const QList &roles = (QList())) const; + virtual QVariant data(int index, int role) const; + virtual int count() const; + virtual QList roles() const; + virtual QString toString(int role) const; + + QDeclarativeListProperty 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&); + + Q_INVOKABLE QScriptValue get(int index) const; + + enum Status { Null, Ready, Loading, Error }; + Status status() const; + qreal progress() const; + + Q_INVOKABLE QString errorString() const; + + virtual void classBegin(); + virtual void componentComplete(); + +Q_SIGNALS: + void statusChanged(QDeclarativeXmlListModel::Status); + void progressChanged(qreal progress); + void countChanged(); + void sourceChanged(); + void xmlChanged(); + void queryChanged(); + void namespaceDeclarationsChanged(); + +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 dataCleared(); + void queryCompleted(const QDeclarativeXmlQueryResult &); + void queryError(void* object, const QString& error); + +private: + Q_DECLARE_PRIVATE(QDeclarativeXmlListModel) + Q_DISABLE_COPY(QDeclarativeXmlListModel) +}; + +class Q_AUTOTEST_EXPORT QDeclarativeXmlListModelRole : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QString query READ query WRITE setQuery NOTIFY queryChanged) + Q_PROPERTY(bool isKey READ isKey WRITE setIsKey NOTIFY isKeyChanged) +public: + QDeclarativeXmlListModelRole() : m_isKey(false) {} + ~QDeclarativeXmlListModelRole() {} + + QString name() const { return m_name; } + void setName(const QString &name) { + if (name == m_name) + return; + m_name = name; + emit nameChanged(); + } + + 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; + } + if (m_query == query) + return; + m_query = query; + emit queryChanged(); + } + + bool isKey() const { return m_isKey; } + void setIsKey(bool b) { + if (m_isKey == b) + return; + m_isKey = b; + emit isKeyChanged(); + } + + bool isValid() { + return !m_name.isEmpty() && !m_query.isEmpty(); + } + +Q_SIGNALS: + void nameChanged(); + void queryChanged(); + void isKeyChanged(); + +private: + QString m_name; + QString m_query; + bool m_isKey; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeXmlListModel) +QML_DECLARE_TYPE(QDeclarativeXmlListModelRole) + +QT_END_HEADER + +#endif // QDECLARATIVEXMLLISTMODEL_H diff --git a/src/declarative/util/qlistmodelinterface.cpp b/src/declarative/util/qlistmodelinterface.cpp new file mode 100644 index 00000000..847c5e1a --- /dev/null +++ b/src/declarative/util/qlistmodelinterface.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclaractive module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qlistmodelinterface_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QListModelInterface + \since 4.7 + \brief The QListModelInterface class can be subclassed to provide C++ models to QDeclarativeGraphics Views + + This class is comprised primarily of pure virtual functions which + you must implement in a subclass. You can then use the subclass + as a model for a QDeclarativeGraphics view, such as a QDeclarativeListView. +*/ + +/*! \fn QListModelInterface::QListModelInterface(QObject *parent) + Constructs a QListModelInterface with the specified \a parent. +*/ + + /*! \fn QListModelInterface::QListModelInterface(QObjectPrivate &dd, QObject *parent) + + \internal + */ + +/*! \fn QListModelInterface::~QListModelInterface() + The destructor is virtual. + */ + +/*! \fn int QListModelInterface::count() const + Returns the number of data entries in the model. +*/ + +/*! \fn QVariant QListModelInterface::data(int index, int role) const + Returns the data at the given \a index for the specified \a roles. +*/ + +/*! \fn QList QListModelInterface::roles() const + Returns the list of roles for which the list model interface + provides data. +*/ + +/*! \fn QString QListModelInterface::toString(int role) const + Returns a string description of the specified \a role. +*/ + +/*! \fn void QListModelInterface::itemsInserted(int index, int count) + Emit this signal when \a count items are inserted at \a index. + */ + +/*! \fn void QListModelInterface::itemsRemoved(int index, int count) + Emit this signal when \a count items are removed at \a index. + */ + +/*! \fn void QListModelInterface::itemsMoved(int from, int to, int count) + Emit this signal when \a count items are moved from index \a from + to index \a to. + */ + +/*! \fn void QListModelInterface::itemsChanged(int index, int count, const QList &roles) + Emit this signal when \a count items at \a index have had their + \a roles changed. + */ + +QT_END_NAMESPACE diff --git a/src/declarative/util/qlistmodelinterface_p.h b/src/declarative/util/qlistmodelinterface_p.h new file mode 100644 index 00000000..ea68b12e --- /dev/null +++ b/src/declarative/util/qlistmodelinterface_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLISTMODELINTERFACE_H +#define QLISTMODELINTERFACE_H + +#include +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_PRIVATE_EXPORT QListModelInterface : public QObject +{ + Q_OBJECT + public: + QListModelInterface(QObject *parent = 0) : QObject(parent) {} + virtual ~QListModelInterface() {} + + virtual int count() const = 0; + virtual QVariant data(int index, int role) const = 0; + + virtual QList roles() const = 0; + virtual QString toString(int role) const = 0; + + Q_SIGNALS: + void itemsInserted(int index, int count); + void itemsRemoved(int index, int count); + void itemsMoved(int from, int to, int count); + void itemsChanged(int index, int count, const QList &roles); + + protected: + QListModelInterface(QObjectPrivate &dd, QObject *parent) + : QObject(dd, parent) {} +}; + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif //QTREEMODELINTERFACE_H diff --git a/src/declarative/util/util.pri b/src/declarative/util/util.pri new file mode 100644 index 00000000..62fa8f16 --- /dev/null +++ b/src/declarative/util/util.pri @@ -0,0 +1,72 @@ +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/qdeclarativeapplication.cpp \ + $$PWD/qdeclarativeutilmodule.cpp\ + $$PWD/qdeclarativeview.cpp \ + $$PWD/qdeclarativeconnections.cpp \ + $$PWD/qdeclarativepackage.cpp \ + $$PWD/qdeclarativeanimation.cpp \ + $$PWD/qdeclarativesystempalette.cpp \ + $$PWD/qdeclarativespringanimation.cpp \ + $$PWD/qdeclarativesmoothedanimation.cpp \ + $$PWD/qdeclarativestate.cpp\ + $$PWD/qdeclarativetransitionmanager.cpp \ + $$PWD/qdeclarativestateoperations.cpp \ + $$PWD/qdeclarativepropertychanges.cpp \ + $$PWD/qdeclarativestategroup.cpp \ + $$PWD/qdeclarativetransition.cpp \ + $$PWD/qdeclarativelistmodel.cpp\ + $$PWD/qdeclarativelistaccessor.cpp \ + $$PWD/qdeclarativeopenmetaobject.cpp \ + $$PWD/qdeclarativetimeline.cpp \ + $$PWD/qdeclarativetimer.cpp \ + $$PWD/qdeclarativebind.cpp \ + $$PWD/qdeclarativepropertymap.cpp \ + $$PWD/qdeclarativepixmapcache.cpp \ + $$PWD/qdeclarativebehavior.cpp \ + $$PWD/qdeclarativefontloader.cpp \ + $$PWD/qdeclarativestyledtext.cpp \ + $$PWD/qdeclarativelistmodelworkeragent.cpp \ + $$PWD/qlistmodelinterface.cpp + +HEADERS += \ + $$PWD/qdeclarativeapplication_p.h \ + $$PWD/qdeclarativeutilmodule_p.h\ + $$PWD/qdeclarativeview.h \ + $$PWD/qdeclarativeconnections_p.h \ + $$PWD/qdeclarativepackage_p.h \ + $$PWD/qdeclarativeanimation_p.h \ + $$PWD/qdeclarativeanimation_p_p.h \ + $$PWD/qdeclarativesystempalette_p.h \ + $$PWD/qdeclarativespringanimation_p.h \ + $$PWD/qdeclarativesmoothedanimation_p.h \ + $$PWD/qdeclarativesmoothedanimation_p_p.h \ + $$PWD/qdeclarativestate_p.h\ + $$PWD/qdeclarativestateoperations_p.h \ + $$PWD/qdeclarativepropertychanges_p.h \ + $$PWD/qdeclarativestate_p_p.h\ + $$PWD/qdeclarativetransitionmanager_p_p.h \ + $$PWD/qdeclarativestategroup_p.h \ + $$PWD/qdeclarativetransition_p.h \ + $$PWD/qdeclarativelistmodel_p.h\ + $$PWD/qdeclarativelistmodel_p_p.h\ + $$PWD/qdeclarativelistaccessor_p.h \ + $$PWD/qdeclarativeopenmetaobject_p.h \ + $$PWD/qdeclarativenullablevalue_p_p.h \ + $$PWD/qdeclarativetimeline_p_p.h \ + $$PWD/qdeclarativetimer_p.h \ + $$PWD/qdeclarativebind_p.h \ + $$PWD/qdeclarativepropertymap.h \ + $$PWD/qdeclarativepixmapcache_p.h \ + $$PWD/qdeclarativebehavior_p.h \ + $$PWD/qdeclarativefontloader_p.h \ + $$PWD/qdeclarativestyledtext_p.h \ + $$PWD/qdeclarativelistmodelworkeragent_p.h \ + $$PWD/qlistmodelinterface_p.h + +contains(QT_CONFIG, xmlpatterns) { + QT+=xmlpatterns + SOURCES += $$PWD/qdeclarativexmllistmodel.cpp + HEADERS += $$PWD/qdeclarativexmllistmodel_p.h +} -- cgit v1.2.3