diff options
408 files changed, 39488 insertions, 168 deletions
diff --git a/bin/snapshot b/bin/snapshot new file mode 100644 index 0000000..ffad857 --- /dev/null +++ b/bin/snapshot @@ -0,0 +1,329 @@ +#!/usr/bin/perl -w +###################################################################### +# +# +###################################################################### + +# use packages ------------------------------------------------------- +use File::Basename; +use File::Path; +use Cwd; +use Config; +use strict; + +my $targetPath = ""; +my $qtdir = $ENV{"QTDIR"}; + +my @files = ( + "3rdparty/easing/easing.cpp", + "corelib/tools/qeasingcurve.h", + "corelib/tools/qeasingcurve.cpp", + "corelib/animation/animation.pri", + "corelib/animation/qabstractanimation.cpp", + "corelib/animation/qabstractanimation.h", + "corelib/animation/qabstractanimation_p.h", + "corelib/animation/qvariantanimation.cpp", + "corelib/animation/qvariantanimation.h", + "corelib/animation/qanimationgroup.cpp", + "corelib/animation/qvariantanimation_p.h", + "corelib/animation/qanimationgroup.h", + "corelib/animation/qanimationgroup_p.h", + "corelib/animation/qparallelanimationgroup.cpp", + "corelib/animation/qparallelanimationgroup.h", + "corelib/animation/qparallelanimationgroup_p.h", + "corelib/animation/qpauseanimation.cpp", + "corelib/animation/qpauseanimation.h", + "corelib/animation/qpropertyanimation.cpp", + "corelib/animation/qpropertyanimation.h", + "corelib/animation/qpropertyanimation_p.h", + "corelib/animation/qsequentialanimationgroup.cpp", + "corelib/animation/qsequentialanimationgroup.h", + "corelib/animation/qsequentialanimationgroup_p.h", + "corelib/statemachine/statemachine.pri", + "corelib/statemachine/qabstractstate.cpp", + "corelib/statemachine/qabstractstate.h", + "corelib/statemachine/qabstractstate_p.h", + "corelib/statemachine/qabstracttransition.cpp", + "corelib/statemachine/qabstracttransition.h", + "corelib/statemachine/qabstracttransition_p.h", + "corelib/statemachine/qactionstate.h", + "corelib/statemachine/qactionstate_p.h", + "corelib/statemachine/qactionstate.cpp", + "corelib/statemachine/qboundevent_p.h", + "corelib/statemachine/qeventtransition.cpp", + "corelib/statemachine/qeventtransition.h", + "corelib/statemachine/qeventtransition_p.h", + "corelib/statemachine/qfinalstate.cpp", + "corelib/statemachine/qfinalstate.h", + "corelib/statemachine/qhistorystate.cpp", + "corelib/statemachine/qhistorystate.h", + "corelib/statemachine/qhistorystate_p.h", + "corelib/statemachine/qsignalevent.h", + "corelib/statemachine/qsignaleventgenerator_p.h", + "corelib/statemachine/qsignaltransition.cpp", + "corelib/statemachine/qsignaltransition.h", + "corelib/statemachine/qsignaltransition_p.h", + "corelib/statemachine/qstate.cpp", + "corelib/statemachine/qstate.h", + "corelib/statemachine/qstateaction.cpp", + "corelib/statemachine/qstateaction.h", + "corelib/statemachine/qstateaction_p.h", + "corelib/statemachine/qstatefinishedevent.h", + "corelib/statemachine/qstatefinishedtransition.cpp", + "corelib/statemachine/qstatefinishedtransition.h", + "corelib/statemachine/qstatemachine.cpp", + "corelib/statemachine/qstatemachine.h", + "corelib/statemachine/qstatemachine_p.h", + "corelib/statemachine/qstate_p.h", + "corelib/statemachine/qtransition.cpp", + "corelib/statemachine/qtransition.h", + "corelib/statemachine/qtransition_p.h", + "gui/statemachine/qkeyeventtransition.h", + "gui/statemachine/qkeyeventtransition.cpp", + "gui/statemachine/qbasickeyeventtransition.cpp", + "gui/statemachine/qbasickeyeventtransition_p.h", + "gui/statemachine/qbasicmouseeventtransition.cpp", + "gui/statemachine/qbasicmouseeventtransition_p.h", + "gui/statemachine/qguistatemachine.cpp", + "gui/statemachine/qkeyeventtransition.cpp", + "gui/statemachine/qkeyeventtransition.h", + "gui/statemachine/qmouseeventtransition.cpp", + "gui/statemachine/qmouseeventtransition.h", + "gui/statemachine/statemachine.pri", # needs special handling + ); + + +while ( @ARGV ) { + my $arg = shift @ARGV; + if ("$arg" eq "-to") { + $targetPath = shift @ARGV; + } +} + +if ($targetPath eq "") { + die("missing -to option"); +} + +my $projectXML = "$targetPath\\files.xml"; +open(OXML, "> " . $projectXML) || die "Could not open $projectXML for writing (no write permission?)"; + +print "COPYING SOURCES...\n"; +foreach my $files(@files) { + copyFile("$qtdir/src/$files","$targetPath/src"); +} +copyFile("$qtdir/doc/src/animation.qdoc","$targetPath/doc"); +copyFile("$qtdir/doc/src/statemachine.qdoc","$targetPath/doc"); +copyFile("$qtdir/src/3rdparty/easing/legal.qdoc","$targetPath/doc"); + +copyFile("$qtdir/doc/src/snippets/code/src_corelib_tools_qeasingcurve.cpp","$targetPath/doc/src/snippets/code"); + + +my %animation_examples = ( + easing => [ "easing.pro", + "main.cpp", + "window.cpp", + "window.h", + "animation.h", + "form.ui", + "images/qt-logo.png", + "resources.qrc"], + moveblocks => [ "moveblocks.pro", + "main.cpp" ], + animatedtiles => [ "animatedtiles.pro", + "animatedtiles.qrc", + "main.cpp", + "images/*"], + "sub-attaq" => ["sub-attaq.pro", + "animationmanager.cpp", + "animationmanager.h", + "boat.cpp", + "boat_p.h", + "boat.h", + "bomb.cpp", + "bomb.h", + "custompropertyanimation.h", + "custompropertyanimation_p.h", + "custompropertyanimation.cpp", + "graphicsscene.cpp", + "graphicsscene.h", + "main.cpp", + "mainwindow.cpp", + "mainwindow.h", + "pics/scalable/*", + "pics/big/*", + "pics/small/*", + "pics/welcome/*", + "pixmapitem.cpp", + "pixmapitem.h", + "subattaq.qrc", + "submarine.cpp", + "submarine.h", + "submarine_p.h", + "states.cpp", + "states.h", + "torpedo.cpp", + "torpedo.h"], + "stickman" => ["stickman.pro", + "main.cpp", + "animation.cpp", + "animation.h", + "graphicsview.cpp", + "graphicsview.h", + "lifecycle.cpp", + "lifecycle.h", + "node.cpp", + "node.h", + "stickman.cpp", + "stickman.h", + "animations/chilling", + "animations/dancing", + "animations/dead", + "animations/jumping"] + ); + +my $exDir; +print "COPYING EXAMPLES...\n"; +for $exDir ( keys %animation_examples ) { + print " $exDir...\n"; + my $i = 0; + for $i ( 0 .. $#{ $animation_examples{$exDir} } ) { + my $ex_file = $animation_examples{$exDir}[$i]; + + my $copyTargetPath; + my $glob = 0; + if (index($ex_file,"/") > 0) { + my($basefile, $fullPath) = fileparse("$targetPath/examples/$exDir/$ex_file"); + if ($basefile eq "*") { + $glob = 1; + } + mkpath "$fullPath", 0777 unless(-e "$fullPath"); + $copyTargetPath = "$fullPath"; + } else { + $copyTargetPath = "$targetPath/examples/$exDir"; + } + my $lastCh = substr($copyTargetPath, length($copyTargetPath) - 1, 1); + if ($lastCh eq "/" || $lastCh eq "\\") { + chop($copyTargetPath); + } + + if ($glob eq 1) { + my @globFiles = < $qtdir/examples/animation/$exDir/$ex_file >; + foreach my $globFile(@globFiles) { + copyFile("$globFile", "$copyTargetPath"); + } + } else { + copyFile("$qtdir/examples/animation/$exDir/$ex_file", "$copyTargetPath"); + } + } +} + +close OXML; +print("Finished!"); +###################################################################### +# Syntax: copyFile(gitfile, destinationPath) +# Params: gitfile, string, filename to create duplicate for +# destinationPath, string, destination name of duplicate +# +# Purpose: Copies to the solutions area. +# Returns: -- +# Warning: Dies if script cannot get write access. +###################################################################### +sub copyFile +{ + my ($gitfile, $destinationPath) = @_; + # Bi-directional synchronization + open( I, "< " . $gitfile ) || die "Could not open $gitfile for reading"; + local $/; + binmode I; + my $filecontents = <I>; + my ($baseFileName, $path, $ext) = fileparse($gitfile, qr/\.[^.]*/); + if ($ext eq ".h" or $ext eq ".cpp" or $ext eq ".qdoc") { + # both public and private classes + $filecontents =~s/QAbstractAnimation/QtAbstractAnimation/g; + $filecontents =~s/QAnimationGroup/QtAnimationGroup/g; + $filecontents =~s/QParallelAnimationGroup/QtParallelAnimationGroup/g; + $filecontents =~s/QSequentialAnimationGroup/QtSequentialAnimationGroup/g; + $filecontents =~s/QEasingCurve/QtEasingCurve/g; + $filecontents =~s/QVariantAnimation/QtVariantAnimation/g; + $filecontents =~s/QPropertyAnimation/QtPropertyAnimation/g; + $filecontents =~s/QItemAnimation/QtItemAnimation/g; + $filecontents =~s/QPauseAnimation/QtPauseAnimation/g; + $filecontents =~s/QAbstractState/QtAbstractState/g; + $filecontents =~s/QAbstractStateGroup/QtAbstractStateGroup/g; + $filecontents =~s/QAbstractTransition/QtAbstractTransition/g; + $filecontents =~s/QActionState/QtActionState/g; + $filecontents =~s/QEventTransition/QtEventTransition/g; + $filecontents =~s/QFinalState/QtFinalState/g; + $filecontents =~s/QHistoryState/QtHistoryState/g; + $filecontents =~s/QParallelStateGroup/QtParallelStateGroup/g; + $filecontents =~s/QSignalEvent/QtSignalEvent/g; + $filecontents =~s/QSignalTransition/QtSignalTransition/g; + $filecontents =~s/QState/QtState/g; + $filecontents =~s/QStateAction/QtStateAction/g; + $filecontents =~s/QStateInvokeMethodAction/QtStateInvokeMethodAction/g; + $filecontents =~s/QStateFinishedEvent/QtStateFinishedEvent/g; + $filecontents =~s/QStateFinishedTransition/QtStateFinishedTransition/g; + $filecontents =~s/QStateMachine/QtStateMachine/g; + $filecontents =~s/QTransition/QtTransition/g; + $filecontents =~s/QMouseEventTransition/QtMouseEventTransition/g; + $filecontents =~s/QKeyEventTransition/QtKeyEventTransition/g; + $filecontents =~s/QGraphicsWidget/QtGraphicsWidget/g; + $filecontents =~s/Q_CORE_EXPORT/Q_ANIMATION_EXPORT/g; + $filecontents =~s/Q_GUI_EXPORT/Q_ANIMATION_EXPORT/g; + $filecontents =~s/QBoundEvent/QtBoundEvent/g; + + $filecontents =~s/class Q_GUI_EXPORT/class/g; + $filecontents =~s/class Q_AUTOTEST_EXPORT/class/g; + + $filecontents =~s/(#\s*include\s+["])q/${1}qt/g; + + # moc stuff + $filecontents =~s/(#\s*include\s+["])moc_q/${1}moc_qt/g; + + $filecontents =~s/\\since 4\.[0-9]//g; + $filecontents =~s/\\ingroup [a-z]+//g; + + if (substr($filecontents, 0, 10) eq "/*********") { + my $endOfComment = index($filecontents, "*/"); + $filecontents = substr($filecontents, $endOfComment + 2); + } + } + if ($ext eq ".pri" ) { + $filecontents =~s/\$\$PWD\/q/\$\$PWD\/qt/g; + $filecontents =~s/animation\/q/\$\$PWD\/qt/g; + + # oooh such a hack this is + if ($baseFileName eq "statemachine") { + if (index($gitfile, "corelib/statemachine") >= 0) { + $baseFileName = "corelib_statemachine"; + } + if (index($gitfile, "gui/statemachine") >= 0) { + $baseFileName = "gui_statemachine"; + } + + } + + } + + if ($ext eq ".pro") { + $filecontents = "$filecontents\ninclude(../../src/qtanimationframework.pri)\n"; + } + close I; + + mkpath $destinationPath, 0777 unless(-e "$destinationPath"); + + if ($ext eq ".h" or $ext eq ".cpp" or $ext eq ".qdoc") { + $baseFileName =~s/^q/qt/g; + } + my $targetFile = "$destinationPath/$baseFileName$ext"; + open(O, "> " . $targetFile) || die "Could not open $targetFile for writing (no write permission?)"; + local $/; + binmode O; + print O $filecontents; + close O; + + my $xmlEntry = substr($targetFile, length($targetPath) + 1); + print "$xmlEntry\n"; + print OXML "<add>$xmlEntry</add>\n"; +} + @@ -23,6 +23,7 @@ my $basedir = $ENV{"QTDIR"}; $basedir =~ s=\\=/=g; my %modules = ( # path to module name map "QtGui" => "$basedir/src/gui", + "QtAnimation" => "$basedir/src/animation", "QtOpenGL" => "$basedir/src/opengl", "QtCore" => "$basedir/src/corelib", "QtXml" => "$basedir/src/xml", @@ -680,6 +681,7 @@ foreach (@modules_to_sync) { foreach(split(/ /, "$1")) { $master_contents .= "#include <QtCore/QtCore>\n" if("$_" eq "core"); $master_contents .= "#include <QtGui/QtGui>\n" if("$_" eq "gui"); + $master_contents .= "#include <QtAnimation/QtAnimation>\n" if("$_" eq "experimental-animation"); $master_contents .= "#include <QtNetwork/QtNetwork>\n" if("$_" eq "network"); $master_contents .= "#include <QtSvg/QtSvg>\n" if("$_" eq "svg"); $master_contents .= "#include <QtScript/QtScript>\n" if("$_" eq "script"); diff --git a/doc/src/animation.qdoc b/doc/src/animation.qdoc new file mode 100644 index 0000000..081660c --- /dev/null +++ b/doc/src/animation.qdoc @@ -0,0 +1,365 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page animation.html + \title The Animation Framework + \ingroup architecture + \ingroup animation + \brief An overview of the Animation Framework + + \keyword Animation + + The animation framework is part of the Kinetic project, and aims + to provide an easy way for creating animated and smooth GUI's. By + animating Qt properties, the framework provides great freedom for + animating widgets and other \l{QObject}s. The framework can also + be used with the Graphics View framework. + + In this overview, we explain the basics of its architecture. We + also show examples of the most common techniques that the + framework allows for animating QObjects and graphics items. + + \tableofcontents + + \section1 The Animation Architecture + + We will in this section take a high-level look at the animation + framework's architecture and how it is used to animate Qt + properties. + + The animation framework foundation consists of the base class + QAbstractAnimation, and its two subclasses QVariantAnimation and + QAnimationGroup. QAbstractAnimation is the ancestor of all + animations. It represents basic properties that are common for all + animations in the framework; notably, the ability to start, stop, + and pause an animation. It is also receives the time change + notifications. + + The animation framework further provides the QPropertyAnimation + class, which inherits QVariantAnimation and performs animation of + a Qt property, which is part of Qt's \l{Meta-Object + System}{meta-object system}. The class performs an interpolation + over the property using an easing curve. So when you want to + animate a value, you can declare it as a property and make your + class a QObject. Note that this gives us great freedom in + animating already existing widgets and other \l{QObject}s. + + Complex animations can be constructed by building a tree structure + of \l{QAbstractAnimation}s. The tree is built by using + \l{QAnimationGroup}s, which function as containers for other + animations. Note also that the groups are subclasses of + QAbstractAnimation, so groups can themselves contain other groups. + + The animation framework can be used on its own, but is also + designed to be part of the state machine framework (See the + \l{The State Machine Framework}{state machine framework} for an + introduction to the Qt state machine). The state machine provides + a special state that can play an animation. A QState can also set + properties when the state is entered or exited, and this special + animation state will interpolate between these values when given a + QPropertyAnimation. We will look more closely at this later. + + Behind the scenes, the animations are controlled by a global + timer, which sends \l{QAbstractAnimation::updateCurrentTime()}{updates} to + all animations that are playing. + + For detailed descriptions of the classes' function and roles in + the framework, please look up their class descriptions. + + \section1 Animating Qt Properties + + As mentioned in the previous section, the QPropertyAnimation class + can interpolate over Qt properties. It is this class that should + be used for animation of values; in fact, its superclass, + QVariantAnimation, is an abstract class, and cannot be used + directly. + + A major reason we chose to animate Qt properties is that it + presents us with freedom to animate already existing classes in + the Qt API. Notably, the QWidget class (which we can also embed in + a QGraphicsView) has properties for its bounds, colors, etc. + Let's look at a small example: + + \code + QPushButton button("Animated Button"); + button.show(); + + QPropertyAnimation animation(&button, "geometry"); + animation.setDuration(10000); + animation.setStartValue(QRect(0, 0, 100, 30)); + animation.setEndValue(QRect(250, 250, 100, 30)); + + animation.start(); + \endcode + + This code will move \c button from the top left corner of the + screen to the position (250, 250). + + The example above will do a linear interpolation between the + start and end value. It is also possible to set values + situated between the start and end value. The interpolation + will then go by these points. + + \code + QPushButton button("Animated Button"); + button.show(); + + QPropertyAnimation animation(&button, "geometry"); + animation.setDuration(10000); + + animation.setKeyValueAt(0, QRect(0, 0, 100, 30)); + animation.setKeyValueAt(0.8, QRect(250, 250, 100, 30)); + animation.setKeyValueAt(1, QRect(0, 0, 100, 30)); + + animation.start(); + \endcode + + In this example, the animation will take the button to (250, 250) + in 8 seconds, and then move it back to its original position in + the remaining 2 seconds. The movement will be linearly + interpolated between these points. + + You also have the possibility to animate values of a QObject + that is not declared as a Qt property. The only requirement is + that this value has a setter. You can then subclass the class + containing the value and declare a property that uses this setter. + Note that all Qt properties requires a getter, so you will need to + provide a getter yourself if this is not defined. + + \code + class MyGraphicsRectItem : public QObject, public QGraphicsRectItem + { + Q_OBJECT + Q_PROPERTY(QRectF geometry READ geometry WRITE setGeometry) + }; + \endcode + + In the above code example, we subclass QGraphicsRectItem and + define a geometry property. We can now animate the widgets + geometry even if QGraphicsRectItem does not provide the geometry + property. + + For a general introduction to the Qt property system, see its + \l{Qt's Property System}{overview}. + + \section1 Animations and the Graphics View Framework + + When you want to animate \l{QGraphicsItem}s, you also use + QPropertyAnimation. But, unfortunetly, QGraphicsItem does not + inherit QObject. A good solution is to subclass the graphics item + you wish to animate. This class will then also inherit QObject. + This way, QPropertyAnimation can be used for \l{QGraphicsItem}s. + The example below shows how this is done. Another possibility is + to inherit QGraphicsWidget, which already is a QObject. + + \code + class Pixmap : public QObject, public QGraphicsPixmapItem + { + Q_OBJECT + Q_PROPERTY(QPointF pos READ pos WRITE setPos) + ... + \endcode + + As described in the previous section, we need to define + properties that we wish to animate. + + Note that QObject must be the first class inherited as the + meta-object system demands this. + + \warning The QItemAnimation class, which was initially intended + for animating \l{QGraphicsItem}s may be deprecated or removed from + the animation framework. + + \omit (need something about the list of animations). \endomit + + \section1 Easing Curves + + As mentioned, QPropertyAnimation performs an interpolation between + the start and end property value. In addition to adding more key + values to the animation, you can also use an easing curve. Easing + curves describe a function that controls how the speed of the + interpolation between 0 and 1 should be, and are useful if you + want to control the speed of an animation without changing the + path of the interpolation. + + \code + QPushButton button("Animated Button"); + button.show(); + + QPropertyAnimation animation(&button, "geometry"); + animation.setDuration(3000); + animation.setStartValue(QRect(0, 0, 100, 30)); + animation.setEndValue(QRect(250, 250, 100, 30)); + + animation.setEasingCurve(QEasingCurve::OutBounce); + + animation.start(); + \endcode + + Here the animation will follow a curve that makes it bounce like a + ball as if it was dropped from the start to the end position. + QEasingCurve has a large collection of curves for you to choose + from. These are defined by the QEasingCurve::Type enum. If you are + in need of another curve, you can also implement one yourself, and + register it with QEasingCurve. + + \omit Drop this for the first Lab release + (Example of custom easing curve (without the actual impl of + the function I expect) + \endomit + + \section1 Putting Animations Together + + An application will often contain more than one animation. For + instance, you might want to move more than one graphics item + simultaneously or move them in sequence after each other. + + The subclasses of QAnimationGroup (QSequentialAnimationGroup and + QParallelAnimationGroup) are containers for other animations so + that these animations can be animated either in sequence or + parallel. The QAnimationGroup is an example of an animation that + does not animate properties, but it gets notified of time changes + periodically. This enables it to forward those time changes to its + contained animations, and thereby controlling when its animations + are played. + + Let's look at code examples that use both + QSequentialAnimationGroup and QParallelAnimationGroup, starting + off with the latter. + + \code + QPushButton *bonnie = new QPushButton("Bonnie"); + bonnie.show(); + + QPushButton *clyde = new QPushButton("Clyde"); + clyde.show(); + + QPropertyAnimation *anim1 = new QPropertyAnimation(bonnie, "geometry"); + // Set up anim1 + + QPropertyAnimation *anim2 = new QPropertyAnimation(clyde, "geometry"); + // Set up anim2 + + QParallelAnimationGroup *group = new QParallelAnimationGroup; + group->addAnimation(anim1); + group->addAnimation(anim2); + + group->start(); + \endcode + + A parallel group plays more than one animation at the same time. + Calling its \l{QAbstractAnimation::}{start()} function will start + all animations it governs. + + \code + QPushButton button("Animated Button"); + button.show(); + + QPropertyAnimation anim1(&button, "geometry"); + anim1.setDuration(3000); + anim1.setStartValue(QRect(0, 0, 100, 30)); + anim1.setEndValue(QRect(500, 500, 100, 30)); + + QPropertyAnimation anim2(&button, "geometry"); + anim2.setDuration(3000); + anim2.setStartValue(QRect(500, 500, 100, 30)); + anim2.setEndValue(QRect(1000, 500, 100, 30)); + + QSequentialAnimationGroup group; + + group.addAnimation(&anim1); + group.addAnimation(&anim2); + + group.start(); + \endcode + + As you no doubt have guessed, QSequentialAnimationGroup plays + its animations in sequence. It starts the next animation in + the list after the previous is finished. + + Since an animation group is an animation itself, you can add + it to another group. This way, you can build a tree structure + of animations which specifies when the animations are played + in relation to each other. + + \section1 Animations and States + + When using a \l{The State Machine Framework}{state machine}, we + have a special state, QAnimationState, that will play one or more + animations. + + The QState::addAnimatedTransition() convenience function lets you + associate an animation to a state transition. The function will + create the QAnimationState for you, and insert it into the state + machine. We also have the possibility to associate properties with + the states rather than setting the start and end values ourselves. + Below is a complete code example that animates the geometry of a + QPushButton. + + \code + QPushButton *button = new QPushButton("Animated Button"); + button->show(); + + QStateMachine *machine = new QStateMachine; + + QState *state1 = new QState(machine->rootState()); + state1->setPropertyOnEntry(button, "geometry", + QRect(0, 0, 100, 30)); + machine->setInitialState(state1); + + QState *state2 = new QState(machine->rootState()); + state2->setPropertyOnEntry(button, "geometry", + QRect(250, 250, 100, 30)); + + state1->addAnimatedTransition(button, SIGNAL(clicked()), state2, + new QPropertyAnimation(button, "geometry")); + state2->addAnimatedTransition(button, SIGNAL(clicked()), state1, + new QPropertyAnimation(button, "geometry")); + + machine->start(); + \endcode + + For a more comprehensive example of how to use the state machine + framework for animations, see the states example (it lives in the + \c{examples/animation/states} directory). +*/ + diff --git a/doc/src/diagrams/programs/easingcurve/easingcurve.pro b/doc/src/diagrams/programs/easingcurve/easingcurve.pro new file mode 100644 index 0000000..0b80127 --- /dev/null +++ b/doc/src/diagrams/programs/easingcurve/easingcurve.pro @@ -0,0 +1,13 @@ +###################################################################### +# Automatically generated by qmake (2.01a) fr 13. feb 13:26:38 2009 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += main.cpp + +CONFIG += console
\ No newline at end of file diff --git a/doc/src/diagrams/programs/easingcurve/main.cpp b/doc/src/diagrams/programs/easingcurve/main.cpp new file mode 100644 index 0000000..98e9d37 --- /dev/null +++ b/doc/src/diagrams/programs/easingcurve/main.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> + +void createCurveIcons(); + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + createCurveIcons(); + return app.exit(); +} + +void createCurveIcons() +{ + QDir dir(QDir::current()); + if (dir.dirName() == QLatin1String("debug") || dir.dirName() == QLatin1String("release")) { + dir.cdUp(); + } + dir.cdUp(); + dir.cdUp(); + dir.cdUp(); + QSize iconSize(128, 128); + QPixmap pix(iconSize); + QPainter painter(&pix); + QLinearGradient gradient(0,0, 0, iconSize.height()); + gradient.setColorAt(0.0, QColor(240, 240, 240)); + gradient.setColorAt(1.0, QColor(224, 224, 224)); + QBrush brush(gradient); + const QMetaObject &mo = QEasingCurve::staticMetaObject; + QMetaEnum metaEnum = mo.enumerator(mo.indexOfEnumerator("Type")); + QFont oldFont = painter.font(); + // Skip QEasingCurve::Custom + QString output(QString::fromAscii("%1/images").arg(dir.absolutePath())); + printf("Generating images to %s\n", qPrintable(output)); + for (int i = 0; i < QEasingCurve::NCurveTypes - 1; ++i) { + painter.setFont(oldFont); + QString name(QLatin1String(metaEnum.key(i))); + painter.fillRect(QRect(QPoint(0, 0), iconSize), brush); + QEasingCurve curve((QEasingCurve::Type)i); + painter.setPen(QColor(0, 0, 255, 64)); + qreal xAxis = iconSize.height()/1.5; + qreal yAxis = iconSize.width()/3; + painter.drawLine(0, xAxis, iconSize.width(), xAxis); // hor + painter.drawLine(yAxis, 0, yAxis, iconSize.height()); // ver + + qreal curveScale = iconSize.height()/2; + + painter.drawLine(yAxis - 2, xAxis - curveScale, yAxis + 2, xAxis - curveScale); // hor + painter.drawLine(yAxis + curveScale, xAxis + 2, yAxis + curveScale, xAxis - 2); // ver + painter.drawText(yAxis + curveScale - 8, xAxis - curveScale - 4, QLatin1String("(1,1)")); + + painter.drawText(yAxis + 42, xAxis + 10, QLatin1String("progress")); + painter.drawText(15, xAxis - curveScale - 10, QLatin1String("ease")); + + painter.setPen(QPen(Qt::red, 1, Qt::DotLine)); + painter.drawLine(yAxis, xAxis - curveScale, yAxis + curveScale, xAxis - curveScale); // hor + painter.drawLine(yAxis + curveScale, xAxis, yAxis + curveScale, xAxis - curveScale); // ver + + QPoint currentPos(yAxis, xAxis); + + painter.setPen(Qt::black); + QFont font = oldFont; + font.setPixelSize(oldFont.pixelSize() + 15); + painter.setFont(font); + painter.drawText(0, iconSize.height() - 20, iconSize.width(), 20, Qt::AlignHCenter, name); + + for (qreal t = 0; t < 1.0; t+=1.0/curveScale) { + QPoint to; + to.setX(yAxis + curveScale * t); + to.setY(xAxis - curveScale * curve.valueForProgress(t)); + painter.drawLine(currentPos, to); + currentPos = to; + } + QString fileName(QString::fromAscii("qeasingcurve-%1.png").arg(name.toLower())); + printf("%s\n", qPrintable(fileName)); + pix.save(QString::fromAscii("%1/%2").arg(output).arg(fileName), "PNG"); + } +} + + diff --git a/doc/src/examples.qdoc b/doc/src/examples.qdoc index 29c6c0b..06d7727 100644 --- a/doc/src/examples.qdoc +++ b/doc/src/examples.qdoc @@ -308,6 +308,12 @@ \o \l{sql/sqlwidgetmapper}{SQL Widget Mapper}\raisedaster \endlist + \section1 State Machine + + \list + \o \l{statemachine/trafficlight}{Traffic Light}\raisedaster + \endlist + \section1 Threads \list diff --git a/doc/src/examples/trafficlight.qdoc b/doc/src/examples/trafficlight.qdoc new file mode 100644 index 0000000..16ee8ad --- /dev/null +++ b/doc/src/examples/trafficlight.qdoc @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +/*! + \example statemachine/trafficlight + \title Traffic Light Example + + The Traffic Light example shows how to use \l{The State Machine Framework} + to implement the control flow of a traffic light. + + \image trafficlight-example.png + + In this example we write a TrafficLightWidget class. The traffic light has + three lights: Red, yellow and green. The traffic light transitions from + one light to another (red to yellow to green to yellow to red again) at + certain intervals. + + \snippet examples/statemachine/trafficlight/main.cpp 0 + + The LightWidget class represents a single light of the traffic light. It + provides a setOn() function to turn the light on or off. It paints itself + in the color that's passed to the constructor. + + \snippet examples/statemachine/trafficlight/main.cpp 2 + + The TrafficLightWidget class represents the visual part of the traffic + light; it's a widget that contains three lights, and provides accessor + functions for these. + + \snippet examples/statemachine/trafficlight/main.cpp 1 + + The LightState class represents a state that turns a light on when the + state is entered, and off when the state is exited. The class is a timer, + and as we shall see the timeout is used to transition from one LightState + to another. + + \snippet examples/statemachine/trafficlight/main.cpp 3 + + The TrafficLight class combines the TrafficLightWidget with control flow + based on the LightState class. The state graph has four states: + red-to-yellow, yellow-to-green, green-to-yellow and yellow-to-red. The + initial state is red-to-yellow; when the state's timer times out, the + state machine transitions to yellow-to-green. The same process repeats + through the other states. + + \snippet examples/statemachine/trafficlight/main.cpp 4 + + The main() function constructs a TrafficLight and shows it. + +*/ diff --git a/doc/src/external-resources.qdoc b/doc/src/external-resources.qdoc index f48c3d7..3bfb5af 100644 --- a/doc/src/external-resources.qdoc +++ b/doc/src/external-resources.qdoc @@ -334,6 +334,16 @@ */ /*! + \externalpage http://www.w3.org/TR/scxml/ + \title State Chart XML: State Machine Notation for Control Abstraction +*/ + +/*! + \externalpage http://www.wisdom.weizmann.ac.il/~dharel/SCANNED.PAPERS/Statecharts.pdf + \title Statecharts: A visual formalism for complex systems +*/ + +/*! \externalpage http://www.gnu.org/licenses/gpl.html \title GNU General Public License */ diff --git a/doc/src/groups.qdoc b/doc/src/groups.qdoc index c9cedc4..0411c3a 100644 --- a/doc/src/groups.qdoc +++ b/doc/src/groups.qdoc @@ -69,6 +69,18 @@ */ /*! + \group animations + \ingroup groups + + \title Animation Framework + \brief Classes for animations, states and transitions. + + These classes provide a framework for creating both simple and complex + animations. The Animation Framework also provides states and animated + transitions, making it easy to create animated stateful forms. +*/ + +/*! \group abstractwidgets \title Abstract Widget Classes \ingroup groups @@ -597,3 +609,14 @@ These classes are relevant to developers who are working with Qt Script's debugging features. */ + +/*! + \group statemachine + \ingroup groups + + \title State Machine Classes + \brief Classes for constructing and executing state graphs. + + These classes are provided by \l{The State Machine Framework} for creating + event-driven state machines. +*/ diff --git a/doc/src/images/qeasingcurve-cosinecurve.png b/doc/src/images/qeasingcurve-cosinecurve.png Binary files differnew file mode 100644 index 0000000..b27e763 --- /dev/null +++ b/doc/src/images/qeasingcurve-cosinecurve.png diff --git a/doc/src/images/qeasingcurve-inback.png b/doc/src/images/qeasingcurve-inback.png Binary files differnew file mode 100644 index 0000000..8506c0f --- /dev/null +++ b/doc/src/images/qeasingcurve-inback.png diff --git a/doc/src/images/qeasingcurve-inbounce.png b/doc/src/images/qeasingcurve-inbounce.png Binary files differnew file mode 100644 index 0000000..275b38c --- /dev/null +++ b/doc/src/images/qeasingcurve-inbounce.png diff --git a/doc/src/images/qeasingcurve-incirc.png b/doc/src/images/qeasingcurve-incirc.png Binary files differnew file mode 100644 index 0000000..b985e9c --- /dev/null +++ b/doc/src/images/qeasingcurve-incirc.png diff --git a/doc/src/images/qeasingcurve-incubic.png b/doc/src/images/qeasingcurve-incubic.png Binary files differnew file mode 100644 index 0000000..e417ee1 --- /dev/null +++ b/doc/src/images/qeasingcurve-incubic.png diff --git a/doc/src/images/qeasingcurve-incurve.png b/doc/src/images/qeasingcurve-incurve.png Binary files differnew file mode 100644 index 0000000..d9a9340 --- /dev/null +++ b/doc/src/images/qeasingcurve-incurve.png diff --git a/doc/src/images/qeasingcurve-inelastic.png b/doc/src/images/qeasingcurve-inelastic.png Binary files differnew file mode 100644 index 0000000..b242fd3 --- /dev/null +++ b/doc/src/images/qeasingcurve-inelastic.png diff --git a/doc/src/images/qeasingcurve-inexpo.png b/doc/src/images/qeasingcurve-inexpo.png Binary files differnew file mode 100644 index 0000000..f06316c --- /dev/null +++ b/doc/src/images/qeasingcurve-inexpo.png diff --git a/doc/src/images/qeasingcurve-inoutback.png b/doc/src/images/qeasingcurve-inoutback.png Binary files differnew file mode 100644 index 0000000..9fd1446 --- /dev/null +++ b/doc/src/images/qeasingcurve-inoutback.png diff --git a/doc/src/images/qeasingcurve-inoutbounce.png b/doc/src/images/qeasingcurve-inoutbounce.png Binary files differnew file mode 100644 index 0000000..fb65f31 --- /dev/null +++ b/doc/src/images/qeasingcurve-inoutbounce.png diff --git a/doc/src/images/qeasingcurve-inoutcirc.png b/doc/src/images/qeasingcurve-inoutcirc.png Binary files differnew file mode 100644 index 0000000..123cc54 --- /dev/null +++ b/doc/src/images/qeasingcurve-inoutcirc.png diff --git a/doc/src/images/qeasingcurve-inoutcubic.png b/doc/src/images/qeasingcurve-inoutcubic.png Binary files differnew file mode 100644 index 0000000..b07695c --- /dev/null +++ b/doc/src/images/qeasingcurve-inoutcubic.png diff --git a/doc/src/images/qeasingcurve-inoutelastic.png b/doc/src/images/qeasingcurve-inoutelastic.png Binary files differnew file mode 100644 index 0000000..65851e1 --- /dev/null +++ b/doc/src/images/qeasingcurve-inoutelastic.png diff --git a/doc/src/images/qeasingcurve-inoutexpo.png b/doc/src/images/qeasingcurve-inoutexpo.png Binary files differnew file mode 100644 index 0000000..7cbfb13 --- /dev/null +++ b/doc/src/images/qeasingcurve-inoutexpo.png diff --git a/doc/src/images/qeasingcurve-inoutquad.png b/doc/src/images/qeasingcurve-inoutquad.png Binary files differnew file mode 100644 index 0000000..c5eed06 --- /dev/null +++ b/doc/src/images/qeasingcurve-inoutquad.png diff --git a/doc/src/images/qeasingcurve-inoutquart.png b/doc/src/images/qeasingcurve-inoutquart.png Binary files differnew file mode 100644 index 0000000..3b66c0d --- /dev/null +++ b/doc/src/images/qeasingcurve-inoutquart.png diff --git a/doc/src/images/qeasingcurve-inoutquint.png b/doc/src/images/qeasingcurve-inoutquint.png Binary files differnew file mode 100644 index 0000000..c74efe9 --- /dev/null +++ b/doc/src/images/qeasingcurve-inoutquint.png diff --git a/doc/src/images/qeasingcurve-inoutsine.png b/doc/src/images/qeasingcurve-inoutsine.png Binary files differnew file mode 100644 index 0000000..5964f31 --- /dev/null +++ b/doc/src/images/qeasingcurve-inoutsine.png diff --git a/doc/src/images/qeasingcurve-inquad.png b/doc/src/images/qeasingcurve-inquad.png Binary files differnew file mode 100644 index 0000000..3373310 --- /dev/null +++ b/doc/src/images/qeasingcurve-inquad.png diff --git a/doc/src/images/qeasingcurve-inquart.png b/doc/src/images/qeasingcurve-inquart.png Binary files differnew file mode 100644 index 0000000..28086d8 --- /dev/null +++ b/doc/src/images/qeasingcurve-inquart.png diff --git a/doc/src/images/qeasingcurve-inquint.png b/doc/src/images/qeasingcurve-inquint.png Binary files differnew file mode 100644 index 0000000..330aa85 --- /dev/null +++ b/doc/src/images/qeasingcurve-inquint.png diff --git a/doc/src/images/qeasingcurve-insine.png b/doc/src/images/qeasingcurve-insine.png Binary files differnew file mode 100644 index 0000000..63d9238 --- /dev/null +++ b/doc/src/images/qeasingcurve-insine.png diff --git a/doc/src/images/qeasingcurve-linear.png b/doc/src/images/qeasingcurve-linear.png Binary files differnew file mode 100644 index 0000000..2a05885 --- /dev/null +++ b/doc/src/images/qeasingcurve-linear.png diff --git a/doc/src/images/qeasingcurve-outback.png b/doc/src/images/qeasingcurve-outback.png Binary files differnew file mode 100644 index 0000000..7cb34c6 --- /dev/null +++ b/doc/src/images/qeasingcurve-outback.png diff --git a/doc/src/images/qeasingcurve-outbounce.png b/doc/src/images/qeasingcurve-outbounce.png Binary files differnew file mode 100644 index 0000000..932fc16 --- /dev/null +++ b/doc/src/images/qeasingcurve-outbounce.png diff --git a/doc/src/images/qeasingcurve-outcirc.png b/doc/src/images/qeasingcurve-outcirc.png Binary files differnew file mode 100644 index 0000000..a1a6cb6 --- /dev/null +++ b/doc/src/images/qeasingcurve-outcirc.png diff --git a/doc/src/images/qeasingcurve-outcubic.png b/doc/src/images/qeasingcurve-outcubic.png Binary files differnew file mode 100644 index 0000000..aa1d604 --- /dev/null +++ b/doc/src/images/qeasingcurve-outcubic.png diff --git a/doc/src/images/qeasingcurve-outcurve.png b/doc/src/images/qeasingcurve-outcurve.png Binary files differnew file mode 100644 index 0000000..a949ae4 --- /dev/null +++ b/doc/src/images/qeasingcurve-outcurve.png diff --git a/doc/src/images/qeasingcurve-outelastic.png b/doc/src/images/qeasingcurve-outelastic.png Binary files differnew file mode 100644 index 0000000..2a9ba39 --- /dev/null +++ b/doc/src/images/qeasingcurve-outelastic.png diff --git a/doc/src/images/qeasingcurve-outexpo.png b/doc/src/images/qeasingcurve-outexpo.png Binary files differnew file mode 100644 index 0000000..e771c2e --- /dev/null +++ b/doc/src/images/qeasingcurve-outexpo.png diff --git a/doc/src/images/qeasingcurve-outinback.png b/doc/src/images/qeasingcurve-outinback.png Binary files differnew file mode 100644 index 0000000..7523727 --- /dev/null +++ b/doc/src/images/qeasingcurve-outinback.png diff --git a/doc/src/images/qeasingcurve-outinbounce.png b/doc/src/images/qeasingcurve-outinbounce.png Binary files differnew file mode 100644 index 0000000..ab73d02 --- /dev/null +++ b/doc/src/images/qeasingcurve-outinbounce.png diff --git a/doc/src/images/qeasingcurve-outincirc.png b/doc/src/images/qeasingcurve-outincirc.png Binary files differnew file mode 100644 index 0000000..ec4b8d3 --- /dev/null +++ b/doc/src/images/qeasingcurve-outincirc.png diff --git a/doc/src/images/qeasingcurve-outincubic.png b/doc/src/images/qeasingcurve-outincubic.png Binary files differnew file mode 100644 index 0000000..8b8ae68 --- /dev/null +++ b/doc/src/images/qeasingcurve-outincubic.png diff --git a/doc/src/images/qeasingcurve-outinelastic.png b/doc/src/images/qeasingcurve-outinelastic.png Binary files differnew file mode 100644 index 0000000..89dde2c --- /dev/null +++ b/doc/src/images/qeasingcurve-outinelastic.png diff --git a/doc/src/images/qeasingcurve-outinexpo.png b/doc/src/images/qeasingcurve-outinexpo.png Binary files differnew file mode 100644 index 0000000..5909901 --- /dev/null +++ b/doc/src/images/qeasingcurve-outinexpo.png diff --git a/doc/src/images/qeasingcurve-outinquad.png b/doc/src/images/qeasingcurve-outinquad.png Binary files differnew file mode 100644 index 0000000..7ddefee --- /dev/null +++ b/doc/src/images/qeasingcurve-outinquad.png diff --git a/doc/src/images/qeasingcurve-outinquart.png b/doc/src/images/qeasingcurve-outinquart.png Binary files differnew file mode 100644 index 0000000..00ef597 --- /dev/null +++ b/doc/src/images/qeasingcurve-outinquart.png diff --git a/doc/src/images/qeasingcurve-outinquint.png b/doc/src/images/qeasingcurve-outinquint.png Binary files differnew file mode 100644 index 0000000..361bfaa4 --- /dev/null +++ b/doc/src/images/qeasingcurve-outinquint.png diff --git a/doc/src/images/qeasingcurve-outinsine.png b/doc/src/images/qeasingcurve-outinsine.png Binary files differnew file mode 100644 index 0000000..1737041 --- /dev/null +++ b/doc/src/images/qeasingcurve-outinsine.png diff --git a/doc/src/images/qeasingcurve-outquad.png b/doc/src/images/qeasingcurve-outquad.png Binary files differnew file mode 100644 index 0000000..6f27cbd --- /dev/null +++ b/doc/src/images/qeasingcurve-outquad.png diff --git a/doc/src/images/qeasingcurve-outquart.png b/doc/src/images/qeasingcurve-outquart.png Binary files differnew file mode 100644 index 0000000..d45a0b8 --- /dev/null +++ b/doc/src/images/qeasingcurve-outquart.png diff --git a/doc/src/images/qeasingcurve-outquint.png b/doc/src/images/qeasingcurve-outquint.png Binary files differnew file mode 100644 index 0000000..6e7df0e --- /dev/null +++ b/doc/src/images/qeasingcurve-outquint.png diff --git a/doc/src/images/qeasingcurve-outsine.png b/doc/src/images/qeasingcurve-outsine.png Binary files differnew file mode 100644 index 0000000..7546a0d --- /dev/null +++ b/doc/src/images/qeasingcurve-outsine.png diff --git a/doc/src/images/qeasingcurve-sinecurve.png b/doc/src/images/qeasingcurve-sinecurve.png Binary files differnew file mode 100644 index 0000000..ca67d44 --- /dev/null +++ b/doc/src/images/qeasingcurve-sinecurve.png diff --git a/doc/src/images/statemachine-button-history.png b/doc/src/images/statemachine-button-history.png Binary files differnew file mode 100644 index 0000000..cd66478 --- /dev/null +++ b/doc/src/images/statemachine-button-history.png diff --git a/doc/src/images/statemachine-button-nested.png b/doc/src/images/statemachine-button-nested.png Binary files differnew file mode 100644 index 0000000..60360d1 --- /dev/null +++ b/doc/src/images/statemachine-button-nested.png diff --git a/doc/src/images/statemachine-button.png b/doc/src/images/statemachine-button.png Binary files differnew file mode 100644 index 0000000..75d9e53 --- /dev/null +++ b/doc/src/images/statemachine-button.png diff --git a/doc/src/images/statemachine-finished.png b/doc/src/images/statemachine-finished.png Binary files differnew file mode 100644 index 0000000..802621e --- /dev/null +++ b/doc/src/images/statemachine-finished.png diff --git a/doc/src/images/statemachine-nonparallel.png b/doc/src/images/statemachine-nonparallel.png Binary files differnew file mode 100644 index 0000000..1fe60d8 --- /dev/null +++ b/doc/src/images/statemachine-nonparallel.png diff --git a/doc/src/images/statemachine-parallel.png b/doc/src/images/statemachine-parallel.png Binary files differnew file mode 100644 index 0000000..1868792 --- /dev/null +++ b/doc/src/images/statemachine-parallel.png diff --git a/doc/src/images/trafficlight-example.png b/doc/src/images/trafficlight-example.png Binary files differnew file mode 100644 index 0000000..3431542 --- /dev/null +++ b/doc/src/images/trafficlight-example.png diff --git a/doc/src/snippets/code/src_corelib_tools_qeasingcurve.cpp b/doc/src/snippets/code/src_corelib_tools_qeasingcurve.cpp new file mode 100644 index 0000000..65358ea --- /dev/null +++ b/doc/src/snippets/code/src_corelib_tools_qeasingcurve.cpp @@ -0,0 +1,4 @@ +//! [0] +qreal myEasingFunction(qreal progress); +//! [0] + diff --git a/doc/src/statemachine.qdoc b/doc/src/statemachine.qdoc new file mode 100644 index 0000000..c79839f --- /dev/null +++ b/doc/src/statemachine.qdoc @@ -0,0 +1,272 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +/*! + \page statemachine-api.html + \title The State Machine Framework + \brief An overview of the State Machine framework for constructing and executing state graphs. + + \tableofcontents + + The State Machine framework provides classes for creating and executing + state graphs. The concepts and notation are based on those from Harel's + \l{Statecharts: A visual formalism for complex systems}{Statecharts}, which + is also the basis of UML state diagrams. The semantics of state machine + execution are based on \l{State Chart XML: State Machine Notation for + Control Abstraction}{State Chart XML (SCXML)}. + + Statecharts provide a graphical way of modeling how a system reacts to + stimuli. This is done by defining the possible \e states that the system can + be in, and how the system can move from one state to another (\e transitions + between states). A key characteristic of event-driven systems (such as Qt + applications) is that behavior often depends not only on the last or current + event, but also the events that preceded it. With statecharts, this + information is easy to express. + + The State Machine framework provides an API and execution model that can be + used to effectively embed the elements and semantics of statecharts in Qt + applications. The framework integrates tightly with Qt's existing event + system and meta-object system; for example, transitions between states can + be triggered by signals, and states can be configured to set properties and + invoke methods on QObjects. + + \section1 A Simple State Machine + + To demonstrate the core functionality of the State Machine API, let's look + at a small example: A state machine with three states, \c s1, \c s2 and \c + s3. The state machine is controlled by a single QPushButton; when the button + is clicked, the machine transitions to another state. Initially, the state + machine is in state \c s1. The statechart for this machine is as follows: + + \img statemachine-button.png + \omit + \caption This is a caption + \endomit + + The following snippet shows the code needed to create such a state machine. + + \code + QStateMachine machine; + QState *s1 = new QState(); + QState *s2 = new QState(); + QState *s3 = new QState(); + + s1->addTransition(button, SIGNAL(clicked()), s2); + s2->addTransition(button, SIGNAL(clicked()), s3); + s3->addTransition(button, SIGNAL(clicked()), s1); + + machine.addState(s1); + machine.addState(s2); + machine.addState(s3); + machine.setInitialState(s1); + + machine.start(); + \endcode + + Once the state machine has been set up, you need to start it by calling + QStateMachine::start(). The state machine executes asynchronously, i.e. it + becomes part of your application's event loop. + + The above state machine is perfectly fine, but it doesn't \e do anything; it + merely transitions from one state to another. The + QState::setPropertyOnEntry() function can be used to have a state set a + property of a QObject when the state is entered. In the following snippet, + the value that should be assigned to a QLabel's text property is specified + for each state: + + \code + s1->setPropertyOnEntry(label, "text", "In state s1"); + s2->setPropertyOnEntry(label, "text", "In state s2"); + s3->setPropertyOnEntry(label, "text", "In state s3"); + \endcode + + When any of the states is entered, the label's text will be changed + accordingly. + + The QState::invokeMethodOnEntry() function can be used to have a state + invoke a method (a slot) of a QObject when the state is entered. In the + following snippet, the button's showMaximized() slot will be called when + state \c s3 is entered: + + \code + s2->invokeMethodOnEntry(button, "showMaximized"); + \endcode + + \section1 Sharing Transitions By Grouping States + + The state machine defined in the previous section never finishes. In order + for a state machine to be able to finish, it needs to have a top-level \e + final state. When the state machine enters a top-level final state, the + machine will emit the finished() signal and halt. + + Assume we wanted the user to be able to quit the application at any time by + clicking a Quit button. In order to achieve this, we need to create a final + state and make it the target of a transition associated with the Quit + button's clicked() signal. We could add a transition from each of \c s1, \c + s2 and \c s3; however, this seems redundant, and one would also have to + remember to add such a transition from every new state that is added in the + future. + + We can achieve the same behavior (namely that clicking the Quit button quits + the state machine, regardless of which state the state machine is in) by + grouping states \c s1, \c s2 and \c s3. This is done by creating a new + top-level state and making the three original states children of the new + state. The following diagram shows the new state machine. + + \img statemachine-button-nested.png + \omit + \caption This is a caption + \endomit + + The three original states have been renamed \c s11, \c s12 and \c s13 to + reflect that they are now children of the new top-level state, \c s1. Child + states implicitly inherit the transitions of their parent state. This means + it is now sufficient to add a single transition from \c s1 to the final + state \c s2. New states added to \c s1 will also automatically inherit this + transition. + + All that's needed to group states is to specify the proper parent when the + state is created. You also need to specify which of the child states is the + initial one (i.e. which child state the state machine should enter when the + parent state is the target of a transition). + + \code + QState *s1 = new QState(); + QState *s11 = new QState(s1); + QState *s12 = new QState(s1); + QState *s13 = new QState(s1); + s1->setInitialState(s11); + machine.addState(s1); + \endcode + + \code + QFinalState *s2 = new QFinalState(); + s1->addTransition(quitButton, SIGNAL(clicked()), s2); + machine.addState(s2); + + QObject::connect(&machine, SIGNAL(finished()), QApplication::instance(), SLOT(quit())); + \endcode + + In this case we want the application to quit when the state machine is + finished, so the machine's finished() signal is connected to the + application's quit() slot. + + A child state can override an inherited transition. For example, the + following code adds a transition that effectively causes the Quit button to + be ignored when the state machine is in state \c s12. + + \code + s12>addTransition(quitButton, SIGNAL(clicked()), s12); + \endcode + + \section1 Using History States to Save and Restore the Current State + + Imagine that we wanted to add an "interrupt" mechanism to the example + discussed in the previous section; the user should be able to click a button + to have the state machine perform some non-related task, after which the + state machine should resume whatever it was doing before (i.e. return to the + old state, which is one of \c s11, \c s12 and \c s13 in this case). + + Such behavior can easily be modeled using \e{history states}. A history + state (QHistoryState object) is a pseudo-state that represents the child + state that the parent state was in the last time the parent state was + exited. + + A history state is created as a child of the state for which we wish to + record the current child state; when the state machine detects the presence + of such a state at runtime, it automatically records the current (real) + child state when the parent state is exited. A transition to the history + state is in fact a transition to the child state that the state machine had + previously saved; the state machine automatically "forwards" the transition + to the real child state. + + The following diagram shows the state machine after the interrupt mechanism + has been added. + + \img statemachine-button-history.png + \omit + \caption This is a caption + \endomit + + The following code shows how it can be implemented; in this example we + simply display a message box when \c s3 is entered, then immediately return + to the previous child state of \c s1 via the history state. + + \code + QHistoryState *s1h = s1->addHistoryState(); + + QState *s3 = new QState(); + s3->setPropertyOnEntry(label, "text", "In s3"); + QMessageBox mbox; + mbox.addButton(QMessageBox::Ok); + mbox.setText("Interrupted!"); + mbox.setIcon(QMessageBox::Information); + s3->invokeMethodOnEntry(&mbox, "exec"); + s3->addTransition(s1h); + machine.addState(s3); + + s1->addTransition(interruptButton, SIGNAL(clicked()), s3); + \endcode + + \section1 Using Parallel States to Avoid a Combinatorial Explosion of States + + Assume that you wanted to model a set of mutually exclusive properties of a + car in a single state machine. Let's say the properties we are interested in + are Clean vs Dirty, and Moving vs Not moving. It would take four mutually + exclusive states and eight transitions to be able to represent and freely + move between all possible combinations. + + \img statemachine-nonparallel.png + \omit + \caption This is a caption + \endomit + + If we added a third property (say, Red vs Blue), the total number of states + would double, to eight; and if we added a fourth property (say, Enclosed vs + Convertible), the total number of states would double again, to 16. + + Using parallel states, the total number of states and transitions grows + linearly as we add more properties, instead of exponentially. Furthermore, + states can be added to or removed from the parallel state without affecting + any of their sibling states. + + \img statemachine-parallel.png + \omit + \caption This is a caption + \endomit + + To create a parallel state group, pass QState::ParallelStateGroup to the + QState constructor. + + \code + QState *s1 = new QState(QState::ParallelStateGroup); + // s11 and s12 will be entered in parallel + QState *s11 = new QState(s1); + QState *s12 = new QState(s1); + \endcode + + \section1 Detecting that a Composite State has Finished + + A child state can be final; when a final child state is entered, a + QStateFinishedEvent is generated for the parent state. You can use the + QStateFinishedTransition class to trigger a transition based on this event. + + \img statemachine-finished.png + \omit + \caption This is a caption + \endomit + + This is useful when you want to hide the internal details of a state; + i.e. the only thing the outside world should be able to do is enter the + state, and get a notification when the state has finished (i.e. when a final + child state has been entered). + + */ diff --git a/examples/animation/animatedtiles/animatedtiles.pro b/examples/animation/animatedtiles/animatedtiles.pro new file mode 100644 index 0000000..9e9062c --- /dev/null +++ b/examples/animation/animatedtiles/animatedtiles.pro @@ -0,0 +1,2 @@ +SOURCES += main.cpp +RESOURCES += animatedtiles.qrc diff --git a/examples/animation/animatedtiles/animatedtiles.qrc b/examples/animation/animatedtiles/animatedtiles.qrc new file mode 100644 index 0000000..c43a979 --- /dev/null +++ b/examples/animation/animatedtiles/animatedtiles.qrc @@ -0,0 +1,11 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>images/Time-For-Lunch-2.jpg</file> + <file>images/centered.png</file> + <file>images/ellipse.png</file> + <file>images/figure8.png</file> + <file>images/kinetic.png</file> + <file>images/random.png</file> + <file>images/tile.png</file> +</qresource> +</RCC> diff --git a/examples/animation/animatedtiles/images/Time-For-Lunch-2.jpg b/examples/animation/animatedtiles/images/Time-For-Lunch-2.jpg Binary files differnew file mode 100644 index 0000000..c57a555 --- /dev/null +++ b/examples/animation/animatedtiles/images/Time-For-Lunch-2.jpg diff --git a/examples/animation/animatedtiles/images/centered.png b/examples/animation/animatedtiles/images/centered.png Binary files differnew file mode 100644 index 0000000..e416156 --- /dev/null +++ b/examples/animation/animatedtiles/images/centered.png diff --git a/examples/animation/animatedtiles/images/ellipse.png b/examples/animation/animatedtiles/images/ellipse.png Binary files differnew file mode 100644 index 0000000..2c3ba88 --- /dev/null +++ b/examples/animation/animatedtiles/images/ellipse.png diff --git a/examples/animation/animatedtiles/images/figure8.png b/examples/animation/animatedtiles/images/figure8.png Binary files differnew file mode 100644 index 0000000..6b05804 --- /dev/null +++ b/examples/animation/animatedtiles/images/figure8.png diff --git a/examples/animation/animatedtiles/images/kinetic.png b/examples/animation/animatedtiles/images/kinetic.png Binary files differnew file mode 100644 index 0000000..55cfa55 --- /dev/null +++ b/examples/animation/animatedtiles/images/kinetic.png diff --git a/examples/animation/animatedtiles/images/random.png b/examples/animation/animatedtiles/images/random.png Binary files differnew file mode 100644 index 0000000..415d96f --- /dev/null +++ b/examples/animation/animatedtiles/images/random.png diff --git a/examples/animation/animatedtiles/images/tile.png b/examples/animation/animatedtiles/images/tile.png Binary files differnew file mode 100644 index 0000000..c8f39d8 --- /dev/null +++ b/examples/animation/animatedtiles/images/tile.png diff --git a/examples/animation/animatedtiles/main.cpp b/examples/animation/animatedtiles/main.cpp new file mode 100644 index 0000000..cfaa4ce --- /dev/null +++ b/examples/animation/animatedtiles/main.cpp @@ -0,0 +1,247 @@ +#include <QtGui> +#ifdef QT_EXPERIMENTAL_SOLUTION +# include "qgraphicswidget.h" +# include "qstate.h" +# include "qstatemachine.h" +# include "qabstracttransition.h" +# include "qgraphicswidget.h" +# include "qparallelanimationgroup.h" +# include "qpropertyanimation.h" +#else +#include <QtCore/qstate.h> +#endif + +class Pixmap : public QObject, public QGraphicsPixmapItem +{ + Q_OBJECT + Q_PROPERTY(QPointF pos READ pos WRITE setPos) +public: + Pixmap(const QPixmap &pix) + : QObject(), QGraphicsPixmapItem(pix) + { + setCacheMode(DeviceCoordinateCache); + } +}; + +class Button : public QGraphicsWidget +{ + Q_OBJECT +public: + Button(const QPixmap &pixmap, QGraphicsItem *parent = 0) + : QGraphicsWidget(parent), _pix(pixmap) + { + setAcceptHoverEvents(true); + setCacheMode(DeviceCoordinateCache); + } + + QRectF boundingRect() const + { + return QRectF(-65, -65, 130, 130); + } + + QPainterPath shape() const + { + QPainterPath path; + path.addEllipse(boundingRect()); + return path; + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) + { + bool down = option->state & QStyle::State_Sunken; + QRectF r = boundingRect(); + QLinearGradient grad(r.topLeft(), r.bottomRight()); + grad.setColorAt(down ? 1 : 0, option->state & QStyle::State_MouseOver ? Qt::white : Qt::lightGray); + grad.setColorAt(down ? 0 : 1, Qt::darkGray); + painter->setPen(Qt::darkGray); + painter->setBrush(grad); + painter->drawEllipse(r); + QLinearGradient grad2(r.topLeft(), r.bottomRight()); + grad.setColorAt(down ? 1 : 0, Qt::darkGray); + grad.setColorAt(down ? 0 : 1, Qt::lightGray); + painter->setPen(Qt::NoPen); + painter->setBrush(grad); + if (down) + painter->translate(2, 2); + painter->drawEllipse(r.adjusted(5, 5, -5, -5)); + painter->drawPixmap(-_pix.width()/2, -_pix.height()/2, _pix); + } + +signals: + void pressed(); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *) + { + emit pressed(); + update(); + } + + void mouseReleaseEvent(QGraphicsSceneMouseEvent *) + { + update(); + } + +private: + QPixmap _pix; +}; + +class View : public QGraphicsView +{ +public: + View(QGraphicsScene *scene) : QGraphicsView(scene) { } + +protected: + void resizeEvent(QResizeEvent *event) + { + QGraphicsView::resizeEvent(event); + fitInView(sceneRect(), Qt::KeepAspectRatio); + } +}; + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + QPixmap kineticPix(":/images/kinetic.png"); + QPixmap bgPix(":/images/Time-For-Lunch-2.jpg"); + + QGraphicsScene scene(-350, -350, 700, 700); + + QList<Pixmap *> items; + for (int i = 0; i < 64; ++i) { + Pixmap *item = new Pixmap(kineticPix); + item->setOffset(-kineticPix.width()/2, -kineticPix.height()/2); + item->setZValue(i); + items << item; + scene.addItem(item); + } + + // Buttons + QGraphicsItem *buttonParent = new QGraphicsRectItem; + Button *ellipseButton = new Button(QPixmap(":/images/ellipse.png"), buttonParent); + Button *figure8Button = new Button(QPixmap(":/images/figure8.png"), buttonParent); + Button *randomButton = new Button(QPixmap(":/images/random.png"), buttonParent); + Button *tiledButton = new Button(QPixmap(":/images/tile.png"), buttonParent); + Button *centeredButton = new Button(QPixmap(":/images/centered.png"), buttonParent); + + ellipseButton->setPos(-100, -100); + figure8Button->setPos(100, -100); + randomButton->setPos(0, 0); + tiledButton->setPos(-100, 100); + centeredButton->setPos(100, 100); + + scene.addItem(buttonParent); + buttonParent->scale(0.75, 0.75); + buttonParent->setPos(200, 200); + buttonParent->setZValue(65); + + // States + QState *rootState = new QState; + QState *ellipseState = new QState(rootState); + QState *figure8State = new QState(rootState); + QState *randomState = new QState(rootState); + QState *tiledState = new QState(rootState); + QState *centeredState = new QState(rootState); + + // Values + for (int i = 0; i < 64; ++i) { + Pixmap *item = items.at(i); + // Ellipse + ellipseState->assignProperty(item, "pos", + QPointF(cos((i / 63.0) * 6.28) * 250, + sin((i / 63.0) * 6.28) * 250)); + + // Figure 8 + figure8State->assignProperty(item, "pos", + QPointF(sin((i / 63.0) * 6.28) * 250, + sin(((i * 2)/63.0) * 6.28) * 250)); + + // Random + randomState->assignProperty(item, "pos", + QPointF(-250 + qrand() % 500, + -250 + qrand() % 500)); + + // Tiled + tiledState->assignProperty(item, "pos", + QPointF(((i % 8) - 4) * kineticPix.width() + kineticPix.width() / 2, + ((i / 8) - 4) * kineticPix.height() + kineticPix.height() / 2)); + + // Centered + centeredState->assignProperty(item, "pos", QPointF()); + } + + // Ui + View *view = new View(&scene); + view->setWindowTitle(QT_TRANSLATE_NOOP(QGraphicsView, "Animated Tiles")); + view->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); + view->setBackgroundBrush(bgPix); + view->setCacheMode(QGraphicsView::CacheBackground); + view->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + view->show(); + + QStateMachine states; + states.addState(rootState); + states.setInitialState(rootState); + rootState->setInitialState(centeredState); + + QParallelAnimationGroup *group = new QParallelAnimationGroup; + for (int i = 0; i < 64; ++i) { + QPropertyAnimation *anim = new QPropertyAnimation(items[i], "pos"); + anim->setDuration(750 + i * 25); + anim->setEasingCurve(QEasingCurve::InOutBack); + group->addAnimation(anim); + } + QAbstractTransition *trans = rootState->addTransition(ellipseButton, SIGNAL(pressed()), ellipseState); + trans->addAnimation(group); + + group = new QParallelAnimationGroup; + for (int i = 0; i < 64; ++i) { + QPropertyAnimation *anim = new QPropertyAnimation(items[i], "pos"); + anim->setDuration(750 + i * 25); + anim->setEasingCurve(QEasingCurve::InOutBack); + group->addAnimation(anim); + } + trans = rootState->addTransition(figure8Button, SIGNAL(pressed()), figure8State); + trans->addAnimation(group); + + group = new QParallelAnimationGroup; + for (int i = 0; i < 64; ++i) { + QPropertyAnimation *anim = new QPropertyAnimation(items[i], "pos"); + anim->setDuration(750 + i * 25); + anim->setEasingCurve(QEasingCurve::InOutBack); + group->addAnimation(anim); + } + trans = rootState->addTransition(randomButton, SIGNAL(pressed()), randomState); + trans->addAnimation(group); + + group = new QParallelAnimationGroup; + for (int i = 0; i < 64; ++i) { + QPropertyAnimation *anim = new QPropertyAnimation(items[i], "pos"); + anim->setDuration(750 + i * 25); + anim->setEasingCurve(QEasingCurve::InOutBack); + group->addAnimation(anim); + } + trans = rootState->addTransition(tiledButton, SIGNAL(pressed()), tiledState); + trans->addAnimation(group); + + group = new QParallelAnimationGroup; + for (int i = 0; i < 64; ++i) { + QPropertyAnimation *anim = new QPropertyAnimation(items[i], "pos"); + anim->setDuration(750 + i * 25); + anim->setEasingCurve(QEasingCurve::InOutBack); + group->addAnimation(anim); + } + trans = rootState->addTransition(centeredButton, SIGNAL(pressed()), centeredState); + trans->addAnimation(group); + + states.start(); + QTimer timer; + timer.start(125); + timer.setSingleShot(true); + rootState->addTransition(&timer, SIGNAL(timeout()), ellipseState); + + return app.exec(); +} + +#include "main.moc" diff --git a/examples/animation/animation.pro b/examples/animation/animation.pro new file mode 100644 index 0000000..d5121a1 --- /dev/null +++ b/examples/animation/animation.pro @@ -0,0 +1,21 @@ +TEMPLATE = \ + subdirs +SUBDIRS += \ + animatedtiles \ + appchooser \ + easing \ + example \ + moveblocks \ + padnavigator-ng \ + photobrowser \ + piemenu \ + selectbutton \ + states \ + stickman \ + sub-attaq + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/animation +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS animation.pro README +sources.path = $$[QT_INSTALL_EXAMPLES]/animation +INSTALLS += target sources diff --git a/examples/animation/appchooser/accessories-dictionary.png b/examples/animation/appchooser/accessories-dictionary.png Binary files differnew file mode 100644 index 0000000..e9bd55d --- /dev/null +++ b/examples/animation/appchooser/accessories-dictionary.png diff --git a/examples/animation/appchooser/akregator.png b/examples/animation/appchooser/akregator.png Binary files differnew file mode 100644 index 0000000..a086f45 --- /dev/null +++ b/examples/animation/appchooser/akregator.png diff --git a/examples/animation/appchooser/appchooser.pro b/examples/animation/appchooser/appchooser.pro new file mode 100644 index 0000000..8cda19a --- /dev/null +++ b/examples/animation/appchooser/appchooser.pro @@ -0,0 +1,9 @@ +# Input +SOURCES += main.cpp +RESOURCES += appchooser.qrc + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/animation/appchooser +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS appchooser.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/animation/appchooser +INSTALLS += target sources diff --git a/examples/animation/appchooser/appchooser.qrc b/examples/animation/appchooser/appchooser.qrc new file mode 100644 index 0000000..28a3e1c --- /dev/null +++ b/examples/animation/appchooser/appchooser.qrc @@ -0,0 +1,8 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>accessories-dictionary.png</file> + <file>akregator.png</file> + <file>digikam.png</file> + <file>k3b.png</file> +</qresource> +</RCC> diff --git a/examples/animation/appchooser/digikam.png b/examples/animation/appchooser/digikam.png Binary files differnew file mode 100644 index 0000000..9de9fb2 --- /dev/null +++ b/examples/animation/appchooser/digikam.png diff --git a/examples/animation/appchooser/k3b.png b/examples/animation/appchooser/k3b.png Binary files differnew file mode 100644 index 0000000..bbcafcf --- /dev/null +++ b/examples/animation/appchooser/k3b.png diff --git a/examples/animation/appchooser/main.cpp b/examples/animation/appchooser/main.cpp new file mode 100644 index 0000000..1a43ed7 --- /dev/null +++ b/examples/animation/appchooser/main.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include <QtCore> +#include <QtGui> +#ifdef QT_EXPERIMENTAL_SOLUTION +#include "qtgraphicswidget.h" +#endif + + +class Pixmap : public QGraphicsWidget +{ + Q_OBJECT + +public: + Pixmap(const QPixmap &pix, QGraphicsItem *parent = 0) + : QGraphicsWidget(parent), orig(pix), p(pix) + { + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + painter->drawPixmap(QPointF(), p); + } + + virtual void mousePressEvent(QGraphicsSceneMouseEvent * ) + { + emit clicked(); + } + + virtual void setGeometry(const QRectF &rect) + { + QGraphicsWidget::setGeometry(rect); + + if (rect.size().width() > orig.size().width()) + p = orig.scaled(rect.size().toSize()); + else + p = orig; + } + +Q_SIGNALS: + void clicked(); + +private: + QPixmap orig; + QPixmap p; +}; + +void createStates(const QObjectList &objects, + const QRect &selectedRect, QState *parent) +{ + for (int i = 0; i < objects.size(); ++i) { + QState *state = new QState(parent); + state->assignProperty(objects.at(i), "geometry", selectedRect); + QAbstractTransition *trans = parent->addTransition(objects.at(i), SIGNAL(clicked()), state); + for (int j = 0; j < objects.size(); ++j) { + QPropertyAnimation *animation = new QPropertyAnimation(objects.at(j), "geometry"); + animation->setDuration(2000); + trans->addAnimation(animation); + } + } +} + +int main(int argc, char **argv) +{ + Q_INIT_RESOURCE(appchooser); + + QApplication app(argc, argv); + + Pixmap *p1 = new Pixmap(QPixmap(":/digikam.png")); + Pixmap *p2 = new Pixmap(QPixmap(":/akregator.png")); + Pixmap *p3 = new Pixmap(QPixmap(":/accessories-dictionary.png")); + Pixmap *p4 = new Pixmap(QPixmap(":/k3b.png")); + + p1->setObjectName("p1"); + p2->setObjectName("p2"); + p3->setObjectName("p3"); + p4->setObjectName("p4"); + + p1->setGeometry(QRectF(0.0, 0.0, 64.0, 64.0)); + p2->setGeometry(QRectF(236.0, 0.0, 64.0, 64.0)); + p3->setGeometry(QRectF(236.0, 236.0, 64.0, 64.0)); + p4->setGeometry(QRectF(0.0, 236.0, 64.0, 64.0)); + + QGraphicsScene scene(0, 0, 300, 300); + scene.setBackgroundBrush(Qt::white); + scene.addItem(p1); + scene.addItem(p2); + scene.addItem(p3); + scene.addItem(p4); + + QGraphicsView window(&scene); + window.setFrameStyle(0); + window.setAlignment(Qt::AlignLeft | Qt::AlignTop); + window.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + window.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + QStateMachine machine; + machine.setGlobalRestorePolicy(QState::RestoreProperties); + + QState *group = new QState(machine.rootState()); + group->setObjectName("group"); + QRect selectedRect(86, 86, 128, 128); + + QState *idleState = new QState(group); + group->setInitialState(idleState); + + createStates(QObjectList() << p1 << p2 << p3 << p4, selectedRect, group); + + machine.setInitialState(group); + machine.start(); + + window.resize(300, 300); + window.show(); + + return app.exec(); +} + +#include "main.moc" diff --git a/examples/animation/easing/animation.h b/examples/animation/easing/animation.h new file mode 100644 index 0000000..db67810 --- /dev/null +++ b/examples/animation/easing/animation.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef ANIMATION_H +#define ANIMATION_H + +#include <QtGui> + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qpropertyanimation.h" +#else +# include <QtCore/qpropertyanimation.h> +#endif + +class Animation : public QPropertyAnimation { +public: + enum PathType { + LinearPath, + CirclePath, + NPathTypes + }; + Animation(QObject *target, const QByteArray &prop) + : QPropertyAnimation(target, prop) + { + setPathType(LinearPath); + } + + void setPathType(PathType pathType) + { + if (pathType >= NPathTypes) + qWarning("Unknown pathType %d", pathType); + + m_pathType = pathType; + m_path = QPainterPath(); + } + + void updateCurrentTime(int msecs) + { + if (m_pathType == CirclePath) { + if (m_path.isEmpty()) { + QPointF to = endValue().toPointF(); + QPointF from = startValue().toPointF(); + m_path.moveTo(from); + m_path.addEllipse(QRectF(from, to)); + } + int dura = duration(); + const qreal progress = ((dura == 0) ? 1 : ((((currentTime() - 1) % dura) + 1) / qreal(dura))); + + qreal easedProgress = easingCurve().valueForProgress(progress); + if (easedProgress > 1.0) { + easedProgress -= 1.0; + } else if (easedProgress < 0) { + easedProgress += 1.0; + } + QPointF pt = m_path.pointAtPercent(easedProgress); + updateCurrentValue(pt); + emit valueChanged(pt); + } else { + QPropertyAnimation::updateCurrentTime(msecs); + } + } + + QPainterPath m_path; + PathType m_pathType; +}; + +#endif // ANIMATION_H diff --git a/examples/animation/easing/easing.pro b/examples/animation/easing/easing.pro new file mode 100644 index 0000000..fa5b22d --- /dev/null +++ b/examples/animation/easing/easing.pro @@ -0,0 +1,16 @@ +###################################################################### +# Automatically generated by qmake (2.01a) to 2. okt 23:22:11 2008 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +HEADERS += window.h animation.h +SOURCES += main.cpp window.cpp + +FORMS += form.ui + +RESOURCES = resources.qrc diff --git a/examples/animation/easing/form.ui b/examples/animation/easing/form.ui new file mode 100644 index 0000000..b60ade8 --- /dev/null +++ b/examples/animation/easing/form.ui @@ -0,0 +1,201 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Form</class> + <widget class="QWidget" name="Form"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>545</width> + <height>471</height> + </rect> + </property> + <property name="windowTitle"> + <string>Easing curves</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0" colspan="2"> + <widget class="QListWidget" name="easingCurvePicker"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>120</height> + </size> + </property> + <property name="verticalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + <property name="movement"> + <enum>QListView::Static</enum> + </property> + <property name="isWrapping" stdset="0"> + <bool>false</bool> + </property> + <property name="viewMode"> + <enum>QListView::IconMode</enum> + </property> + <property name="selectionRectVisible"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="1" column="0"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Path type</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QRadioButton" name="lineRadio"> + <property name="text"> + <string>Line</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <attribute name="buttonGroup"> + <string>buttonGroup</string> + </attribute> + </widget> + </item> + <item> + <widget class="QRadioButton" name="circleRadio"> + <property name="text"> + <string>Circle</string> + </property> + <attribute name="buttonGroup"> + <string>buttonGroup</string> + </attribute> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Properties</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::AllNonFixedFieldsGrow</enum> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Period</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QDoubleSpinBox" name="periodSpinBox"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="minimum"> + <double>-1.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.100000000000000</double> + </property> + <property name="value"> + <double>-1.000000000000000</double> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Amplitude</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QDoubleSpinBox" name="amplitudeSpinBox"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="minimum"> + <double>-1.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.100000000000000</double> + </property> + <property name="value"> + <double>-1.000000000000000</double> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Overshoot</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QDoubleSpinBox" name="overshootSpinBox"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="minimum"> + <double>-1.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.100000000000000</double> + </property> + <property name="value"> + <double>-1.000000000000000</double> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="1" column="1"> + <widget class="QGraphicsView" name="graphicsView"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> + <buttongroups> + <buttongroup name="buttonGroup"/> + </buttongroups> +</ui> diff --git a/examples/animation/easing/images/qt-logo.png b/examples/animation/easing/images/qt-logo.png Binary files differnew file mode 100644 index 0000000..14ddf2a --- /dev/null +++ b/examples/animation/easing/images/qt-logo.png diff --git a/examples/animation/easing/main.cpp b/examples/animation/easing/main.cpp new file mode 100644 index 0000000..139f2da --- /dev/null +++ b/examples/animation/easing/main.cpp @@ -0,0 +1,22 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#include "window.h" + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + Window w; + w.resize(400, 400); + w.show(); + return app.exec(); +} diff --git a/examples/animation/easing/resources.qrc b/examples/animation/easing/resources.qrc new file mode 100644 index 0000000..7e112d3 --- /dev/null +++ b/examples/animation/easing/resources.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> + <qresource> + <file>images/qt-logo.png</file> + </qresource> + </RCC>
\ No newline at end of file diff --git a/examples/animation/easing/window.cpp b/examples/animation/easing/window.cpp new file mode 100644 index 0000000..c6ea360 --- /dev/null +++ b/examples/animation/easing/window.cpp @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "window.h" + +Window::Window(QWidget *parent) + : QWidget(parent), m_iconSize(64, 64) +{ + m_ui.setupUi(this); + QButtonGroup *buttonGroup = qFindChild<QButtonGroup *>(this); // ### workaround for uic in 4.4 + m_ui.easingCurvePicker->setIconSize(m_iconSize); + m_ui.easingCurvePicker->setMinimumHeight(m_iconSize.height() + 50); + buttonGroup->setId(m_ui.lineRadio, 0); + buttonGroup->setId(m_ui.circleRadio, 1); + + QEasingCurve dummy; + m_ui.periodSpinBox->setValue(dummy.period()); + m_ui.amplitudeSpinBox->setValue(dummy.amplitude()); + m_ui.overshootSpinBox->setValue(dummy.overshoot()); + + connect(m_ui.easingCurvePicker, SIGNAL(currentRowChanged(int)), this, SLOT(curveChanged(int))); + connect(buttonGroup, SIGNAL(buttonClicked(int)), this, SLOT(pathChanged(int))); + connect(m_ui.periodSpinBox, SIGNAL(valueChanged(double)), this, SLOT(periodChanged(double))); + connect(m_ui.amplitudeSpinBox, SIGNAL(valueChanged(double)), this, SLOT(amplitudeChanged(double))); + connect(m_ui.overshootSpinBox, SIGNAL(valueChanged(double)), this, SLOT(overshootChanged(double))); + createCurveIcons(); + + QPixmap pix(QLatin1String(":/images/qt-logo.png")); + m_item = new PixmapItem(pix); + m_scene.addItem(m_item); + m_ui.graphicsView->setScene(&m_scene); + + m_anim = new Animation(m_item, "pos"); + m_anim->setEasingCurve(QEasingCurve::OutBounce); + m_ui.easingCurvePicker->setCurrentRow(int(QEasingCurve::OutBounce)); + + startAnimation(); +} + +void Window::createCurveIcons() +{ + QPixmap pix(m_iconSize); + QPainter painter(&pix); + QLinearGradient gradient(0,0, 0, m_iconSize.height()); + gradient.setColorAt(0.0, QColor(240, 240, 240)); + gradient.setColorAt(1.0, QColor(224, 224, 224)); + QBrush brush(gradient); + const QMetaObject &mo = QEasingCurve::staticMetaObject; + QMetaEnum metaEnum = mo.enumerator(mo.indexOfEnumerator("Type")); + // Skip QEasingCurve::Custom + for (int i = 0; i < QEasingCurve::NCurveTypes - 1; ++i) { + painter.fillRect(QRect(QPoint(0, 0), m_iconSize), brush); + QEasingCurve curve((QEasingCurve::Type)i); + painter.setPen(QColor(0, 0, 255, 64)); + qreal xAxis = m_iconSize.height()/1.5; + qreal yAxis = m_iconSize.width()/3; + painter.drawLine(0, xAxis, m_iconSize.width(), xAxis); + painter.drawLine(yAxis, 0, yAxis, m_iconSize.height()); + painter.setPen(Qt::black); + + qreal curveScale = m_iconSize.height()/2; + QPoint currentPos(yAxis, xAxis); + + for (qreal t = 0; t < 1.0; t+=1.0/curveScale) { + QPoint to; + to.setX(yAxis + curveScale * t); + to.setY(xAxis - curveScale * curve.valueForProgress(t)); + painter.drawLine(currentPos, to); + currentPos = to; + } + QListWidgetItem *item = new QListWidgetItem; + item->setIcon(QIcon(pix)); + item->setText(metaEnum.key(i)); + m_ui.easingCurvePicker->addItem(item); + } +} + +void Window::startAnimation() +{ + m_anim->setStartValue(QPointF(0, 0)); + m_anim->setEndValue(QPointF(100, 100)); + m_anim->setDuration(2000); + m_anim->setIterationCount(-1); // forever + m_anim->start(); +} + +void Window::curveChanged(int row) +{ + QEasingCurve::Type curveType = (QEasingCurve::Type)row; + m_anim->setEasingCurve(curveType); + m_anim->setCurrentTime(0); + + bool isElastic = curveType >= QEasingCurve::InElastic && curveType <= QEasingCurve::OutInElastic; + bool isBounce = curveType >= QEasingCurve::InBounce && curveType <= QEasingCurve::OutInBounce; + m_ui.periodSpinBox->setEnabled(isElastic); + m_ui.amplitudeSpinBox->setEnabled(isElastic || isBounce); + m_ui.overshootSpinBox->setEnabled(curveType >= QEasingCurve::InBack && curveType <= QEasingCurve::OutInBack); +} + +void Window::pathChanged(int index) +{ + m_anim->setPathType((Animation::PathType)index); +} + +void Window::periodChanged(double value) +{ + QEasingCurve curve = m_anim->easingCurve(); + curve.setPeriod(value); + m_anim->setEasingCurve(curve); +} + +void Window::amplitudeChanged(double value) +{ + QEasingCurve curve = m_anim->easingCurve(); + curve.setAmplitude(value); + m_anim->setEasingCurve(curve); +} + +void Window::overshootChanged(double value) +{ + QEasingCurve curve = m_anim->easingCurve(); + curve.setOvershoot(value); + m_anim->setEasingCurve(curve); +} + diff --git a/examples/animation/easing/window.h b/examples/animation/easing/window.h new file mode 100644 index 0000000..4ba6eb6 --- /dev/null +++ b/examples/animation/easing/window.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> + +#include "ui_form.h" +#include "animation.h" + +class PixmapItem : public QObject, public QGraphicsPixmapItem +{ + Q_OBJECT + Q_PROPERTY(QPointF pos READ pos WRITE setPos) +public: + PixmapItem(const QPixmap &pix) : QGraphicsPixmapItem(pix) + { + } +}; + +class Window : public QWidget { + Q_OBJECT +public: + Window(QWidget *parent = 0); +private slots: + void curveChanged(int row); + void pathChanged(int index); + void periodChanged(double); + void amplitudeChanged(double); + void overshootChanged(double); + +private: + void createCurveIcons(); + void startAnimation(); + + Ui::Form m_ui; + QGraphicsScene m_scene; + PixmapItem *m_item; + Animation *m_anim; + QSize m_iconSize; + + +}; diff --git a/examples/animation/example/example.pro b/examples/animation/example/example.pro new file mode 100644 index 0000000..bc79b82 --- /dev/null +++ b/examples/animation/example/example.pro @@ -0,0 +1,12 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Thu Sep 25 14:03:47 2008 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +HEADERS += mainwindow.h +SOURCES += main.cpp mainwindow.cpp diff --git a/examples/animation/example/main.cpp b/examples/animation/example/main.cpp new file mode 100644 index 0000000..d5f5607 --- /dev/null +++ b/examples/animation/example/main.cpp @@ -0,0 +1,23 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + //Q_INIT_RESOURCE(example); + QApplication app(argc, argv); + MainWindow w; + w.show(); + return app.exec(); +} + diff --git a/examples/animation/example/mainwindow.cpp b/examples/animation/example/mainwindow.cpp new file mode 100644 index 0000000..2b0e035 --- /dev/null +++ b/examples/animation/example/mainwindow.cpp @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.h" + +MainWindow::MainWindow() : QMainWindow(0) +{ + // Text edit and button + listWidget = new QListWidget; + new QListWidgetItem("Rachel", listWidget); + new QListWidgetItem("Andreas", listWidget); + new QListWidgetItem("David", listWidget); + new QListWidgetItem("Olivier", listWidget); + new QListWidgetItem("Andy", listWidget); + new QListWidgetItem("Martial", listWidget); + new QListWidgetItem("Kazou", listWidget); + new QListWidgetItem("Fred", listWidget); + new QListWidgetItem("Ingrid", listWidget); + QGraphicsProxyWidget *listProxy = new QGraphicsProxyWidget; + listProxy->setWidget(listWidget); + + labelWidget = new QLabel; + QGraphicsProxyWidget *labelProxy = new QGraphicsProxyWidget; + labelProxy->setWidget(labelWidget); + labelWidget->setAttribute(Qt::WA_NoSystemBackground); + + label2Widget = new QLabel; + label2Widget->setAlignment(Qt::AlignCenter); + QGraphicsProxyWidget *label2Proxy = new QGraphicsProxyWidget; + label2Proxy->setWidget(label2Widget); + label2Widget->setAttribute(Qt::WA_NoSystemBackground); + + editWidget = new QLineEdit; + QGraphicsProxyWidget *editProxy = new QGraphicsProxyWidget; + editProxy->setWidget(editWidget); + editWidget->setAttribute(Qt::WA_NoSystemBackground); + + // Parent widget + QGraphicsWidget *widget = new QGraphicsWidget; + // Parent widget + QGraphicsWidget *widget2 = new QGraphicsWidget; + + QGraphicsLinearLayout *vLayout = new QGraphicsLinearLayout(Qt::Vertical, widget); + vLayout->addItem(listProxy); + vLayout->addItem(widget2); + widget->setLayout(vLayout); + + QPushButton *button = new QPushButton; + QGraphicsProxyWidget *buttonProxy = new QGraphicsProxyWidget; + buttonProxy->setWidget(button); + + QPushButton *button2 = new QPushButton; + QGraphicsProxyWidget *buttonProxy2 = new QGraphicsProxyWidget; + buttonProxy2->setWidget(button2); + + QPushButton *button3 = new QPushButton; + QGraphicsProxyWidget *buttonProxy3 = new QGraphicsProxyWidget; + buttonProxy3->setWidget(button3); + + QPushButton *button4 = new QPushButton; + QGraphicsProxyWidget *buttonProxy4 = new QGraphicsProxyWidget; + buttonProxy4->setWidget(button4); + + QGraphicsLinearLayout *hLayout = new QGraphicsLinearLayout(Qt::Horizontal, widget2); + hLayout->addItem(buttonProxy); + hLayout->addItem(buttonProxy2); + hLayout->addItem(buttonProxy3); + widget2->setLayout(hLayout); + + scene = new QGraphicsScene(0, 0, 700, 600); + scene->setBackgroundBrush(scene->palette().window()); + scene->addItem(widget); + scene->addItem(editProxy); + scene->addItem(label2Proxy); + scene->addItem(labelProxy); + scene->addItem(buttonProxy4); + + machine = new QStateMachine(); + + group = new QState(machine->rootState()); + state1 = new QState(group); + state2 = new QState(group); + state3 = new QState(group); + group->setInitialState(state1); + + machine->setInitialState(group); + + // State 1 + state1->assignProperty(button, "text", "Edit"); + state1->assignProperty(button2, "text", "Add"); + state1->assignProperty(button3, "text", "Remove"); + state1->assignProperty(button4, "text", "Accept"); + state1->addTransition(button2, SIGNAL(clicked()), state3); + state1->assignProperty(listProxy, "geometry", QRectF(0, 0, 700, 560)); + state1->assignProperty(widget, "geometry", QRectF(0, 0, 700, 600)); + state1->assignProperty(editProxy, "opacity", double(0)); + state1->assignProperty(labelProxy, "opacity", double(0)); + state1->assignProperty(label2Proxy, "opacity", double(0)); + state1->assignProperty(buttonProxy4, "opacity", double(0)); + state1->assignProperty(labelWidget, "text", "Name : "); + state1->assignProperty(label2Widget, "text", "Edit a contact"); + state1->assignProperty(label2Proxy, "geometry", QRectF(375, -50, 300, 30)); + state1->assignProperty(labelProxy, "geometry", QRectF(350, 300, 100, 30)); + state1->assignProperty(editProxy, "geometry", QRectF(750, 300, 250, 30)); + state1->assignProperty(buttonProxy4, "geometry", QRectF(500, 350, 80, 25)); + + // State 2 + state2->assignProperty(button, "text", "Close Editing"); + state2->assignProperty(listProxy, "geometry", QRectF(0, 0, 350, 560)); + state2->addTransition(button2, SIGNAL(clicked()), state3); + state2->addTransition(button4, SIGNAL(clicked()), state1); + + state2->assignProperty(editProxy, "opacity", double(1)); + state2->assignProperty(labelProxy, "opacity", double(1)); + state2->assignProperty(label2Proxy, "opacity", double(1)); + state2->assignProperty(buttonProxy4, "opacity", double(1)); + + state2->assignProperty(label2Proxy, "geometry", QRectF(375, 250, 300, 30)); + state2->assignProperty(editProxy, "geometry", QRectF(440, 300, 260, 30)); + + // State 3 + state3->assignProperty(button4, "text", "Create New"); + state3->assignProperty(listProxy, "geometry", QRectF(0, 0, 350, 560)); + state3->addTransition(button4, SIGNAL(clicked()), state1); + state3->addTransition(button, SIGNAL(clicked()), state1); + state3->assignProperty(editProxy, "opacity", double(1)); + state3->assignProperty(labelProxy, "opacity", double(1)); + state3->assignProperty(label2Proxy, "opacity", double(1)); + state3->assignProperty(buttonProxy4, "opacity", double(1)); + + state3->assignProperty(label2Proxy, "geometry", QRectF(375, 250, 300, 30)); + state3->assignProperty(editProxy, "geometry", QRectF(440, 300, 260, 30)); + + { + QAnimationGroup *animationGroup = new QParallelAnimationGroup; + QVariantAnimation *anim = new QPropertyAnimation(labelProxy, "opacity"); + animationGroup->addAnimation(anim); + anim = new QPropertyAnimation(label2Proxy, "geometry"); + animationGroup->addAnimation(anim); + anim = new QPropertyAnimation(editProxy, "geometry"); + animationGroup->addAnimation(anim); + anim = new QPropertyAnimation(listProxy, "geometry"); + animationGroup->addAnimation(anim); + + QAbstractTransition *trans = state1->addTransition(button, SIGNAL(clicked()), state2); + trans->addAnimation(animationGroup); + } + + { + QVariantAnimation *anim; + QAnimationGroup *animationGroup = new QParallelAnimationGroup; + anim = new QPropertyAnimation(label2Proxy, "geometry"); + animationGroup->addAnimation(anim); + anim = new QPropertyAnimation(editProxy, "geometry"); + animationGroup->addAnimation(anim); + anim = new QPropertyAnimation(listProxy, "geometry"); + animationGroup->addAnimation(anim); + QAbstractTransition *trans = state2->addTransition(button, SIGNAL(clicked()), state1); + trans->addAnimation(animationGroup); + } + + currentState = state1; + + view = new QGraphicsView(scene); + + setCentralWidget(view); + + state3->invokeMethodOnEntry(this, "onEnterState3"); + state2->invokeMethodOnEntry(this, "onEnterState2"); + state1->invokeMethodOnEntry(this, "onEnterState1"); + + connect(listWidget, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(onItemClicked(QListWidgetItem*))); + connect(button3, SIGNAL(clicked()), this, SLOT(onRemoveClicked())); + + machine->start(); +} + +void MainWindow::onEnterState2() +{ + currentState = state2; + if (listWidget->currentItem()) + editWidget->setText(listWidget->currentItem()->text()); +} + +void MainWindow::onEnterState1() +{ + if (currentState == state2 && listWidget->currentItem()) + listWidget->currentItem()->setText(editWidget->text()); + if (currentState == state3 && !editWidget->text().isNull()) { + new QListWidgetItem(editWidget->text(), listWidget); + editWidget->clear(); + } + currentState = state1; +} + +void MainWindow::onEnterState3() +{ + currentState = state3; +} + +void MainWindow::onItemClicked(QListWidgetItem*) +{ + if (currentState == state2) + { + editWidget->setText(listWidget->currentItem()->text()); + editWidget->clear(); + } +} + +void MainWindow::onRemoveClicked() +{ + QListWidgetItem *listItem = listWidget->takeItem(listWidget->currentRow()); + delete listItem; +} diff --git a/examples/animation/example/mainwindow.h b/examples/animation/example/mainwindow.h new file mode 100644 index 0000000..6be7463 --- /dev/null +++ b/examples/animation/example/mainwindow.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef __MAINWINDOW__H__ +#define __MAINWINDOW__H__ + +#include <QtGui> + +class MainWindow : public QMainWindow +{ +Q_OBJECT +public: + MainWindow(); + +private slots : + void onEnterState3(); + void onEnterState2(); + void onEnterState1(); + void onItemClicked(QListWidgetItem*); + void onRemoveClicked(); +private: + QListWidget *listWidget; + QLabel *labelWidget; + QLabel *label2Widget; + QLineEdit *editWidget; + QGraphicsScene *scene; + QGraphicsView *view; + QState *state1; + QState *state2; + QState *state3; + QState *currentState; + QState *group; + QStateMachine *machine; +}; + +#endif //__MAINWINDOW__H__ + diff --git a/examples/animation/moveblocks/main.cpp b/examples/animation/moveblocks/main.cpp new file mode 100644 index 0000000..1f253f3 --- /dev/null +++ b/examples/animation/moveblocks/main.cpp @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include <QtCore> +#include <QtGui> +#if defined(QT_EXPERIMENTAL_SOLUTION) +#include "qstatemachine.h" +#include "qstate.h" +#include "qabstracttransition.h" +#include "qpropertyanimation.h" +#include "qsequentialanimationgroup.h" +#include "qparallelanimationgroup.h" +#include "qgraphicswidget.h" +#endif +#include <time.h> + +class StateSwitchEvent: public QEvent +{ +public: + StateSwitchEvent() + : QEvent(Type(StateSwitchType)) + { + } + + StateSwitchEvent(int rand) + : QEvent(Type(StateSwitchType)), + m_rand(rand) + { + } + + enum { StateSwitchType = QEvent::User + 256 }; + + int rand() const { return m_rand; } + +private: + int m_rand; +}; + + +class QGraphicsRectWidget : public QGraphicsWidget +{ +public: + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, + QWidget *) + { + painter->fillRect(rect(), Qt::blue); + } +}; + +class StateSwitchTransition: public QAbstractTransition +{ +public: + StateSwitchTransition(int rand) + : QAbstractTransition(), + m_rand(rand) + { + } + +protected: + virtual bool eventTest(QEvent *event) const + { + return (event->type() == QEvent::Type(StateSwitchEvent::StateSwitchType)) + && (static_cast<StateSwitchEvent *>(event)->rand() == m_rand); + } + + virtual void onTransition() {} + +private: + int m_rand; +}; + +class StateSwitcher : public QState +{ + Q_OBJECT +public: + StateSwitcher(QStateMachine *machine) + : QState(machine->rootState()), m_machine(machine), + m_stateCount(0), m_lastIndex(0) + { } + + virtual void onEntry() + { + int n; + while ((n = (qrand() % m_stateCount + 1)) == m_lastIndex) + { } + m_lastIndex = n; + m_machine->postEvent(new StateSwitchEvent(n)); + } + virtual void onExit() {} + + void addState(QState *state, QAbstractAnimation *animation) { + StateSwitchTransition *trans = new StateSwitchTransition(++m_stateCount); + trans->setTargetState(state); + addTransition(trans); + trans->addAnimation(animation); + } + + +private: + QStateMachine *m_machine; + int m_stateCount; + int m_lastIndex; +}; + +QState *createGeometryState(QObject *w1, const QRect &rect1, + QObject *w2, const QRect &rect2, + QObject *w3, const QRect &rect3, + QObject *w4, const QRect &rect4, + QState *parent) +{ + QState *result = new QState(parent); + result->assignProperty(w1, "geometry", rect1); + result->assignProperty(w1, "geometry", rect1); + result->assignProperty(w2, "geometry", rect2); + result->assignProperty(w3, "geometry", rect3); + result->assignProperty(w4, "geometry", rect4); + + return result; +} + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + +#if 0 + QWidget window; + QPalette palette; + palette.setBrush(QPalette::Window, Qt::black); + window.setPalette(palette); + QPushButton *button1 = new QPushButton("A", &window); + QPushButton *button2 = new QPushButton("B", &window); + QPushButton *button3 = new QPushButton("C", &window); + QPushButton *button4 = new QPushButton("D", &window); + + button1->setObjectName("button1"); + button2->setObjectName("button2"); + button3->setObjectName("button3"); + button4->setObjectName("button4"); +#else + QGraphicsRectWidget *button1 = new QGraphicsRectWidget; + QGraphicsRectWidget *button2 = new QGraphicsRectWidget; + QGraphicsRectWidget *button3 = new QGraphicsRectWidget; + QGraphicsRectWidget *button4 = new QGraphicsRectWidget; + button2->setZValue(1); + button3->setZValue(2); + button4->setZValue(3); + QGraphicsScene scene(0, 0, 300, 300); + scene.setBackgroundBrush(Qt::black); + scene.addItem(button1); + scene.addItem(button2); + scene.addItem(button3); + scene.addItem(button4); + + QGraphicsView window(&scene); + window.setFrameStyle(0); + window.setAlignment(Qt::AlignLeft | Qt::AlignTop); + window.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + window.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +#endif + QStateMachine machine; + + QState *group = new QState(); + group->setObjectName("group"); + QTimer timer; + timer.setInterval(1250); + timer.setSingleShot(true); + group->invokeMethodOnEntry(&timer, "start"); + + QState *state1; + QState *state2; + QState *state3; + QState *state4; + QState *state5; + QState *state6; + QState *state7; + + state1 = createGeometryState(button1, QRect(100, 0, 50, 50), + button2, QRect(150, 0, 50, 50), + button3, QRect(200, 0, 50, 50), + button4, QRect(250, 0, 50, 50), + group); + state2 = createGeometryState(button1, QRect(250, 100, 50, 50), + button2, QRect(250, 150, 50, 50), + button3, QRect(250, 200, 50, 50), + button4, QRect(250, 250, 50, 50), + group); + state3 = createGeometryState(button1, QRect(150, 250, 50, 50), + button2, QRect(100, 250, 50, 50), + button3, QRect(50, 250, 50, 50), + button4, QRect(0, 250, 50, 50), + group); + state4 = createGeometryState(button1, QRect(0, 150, 50, 50), + button2, QRect(0, 100, 50, 50), + button3, QRect(0, 50, 50, 50), + button4, QRect(0, 0, 50, 50), + group); + state5 = createGeometryState(button1, QRect(100, 100, 50, 50), + button2, QRect(150, 100, 50, 50), + button3, QRect(100, 150, 50, 50), + button4, QRect(150, 150, 50, 50), + group); + state6 = createGeometryState(button1, QRect(50, 50, 50, 50), + button2, QRect(200, 50, 50, 50), + button3, QRect(50, 200, 50, 50), + button4, QRect(200, 200, 50, 50), + group); + state7 = createGeometryState(button1, QRect(0, 0, 50, 50), + button2, QRect(250, 0, 50, 50), + button3, QRect(0, 250, 50, 50), + button4, QRect(250, 250, 50, 50), + group); + group->setInitialState(state1); + + QParallelAnimationGroup animationGroup; + QSequentialAnimationGroup *subGroup; + + QPropertyAnimation *anim = new QPropertyAnimation(button4, "geometry"); + anim->setDuration(1000); + anim->setEasingCurve(QEasingCurve::OutElastic); + animationGroup.addAnimation(anim); + + subGroup = new QSequentialAnimationGroup(&animationGroup); + subGroup->addPause(100); + anim = new QPropertyAnimation(button3, "geometry"); + anim->setDuration(1000); + anim->setEasingCurve(QEasingCurve::OutElastic); + subGroup->addAnimation(anim); + + subGroup = new QSequentialAnimationGroup(&animationGroup); + subGroup->addPause(150); + anim = new QPropertyAnimation(button2, "geometry"); + anim->setDuration(1000); + anim->setEasingCurve(QEasingCurve::OutElastic); + subGroup->addAnimation(anim); + + subGroup = new QSequentialAnimationGroup(&animationGroup); + subGroup->addPause(200); + anim = new QPropertyAnimation(button1, "geometry"); + anim->setDuration(1000); + anim->setEasingCurve(QEasingCurve::OutElastic); + subGroup->addAnimation(anim); + + StateSwitcher *stateSwitcher = new StateSwitcher(&machine); + stateSwitcher->setObjectName("stateSwitcher"); + group->addTransition(&timer, SIGNAL(timeout()), stateSwitcher); + stateSwitcher->addState(state1, &animationGroup); + stateSwitcher->addState(state2, &animationGroup); + stateSwitcher->addState(state3, &animationGroup); + stateSwitcher->addState(state4, &animationGroup); + stateSwitcher->addState(state5, &animationGroup); + stateSwitcher->addState(state6, &animationGroup); + stateSwitcher->addState(state7, &animationGroup); + + machine.addState(group); + machine.setInitialState(group); + machine.start(); + + + window.resize(300, 300); + window.show(); + + qsrand(time(0)); + + return app.exec(); +} + +#include "main.moc" diff --git a/examples/animation/moveblocks/moveblocks.pro b/examples/animation/moveblocks/moveblocks.pro new file mode 100644 index 0000000..7a82ca5 --- /dev/null +++ b/examples/animation/moveblocks/moveblocks.pro @@ -0,0 +1,8 @@ +# Input +SOURCES += main.cpp + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/animation/moveblocks +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS states.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/animation/moveblocks +INSTALLS += target sources diff --git a/examples/animation/padnavigator-ng/backside.ui b/examples/animation/padnavigator-ng/backside.ui new file mode 100644 index 0000000..afa488c --- /dev/null +++ b/examples/animation/padnavigator-ng/backside.ui @@ -0,0 +1,208 @@ +<ui version="4.0" > + <class>BackSide</class> + <widget class="QWidget" name="BackSide" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>378</width> + <height>385</height> + </rect> + </property> + <property name="windowTitle" > + <string>BackSide</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2" > + <item> + <widget class="QGroupBox" name="groupBox" > + <property name="title" > + <string>Settings</string> + </property> + <property name="flat" > + <bool>true</bool> + </property> + <property name="checkable" > + <bool>true</bool> + </property> + <layout class="QGridLayout" name="gridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Title:</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="hostName" > + <property name="text" > + <string>Pad Navigator Example</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Modified:</string> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Extent</string> + </property> + </widget> + </item> + <item row="2" column="1" > + <layout class="QHBoxLayout" name="horizontalLayout" > + <item> + <widget class="QSlider" name="horizontalSlider" > + <property name="value" > + <number>42</number> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="spinBox" > + <property name="value" > + <number>42</number> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="1" > + <widget class="QDateTimeEdit" name="dateTimeEdit" /> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2" > + <property name="title" > + <string>Other input</string> + </property> + <property name="flat" > + <bool>true</bool> + </property> + <property name="checkable" > + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2" > + <item> + <widget class="QTreeWidget" name="treeWidget" > + <column> + <property name="text" > + <string>Widgets On Graphics View</string> + </property> + </column> + <item> + <property name="text" > + <string>QGraphicsProxyWidget</string> + </property> + <item> + <property name="text" > + <string>QGraphicsWidget</string> + </property> + <item> + <property name="text" > + <string>QObject</string> + </property> + </item> + <item> + <property name="text" > + <string>QGraphicsItem</string> + </property> + </item> + <item> + <property name="text" > + <string>QGraphicsLayoutItem</string> + </property> + </item> + </item> + </item> + <item> + <property name="text" > + <string>QGraphicsGridLayout</string> + </property> + <item> + <property name="text" > + <string>QGraphicsLayout</string> + </property> + <item> + <property name="text" > + <string>QGraphicsLayoutItem</string> + </property> + </item> + </item> + </item> + <item> + <property name="text" > + <string>QGraphicsLinearLayout</string> + </property> + <item> + <property name="text" > + <string>QGraphicsLayout</string> + </property> + <item> + <property name="text" > + <string>QGraphicsLayoutItem</string> + </property> + </item> + </item> + </item> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <tabstops> + <tabstop>groupBox</tabstop> + <tabstop>hostName</tabstop> + <tabstop>dateTimeEdit</tabstop> + <tabstop>horizontalSlider</tabstop> + <tabstop>spinBox</tabstop> + <tabstop>groupBox_2</tabstop> + <tabstop>treeWidget</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>horizontalSlider</sender> + <signal>valueChanged(int)</signal> + <receiver>spinBox</receiver> + <slot>setValue(int)</slot> + <hints> + <hint type="sourcelabel" > + <x>184</x> + <y>125</y> + </hint> + <hint type="destinationlabel" > + <x>275</x> + <y>127</y> + </hint> + </hints> + </connection> + <connection> + <sender>spinBox</sender> + <signal>valueChanged(int)</signal> + <receiver>horizontalSlider</receiver> + <slot>setValue(int)</slot> + <hints> + <hint type="sourcelabel" > + <x>272</x> + <y>114</y> + </hint> + <hint type="destinationlabel" > + <x>190</x> + <y>126</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/examples/animation/padnavigator-ng/images/artsfftscope.png b/examples/animation/padnavigator-ng/images/artsfftscope.png Binary files differnew file mode 100644 index 0000000..b4b8775 --- /dev/null +++ b/examples/animation/padnavigator-ng/images/artsfftscope.png diff --git a/examples/animation/padnavigator-ng/images/blue_angle_swirl.jpg b/examples/animation/padnavigator-ng/images/blue_angle_swirl.jpg Binary files differnew file mode 100644 index 0000000..5bf0deb --- /dev/null +++ b/examples/animation/padnavigator-ng/images/blue_angle_swirl.jpg diff --git a/examples/animation/padnavigator-ng/images/kontact_contacts.png b/examples/animation/padnavigator-ng/images/kontact_contacts.png Binary files differnew file mode 100644 index 0000000..6fb4cc8 --- /dev/null +++ b/examples/animation/padnavigator-ng/images/kontact_contacts.png diff --git a/examples/animation/padnavigator-ng/images/kontact_journal.png b/examples/animation/padnavigator-ng/images/kontact_journal.png Binary files differnew file mode 100644 index 0000000..b1fedb6 --- /dev/null +++ b/examples/animation/padnavigator-ng/images/kontact_journal.png diff --git a/examples/animation/padnavigator-ng/images/kontact_mail.png b/examples/animation/padnavigator-ng/images/kontact_mail.png Binary files differnew file mode 100644 index 0000000..672f8fa --- /dev/null +++ b/examples/animation/padnavigator-ng/images/kontact_mail.png diff --git a/examples/animation/padnavigator-ng/images/kontact_notes.png b/examples/animation/padnavigator-ng/images/kontact_notes.png Binary files differnew file mode 100644 index 0000000..229bf73 --- /dev/null +++ b/examples/animation/padnavigator-ng/images/kontact_notes.png diff --git a/examples/animation/padnavigator-ng/images/kopeteavailable.png b/examples/animation/padnavigator-ng/images/kopeteavailable.png Binary files differnew file mode 100644 index 0000000..2eaf41a --- /dev/null +++ b/examples/animation/padnavigator-ng/images/kopeteavailable.png diff --git a/examples/animation/padnavigator-ng/images/metacontact_online.png b/examples/animation/padnavigator-ng/images/metacontact_online.png Binary files differnew file mode 100644 index 0000000..6a398dd --- /dev/null +++ b/examples/animation/padnavigator-ng/images/metacontact_online.png diff --git a/examples/animation/padnavigator-ng/images/minitools.png b/examples/animation/padnavigator-ng/images/minitools.png Binary files differnew file mode 100644 index 0000000..0248c9d --- /dev/null +++ b/examples/animation/padnavigator-ng/images/minitools.png diff --git a/examples/animation/padnavigator-ng/main.cpp b/examples/animation/padnavigator-ng/main.cpp new file mode 100644 index 0000000..5b35b62 --- /dev/null +++ b/examples/animation/padnavigator-ng/main.cpp @@ -0,0 +1,24 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#include "panel.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + Q_INIT_RESOURCE(padnavigator); + + Panel panel(3, 3); + panel.show(); + + return app.exec(); +} diff --git a/examples/animation/padnavigator-ng/padnavigator.pro b/examples/animation/padnavigator-ng/padnavigator.pro new file mode 100644 index 0000000..0d094c6 --- /dev/null +++ b/examples/animation/padnavigator-ng/padnavigator.pro @@ -0,0 +1,24 @@ +HEADERS += \ + panel.h \ + roundrectitem.h \ + splashitem.h + +SOURCES += \ + panel.cpp \ + roundrectitem.cpp \ + splashitem.cpp \ + main.cpp + +RESOURCES += \ + padnavigator.qrc + +FORMS += \ + backside.ui + +contains(QT_CONFIG, opengl):QT += opengl + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/graphicsview/padnavigator +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS padnavigator.pro images +sources.path = $$[QT_INSTALL_EXAMPLES]/graphicsview/padnavigator +INSTALLS += target sources diff --git a/examples/animation/padnavigator-ng/padnavigator.qrc b/examples/animation/padnavigator-ng/padnavigator.qrc new file mode 100644 index 0000000..30ee8e1 --- /dev/null +++ b/examples/animation/padnavigator-ng/padnavigator.qrc @@ -0,0 +1,14 @@ +<RCC> + <qresource> + <file>images/blue_angle_swirl.jpg</file> + <file>images/artsfftscope.png</file> + <file>images/kontact_contacts.png</file> + <file>images/kontact_journal.png</file> + <file>images/kontact_mail.png</file> + <file>images/kontact_notes.png</file> + <file>images/kopeteavailable.png</file> + <file>images/metacontact_online.png</file> + <file>images/minitools.png</file> + <file>images/blue_angle_swirl.jpg</file> + </qresource> +</RCC> diff --git a/examples/animation/padnavigator-ng/panel.cpp b/examples/animation/padnavigator-ng/panel.cpp new file mode 100644 index 0000000..94dbdec --- /dev/null +++ b/examples/animation/padnavigator-ng/panel.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "panel.h" +#include "roundrectitem.h" +#include "splashitem.h" +#include "ui_backside.h" + +#ifndef QT_NO_OPENGL +#include <QtOpenGL/QtOpenGL> +#else +#endif +#include <QtGui/QtGui> + +Panel::Panel(int width, int height) + : selectedIndex(0), + grid(width*height), + width(width), + height(height), + flipped(false), + flippingGroup(0), + rotationXanim(0), + rotationYanim(0) +{ + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setCacheMode(CacheBackground); + setViewportUpdateMode(FullViewportUpdate); + setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + setBackgroundBrush(QPixmap(":/images/blue_angle_swirl.jpg")); +#ifndef QT_NO_OPENGL + setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers))); +#endif + + QRectF bounds((-width / 2.0) * 150, (-height / 2.0) * 150, width * 150, height * 150); + + setScene(new QGraphicsScene(bounds, this)); + + baseItem = new RoundRectItem(bounds, QColor(226, 255, 92, 64)); + scene()->addItem(baseItem); + + QWidget *embed = new QWidget; + ui = new Ui_BackSide; + ui->setupUi(embed); + ui->hostName->setFocus(); + + backItem = new RoundRectItem(bounds, embed->palette().window(), embed); + backItem->setYRotation(180); + backItem->setParentItem(baseItem); + + selectionItem = new RoundRectItem(QRectF(-60, -60, 120, 120), Qt::gray); + selectionItem->setParentItem(baseItem); + selectionItem->setZValue(-1); + selectionItem->setPos(posForLocation(0)); + + int currentIndex = 0; + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + RoundRectItem *item = new RoundRectItem(QRectF(-54, -54, 108, 108), + QColor(214, 240, 110, 128)); + item->setPos(posForLocation(currentIndex)); + + item->setParentItem(baseItem); + item->setFlag(QGraphicsItem::ItemIsFocusable); + grid[currentIndex++] = item; + + switch (qrand() % 9) { + case 0: item->setPixmap(QPixmap(":/images/kontact_contacts.png")); break; + case 1: item->setPixmap(QPixmap(":/images/kontact_journal.png")); break; + case 2: item->setPixmap(QPixmap(":/images/kontact_notes.png")); break; + case 3: item->setPixmap(QPixmap(":/images/kopeteavailable.png")); break; + case 4: item->setPixmap(QPixmap(":/images/metacontact_online.png")); break; + case 5: item->setPixmap(QPixmap(":/images/minitools.png")); break; + case 6: item->setPixmap(QPixmap(":/images/kontact_journal.png")); break; + case 7: item->setPixmap(QPixmap(":/images/kontact_contacts.png")); break; + case 8: item->setPixmap(QPixmap(":/images/kopeteavailable.png")); break; + default: + break; + } + + connect(item, SIGNAL(activated()), this, SLOT(flip())); + } + } + + grid.first()->setFocus(); + + connect(backItem, SIGNAL(activated()), + this, SLOT(flip())); + + splash = new SplashItem; + splash->setZValue(5); + splash->setPos(-splash->rect().width() / 2, scene()->sceneRect().top()); + scene()->addItem(splash); + + splash->grabKeyboard(); + + //initialize the position + baseItem->setYRotation(selectionItem->x()/6.); + baseItem->setXRotation(selectionItem->y()/6.); + + setWindowTitle(tr("Pad Navigator Example")); +} + +Panel::~Panel() +{ +} + +void Panel::keyPressEvent(QKeyEvent *event) +{ + if (splash->isVisible() || event->key() == Qt::Key_Return || flipped) { + QGraphicsView::keyPressEvent(event); + return; + } + + selectedIndex = (selectedIndex + grid.count() + (event->key() == Qt::Key_Right) - (event->key() == Qt::Key_Left) + + width * ((event->key() == Qt::Key_Down) - (event->key() == Qt::Key_Up))) % grid.count(); + grid[selectedIndex]->setFocus(); + + const QPointF pos = posForLocation(selectedIndex); + + const double angleY = pos.x() / 6., + angleX = pos.y() / 6.; + + QAnimationGroup *group = new QParallelAnimationGroup(); + + QVariantAnimation *anim = new QPropertyAnimation(baseItem, "xRotation"); + anim->setEndValue(angleX); + anim->setDuration(150); + anim->setEasingCurve(QEasingCurve::OutInSine); + group->addAnimation(anim); + + anim = new QPropertyAnimation(baseItem, "yRotation"); + anim->setEndValue(angleY); + anim->setDuration(150); + anim->setEasingCurve(QEasingCurve::OutInSine); + group->addAnimation(anim); + + anim = new QPropertyAnimation(selectionItem, "pos"); + anim->setEndValue(pos); + anim->setDuration(150); + anim->setEasingCurve(QEasingCurve::Linear); + group->addAnimation(anim); + + group->start(QAbstractAnimation::DeleteWhenStopped); +} + +void Panel::resizeEvent(QResizeEvent *event) +{ + QGraphicsView::resizeEvent(event); + fitInView(scene()->sceneRect(), Qt::KeepAspectRatio); +} + +void Panel::flip() +{ + grid[selectedIndex]->setFocus(); + + if (flippingGroup == 0) { + flippingGroup = new QParallelAnimationGroup(this); + + const qreal zoomOut = qreal(.75); + + //slight scaling down while flipping + QVariantAnimation *anim = new QPropertyAnimation(baseItem, "yScale"); + anim->setKeyValueAt(qreal(.5), zoomOut); + anim->setEndValue(1); + anim->setEasingCurve(QEasingCurve::OutInSine); + anim->setDuration(500); + flippingGroup->addAnimation(anim); + + anim = new QPropertyAnimation(baseItem, "xScale"); + anim->setKeyValueAt(qreal(.5), zoomOut); + anim->setEndValue(1); + anim->setEasingCurve(QEasingCurve::OutInSine); + anim->setDuration(500); + flippingGroup->addAnimation(anim); + + rotationXanim = new QPropertyAnimation(baseItem, "xRotation"); + rotationXanim->setEndValue(0); + rotationXanim->setDuration(500); + flippingGroup->addAnimation(rotationXanim); + + rotationYanim = new QPropertyAnimation(baseItem, "yRotation"); + rotationYanim->setEndValue(180); + rotationYanim->setDuration(500); + flippingGroup->addAnimation(rotationYanim); + } + + if (flippingGroup->currentTime() != 0 && flippingGroup->direction() == QAbstractAnimation::Forward) { + flippingGroup->setDirection(QAbstractAnimation::Backward); + } else { + flippingGroup->setDirection(QAbstractAnimation::Forward); + if (flippingGroup->currentTime() == 0) { + //we always make sure when it is at the beginning + rotationXanim->setStartValue(baseItem->xRotation()); + rotationYanim->setStartValue(baseItem->yRotation()); + } + } + flippingGroup->start(); + flipped = !flipped; +} + +QPointF Panel::posForLocation(int index) const +{ + const int x = index % width, + y = index / width; + return QPointF(x * 150, y * 150) + - QPointF((width - 1) * 75, (height - 1) * 75); +} diff --git a/examples/animation/padnavigator-ng/panel.h b/examples/animation/padnavigator-ng/panel.h new file mode 100644 index 0000000..cbceed1 --- /dev/null +++ b/examples/animation/padnavigator-ng/panel.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/qgraphicsview.h> +#ifdef QT_EXPERIMENTAL_SOLUTION +#include "qtgraphicswidget.h" +#else +#include <QtGui/qgraphicswidget.h> +#endif + +QT_BEGIN_NAMESPACE +class Ui_BackSide; +QT_END_NAMESPACE; + +class RoundRectItem; +class QAnimationGroup; +class QPropertyAnimation; + +class Panel : public QGraphicsView +{ + Q_OBJECT +public: + Panel(int width, int height); + ~Panel(); + +protected: + void keyPressEvent(QKeyEvent *event); + void resizeEvent(QResizeEvent *event); + +private Q_SLOTS: + void flip(); + +private: + QPointF posForLocation(int index) const; + + QGraphicsWidget *selectionItem; + QGraphicsWidget *baseItem; + RoundRectItem *backItem; + QGraphicsWidget *splash; + int selectedIndex; + + QVector<QGraphicsItem*> grid; + + int width; + int height; + bool flipped; + Ui_BackSide *ui; + + QAnimationGroup *flippingGroup; + QPropertyAnimation *rotationXanim, *rotationYanim; +}; diff --git a/examples/animation/padnavigator-ng/roundrectitem.cpp b/examples/animation/padnavigator-ng/roundrectitem.cpp new file mode 100644 index 0000000..e498538 --- /dev/null +++ b/examples/animation/padnavigator-ng/roundrectitem.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "roundrectitem.h" + +#include <QtGui/QtGui> + +RoundRectItem::RoundRectItem(const QRectF &rect, const QBrush &brush, QWidget *embeddedWidget) + : QGraphicsWidget(), + brush(brush), + proxyWidget(0), + m_rect(rect) +{ + if (embeddedWidget) { + proxyWidget = new QGraphicsProxyWidget(this); + proxyWidget->setFocusPolicy(Qt::StrongFocus); + proxyWidget->setWidget(embeddedWidget); + } +} + +void RoundRectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + const bool widgetHidden = parentItem() == 0 || qAbs(static_cast<QGraphicsWidget*>(parentItem())->yRotation()) < 90; + + if (proxyWidget) { + if (widgetHidden) { + proxyWidget->hide(); + } else { + if (!proxyWidget->isVisible()) { + proxyWidget->setGeometry(boundingRect().adjusted(25, 25, -25, -25)); + proxyWidget->show(); + proxyWidget->setFocus(); + } + painter->setBrush(brush); + painter->setPen(QPen(Qt::black, 1)); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + painter->drawRoundRect(m_rect); + } + } else if (widgetHidden) { + painter->setPen(Qt::NoPen); + painter->setBrush(QColor(0, 0, 0, 64)); + painter->drawRoundRect(m_rect.translated(2, 2)); + + QLinearGradient gradient(m_rect.topLeft(), m_rect.bottomRight()); + const QColor col = brush.color(); + gradient.setColorAt(0, col); + gradient.setColorAt(1, col.dark(200)); + painter->setBrush(gradient); + painter->setPen(QPen(Qt::black, 1)); + painter->drawRoundRect(m_rect); + if (!pix.isNull()) { + painter->scale(qreal(1.95), qreal(1.95)); + painter->drawPixmap(-pix.width() / 2, -pix.height() / 2, pix); + } + } + +} + +QRectF RoundRectItem::boundingRect() const +{ + qreal penW = qreal(.5); + qreal shadowW = 2; + return m_rect.adjusted(-penW, -penW, penW + shadowW, penW + shadowW); +} + +void RoundRectItem::setPixmap(const QPixmap &pixmap) +{ + pix = pixmap; + if (scene() && isVisible()) + update(); +} + +void RoundRectItem::keyPressEvent(QKeyEvent *event) +{ + if (event->isAutoRepeat() || event->key() != Qt::Key_Return) { + QGraphicsWidget::keyPressEvent(event); + return; + } + + if (!proxyWidget) { + setXScale(qreal(.9)); + setYScale(qreal(.9)); + } + emit activated(); +} + +void RoundRectItem::keyReleaseEvent(QKeyEvent *event) +{ + if (event->isAutoRepeat() || event->key() != Qt::Key_Return) { + QGraphicsWidget::keyReleaseEvent(event); + return; + } + + if (!proxyWidget) { + setXScale(1); + setYScale(1); + } +} diff --git a/examples/animation/padnavigator-ng/roundrectitem.h b/examples/animation/padnavigator-ng/roundrectitem.h new file mode 100644 index 0000000..56a6d3c --- /dev/null +++ b/examples/animation/padnavigator-ng/roundrectitem.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qobject.h> +#include <QtGui/qbrush.h> +#ifdef QT_EXPERIMENTAL_SOLUTION +#include "qtgraphicswidget.h" +#else +#include <QtGui/qgraphicswidget.h> +#endif + +QT_BEGIN_NAMESPACE +class QGraphicsProxyWidget; +QT_END_NAMESPACE; + +class RoundRectItem : public QGraphicsWidget +{ + Q_OBJECT +public: + RoundRectItem(const QRectF &rect, const QBrush &brush, QWidget *embeddedWidget = 0); + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *); + QRectF boundingRect() const; + + void setPixmap(const QPixmap &pixmap); + +Q_SIGNALS: + void activated(); + +protected: + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *event); + +private: + QRectF m_rect; + QBrush brush; + QPixmap pix; + QGraphicsProxyWidget *proxyWidget; +}; diff --git a/examples/animation/padnavigator-ng/splashitem.cpp b/examples/animation/padnavigator-ng/splashitem.cpp new file mode 100644 index 0000000..84a8945 --- /dev/null +++ b/examples/animation/padnavigator-ng/splashitem.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "splashitem.h" + +#include <QtGui/QtGui> + +SplashItem::SplashItem(QGraphicsItem *parent) + : QGraphicsWidget(parent) +{ + + text = tr("Welcome to the Pad Navigator Example. You can use the" + " keyboard arrows to navigate the icons, and press enter" + " to activate an item. Please press any key to continue."); + resize(400, 175); +} + +void SplashItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + painter->setPen(QPen(Qt::black, 2)); + painter->setBrush(QColor(245, 245, 255, 220)); + painter->setClipRect(rect()); + painter->drawRoundRect(3, -100 + 3, 400 - 6, 250 - 6); + + QRectF textRect = rect().adjusted(10, 10, -10, -10); + int flags = Qt::AlignTop | Qt::AlignLeft | Qt::TextWordWrap; + + QFont font; + font.setPixelSize(18); + painter->setPen(Qt::black); + painter->setFont(font); + painter->drawText(textRect, flags, text); +} + +void SplashItem::keyPressEvent(QKeyEvent * /* event */) +{ + QVariantAnimation *anim = new QPropertyAnimation(this, "pos"); + anim->setEndValue(QPointF(x(), scene()->sceneRect().top() - rect().height())); + anim->setDuration(350); + anim->start(QAbstractAnimation::DeleteWhenStopped); + + anim = new QPropertyAnimation(this, "opacity"); + anim->setEndValue(0); + anim->start(QAbstractAnimation::DeleteWhenStopped); + + connect(anim, SIGNAL(finished()), SLOT(close())); +} diff --git a/examples/animation/padnavigator-ng/splashitem.h b/examples/animation/padnavigator-ng/splashitem.h new file mode 100644 index 0000000..6428b69 --- /dev/null +++ b/examples/animation/padnavigator-ng/splashitem.h @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qobject.h> +#ifdef QT_EXPERIMENTAL_SOLUTION +#include "qtgraphicswidget.h" +#else +#include <QtGui/qgraphicswidget.h> +#endif + +class SplashItem : public QGraphicsWidget +{ + Q_OBJECT +public: + SplashItem(QGraphicsItem *parent = 0); + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + +protected: + void keyPressEvent(QKeyEvent *event); + +private: + QString text; +}; diff --git a/examples/animation/photobrowser/main.cpp b/examples/animation/photobrowser/main.cpp new file mode 100644 index 0000000..98f2a9e --- /dev/null +++ b/examples/animation/photobrowser/main.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> + +#include "river.h" +#include "menu.h" + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + if (app.arguments().size() == 1) { + qWarning("you have to specifiy a path to look for the photos"); + return 0; + } + + + QGraphicsScene scene; + scene.setSceneRect(QRectF(QPointF(), River::fixedSize())); + + QGraphicsView view(&scene); + view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + const int fw = view.frameWidth() * 2; + view.setFixedSize(River::fixedSize() + QSize(fw,fw)); + + River river(app.arguments()[1]); + scene.addItem(&river); + + Menu menu(&river); + menu.addAction(QLatin1String("River Mode"), &river, SLOT(setRiverMode())); + menu.addAction(QLatin1String("Grid Mode"), &river, SLOT(setGridMode())); + menu.addAction(QLatin1String("Cover Flow"), &river, SLOT(setCoverMode())); + menu.addAction(QLatin1String("Hide Menu"), &menu, SLOT(hide())); + menu.addAction(QLatin1String("Exit"), &app, SLOT(quit())); + menu.setZValue(2); + menu.setFocus(); + + river.menu = &menu; + view.show(); + + return app.exec(); +} diff --git a/examples/animation/photobrowser/menu.cpp b/examples/animation/photobrowser/menu.cpp new file mode 100644 index 0000000..d4efe9c --- /dev/null +++ b/examples/animation/photobrowser/menu.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "menu.h" + +Menu::Menu(QGraphicsItem *parent) : QGraphicsWidget(parent), m_selected(0) +{ + setFlag(QGraphicsItem::ItemIsFocusable); + m_selection = new QGraphicsRectItem(this); + QLinearGradient linearGrad(QPointF(0, 0), QPointF(0,50)); + linearGrad.setColorAt(0, QColor(255,255,255,128)); + linearGrad.setColorAt(1, QColor(255,255,255,16)); + m_selection->setBrush(linearGrad); + m_selection->setPen(QPen(Qt::transparent)); +} + +Menu::~Menu() +{ +} + + +MenuAction *Menu::addAction(const QString &text, QObject *receiver, const char* slot) +{ + MenuAction *action = new MenuAction(text, this); + if (!m_actions.isEmpty()) { + MenuAction *last = m_actions.last(); + action->setPos(last->pos() + last->boundingRect().bottomLeft()); + } + m_actions.append(action); + if (m_selection->boundingRect().width() < action->boundingRect().width()) + m_selection->setRect(action->boundingRect()); + + QObject::connect(action, SIGNAL(triggered()), receiver, slot); + return action; +} + +QRectF Menu::boundingRect() const +{ + QRectF res; + foreach (MenuAction *a, m_actions) + res |= a->boundingRect(); + return res; +} + +void Menu::keyPressEvent (QKeyEvent * event) +{ + switch (event->key()) { + case Qt::Key_Escape: + hide(); + break; + case Qt::Key_Up: + m_selected -= 2; + case Qt::Key_Down: + if (!m_actions.isEmpty()) { + m_selected = (m_selected + 1 + m_actions.count()) % m_actions.count(); + QItemAnimation *anim = new QItemAnimation(m_selection, QItemAnimation::Position); + anim->setEndValue(m_actions.at(m_selected)->pos()); + anim->start(QAbstractAnimation::DeleteWhenStopped); + } + break; + case Qt::Key_Enter: + case Qt::Key_Return: + if (!m_actions.isEmpty()) { + QItemAnimation *anim = new QItemAnimation(m_selection, QItemAnimation::RotationX); + anim->setEndValue(m_selection->xRotation() < 180 ? qreal(360) : qreal(0)); + anim->start(QAbstractAnimation::DeleteWhenStopped); + m_actions.at(m_selected)->trigger(); + hide(); + } + break; + default: + QGraphicsItem::keyPressEvent(event); + } +} + +void Menu::show() +{ + QItemAnimation *anim = new QItemAnimation(this, QItemAnimation::Opacity); + anim->setEndValue(qreal(1.)); + anim->start(QAbstractAnimation::DeleteWhenStopped); + anim = new QItemAnimation(m_selection, QItemAnimation::ScaleFactorX); + anim->setEndValue(qreal(1)); + anim->start(QAbstractAnimation::DeleteWhenStopped); + anim = new QItemAnimation(m_selection, QItemAnimation::ScaleFactorY); + anim->setEndValue(qreal(1)); + anim->start(QAbstractAnimation::DeleteWhenStopped); + setFocus(); +} + +void Menu::hide() +{ + QItemAnimation *anim = new QItemAnimation(m_selection, QItemAnimation::ScaleFactorX); + anim->setEndValue(qreal(.1)); + anim->start(QAbstractAnimation::DeleteWhenStopped); + anim = new QItemAnimation(m_selection, QItemAnimation::ScaleFactorY); + anim->setEndValue(qreal(.1)); + anim->start(QAbstractAnimation::DeleteWhenStopped); + anim = new QItemAnimation(this, QItemAnimation::Opacity); + anim->setEndValue(qreal(0)); + anim->start(QAbstractAnimation::DeleteWhenStopped); + parentItem()->setFocus(); +} + + +MenuAction::MenuAction(const QString &text, Menu * parent) + : QGraphicsTextItem(text,parent) +{ + QFont f = font(); + f.setPointSize(18); + setFont(f); + setDefaultTextColor(Qt::white); +} + +void MenuAction::trigger() +{ + emit triggered(); +} diff --git a/examples/animation/photobrowser/menu.h b/examples/animation/photobrowser/menu.h new file mode 100644 index 0000000..9514cfe --- /dev/null +++ b/examples/animation/photobrowser/menu.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef __MENU__H__ +#define __MENU__H__ + +#include <QtGui> + +class MenuAction; + +class Menu : public QGraphicsWidget +{ + Q_OBJECT +public: + Menu(QGraphicsItem *parent); + ~Menu(); + + MenuAction *addAction(const QString&, QObject *receiver = 0, const char* slot = 0 ); + + QRectF boundingRect() const; + void keyPressEvent ( QKeyEvent * event ); +public slots: + void show(); + void hide(); +private: + QList<MenuAction*> m_actions; + QGraphicsRectItem *m_selection; + int m_selected; +}; + +class MenuAction : public QGraphicsTextItem +{ + Q_OBJECT +public: + MenuAction(const QString &text, Menu * parent); + void trigger(); +signals: + void triggered(); +}; + + +#endif //__RIVERITEM__H__ diff --git a/examples/animation/photobrowser/photobrowser.pro b/examples/animation/photobrowser/photobrowser.pro new file mode 100644 index 0000000..21f03d6 --- /dev/null +++ b/examples/animation/photobrowser/photobrowser.pro @@ -0,0 +1,17 @@ +###################################################################### +# Automatically generated by qmake (2.01a) ven. 22. août 13:09:33 2008 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += main.cpp \ + river.cpp \ + riveritem.cpp \ + menu.cpp +HEADERS += river.h \ + riveritem.h \ + menu.h diff --git a/examples/animation/photobrowser/river.cpp b/examples/animation/photobrowser/river.cpp new file mode 100644 index 0000000..f3dd0746 --- /dev/null +++ b/examples/animation/photobrowser/river.cpp @@ -0,0 +1,561 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "river.h" +#include "riveritem.h" + +#include "qvariantanimation.h" + +#include <QtCore/QDebug> +#include <QtGui/QKeyEvent> +#include <QtGui/QGraphicsScene> +#include <QtGui/QPainter> + + +#define GRID_ROW_COUNT 3 +#define GRID_COLUMN_COUNT 2 +#define GRID_DIMENSIONS (GRID_ROW_COUNT * GRID_COLUMN_COUNT) +#define ITEM_COUNT 12 + +#define RIVER_MAGNIFY(ITEM) (qreal(0.50) + (ITEM)->zValue()*2 ) +#define GRID_MAGNIFY qreal(1.5) +#define GRID_CURRENT_MAGNIFY 2 + +River::River(const QString &path) : +m_images(QDir(path).entryInfoList(QStringList() << QLatin1String("*.jpg") << QLatin1String("*.png"))), +m_currentImage(0), m_mode(RiverMode), m_selectedItem(-1) , m_topLeftIndex(-1) +{ + setFocusPolicy(Qt::StrongFocus); + setGeometry(QRectF( QPointF(), fixedSize())); + + + for (int i = 0; i < ITEM_COUNT; ++i) { + RiverItem * item = new RiverItem(this); + + //here we also randomize the x position (not when looping) + const int x = qrand() % fixedSize().width(); + item->setPos(x, -1); + + m_items.insert(m_currentImage, item); + addUnusedRiverItem(item); + } + +} + +QPointF River::gridItemPosition(int row, int col) const +{ + if (col < 0) { + col += GRID_COLUMN_COUNT; + row --; + } + return QPointF(rect().width()*(col*2 + 1)/(GRID_COLUMN_COUNT*2), + rect().height()*(row*2 + 1)/(GRID_ROW_COUNT*2)); +} + +QPixmap River::pixmap(int index) const +{ + if (index < 0 || index >= m_images.size()) + return QPixmap(); + + if (m_pixmaps.size() <= index) { + m_pixmaps.resize(index+1); + } + + if (m_pixmaps.at(index).isNull()) { + m_pixmaps[index] = QPixmap(m_images.at(index).absoluteFilePath()); + } + + return m_pixmaps.at(index); +} + +void River::addUnusedRiverItem(RiverItem * item) +{ + if (m_images.isEmpty()) + return; + + QRectF realItemRect = item->mapToParent(item->boundingRect()).boundingRect(); + + int y = item->pos().y(); + int x = item->pos().x(); + if (x >= fixedSize().width() || x < -realItemRect.width() || y < 0) { + //we set the new pixmap + + m_items.remove(m_items.key(item)); + + while (m_items.contains(m_currentImage)) + m_currentImage = (m_currentImage + 1 ) % m_images.size(); + + item->setPixmap(pixmap(m_currentImage)); + + m_items.insert(m_currentImage, item); + //this manages looping as well + m_currentImage = (m_currentImage + 1 ) % m_images.size(); + + item->setZValue(qreal(qrand()%100)/200.0); + + QItemAnimation *anim = new QItemAnimation(item, QItemAnimation::ScaleFactorX, scene()); + anim->setEndValue(RIVER_MAGNIFY(item)); + anim->start(QAbstractAnimation::DeleteWhenStopped); + anim = new QItemAnimation(item, QItemAnimation::ScaleFactorY, scene()); + anim->setEndValue(RIVER_MAGNIFY(item)); + anim->start(QAbstractAnimation::DeleteWhenStopped); + + realItemRect = item->mapToParent(item->boundingRect()).boundingRect(); + + y = -realItemRect.y() + qrand() % (fixedSize().height() - int(realItemRect.height())); + if (x >= fixedSize().width()) { + x = -realItemRect.width()/2; + } + } + + item->setPos(x, y); + + const QPointF target(QPointF(fixedSize().width() + realItemRect.width()/2, y)); + + const int distance = target.x() - x; + + const int duration = (40 - 50*item->zValue() ) * distance; + QItemAnimation *a = new QItemAnimation(item, QItemAnimation::Position, scene()); + a->setEndValue(target); + a->setDuration(duration); + a->start(QAbstractAnimation::DeleteWhenStopped); + connect(a, SIGNAL(finished()), SLOT(animationFinished())); +} + +void River::animationFinished() +{ + QItemAnimation *anim = qobject_cast<QItemAnimation*>(sender()); + if (!anim || anim->propertyName() != QItemAnimation::Position) + return; + + /*RiverItem *item = static_cast<RiverItem*>(anim->graphicsItem()); + if (m_mode != RiverMode) {*/ + /*int key = m_items.key(item); + if (key < m_topLeftIndex || key >= m_topLeftIndex + GRID_DIMENSIONS) { + delete item; + m_items.remove(key); + }*/ + //return; + + //} + + addUnusedRiverItem(static_cast<RiverItem*>(anim->targetItem())); +} + +void River::switchPaused() +{ + const bool paused = m_pausedAnimations.isEmpty(); + if (paused) + m_pausedAnimations = scene()->findChildren<QItemAnimation*>(); + + foreach(QItemAnimation *anim, m_pausedAnimations) { + if (paused) + anim->pause(); + else + anim->resume(); + } + + if (!paused) + m_pausedAnimations.clear(); +} + +void River::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) +{ + painter->fillRect(option->rect, Qt::black); +} + +void River::setMode(Mode m) +{ + if (m_mode == m) + return; + + Mode oldMode = m_mode; + m_mode = m; + switch(m) + { + case RiverMode: + m_mode = oldMode; //some animation may be stopt, and we don't want that animationFinished we were in that mode yet + foreach (RiverItem *item, m_items) { + const int x = qrand() % fixedSize().width(); + const int y = qrand() % fixedSize().width(); + QItemAnimation *anim = new QItemAnimation(item, QItemAnimation::ScaleFactorX, scene()); + anim->setEndValue(RIVER_MAGNIFY(item)); + anim->start(QAbstractAnimation::DeleteWhenStopped); + anim = new QItemAnimation(item, QItemAnimation::ScaleFactorY, scene()); + anim->setEndValue(RIVER_MAGNIFY(item)); + anim->start(QAbstractAnimation::DeleteWhenStopped); + anim = new QItemAnimation(item, QItemAnimation::Position, scene()); + anim->setEndValue(QPointF(x, y)); + anim->start(QAbstractAnimation::DeleteWhenStopped); + connect(anim, SIGNAL(finished()), SLOT(animationFinished())); + } + m_mode = m; + break; + + case GridMode: + + if (oldMode == GridFullScreenMode) { + currentItem()->setFullScreen(false, GRID_CURRENT_MAGNIFY); + } else { + m_topLeftIndex = -GRID_DIMENSIONS; + foreach (RiverItem *item, m_items) { + QItemAnimation *anim = new QItemAnimation(item, QItemAnimation::ScaleFactorX, scene()); + anim->setEndValue(GRID_MAGNIFY); + anim->start(QAbstractAnimation::DeleteWhenStopped); + anim = new QItemAnimation(item, QItemAnimation::ScaleFactorY, scene()); + anim->setEndValue(GRID_MAGNIFY); + anim->start(QAbstractAnimation::DeleteWhenStopped); + } + adjustGrid(m_currentImage - GRID_DIMENSIONS + 1); + setCurrentItem(m_topLeftIndex); + } + break; + case GridFullScreenMode: + //let's put the current item fullscreen + currentItem()->setFullScreen(true, GRID_CURRENT_MAGNIFY); + break; + case CoverMode: + m_gridItem = m_items.values(); + setCurrentItem(m_gridItem.count()/2); + default: + break; + } +} + +River::Mode River::mode() const +{ + return m_mode; +} + +QSize River::fixedSize() +{ + return QSize(352, 416); +} + +//the name of this method is not that good... +void River::makeCenterAvailable(qreal size) +{ + QRectF center(QPointF(), QSizeF(size, size)); + center.moveCenter(rect().center()); + + const QList<QGraphicsItem*> list = scene()->items(center); + + foreach(QGraphicsItem *item, m_items) { + + if (!list.contains(item)) + continue; + + QPointF pos = item->pos(); + + if (pos.y() < rect().center().y()) { + //item is above center + pos.ry() = center.top() - item->boundingRect().height() - 1; + } else { + //item is below the center + pos.ry() = center.bottom() + 1; + } + QItemAnimation *anim = new QItemAnimation(item, QItemAnimation::Position, scene()); + anim->setEndValue(pos); + anim->setDuration(150); + anim->start(QAbstractAnimation::DeleteWhenStopped); + } +} + + +//this is there just to test small interaction +void River::keyPressEvent ( QKeyEvent * keyEvent ) +{ + switch(keyEvent->key()) + { + case Qt::Key_O: + { + QItemAnimation *anim = new QItemAnimation(this, QItemAnimation::Opacity, scene()); + anim->setDuration(2000); + anim->setEndValue(qreal(.5)); + anim->start(QAbstractAnimation::DeleteWhenStopped); + } + break; + case Qt::Key_N: + //that's a test + makeCenterAvailable(60); + break; + case Qt::Key_P: + switchPaused(); + break; + case Qt::Key_V: + setMode(GridMode); + break; + case Qt::Key_R: + setMode(RiverMode); + break; + case Qt::Key_C: + setMode(CoverMode); + break; + case Qt::Key_Return: + case Qt::Key_Enter: + if (m_mode == RiverMode) { + setMode(GridMode); + } else if (m_mode == GridMode) { + setMode(GridFullScreenMode); + } else if (m_mode == GridFullScreenMode) { + setMode(GridMode); + } + break; + case Qt::Key_Escape: + if (m_mode == GridFullScreenMode) { + setMode(GridMode); + } else if (m_mode == GridMode || m_mode == CoverMode) { + setMode(RiverMode); + } else if (m_mode == RiverMode) { + menu->show(); + } + break; + case Qt::Key_Right: + if (m_mode == GridMode) { + navigateBy(+1); + } else if (m_mode == CoverMode) { + setCurrentItem(m_selectedItem + 1); + } + break; + case Qt::Key_Left: + if (m_mode == GridMode) { + navigateBy(-1); + } else if (m_mode == CoverMode) { + setCurrentItem(m_selectedItem - 1); + } + break; + case Qt::Key_Down: + if (m_mode == GridMode) { + navigateBy(GRID_COLUMN_COUNT); + } + break; + case Qt::Key_Up: + if (m_mode == GridMode) { + navigateBy(-GRID_COLUMN_COUNT); + } + break; +// case Qt::Key_PageUp: + case Qt::Key_M: + menu->show(); + break; + case Qt::Key_Space: + if (m_mode == GridMode) { + RiverItem *item = currentItem(); + if(!item) + break; + + //stupid sequence. + QPointF pos = item->pos(); + QAnimationGroup *group = new QSequentialAnimationGroup(); + //item->animator()->beginSequence(); + + QItemAnimation *anim = new QItemAnimation(item, QItemAnimation::Position, scene()); + anim->setEndValue(pos); + group->addAnimation(anim); + anim = new QItemAnimation(item, QItemAnimation::ScaleFactorX, scene()); + anim->setEndValue(qreal(1.)); + anim->setDuration(500); + group->addAnimation(anim); + anim = new QItemAnimation(item, QItemAnimation::ScaleFactorY, scene()); + anim->setEndValue(qreal(1.)); + anim->setDuration(500); + group->addAnimation(anim); + anim = new QItemAnimation(item, QItemAnimation::RotationX, scene()); + anim->setEndValue(qreal(0)); + group->addAnimation(anim); + group->start(QAbstractAnimation::DeleteWhenStopped); + } + default: + break; + } + + QGraphicsItem::keyPressEvent(keyEvent); +} + + +void River::setGridMode() +{ + setMode(GridMode); +} + +void River::setRiverMode() +{ + setMode(RiverMode); +} + +void River::setCoverMode() +{ + setMode(CoverMode); +} + + +void River::adjustGrid(int newTopLeft) +{ + for (int i = newTopLeft ; i < newTopLeft + GRID_DIMENSIONS; i++) { + if (!m_items.contains(i)) { + RiverItem *item = createItem(i); + int row = (i - m_topLeftIndex) / GRID_COLUMN_COUNT; + int col = (i - m_topLeftIndex) % GRID_COLUMN_COUNT; + item->setPos(gridItemPosition(row, col)); + item->setXScale(0); + item->setYScale(0); + QItemAnimation *anim = new QItemAnimation(item, QItemAnimation::ScaleFactorX, scene()); + anim->setEndValue(GRID_MAGNIFY); + anim->start(QAbstractAnimation::DeleteWhenStopped); + anim = new QItemAnimation(item, QItemAnimation::ScaleFactorY, scene()); + anim->setEndValue(GRID_MAGNIFY); + anim->start(QAbstractAnimation::DeleteWhenStopped); + } + } + newTopLeft = newTopLeft - newTopLeft % GRID_COLUMN_COUNT; + + QHash<int, RiverItem *>::iterator it = m_items.begin(); + while (it != m_items.constEnd()) { + const int imageIdx = it.key(); + RiverItem *item = *it; + QSizeF itemSize = item->boundingRect().size(); + if ((imageIdx >= newTopLeft && imageIdx < newTopLeft + GRID_DIMENSIONS) + || boundingRect().adjusted(-itemSize.width()/2, -itemSize.height()/2, itemSize.width()/2, itemSize.height()/2) + .contains(item->pos())) { + int row = (imageIdx-newTopLeft) / GRID_COLUMN_COUNT; + int col = (imageIdx-newTopLeft) % GRID_COLUMN_COUNT; + QItemAnimation *anim = new QItemAnimation(item, QItemAnimation::Position, scene()); + anim->setEndValue(gridItemPosition(row, col)); + anim->start(QAbstractAnimation::DeleteWhenStopped); + ++it; + } else { + ++it; /* ### ideally we should remove the items, but this cause the photobrowser to crash + because the QItemAnimations are not notified the item is deleted + delete item; + it = m_items.erase(it); + */ + } + } + + m_topLeftIndex = newTopLeft; +} + + +RiverItem *River::createItem(int imageIndex) +{ + Q_ASSERT(!m_items.contains(imageIndex)); + RiverItem * item = new RiverItem(this); + item->setPixmap(pixmap(imageIndex)); + m_items.insert(imageIndex,item); + return item; +} + +void River::setCurrentItem(int newCurrentItem) +{ + if (m_mode == CoverMode) + { + m_selectedItem = newCurrentItem; + for (int i = 0; i < m_gridItem.count(); i++) { + QVariantAnimation *anim; + RiverItem *item = m_gridItem.at(i); + + qreal rotation = 0; + qreal width = boundingRect().width() / 2; + qreal x = width; + qreal scale = 3; + qreal z = 1; + qreal y = boundingRect().height()/2; + + if (i < newCurrentItem - 4) { + item->setVisible(false); + item->setPos(QPointF(0, y)); + continue; + } else if(i > newCurrentItem + 4) { + item->setVisible(false); + item->setPos(QPointF(boundingRect().width(), y)); + continue; + } else if (i < newCurrentItem) { + x = (i - newCurrentItem + 4) * width/7; + rotation = -75; + scale = 2; + z = 1.+qreal(i-newCurrentItem)/10.; + } else if (i > newCurrentItem) { + x = width + (i - newCurrentItem + 3) * width/7; + rotation = 75; + scale = 2; + z = 1.-qreal(i-newCurrentItem)/8.; + } + + item->setVisible(true); + item->setZValue(z); + + anim = new QItemAnimation(item, QItemAnimation::RotationY, scene()); + anim->setEndValue(rotation); + anim->start(QAbstractAnimation::DeleteWhenStopped); + + anim = new QItemAnimation(item, QItemAnimation::ScaleFactorX, scene()); + anim->setEndValue(scale); + anim->start(QVariantAnimation::DeleteWhenStopped); + + anim = new QItemAnimation(item, QItemAnimation::ScaleFactorY, scene()); + anim->setEndValue(scale); + anim->start(QVariantAnimation::DeleteWhenStopped); + + anim = new QItemAnimation(item, QItemAnimation::Position, scene()); + anim->setEndValue(QPointF(x,y)); + anim->start(QVariantAnimation::DeleteWhenStopped); + } + return; + } + + + //deselect the current item + if (m_selectedItem >= 0 && m_items.contains(m_selectedItem)) { + RiverItem *item = m_items.value(m_selectedItem); + item->setZValue(qreal(qrand()%100)/200.0); + QItemAnimation *anim = new QItemAnimation(item, QItemAnimation::ScaleFactorX, scene()); + anim->setEndValue(GRID_MAGNIFY); + anim->start(QAbstractAnimation::DeleteWhenStopped); + anim = new QItemAnimation(item, QItemAnimation::ScaleFactorY, scene()); + anim->setEndValue(GRID_MAGNIFY); + anim->start(QAbstractAnimation::DeleteWhenStopped); + } + + if (newCurrentItem < 0) { + m_selectedItem = newCurrentItem; + return; + } + + //ensure visible; + if (newCurrentItem < m_topLeftIndex) { + adjustGrid(newCurrentItem); + } else if (newCurrentItem >= m_topLeftIndex + GRID_DIMENSIONS) { + adjustGrid(newCurrentItem - GRID_DIMENSIONS + GRID_COLUMN_COUNT); + } + + //select the new one + m_selectedItem = newCurrentItem; + RiverItem *item = currentItem(); + Q_ASSERT(item); + item->setZValue(1); + + QItemAnimation *anim = new QItemAnimation(item, QItemAnimation::ScaleFactorX, scene()); + anim->setEndValue(GRID_CURRENT_MAGNIFY); + anim->start(QAbstractAnimation::DeleteWhenStopped); + anim = new QItemAnimation(item, QItemAnimation::ScaleFactorY, scene()); + anim->setEndValue(GRID_CURRENT_MAGNIFY); + anim->start(QAbstractAnimation::DeleteWhenStopped); +} + +void River::navigateBy(int offset) +{ + int newSelection = m_selectedItem + offset; + const int imageCount = m_images.size(); + while (newSelection < 0) + newSelection += imageCount; + newSelection %= imageCount; + setCurrentItem(newSelection); +} diff --git a/examples/animation/photobrowser/river.h b/examples/animation/photobrowser/river.h new file mode 100644 index 0000000..25bf62a --- /dev/null +++ b/examples/animation/photobrowser/river.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef __RIVER__H__ +#define __RIVER__H__ + +#include <QtCore/QDirIterator> +#include <QtGui/QGraphicsWidget> + +#include "menu.h" + +class RiverItem; + +class River : public QGraphicsWidget +{ + Q_OBJECT +public: + enum Mode + { + RiverMode, + GridMode, + GridFullScreenMode, + CoverMode + }; + + River(const QString &path); + void addUnusedRiverItem(RiverItem * item); + + static QSize fixedSize(); + + void switchPaused(); + + void setMode(Mode m); + Mode mode() const; + + Menu *menu; + +protected: + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + void keyPressEvent ( QKeyEvent * keyEvent ); + void makeCenterAvailable(qreal size); + QPointF gridItemPosition(int row, int col) const; + QPixmap pixmap(int index) const; + +protected slots: + void animationFinished(); +public slots: + void setRiverMode(); + void setGridMode(); + void setCoverMode(); + +private: + const QFileInfoList m_images; + int m_currentImage; + mutable QVector<QPixmap> m_pixmaps; + QHash<int, RiverItem*> m_items; + QList<RiverItem*> m_gridItem; + QList<QItemAnimation*> m_pausedAnimations; + Mode m_mode; + + void adjustGrid(int topRight); + RiverItem *currentItem() { return m_items.value(m_selectedItem); } + RiverItem *createItem(int imageIndex); + void setCurrentItem(int currentItem); + void navigateBy(int offset); + + int m_selectedItem; + int m_topLeftIndex; + +}; + + +#endif //__RIVERITEM__H__ diff --git a/examples/animation/photobrowser/riveritem.cpp b/examples/animation/photobrowser/riveritem.cpp new file mode 100644 index 0000000..f6523a7 --- /dev/null +++ b/examples/animation/photobrowser/riveritem.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "riveritem.h" +#include "river.h" + +RiverItem::RiverItem(QGraphicsItem *parent) : QGraphicsPixmapItem(parent), m_fullscreen(false) +{ + setCacheMode(DeviceCoordinateCache); +} + +RiverItem::~RiverItem() +{ +} + +void RiverItem::setPixmap(const QPixmap &pix) +{ + const QSize oldSize = pixmap().size(); + const QSize newSize = pix.size(); + QGraphicsPixmapItem::setPixmap(pix); + + if (newSize != oldSize) { + setOffset(-newSize.width()/2, -newSize.height()/2); + const qreal scaleFactor = qreal(River::fixedSize().height())/(qreal(pix.height()*7)); + setTransform(QTransform().scale(scaleFactor, scaleFactor)); + prepareGeometryChange(); + } +} + + +void RiverItem::setFullScreen(bool b, qreal originScaleFactor) +{ + if (m_fullscreen == b) + return; + + m_fullscreen = b; + + QPointF newPos; + qreal rotationZ; + qreal scaleX, scaleY; + + if (b) { + const QSizeF basePixmapSize = transform().map(boundingRect()).boundingRect().size(); + + newPos = parentItem()->boundingRect().center(); + rotationZ = 90; + scaleY = qreal(River::fixedSize().width()) / basePixmapSize.height() * yScale(); + scaleX = qreal(River::fixedSize().height()) / basePixmapSize.width() * xScale(); + + if (m_nonFSPos.isNull()) { + m_nonFSPos = pos(); //let's save our current (non fullscreen) position + } + + } else { + Q_ASSERT(!m_nonFSPos.isNull()); + rotationZ = 0; + scaleX = originScaleFactor; + scaleY = originScaleFactor; + newPos = m_nonFSPos; + } + + QAnimationGroup *group = new QParallelAnimationGroup(scene()); + QItemAnimation *anim = new QItemAnimation(this, QItemAnimation::Position); + anim->setEndValue(newPos); + group->addAnimation(anim); + anim = new QItemAnimation(this, QItemAnimation::RotationZ); + anim->setEndValue(rotationZ); + group->addAnimation(anim); + anim = new QItemAnimation(this, QItemAnimation::ScaleFactorX); + anim->setEndValue(scaleX); + group->addAnimation(anim); + anim = new QItemAnimation(this, QItemAnimation::ScaleFactorY); + anim->setEndValue(scaleY); + group->addAnimation(anim); + group->start(QAbstractAnimation::DeleteWhenStopped); +} + +void RiverItem::mousePressEvent(QGraphicsSceneMouseEvent*) +{ + //just let it rotate on itself + QItemAnimation *anim = new QItemAnimation(this, QItemAnimation::RotationY); + anim->setEndValue(yRotation() < 180 ? 360 : 0); + anim->setDuration(500); + anim->start(QAbstractAnimation::DeleteWhenStopped); +} + + diff --git a/examples/animation/photobrowser/riveritem.h b/examples/animation/photobrowser/riveritem.h new file mode 100644 index 0000000..39fdcc2 --- /dev/null +++ b/examples/animation/photobrowser/riveritem.h @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef __RIVERITEM__H__ +#define __RIVERITEM__H__ + +#include <QtGui/QGraphicsPixmapItem> + +class RiverItemAnimator; + +class RiverItem : public QGraphicsPixmapItem +{ +public: + RiverItem(QGraphicsItem *parent); + ~RiverItem(); + + void setPixmap(const QPixmap &); + void setFullScreen(bool b, qreal originScaleFactor); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent*); + +private: + QPointF m_nonFSPos; //to save the position when not in fullscreen + bool m_fullscreen; +}; + +#endif //__RIVERITEM__H__ diff --git a/examples/animation/piemenu/main.cpp b/examples/animation/piemenu/main.cpp new file mode 100644 index 0000000..28f62d0 --- /dev/null +++ b/examples/animation/piemenu/main.cpp @@ -0,0 +1,26 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#include "scene.h" +#include "qgraphicspiemenu.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + Scene scene; + + QGraphicsView view(&scene); + view.show(); + + return app.exec(); +} diff --git a/examples/animation/piemenu/piemenu.pro b/examples/animation/piemenu/piemenu.pro new file mode 100644 index 0000000..4d7a067 --- /dev/null +++ b/examples/animation/piemenu/piemenu.pro @@ -0,0 +1,8 @@ +SOURCES += \ + main.cpp \ + qgraphicspiemenu.cpp \ + scene.cpp +HEADERS += \ + qgraphicspiemenu.h \ + qgraphicspiemenu_p.h \ + scene.h diff --git a/examples/animation/piemenu/qgraphicspiemenu.cpp b/examples/animation/piemenu/qgraphicspiemenu.cpp new file mode 100644 index 0000000..c913423 --- /dev/null +++ b/examples/animation/piemenu/qgraphicspiemenu.cpp @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "qgraphicspiemenu.h" +#include "qgraphicspiemenu_p.h" + +#include <QtGui/qaction.h> + +QGraphicsPieMenu::QGraphicsPieMenu(QGraphicsItem *parent) + : QGraphicsWidget(parent), d_ptr(new QGraphicsPieMenuPrivate) +{ + d_ptr->q_ptr = this; + d_ptr->machine = new QStateMachine(); + d_ptr->popupState = new QState(d_ptr->machine->rootState()); + d_ptr->machine->setInitialState(d_ptr->popupState); + d_ptr->menuAction = new QAction(this); +} + +QGraphicsPieMenu::QGraphicsPieMenu(const QString &title, QGraphicsItem *parent) + : QGraphicsWidget(parent), d_ptr(new QGraphicsPieMenuPrivate) +{ + d_ptr->q_ptr = this; + d_ptr->machine = new QStateMachine(); + d_ptr->popupState = new QState(d_ptr->machine->rootState()); + d_ptr->machine->setInitialState(d_ptr->popupState); + d_ptr->menuAction = new QAction(this); + setTitle(title); +} + +QGraphicsPieMenu::QGraphicsPieMenu(const QIcon &icon, const QString &title, QGraphicsItem *parent) + : QGraphicsWidget(parent), d_ptr(new QGraphicsPieMenuPrivate) +{ + d_ptr->q_ptr = this; + d_ptr->machine = new QStateMachine(); + d_ptr->popupState = new QState(d_ptr->machine->rootState()); + d_ptr->machine->setInitialState(d_ptr->popupState); + d_ptr->menuAction = new QAction(this); + setIcon(icon); + setTitle(title); +} + +QGraphicsPieMenu::~QGraphicsPieMenu() +{ + delete d_ptr; +} + +QAction *QGraphicsPieMenu::addAction(const QString &text) +{ + QAction *action = new QAction(text, this); + addAction(action); + return action; +} + +QAction *QGraphicsPieMenu::addAction(const QIcon &icon, const QString &text) +{ + QAction *action = new QAction(icon, text, this); + addAction(action); + return action; +} + +QAction *QGraphicsPieMenu::addAction(const QString &text, const QObject *receiver, const char *member, const QKeySequence &shortcut) +{ + QAction *action = new QAction(text, this); + action->setShortcut(shortcut); + connect(action, SIGNAL(triggered(bool)), receiver, member); + addAction(action); + return action; +} + +QAction *QGraphicsPieMenu::addAction(const QIcon &icon, const QString &text, const QObject *receiver, const char *member, const QKeySequence &shortcut) +{ + QAction *action = new QAction(icon, text, this); + action->setShortcut(shortcut); + connect(action, SIGNAL(triggered(bool)), receiver, member); + addAction(action); + return action; +} + +QAction *QGraphicsPieMenu::addMenu(QGraphicsPieMenu *menu) +{ + QAction *action = menu->menuAction(); + addAction(action); + return action; +} + +QGraphicsPieMenu *QGraphicsPieMenu::addMenu(const QString &title) +{ + QGraphicsPieMenu *menu = new QGraphicsPieMenu(title, this); + addMenu(menu); + return menu; +} + +QGraphicsPieMenu *QGraphicsPieMenu::addMenu(const QIcon &icon, const QString &title) +{ + QGraphicsPieMenu *menu = new QGraphicsPieMenu(icon, title, this); + addMenu(menu); + return menu; +} + +QAction *QGraphicsPieMenu::addSeparator() +{ + QAction *action = new QAction(this); + action->setSeparator(true); + addAction(action); + return action; +} + +QAction *QGraphicsPieMenu::insertMenu(QAction *before, QGraphicsPieMenu *menu) +{ + QAction *action = menu->menuAction(); + insertAction(before, action); + return action; +} + +QAction *QGraphicsPieMenu::insertSeparator(QAction *before) +{ + QAction *action = new QAction(this); + action->setSeparator(true); + insertAction(before, action); + return action; +} + +QAction *QGraphicsPieMenu::menuAction() const +{ + return d_func()->menuAction; +} + +bool QGraphicsPieMenu::isEmpty() const +{ + // ### d->actions + QList<QAction *> actionList = actions(); + bool ret = true; + for (int i = 0; ret && i < actionList.size(); ++i) { + const QAction *action = actionList.at(i); + if (!action->isSeparator() && action->isVisible()) { + ret = false; + break; + } + } + return ret; +} + +void QGraphicsPieMenu::clear() +{ + // ### d->actions + QList<QAction *> actionList = actions(); + for(int i = 0; i < actionList.size(); i++) { + QAction *action = actionList.at(i); + removeAction(action); + if (action->parent() == this && action->associatedGraphicsWidgets().isEmpty()) + delete action; + } +} + +void QGraphicsPieMenu::popup(const QPointF &pos) +{ + Q_UNUSED(pos); + Q_D(QGraphicsPieMenu); + d->machine->start(); +} + +QAction *QGraphicsPieMenu::exec() +{ + return exec(pos()); +} + +QAction *QGraphicsPieMenu::exec(const QPointF &pos) +{ + Q_UNUSED(pos); + return 0; +} + +QAction *QGraphicsPieMenu::exec(QList<QAction *> actions, const QPointF &pos) +{ + QGraphicsPieMenu menu; + for (QList<QAction *>::ConstIterator it = actions.constBegin(); it != actions.constEnd(); ++it) + menu.addAction(*it); + return menu.exec(pos); +} + +QString QGraphicsPieMenu::title() const +{ + Q_D(const QGraphicsPieMenu); + return d->title; +} + +void QGraphicsPieMenu::setTitle(const QString &title) +{ + Q_D(QGraphicsPieMenu); + d->title = title; + updateGeometry(); +} + +QIcon QGraphicsPieMenu::icon() const +{ + Q_D(const QGraphicsPieMenu); + return d->icon; +} + +void QGraphicsPieMenu::setIcon(const QIcon &icon) +{ + Q_D(QGraphicsPieMenu); + d->icon = icon; + updateGeometry(); +} + +QSizeF QGraphicsPieMenu::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + Q_UNUSED(which); + Q_UNUSED(constraint); + return QSizeF(1, 1); +} diff --git a/examples/animation/piemenu/qgraphicspiemenu.h b/examples/animation/piemenu/qgraphicspiemenu.h new file mode 100644 index 0000000..68b4ba9 --- /dev/null +++ b/examples/animation/piemenu/qgraphicspiemenu.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSPIEMENU_H +#define QGRAPHICSPIEMENU_H + +#include <QtGui/qgraphicswidget.h> + +class QGraphicsPieMenuPrivate; +class QGraphicsPieMenu : public QGraphicsWidget +{ + Q_OBJECT +public: + QGraphicsPieMenu(QGraphicsItem *parent = 0); + QGraphicsPieMenu(const QString &title, QGraphicsItem *parent = 0); + QGraphicsPieMenu(const QIcon &icon, const QString &title, QGraphicsItem *parent = 0); + ~QGraphicsPieMenu(); + +#ifdef Q_NO_USING_KEYWORD + inline void addAction(QAction *action) { QGraphicsWidget::addAction(action); } +#else + using QGraphicsWidget::addAction; +#endif + QAction *addAction(const QString &text); + QAction *addAction(const QIcon &icon, const QString &text); + QAction *addAction(const QString &text, const QObject *receiver, const char *member, const QKeySequence &shortcut = 0); + QAction *addAction(const QIcon &icon, const QString &text, const QObject *receiver, const char *member, const QKeySequence &shortcut = 0); + QAction *addMenu(QGraphicsPieMenu *menu); + QGraphicsPieMenu *addMenu(const QString &title); + QGraphicsPieMenu *addMenu(const QIcon &icon, const QString &title); + QAction *addSeparator(); + QAction *insertMenu(QAction *before, QGraphicsPieMenu *menu); + QAction *insertSeparator(QAction *before); + + QAction *menuAction() const; + + bool isEmpty() const; + void clear(); + + void popup(const QPointF &pos); + QAction *exec(); + QAction *exec(const QPointF &pos); + static QAction *exec(QList<QAction *> actions, const QPointF &pos); + + QString title() const; + void setTitle(const QString &title); + + QIcon icon() const; + void setIcon(const QIcon &icon); + +Q_SIGNALS: + void aboutToShow(); + void aboutToHide(); + void triggered(QAction *action); + void hovered(QAction *action); + +protected: + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; + +private: + Q_DISABLE_COPY(QGraphicsPieMenu) + Q_DECLARE_PRIVATE(QGraphicsPieMenu) + QGraphicsPieMenuPrivate *d_ptr; +}; + +#endif diff --git a/examples/animation/piemenu/qgraphicspiemenu_p.h b/examples/animation/piemenu/qgraphicspiemenu_p.h new file mode 100644 index 0000000..87a749c --- /dev/null +++ b/examples/animation/piemenu/qgraphicspiemenu_p.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSPIEMENU_P_H +#define QGRAPHICSPIEMENU_P_H + +#include "qgraphicspiemenu.h" +#include "qgraphicspiemenusection_p.h" + +#include <QtGui/qicon.h> +#include <QtCore/qstate.h> +#include <QtCore/qtransition.h> +#include <QtCore/qstatemachine.h> + +class QAction; +class QEventLoop; +class QGraphicsPieMenuSection; + +class QGraphicsPieMenuPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsPieMenu); +public: + void init(const QIcon &icon = QIcon(), const QString &title = QString()); + + QIcon icon; + QString title; + QStateMachine *machine; + QState *popupState; + //QTransition *transition; + QList<QGraphicsPieMenuSection *> sections; + + QEventLoop *eventLoop; + + QAction *menuAction; + QGraphicsPieMenu *q_ptr; + + void updatePopupState(); +}; + +#endif diff --git a/examples/animation/piemenu/qgraphicspiemenusection_p.h b/examples/animation/piemenu/qgraphicspiemenusection_p.h new file mode 100644 index 0000000..328f742 --- /dev/null +++ b/examples/animation/piemenu/qgraphicspiemenusection_p.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSPIEMENUSECTION_H +#define QGRAPHICSPIEMENUSECTION_H + +#include <QtGui/qgraphicswidget.h> +#include <QtGui/qpainter.h> + +class QGraphicsPieMenuSection : public QGraphicsWidget +{ + Q_OBJECT + Q_PROPERTY(qreal rotation READ rotation WRITE setRotation) +public: + QGraphicsPieMenuSection(QGraphicsItem *parent = 0) + : QGraphicsWidget(parent), rot(0) + { } + + qreal rotation() const + { + return rot; + } + void setRotation(qreal rotation) + { + rot = rotation; + setTransform(QTransform().rotate(rot)); + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) + { + Q_UNUSED(option); + Q_UNUSED(widget); + painter->setPen(QPen(Qt::black, 1)); + painter->setBrush(QBrush(Qt::gray)); + painter->drawPie(QRectF(-100, -100, 200, 200), 0, -30 * 16); + } + +protected: + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &size = QSizeF()) const + { + Q_UNUSED(which); + Q_UNUSED(size); + return QSizeF(100, 30); + } + +private: + qreal rot; +}; + +#endif diff --git a/examples/animation/piemenu/scene.cpp b/examples/animation/piemenu/scene.cpp new file mode 100644 index 0000000..08aad06 --- /dev/null +++ b/examples/animation/piemenu/scene.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "qgraphicspiemenu.h" +#include "scene.h" + +#include <QtGui/qaction.h> +#include <QtGui/qgraphicssceneevent.h> + +Scene::Scene(qreal x, qreal y, qreal width, qreal height, QObject *parent) + : QGraphicsScene(x, y, width, height, parent) +{ +} + +Scene::Scene(const QRectF &sceneRect, QObject *parent) + : QGraphicsScene(sceneRect, parent) +{ +} + +Scene::Scene(QObject *parent) + : QGraphicsScene(parent) +{ +} + +Scene::~Scene() +{ +} + +void Scene::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + QGraphicsPieMenu *menu = new QGraphicsPieMenu; + for (int i = 0; i < 5; ++i) + menu->addAction(new QAction(QString("Item %1").arg(i), menu)); + menu->popup(event->scenePos()); +} diff --git a/examples/animation/piemenu/scene.h b/examples/animation/piemenu/scene.h new file mode 100644 index 0000000..676f55f --- /dev/null +++ b/examples/animation/piemenu/scene.h @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef SCENE_H +#define SCENE_H + +#include <QtGui/qgraphicsscene.h> + +class Scene : public QGraphicsScene +{ + Q_OBJECT +public: + Scene(qreal x, qreal y, qreal width, qreal height, QObject *parent = 0); + Scene(const QRectF &sceneRect, QObject *parent = 0); + Scene(QObject *parent = 0); + ~Scene(); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event); +}; + +#endif diff --git a/examples/animation/research/memberfunctions/main.cpp b/examples/animation/research/memberfunctions/main.cpp new file mode 100644 index 0000000..9142f29 --- /dev/null +++ b/examples/animation/research/memberfunctions/main.cpp @@ -0,0 +1,48 @@ +#include <QtGui> +#include "qvalueanimation.h" + +AbstractProperty *qGraphicsItemProperty(QGraphicsItem *item, const char *property) +{ + if (qstrcmp(property, "pos") == 0) { + return new MemberFunctionProperty<QGraphicsItem, QPointF>(item, &QGraphicsItem::pos, &QGraphicsItem::setPos); + } + return 0; +} + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + QGraphicsScene scene; + QGraphicsView view(&scene); + + QGraphicsItem *item = new QGraphicsRectItem(QRectF(0,0, 200, 100)); + scene.addItem(item); + + QValueAnimation *posAnim = new QValueAnimation; + posAnim->setStartValue(QPointF(0,0)); + posAnim->setEndValue(QPointF(400, 0)); + posAnim->setDuration(1000); + // Alternative 1 + //posAnim->setMemberFunction(item, &QGraphicsItem::pos, &QGraphicsItem::setPos); + + // Alternative 2 + //posAnim->setProperty(qMemberFunctionProperty(item, &QGraphicsItem::pos, &QGraphicsItem::setPos)); + + // Alternative 3 + posAnim->setProperty(qGraphicsItemProperty(item, "pos")); + + // Alternative 4, (by implementing the qGraphicsItemProperty QGraphicsItem::property()) + //posAnim->setProperty(item->property("pos")); + + // can also do this, which abstracts away the whole property thing. + // i.e. this interface can also be used for QObject-properties: + //posAnim->setAnimationProperty(animationProperty); + + posAnim->start(); + + view.resize(800,600); + view.show(); + return app.exec(); +} + diff --git a/examples/animation/research/memberfunctions/memberfunctions.pro b/examples/animation/research/memberfunctions/memberfunctions.pro new file mode 100644 index 0000000..6b67895 --- /dev/null +++ b/examples/animation/research/memberfunctions/memberfunctions.pro @@ -0,0 +1,16 @@ +###################################################################### +# Automatically generated by qmake (2.01a) fr 26. sep 13:21:57 2008 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += main.cpp \ + qvalueanimation.cpp +HEADERS += qvalueanimation.h + +CONFIG += console + diff --git a/examples/animation/research/memberfunctions/qvalueanimation.cpp b/examples/animation/research/memberfunctions/qvalueanimation.cpp new file mode 100644 index 0000000..2fe9be9 --- /dev/null +++ b/examples/animation/research/memberfunctions/qvalueanimation.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + + + +#include "qvalueanimation.h" +#include "qvalueanimation_p.h" + +QT_BEGIN_NAMESPACE + + +void QValueAnimationPrivate::initDefaultStartValue() +{ + Q_Q(QValueAnimation); + if (animProp && !q->startValue().isValid() + && ((currentTime == 0 && (currentIteration || currentIteration == 0)) + || (currentTime == duration && currentIteration == (iterationCount - 1)))) { + setDefaultStartValue(animProp->read()); + } +} + + +QValueAnimation::QValueAnimation(QObject *parent) : QVariantAnimation(*new QValueAnimationPrivate, parent) +{ +} + +QValueAnimation::~QValueAnimation() +{ +} + +void QValueAnimation::setProperty(AbstractProperty *animProp) +{ + Q_D(QValueAnimation); + d->animProp = animProp; +} + +/*! + \reimp + */ +void QValueAnimation::updateCurrentValue(const QVariant &value) +{ + Q_D(QValueAnimation); + if (state() == QAbstractAnimation::Stopped) + return; + + d->animProp->write(value); +} + + +/*! + \reimp +*/ +void QValueAnimation::updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState) +{ + Q_D(QValueAnimation); + // Initialize start value + if (oldState == QAbstractAnimation::Stopped && newState == QAbstractAnimation::Running) + d->initDefaultStartValue(); +} + + + +#include "moc_qvalueanimation.cpp" + +QT_END_NAMESPACE diff --git a/examples/animation/research/memberfunctions/qvalueanimation.h b/examples/animation/research/memberfunctions/qvalueanimation.h new file mode 100644 index 0000000..a4aa213 --- /dev/null +++ b/examples/animation/research/memberfunctions/qvalueanimation.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QVALUEANIMATION_H +#define QVALUEANIMATION_H + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qvariantanimation.h" +#else +# include <QtCore/qvariantanimation.h> +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGraphicsItem; +class QValueAnimationPrivate; + +QT_MODULE(Gui) + +struct AbstractProperty { + virtual void write(const QVariant &value) = 0; + virtual QVariant read() const = 0; +}; + +# define CALL_MEMBER_FN(object,ptrToMember) ((object)->*(ptrToMember)) +template <typename Target, typename T> +class MemberFunctionProperty : public AbstractProperty { +public: + typedef void (Target::*RefWrite)(const T &); + typedef T (Target::*ValRead)(void) const; + + MemberFunctionProperty(Target *target, ValRead readFunc, RefWrite writeFunc) + : m_target(target), m_readFn(readFunc), m_writeFn(writeFunc) {} + + virtual void write(const QVariant &value) { + CALL_MEMBER_FN(m_target, m_writeFn)(qVariantValue<T>(value)); + } + + virtual QVariant read() const { + if (m_readFn) + return qVariantFromValue<T>(CALL_MEMBER_FN(m_target, m_readFn)()); + return QVariant(); + } + +private: + Target *m_target; + ValRead m_readFn; + RefWrite m_writeFn; +}; + + +class QValueAnimation : public QVariantAnimation +{ + Q_OBJECT + +public: + QValueAnimation(QObject *parent = 0); + ~QValueAnimation(); + + template <typename Target, typename T> + void setMemberFunction(Target *target, + T (Target::*readFunc)(void) const, // ### useValRead typedef + void (Target::*writeFunc)(const T &) // ### use RefWrite typedef + ) { + // ### ownership of MemberFunctionProperty + AbstractProperty *animProp = new MemberFunctionProperty<Target, T>(target, readFunc, writeFunc); + setProperty(animProp); + } + + void updateCurrentValue(const QVariant &value); + void updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState); + void setProperty(AbstractProperty *animProp); + +private: + Q_DISABLE_COPY(QValueAnimation); + Q_DECLARE_PRIVATE(QValueAnimation); +}; + +#endif // QVALUEANIMATION_H diff --git a/examples/animation/research/memberfunctions/qvalueanimation_p.h b/examples/animation/research/memberfunctions/qvalueanimation_p.h new file mode 100644 index 0000000..e6e7682 --- /dev/null +++ b/examples/animation/research/memberfunctions/qvalueanimation_p.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QVALUEANIMATION_P_H +#define QVALUEANIMATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QIODevice. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qvariantanimation_p.h> //### + +QT_BEGIN_NAMESPACE + +class QValueAnimationPrivate : public QVariantAnimationPrivate +{ + Q_DECLARE_PUBLIC(QValueAnimation) +public: + QValueAnimationPrivate() : QVariantAnimationPrivate(), animProp(0) + { + } + + void initDefaultStartValue(); + + AbstractProperty *animProp; + + //###TODO +}; + +QT_END_NAMESPACE + +#endif //QVALUEANIMATION_P_H diff --git a/examples/animation/research/propertytransform/main.cpp b/examples/animation/research/propertytransform/main.cpp new file mode 100644 index 0000000..80002c9 --- /dev/null +++ b/examples/animation/research/propertytransform/main.cpp @@ -0,0 +1,47 @@ +#include <QtGui> + +#include "qpropertytransform.h" + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + QGraphicsScene scene; + QGraphicsView view(&scene); + + QGraphicsItem *item = new QGraphicsRectItem(QRectF(0,0, 200, 100)); + scene.addItem(item); + QPropertyTransform transform; + transform.setTargetItem(item); + + QAnimationGroup *group = new QAnimationGroup(QAnimationGroup::Parallel, &scene); + QPropertyAnimation *scaleAnim = new QPropertyAnimation(&transform, "scaleX"); + scaleAnim->setStartValue(1.0); + scaleAnim->setTargetValue(2.0); + scaleAnim->setDuration(10000); + group->add(scaleAnim); + + QPropertyAnimation *scaleAnim2 = new QPropertyAnimation(&transform, "scaleY"); + scaleAnim2->setStartValue(.0); + scaleAnim2->setTargetValue(2.0); + scaleAnim2->setDuration(10000); + QEasingCurve curve(QEasingCurve::InElastic); + curve.setPeriod(2); + curve.setAmplitude(2); + + //scaleAnim2->setEasingCurve(curve); + //scaleAnim2->setEasingCurve(QEasingCurve(QEasingCurve::OutElastic , 2, 2 )); + group->add(scaleAnim2); + + QPropertyAnimation *rotAnim = new QPropertyAnimation(&transform, "rotation"); + rotAnim->setStartValue(0); + rotAnim->setTargetValue(90); + rotAnim->setDuration(10000); + group->add(rotAnim); + + group->start(); + + view.resize(800,600); + view.show(); + return app.exec(); +} diff --git a/examples/animation/research/propertytransform/propertytransform.pro b/examples/animation/research/propertytransform/propertytransform.pro new file mode 100644 index 0000000..94c36b8 --- /dev/null +++ b/examples/animation/research/propertytransform/propertytransform.pro @@ -0,0 +1,14 @@ +###################################################################### +# Automatically generated by qmake (2.01a) fr 26. sep 13:21:57 2008 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += main.cpp +HEADERS += qpropertytransform.h +CONFIG += console + diff --git a/examples/animation/research/propertytransform/qpropertytransform.h b/examples/animation/research/propertytransform/qpropertytransform.h new file mode 100644 index 0000000..e052625 --- /dev/null +++ b/examples/animation/research/propertytransform/qpropertytransform.h @@ -0,0 +1,78 @@ +#include <QtCore/QObject> +#include <QtCore/QPoint> +#include <QtGui/QGraphicsItem> +#include <QtGui/QTransform> +#include <QtCore/QDebug> + +/** + * Experimental. + * Pros: + * 1. Does not add ugly/confusing API to QGraphicsItem. + * + * Cons: + * 1. apply() /m_item->setTransform() is called too many times. (FIXED NOW?) + * + * + */ +class QPropertyTransform : public QObject { + Q_OBJECT +public: + Q_PROPERTY(QPointF center READ center WRITE setCenter); + Q_PROPERTY(qreal rotation READ rotation WRITE setRotation); + Q_PROPERTY(qreal scaleX READ scaleX WRITE setScaleX); + Q_PROPERTY(qreal scaleY READ scaleY WRITE setScaleY); +public: + QPropertyTransform() : m_item(0), m_rotationZ(0), m_scaleX(1.), m_scaleY(1.) {} + + void setTargetItem(QGraphicsItem *item) { + m_item = item; + } + + void setCenter(const QPointF ¢er) { + m_center = center; + apply(); + } + + QPointF center() const { return m_center; } + + void setRotation(qreal rotation) { + m_rotationZ = rotation; + apply(); + } + + qreal rotation() const { return m_rotationZ; } + + void setScaleX(qreal scale) { + m_scaleX = scale; + apply(); + } + + qreal scaleX() const { return m_scaleX; } + + void setScaleY(qreal scale) { + m_scaleY = scale; + apply(); + } + + qreal scaleY() const { return m_scaleY; } + +private: + QTransform transform() const { + return QTransform().translate(m_center.x(), m_center.y()) + .rotate(m_rotationZ) + .scale(m_scaleX, m_scaleY) + .translate(-m_center.x(), -m_center.y()); + } + + void apply() { + if (m_item) + m_item->setTransform(transform()); + } + + QGraphicsItem *m_item; + QPointF m_center; + qreal m_rotationZ; + qreal m_scaleX; + qreal m_scaleY; +}; + diff --git a/examples/animation/research/sound/main.cpp b/examples/animation/research/sound/main.cpp new file mode 100644 index 0000000..2c9fa47 --- /dev/null +++ b/examples/animation/research/sound/main.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +//The purpose of this example is to show that it is possible to have your own +// animation with undefined duration + +#include <QtGui/QtGui> +#include <phonon> + +class SoundAnimation : public QAbstractAnimation +{ +public: + SoundAnimation(const QString &file) + { + //in an idea case we should also control the errors + Phonon::createPath(&m_media, &m_audio); + m_media.setCurrentSource(file); + connect(&m_media, SIGNAL(finished()), SLOT(stop())); + } + + int duration() const + { + return -1; + } + + void updateCurrentTime(int msecs) + { + //nothing to do here... + qDebug() << "updateCurrentTime" << msecs; + } + + void updateState(State state) + { + switch(state) + { + case Running: + m_media.play(); + break; + case Stopped: + m_media.stop(); + break; + case Paused: + m_media.pause(); + break; + } + } + + +private: + Phonon::MediaObject m_media; + Phonon::AudioOutput m_audio; + +}; + + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + Q_INIT_RESOURCE(sound); + + SoundAnimation anim(QLatin1String(":/media/sax.mp3")); + app.connect(&anim, SIGNAL(finished()), SLOT(quit())); + anim.start(); + + return app.exec(); +}
\ No newline at end of file diff --git a/examples/animation/research/sound/media/sax.mp3 b/examples/animation/research/sound/media/sax.mp3 Binary files differnew file mode 100644 index 0000000..0a078b1 --- /dev/null +++ b/examples/animation/research/sound/media/sax.mp3 diff --git a/examples/animation/research/sound/sound.pro b/examples/animation/research/sound/sound.pro new file mode 100644 index 0000000..0ad3050 --- /dev/null +++ b/examples/animation/research/sound/sound.pro @@ -0,0 +1,14 @@ +###################################################################### +# Automatically generated by qmake (2.01a) mer. 21. janv. 13:53:26 2009 +###################################################################### + +TEMPLATE = app +QT += phonon +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +RESOURCES = sound.qrc + +# Input +SOURCES += main.cpp diff --git a/examples/animation/research/sound/sound.qrc b/examples/animation/research/sound/sound.qrc new file mode 100644 index 0000000..8919142 --- /dev/null +++ b/examples/animation/research/sound/sound.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> +<file>media/sax.mp3</file> +</qresource> +</RCC> diff --git a/examples/animation/states/accessories-dictionary.png b/examples/animation/states/accessories-dictionary.png Binary files differnew file mode 100644 index 0000000..e9bd55d --- /dev/null +++ b/examples/animation/states/accessories-dictionary.png diff --git a/examples/animation/states/akregator.png b/examples/animation/states/akregator.png Binary files differnew file mode 100644 index 0000000..a086f45 --- /dev/null +++ b/examples/animation/states/akregator.png diff --git a/examples/animation/states/digikam.png b/examples/animation/states/digikam.png Binary files differnew file mode 100644 index 0000000..9de9fb2 --- /dev/null +++ b/examples/animation/states/digikam.png diff --git a/examples/animation/states/help-browser.png b/examples/animation/states/help-browser.png Binary files differnew file mode 100644 index 0000000..db92faa --- /dev/null +++ b/examples/animation/states/help-browser.png diff --git a/examples/animation/states/k3b.png b/examples/animation/states/k3b.png Binary files differnew file mode 100644 index 0000000..bbcafcf --- /dev/null +++ b/examples/animation/states/k3b.png diff --git a/examples/animation/states/kchart.png b/examples/animation/states/kchart.png Binary files differnew file mode 100644 index 0000000..1dd115b --- /dev/null +++ b/examples/animation/states/kchart.png diff --git a/examples/animation/states/main.cpp b/examples/animation/states/main.cpp new file mode 100644 index 0000000..da50519 --- /dev/null +++ b/examples/animation/states/main.cpp @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qstate.h" +# include "qstatemachine.h" +# include "qtransition.h" +# include "qparallelanimationgroup.h" +# include "qsequentialanimationgroup.h" +# include "qpropertyanimation.h" +#endif + +class Pixmap : public QGraphicsWidget +{ + Q_OBJECT +public: + Pixmap(const QPixmap &pix) : QGraphicsWidget(), p(pix) + { + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + painter->drawPixmap(QPointF(), p); + } + +protected: + QSizeF sizeHint(Qt::SizeHint, const QSizeF & = QSizeF()) + { + return QSizeF(p.width(), p.height()); + } + +private: + QPixmap p; +}; + +int main(int argc, char *argv[]) +{ + Q_INIT_RESOURCE(states); + + QApplication app(argc, argv); + + // Text edit and button + QTextEdit *edit = new QTextEdit; + edit->setText("asdf lkjha yuoiqwe asd iuaysd u iasyd uiy " + "asdf lkjha yuoiqwe asd iuaysd u iasyd uiy " + "asdf lkjha yuoiqwe asd iuaysd u iasyd uiy " + "asdf lkjha yuoiqwe asd iuaysd u iasyd uiy!"); + + QPushButton *button = new QPushButton; + QGraphicsProxyWidget *buttonProxy = new QGraphicsProxyWidget; + buttonProxy->setWidget(button); + QGraphicsProxyWidget *editProxy = new QGraphicsProxyWidget; + editProxy->setWidget(edit); + + QGroupBox *box = new QGroupBox; + box->setFlat(true); + box->setTitle("Options"); + + QVBoxLayout *layout2 = new QVBoxLayout; + box->setLayout(layout2); + layout2->addWidget(new QRadioButton("Herring")); + layout2->addWidget(new QRadioButton("Blue Parrot")); + layout2->addWidget(new QRadioButton("Petunias")); + layout2->addStretch(); + + QGraphicsProxyWidget *boxProxy = new QGraphicsProxyWidget; + boxProxy->setWidget(box); + + // Parent widget + QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(Qt::Vertical, widget); + layout->addItem(editProxy); + layout->addItem(buttonProxy); + widget->setLayout(layout); + + Pixmap *p1 = new Pixmap(QPixmap(":/digikam.png")); + Pixmap *p2 = new Pixmap(QPixmap(":/akregator.png")); + Pixmap *p3 = new Pixmap(QPixmap(":/accessories-dictionary.png")); + Pixmap *p4 = new Pixmap(QPixmap(":/k3b.png")); + Pixmap *p5 = new Pixmap(QPixmap(":/help-browser.png")); + Pixmap *p6 = new Pixmap(QPixmap(":/kchart.png")); + + QGraphicsScene scene(0, 0, 400, 300); + scene.setBackgroundBrush(scene.palette().window()); + scene.addItem(widget); + scene.addItem(boxProxy); + scene.addItem(p1); + scene.addItem(p2); + scene.addItem(p3); + scene.addItem(p4); + scene.addItem(p5); + scene.addItem(p6); + + QStateMachine machine; + QState *root = machine.rootState(); + QState *state1 = new QState(root); + QState *state2 = new QState(root); + QState *state3 = new QState(root); + machine.setInitialState(state1); + + // State 1 + state1->assignProperty(button, "text", "Switch to state 2"); + state1->assignProperty(widget, "geometry", QRectF(0, 0, 400, 150)); + state1->assignProperty(box, "geometry", QRect(-200, 150, 200, 150)); + state1->assignProperty(p1, "geometry", QRectF(68, 185, 64, 64)); + state1->assignProperty(p2, "geometry", QRectF(168, 185, 64, 64)); + state1->assignProperty(p3, "geometry", QRectF(268, 185, 64, 64)); + state1->assignProperty(p4, "geometry", QRectF(68-150, 48-150, 64, 64)); + state1->assignProperty(p5, "geometry", QRectF(168, 48-150, 64, 64)); + state1->assignProperty(p6, "geometry", QRectF(268+150, 48-150, 64, 64)); + state1->assignProperty(p1, "zRotation", qreal(0)); + state1->assignProperty(p2, "zRotation", qreal(0)); + state1->assignProperty(p3, "zRotation", qreal(0)); + state1->assignProperty(p4, "zRotation", qreal(-270)); + state1->assignProperty(p5, "zRotation", qreal(-90)); + state1->assignProperty(p6, "zRotation", qreal(270)); + state1->assignProperty(boxProxy, "opacity", qreal(0)); + state1->assignProperty(p1, "opacity", qreal(1)); + state1->assignProperty(p2, "opacity", qreal(1)); + state1->assignProperty(p3, "opacity", qreal(1)); + state1->assignProperty(p4, "opacity", qreal(0)); + state1->assignProperty(p5, "opacity", qreal(0)); + state1->assignProperty(p6, "opacity", qreal(0)); + + // State 2 + state2->assignProperty(button, "text", "Switch to state 3"); + state2->assignProperty(widget, "geometry", QRectF(200, 150, 200, 150)); + state2->assignProperty(box, "geometry", QRect(9, 150, 190, 150)); + state2->assignProperty(p1, "geometry", QRectF(68-150, 185+150, 64, 64)); + state2->assignProperty(p2, "geometry", QRectF(168, 185+150, 64, 64)); + state2->assignProperty(p3, "geometry", QRectF(268+150, 185+150, 64, 64)); + state2->assignProperty(p4, "geometry", QRectF(64, 48, 64, 64)); + state2->assignProperty(p5, "geometry", QRectF(168, 48, 64, 64)); + state2->assignProperty(p6, "geometry", QRectF(268, 48, 64, 64)); + state2->assignProperty(p1, "zRotation", qreal(-270)); + state2->assignProperty(p2, "zRotation", qreal(90)); + state2->assignProperty(p3, "zRotation", qreal(270)); + state2->assignProperty(p4, "zRotation", qreal(0)); + state2->assignProperty(p5, "zRotation", qreal(0)); + state2->assignProperty(p6, "zRotation", qreal(0)); + state2->assignProperty(boxProxy, "opacity", qreal(1)); + state2->assignProperty(p1, "opacity", qreal(0)); + state2->assignProperty(p2, "opacity", qreal(0)); + state2->assignProperty(p3, "opacity", qreal(0)); + state2->assignProperty(p4, "opacity", qreal(1)); + state2->assignProperty(p5, "opacity", qreal(1)); + state2->assignProperty(p6, "opacity", qreal(1)); + + // State 3 + state3->assignProperty(button, "text", "Switch to state 1"); + state3->assignProperty(p1, "geometry", QRectF(5, 5, 64, 64)); + state3->assignProperty(p2, "geometry", QRectF(5, 5 + 64 + 5, 64, 64)); + state3->assignProperty(p3, "geometry", QRectF(5, 5 + (64 + 5) + 64, 64, 64)); + state3->assignProperty(p4, "geometry", QRectF(5 + 64 + 5, 5, 64, 64)); + state3->assignProperty(p5, "geometry", QRectF(5 + 64 + 5, 5 + 64 + 5, 64, 64)); + state3->assignProperty(p6, "geometry", QRectF(5 + 64 + 5, 5 + (64 + 5) + 64, 64, 64)); + state3->assignProperty(widget, "geometry", QRectF(138, 5, 400 - 138, 200)); + state3->assignProperty(box, "geometry", QRect(5, 205, 400, 90)); + state3->assignProperty(p1, "opacity", qreal(1)); + state3->assignProperty(p2, "opacity", qreal(1)); + state3->assignProperty(p3, "opacity", qreal(1)); + state3->assignProperty(p4, "opacity", qreal(1)); + state3->assignProperty(p5, "opacity", qreal(1)); + state3->assignProperty(p6, "opacity", qreal(1)); + + QParallelAnimationGroup animation1; + + QSequentialAnimationGroup *animation1SubGroup; + animation1SubGroup = new QSequentialAnimationGroup(&animation1); + animation1SubGroup->addPause(250); + animation1SubGroup->addAnimation(new QPropertyAnimation(box, "geometry")); + + animation1.addAnimation(new QPropertyAnimation(widget, "geometry")); + animation1.addAnimation(new QPropertyAnimation(p1, "geometry")); + animation1.addAnimation(new QPropertyAnimation(p2, "geometry")); + animation1.addAnimation(new QPropertyAnimation(p3, "geometry")); + animation1.addAnimation(new QPropertyAnimation(p4, "geometry")); + animation1.addAnimation(new QPropertyAnimation(p5, "geometry")); + animation1.addAnimation(new QPropertyAnimation(p6, "geometry")); + animation1.addAnimation(new QPropertyAnimation(p1, "zRotation")); + animation1.addAnimation(new QPropertyAnimation(p2, "zRotation")); + animation1.addAnimation(new QPropertyAnimation(p3, "zRotation")); + animation1.addAnimation(new QPropertyAnimation(p4, "zRotation")); + animation1.addAnimation(new QPropertyAnimation(p5, "zRotation")); + animation1.addAnimation(new QPropertyAnimation(p6, "zRotation")); + animation1.addAnimation(new QPropertyAnimation(p1, "opacity")); + animation1.addAnimation(new QPropertyAnimation(p2, "opacity")); + animation1.addAnimation(new QPropertyAnimation(p3, "opacity")); + animation1.addAnimation(new QPropertyAnimation(p4, "opacity")); + animation1.addAnimation(new QPropertyAnimation(p5, "opacity")); + animation1.addAnimation(new QPropertyAnimation(p6, "opacity")); + + QParallelAnimationGroup animation2; + animation2.addAnimation(new QPropertyAnimation(box, "geometry")); + animation2.addAnimation(new QPropertyAnimation(widget, "geometry")); + animation2.addAnimation(new QPropertyAnimation(p1, "geometry")); + animation2.addAnimation(new QPropertyAnimation(p2, "geometry")); + animation2.addAnimation(new QPropertyAnimation(p3, "geometry")); + animation2.addAnimation(new QPropertyAnimation(p4, "geometry")); + animation2.addAnimation(new QPropertyAnimation(p5, "geometry")); + animation2.addAnimation(new QPropertyAnimation(p6, "geometry")); + animation2.addAnimation(new QPropertyAnimation(p1, "zRotation")); + animation2.addAnimation(new QPropertyAnimation(p2, "zRotation")); + animation2.addAnimation(new QPropertyAnimation(p3, "zRotation")); + animation2.addAnimation(new QPropertyAnimation(p4, "zRotation")); + animation2.addAnimation(new QPropertyAnimation(p5, "zRotation")); + animation2.addAnimation(new QPropertyAnimation(p6, "zRotation")); + animation2.addAnimation(new QPropertyAnimation(p1, "opacity")); + animation2.addAnimation(new QPropertyAnimation(p2, "opacity")); + animation2.addAnimation(new QPropertyAnimation(p3, "opacity")); + animation2.addAnimation(new QPropertyAnimation(p4, "opacity")); + animation2.addAnimation(new QPropertyAnimation(p5, "opacity")); + animation2.addAnimation(new QPropertyAnimation(p6, "opacity")); + + QParallelAnimationGroup animation3; + animation3.addAnimation(new QPropertyAnimation(box, "geometry")); + animation3.addAnimation(new QPropertyAnimation(widget, "geometry")); + animation3.addAnimation(new QPropertyAnimation(p1, "geometry")); + animation3.addAnimation(new QPropertyAnimation(p2, "geometry")); + animation3.addAnimation(new QPropertyAnimation(p3, "geometry")); + animation3.addAnimation(new QPropertyAnimation(p4, "geometry")); + animation3.addAnimation(new QPropertyAnimation(p5, "geometry")); + animation3.addAnimation(new QPropertyAnimation(p6, "geometry")); + animation3.addAnimation(new QPropertyAnimation(p1, "zRotation")); + animation3.addAnimation(new QPropertyAnimation(p2, "zRotation")); + animation3.addAnimation(new QPropertyAnimation(p3, "zRotation")); + animation3.addAnimation(new QPropertyAnimation(p4, "zRotation")); + animation3.addAnimation(new QPropertyAnimation(p5, "zRotation")); + animation3.addAnimation(new QPropertyAnimation(p6, "zRotation")); + animation3.addAnimation(new QPropertyAnimation(p1, "opacity")); + animation3.addAnimation(new QPropertyAnimation(p2, "opacity")); + animation3.addAnimation(new QPropertyAnimation(p3, "opacity")); + animation3.addAnimation(new QPropertyAnimation(p4, "opacity")); + animation3.addAnimation(new QPropertyAnimation(p5, "opacity")); + animation3.addAnimation(new QPropertyAnimation(p6, "opacity")); + + QAbstractTransition *t1 = state1->addTransition(button, SIGNAL(clicked()), state2); + t1->addAnimation(&animation1); + QAbstractTransition *t2 = state2->addTransition(button, SIGNAL(clicked()), state3); + t2->addAnimation(&animation2); + QAbstractTransition *t3 = state3->addTransition(button, SIGNAL(clicked()), state1); + t3->addAnimation(&animation3); + + machine.start(); + + QGraphicsView view(&scene); + view.show(); + + return app.exec(); +} + +#include "main.moc" diff --git a/examples/animation/states/states.pro b/examples/animation/states/states.pro new file mode 100644 index 0000000..f4d1e0b --- /dev/null +++ b/examples/animation/states/states.pro @@ -0,0 +1,8 @@ +SOURCES += main.cpp +RESOURCES += states.qrc + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/animation/states +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS states.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/animation/states +INSTALLS += target sources diff --git a/examples/animation/states/states.qrc b/examples/animation/states/states.qrc new file mode 100644 index 0000000..60ab3f7 --- /dev/null +++ b/examples/animation/states/states.qrc @@ -0,0 +1,10 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>accessories-dictionary.png</file> + <file>akregator.png</file> + <file>digikam.png</file> + <file>help-browser.png</file> + <file>k3b.png</file> + <file>kchart.png</file> +</qresource> +</RCC> diff --git a/examples/animation/stickman/animation.cpp b/examples/animation/stickman/animation.cpp new file mode 100644 index 0000000..998632a --- /dev/null +++ b/examples/animation/stickman/animation.cpp @@ -0,0 +1,152 @@ +#include "animation.h" + +#include <QPointF> +#include <QIODevice> +#include <QDataStream> + +class Frame +{ +public: + Frame() { + } + + int nodeCount() const + { + return m_nodePositions.size(); + } + + void setNodeCount(int nodeCount) + { + while (nodeCount > m_nodePositions.size()) + m_nodePositions.append(QPointF()); + + while (nodeCount < m_nodePositions.size()) + m_nodePositions.removeLast(); + } + + QPointF nodePos(int idx) const + { + return m_nodePositions.at(idx); + } + + void setNodePos(int idx, const QPointF &pos) + { + m_nodePositions[idx] = pos; + } + +private: + QList<QPointF> m_nodePositions; +}; + +Animation::Animation() +{ + m_currentFrame = 0; + m_frames.append(new Frame); +} + +Animation::~Animation() +{ + qDeleteAll(m_frames); +} + +void Animation::setTotalFrames(int totalFrames) +{ + while (m_frames.size() < totalFrames) + m_frames.append(new Frame); + + while (totalFrames < m_frames.size()) + delete m_frames.takeLast(); +} + +int Animation::totalFrames() const +{ + return m_frames.size(); +} + +void Animation::setCurrentFrame(int currentFrame) +{ + m_currentFrame = qMax(qMin(currentFrame, totalFrames()-1), 0); +} + +int Animation::currentFrame() const +{ + return m_currentFrame; +} + +void Animation::setNodeCount(int nodeCount) +{ + Frame *frame = m_frames.at(m_currentFrame); + frame->setNodeCount(nodeCount); +} + +int Animation::nodeCount() const +{ + Frame *frame = m_frames.at(m_currentFrame); + return frame->nodeCount(); +} + +void Animation::setNodePos(int idx, const QPointF &pos) +{ + Frame *frame = m_frames.at(m_currentFrame); + frame->setNodePos(idx, pos); +} + +QPointF Animation::nodePos(int idx) const +{ + Frame *frame = m_frames.at(m_currentFrame); + return frame->nodePos(idx); +} + +QString Animation::name() const +{ + return m_name; +} + +void Animation::setName(const QString &name) +{ + m_name = name; +} + +void Animation::save(QIODevice *device) const +{ + QDataStream stream(device); + stream << m_name; + stream << m_frames.size(); + foreach (Frame *frame, m_frames) { + stream << frame->nodeCount(); + for (int i=0; i<frame->nodeCount(); ++i) + stream << frame->nodePos(i); + } +} + +void Animation::load(QIODevice *device) +{ + if (!m_frames.isEmpty()) + qDeleteAll(m_frames); + + m_frames.clear(); + + QDataStream stream(device); + stream >> m_name; + + int frameCount; + stream >> frameCount; + + for (int i=0; i<frameCount; ++i) { + + int nodeCount; + stream >> nodeCount; + + Frame *frame = new Frame; + frame->setNodeCount(nodeCount); + + for (int j=0; j<nodeCount; ++j) { + QPointF pos; + stream >> pos; + + frame->setNodePos(j, pos); + } + + m_frames.append(frame); + } +}
\ No newline at end of file diff --git a/examples/animation/stickman/animation.h b/examples/animation/stickman/animation.h new file mode 100644 index 0000000..22afab6 --- /dev/null +++ b/examples/animation/stickman/animation.h @@ -0,0 +1,40 @@ +#ifndef ANIMATION_H +#define ANIMATION_H + +#include <QPointF> +#include <QList> +#include <QString> + +class Frame; +class QIODevice; +class Animation +{ +public: + Animation(); + ~Animation(); + + void setTotalFrames(int totalFrames); + int totalFrames() const; + + void setCurrentFrame(int currentFrame); + int currentFrame() const; + + void setNodeCount(int nodeCount); + int nodeCount() const; + + void setNodePos(int idx, const QPointF &pos); + QPointF nodePos(int idx) const; + + QString name() const; + void setName(const QString &name); + + void save(QIODevice *device) const; + void load(QIODevice *device); + +private: + QString m_name; + QList<Frame *> m_frames; + int m_currentFrame; +}; + +#endif diff --git a/examples/animation/stickman/animations/chilling b/examples/animation/stickman/animations/chilling Binary files differnew file mode 100644 index 0000000..a81fc7a --- /dev/null +++ b/examples/animation/stickman/animations/chilling diff --git a/examples/animation/stickman/animations/dancing b/examples/animation/stickman/animations/dancing Binary files differnew file mode 100644 index 0000000..462f66f --- /dev/null +++ b/examples/animation/stickman/animations/dancing diff --git a/examples/animation/stickman/animations/dead b/examples/animation/stickman/animations/dead Binary files differnew file mode 100644 index 0000000..9859b4b --- /dev/null +++ b/examples/animation/stickman/animations/dead diff --git a/examples/animation/stickman/animations/jumping b/examples/animation/stickman/animations/jumping Binary files differnew file mode 100644 index 0000000..12661a1 --- /dev/null +++ b/examples/animation/stickman/animations/jumping diff --git a/examples/animation/stickman/editor/animationdialog.cpp b/examples/animation/stickman/editor/animationdialog.cpp new file mode 100644 index 0000000..ca0daf0 --- /dev/null +++ b/examples/animation/stickman/editor/animationdialog.cpp @@ -0,0 +1,151 @@ +#include "animationdialog.h" +#include "stickman.h" +#include "animation.h" +#include "node.h" + +#include <QHBoxLayout> +#include <QStackedWidget> +#include <QSpinBox> +#include <QPushButton> +#include <QLabel> +#include <QLineEdit> +#include <QMessageBox> +#include <QFileDialog> + +AnimationDialog::AnimationDialog(StickMan *stickman, QWidget *parent) + : QDialog(parent), m_animation(0), m_stickman(stickman) +{ + initUi(); +} + +AnimationDialog::~AnimationDialog() +{ + delete m_animation; +} + +void AnimationDialog::initUi() +{ + setWindowTitle("Animation"); + setEnabled(false); + + // Second page + m_currentFrame = new QSpinBox(); + m_totalFrames = new QSpinBox(); + m_name = new QLineEdit(); + + connect(m_currentFrame, SIGNAL(valueChanged(int)), this, SLOT(currentFrameChanged(int))); + connect(m_totalFrames, SIGNAL(valueChanged(int)), this, SLOT(totalFramesChanged(int))); + connect(m_name, SIGNAL(textChanged(QString)), this, SLOT(setCurrentAnimationName(QString))); + + QGridLayout *gridLayout = new QGridLayout(this); + gridLayout->addWidget(new QLabel("Name:"), 0, 0, 1, 2); + gridLayout->addWidget(m_name, 0, 2, 1, 2); + gridLayout->addWidget(new QLabel("Frame:"), 1, 0); + gridLayout->addWidget(m_currentFrame, 1, 1); + gridLayout->addWidget(new QLabel("of total # of frames: "), 1, 2); + gridLayout->addWidget(m_totalFrames, 1, 3); +} + +void AnimationDialog::initFromAnimation() +{ + m_currentFrame->setRange(0, m_animation->totalFrames()-1); + m_currentFrame->setValue(m_animation->currentFrame()); + + m_totalFrames->setRange(1, 1000); + m_totalFrames->setValue(m_animation->totalFrames()); + + m_name->setText(m_animation->name()); +} + +void AnimationDialog::saveAnimation() +{ + saveCurrentFrame(); + + QString fileName = QFileDialog::getSaveFileName(this, "Save animation"); + + QFile file(fileName); + if (file.open(QIODevice::WriteOnly)) + m_animation->save(&file); +} + +void AnimationDialog::loadAnimation() +{ + if (maybeSave() != QMessageBox::Cancel) { + QString fileName = QFileDialog::getOpenFileName(this, "Open animation"); + + QFile file(fileName); + if (file.open(QIODevice::ReadOnly)) { + if (m_animation == 0) + newAnimation(); + + m_animation->load(&file); + stickManFromCurrentFrame(); + initFromAnimation(); + } + } +} + +QMessageBox::StandardButton AnimationDialog::maybeSave() +{ + if (m_animation == 0) + return QMessageBox::No; + + QMessageBox::StandardButton button = QMessageBox::question(this, "Save?", "Do you want to save your changes?", + QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + if (button == QMessageBox::Save) + saveAnimation(); + + return button; +} + +void AnimationDialog::newAnimation() +{ + if (maybeSave() != QMessageBox::Cancel) { + setEnabled(true); + delete m_animation; + m_animation = new Animation(); + initFromAnimation(); + } +} + +// Gets the data from the stickman and stores it in current frame +void AnimationDialog::saveCurrentFrame() +{ + int count = m_stickman->nodeCount(); + m_animation->setNodeCount(count); + for (int i=0; i<count; ++i) { + QGraphicsItem *node = m_stickman->node(i); + m_animation->setNodePos(i, node->pos()); + } +} + +// Gets the data from the current frame and sets the stickman +void AnimationDialog::stickManFromCurrentFrame() +{ + int count = m_animation->nodeCount(); + for (int i=0;i<count;++i) { + QGraphicsItem *node = m_stickman->node(i); + node->setPos(m_animation->nodePos(i)); + } +} + +void AnimationDialog::currentFrameChanged(int currentFrame) +{ + saveCurrentFrame(); + qDebug("currentFrame: %d", currentFrame); + m_animation->setCurrentFrame(currentFrame); + stickManFromCurrentFrame(); + initFromAnimation(); +} + +void AnimationDialog::totalFramesChanged(int totalFrames) +{ + m_animation->setTotalFrames(totalFrames); + stickManFromCurrentFrame(); + initFromAnimation(); +} + +void AnimationDialog::setCurrentAnimationName(const QString &name) +{ + m_animation->setName(name); +}
\ No newline at end of file diff --git a/examples/animation/stickman/editor/animationdialog.h b/examples/animation/stickman/editor/animationdialog.h new file mode 100644 index 0000000..c144fd8 --- /dev/null +++ b/examples/animation/stickman/editor/animationdialog.h @@ -0,0 +1,41 @@ +#ifndef ANIMATIONDIALOG_H +#define ANIMATIONDIALOG_H + +#include <QDialog> +#include <QMessageBox> + +class QSpinBox; +class QLineEdit; +class StickMan; +class Animation; +class AnimationDialog: public QDialog +{ + Q_OBJECT +public: + AnimationDialog(StickMan *stickMan, QWidget *parent = 0); + ~AnimationDialog(); + +public slots: + void currentFrameChanged(int currentFrame); + void totalFramesChanged(int totalFrames); + void setCurrentAnimationName(const QString &name); + + void newAnimation(); + void saveAnimation(); + void loadAnimation(); + +private: + void saveCurrentFrame(); + void stickManFromCurrentFrame(); + void initFromAnimation(); + void initUi(); + QMessageBox::StandardButton maybeSave(); + + QSpinBox *m_currentFrame; + QSpinBox *m_totalFrames; + QLineEdit *m_name; + Animation *m_animation; + StickMan *m_stickman; +}; + +#endif diff --git a/examples/animation/stickman/editor/editor.pri b/examples/animation/stickman/editor/editor.pri new file mode 100644 index 0000000..7ad9edb --- /dev/null +++ b/examples/animation/stickman/editor/editor.pri @@ -0,0 +1,2 @@ +SOURCES += $$PWD/animationdialog.cpp $$PWD/mainwindow.cpp +HEADERS += $$PWD/animationdialog.h $$PWD/mainwindow.h diff --git a/examples/animation/stickman/editor/mainwindow.cpp b/examples/animation/stickman/editor/mainwindow.cpp new file mode 100644 index 0000000..158c9b7 --- /dev/null +++ b/examples/animation/stickman/editor/mainwindow.cpp @@ -0,0 +1,35 @@ +#include "mainwindow.h" +#include "animationdialog.h" +#include "stickman.h" + +#include <QMenuBar> +#include <QApplication> + +MainWindow::MainWindow(StickMan *stickMan) +{ + initActions(stickMan); +} + +MainWindow::~MainWindow() +{ +} + +void MainWindow::initActions(StickMan *stickMan) +{ + AnimationDialog *dialog = new AnimationDialog(stickMan, this); + dialog->show(); + + QMenu *fileMenu = menuBar()->addMenu("&File"); + QAction *loadAction = fileMenu->addAction("&Open"); + QAction *saveAction = fileMenu->addAction("&Save"); + QAction *exitAction = fileMenu->addAction("E&xit"); + + QMenu *animationMenu = menuBar()->addMenu("&Animation"); + QAction *newAnimationAction = animationMenu->addAction("&New animation"); + + connect(loadAction, SIGNAL(triggered()), dialog, SLOT(loadAnimation())); + connect(saveAction, SIGNAL(triggered()), dialog, SLOT(saveAnimation())); + connect(exitAction, SIGNAL(triggered()), QApplication::instance(), SLOT(quit())); + connect(newAnimationAction, SIGNAL(triggered()), dialog, SLOT(newAnimation())); + +} diff --git a/examples/animation/stickman/editor/mainwindow.h b/examples/animation/stickman/editor/mainwindow.h new file mode 100644 index 0000000..279fc07 --- /dev/null +++ b/examples/animation/stickman/editor/mainwindow.h @@ -0,0 +1,17 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> + +class StickMan; +class MainWindow: public QMainWindow +{ +public: + MainWindow(StickMan *stickMan); + ~MainWindow(); + +private: + void initActions(StickMan *stickMan); +}; + +#endif diff --git a/examples/animation/stickman/graphicsview.cpp b/examples/animation/stickman/graphicsview.cpp new file mode 100644 index 0000000..d4f0e3a --- /dev/null +++ b/examples/animation/stickman/graphicsview.cpp @@ -0,0 +1,40 @@ +#include "graphicsview.h" +#include "editor/mainwindow.h" +#include "stickman.h" + +#include <QtGui/QKeyEvent> +#include <QtGui/QGraphicsScene> +#include <QtGui/QGraphicsView> + +GraphicsView::GraphicsView(QWidget *parent) : QGraphicsView(parent), m_editor(0) {} + +void GraphicsView::keyPressEvent(QKeyEvent *e) +{ + if (e->key() == Qt::Key_Escape) + close(); + +#if 0 + if (e->key() == Qt::Key_F1) { + if (m_editor == 0) { + QGraphicsScene *scene = new QGraphicsScene; + StickMan *stickMan = new StickMan; + stickMan->setDrawSticks(true); + scene->addItem(stickMan); + + QGraphicsView *view = new QGraphicsView; + view->setBackgroundBrush(Qt::black); + view->setRenderHints(QPainter::Antialiasing); + view->setScene(scene); + + m_editor = new MainWindow(stickMan); + m_editor->setCentralWidget(view); + } + + m_editor->showMaximized(); + } +#endif + + emit keyPressed(Qt::Key(e->key())); +} + + diff --git a/examples/animation/stickman/graphicsview.h b/examples/animation/stickman/graphicsview.h new file mode 100644 index 0000000..58ed95b --- /dev/null +++ b/examples/animation/stickman/graphicsview.h @@ -0,0 +1,23 @@ +#ifndef GRAPHICSVIEW_H +#define GRAPHICSVIEW + +#include <QtGui/QGraphicsView> + +class MainWindow; +class GraphicsView: public QGraphicsView +{ + Q_OBJECT +public: + GraphicsView(QWidget *parent = 0); + +protected: + void keyPressEvent(QKeyEvent *); + +signals: + void keyPressed(int key); + +private: + MainWindow *m_editor; +}; + +#endif diff --git a/examples/animation/stickman/lifecycle.cpp b/examples/animation/stickman/lifecycle.cpp new file mode 100644 index 0000000..9233760 --- /dev/null +++ b/examples/animation/stickman/lifecycle.cpp @@ -0,0 +1,194 @@ +#include "lifecycle.h" +#include "stickman.h" +#include "node.h" +#include "animation.h" +#include "graphicsview.h" + +#include <QtCore> +#include <QtGui> +#if defined(QT_EXPERIMENTAL_SOLUTION) +#include "qstatemachine.h" +#include "qstate.h" +#include "qeventtransition.h" +#include "qsignaltransition.h" +#include "qsignalevent.h" +#include "qpropertyanimation.h" +#include "qparallelanimationgroup.h" +#endif + +class KeyPressTransition: public QSignalTransition +{ +public: + KeyPressTransition(GraphicsView *receiver, Qt::Key key) + : QSignalTransition(receiver, SIGNAL(keyPressed(int))), m_key(key) + { + } + KeyPressTransition(GraphicsView *receiver, Qt::Key key, QAbstractState *target) + : QSignalTransition(receiver, SIGNAL(keyPressed(int)), QList<QAbstractState*>() << target), m_key(key) + { + } + + virtual bool eventTest(QEvent *e) const + { + if (QSignalTransition::eventTest(e)) { + QVariant key = static_cast<QSignalEvent*>(e)->arguments().at(0); + return (key.toInt() == int(m_key)); + } + + return false; + } +private: + Qt::Key m_key; +}; + +class LightningStrikesTransition: public QEventTransition +{ +public: + LightningStrikesTransition(QAbstractState *target) + : QEventTransition(this, QEvent::Timer, QList<QAbstractState*>() << target) + { + qsrand((uint)QDateTime::currentDateTime().toTime_t()); + startTimer(1000); + } + + virtual bool eventTest(QEvent *e) const + { + return QEventTransition::eventTest(e) && ((qrand() % 50) == 0); + } +}; + +LifeCycle::LifeCycle(StickMan *stickMan, GraphicsView *keyReceiver) + : m_stickMan(stickMan), m_keyReceiver(keyReceiver) +{ + // Create animation group to be used for all transitions + m_animationGroup = new QParallelAnimationGroup(); + const int stickManNodeCount = m_stickMan->nodeCount(); + for (int i=0; i<stickManNodeCount; ++i) { + QPropertyAnimation *pa = new QPropertyAnimation(m_stickMan->node(i), "position"); + m_animationGroup->addAnimation(pa); + } + + // Set up intial state graph + m_machine = new QStateMachine(); + m_machine->setGlobalRestorePolicy(QState::RestoreProperties); + + m_alive = new QState(m_machine->rootState()); + m_alive->setObjectName("alive"); + + // Make it blink when lightning strikes before entering dead animation + QState *lightningBlink = new QState(m_machine->rootState()); + lightningBlink->setRestorePolicy(QState::DoNotRestoreProperties); + lightningBlink->assignProperty(m_stickMan->scene(), "backgroundBrush", Qt::white); + lightningBlink->assignProperty(m_stickMan, "penColor", Qt::black); + lightningBlink->assignProperty(m_stickMan, "fillColor", Qt::white); + lightningBlink->assignProperty(m_stickMan, "isDead", true); + + QTimer *timer = new QTimer(lightningBlink); + timer->setSingleShot(true); + timer->setInterval(100); + lightningBlink->invokeMethodOnEntry(timer, "start"); + lightningBlink->invokeMethodOnExit(timer, "stop"); + + m_dead = new QState(m_machine->rootState()); + m_dead->setRestorePolicy(QState::DoNotRestoreProperties); + m_dead->assignProperty(m_stickMan->scene(), "backgroundBrush", Qt::black); + m_dead->assignProperty(m_stickMan, "penColor", Qt::white); + m_dead->assignProperty(m_stickMan, "fillColor", Qt::black); + m_dead->setObjectName("dead"); + + // Idle state (sets no properties) + m_idle = new QState(m_alive); + m_idle->setObjectName("idle"); + + m_alive->setInitialState(m_idle); + + // Lightning strikes at random + m_alive->addTransition(new LightningStrikesTransition(lightningBlink)); + //m_alive->addTransition(new KeyPressTransition(m_keyReceiver, Qt::Key_L, lightningBlink)); + connectByAnimation(lightningBlink, m_dead, new QSignalTransition(timer, SIGNAL(timeout()))); + + m_machine->setInitialState(m_alive); +} + +void LifeCycle::setResetKey(Qt::Key resetKey) +{ + // When resetKey is pressed, enter the idle state and do a restoration animation + // (requires no animation pointer, since no property is being set in the idle state) + KeyPressTransition *trans = new KeyPressTransition(m_keyReceiver, resetKey, m_idle); + trans->addAnimation(m_animationGroup); + m_alive->addTransition(trans); +} + +void LifeCycle::setDeathAnimation(const QString &fileName) +{ + QState *deathAnimation = makeState(m_dead, fileName); + m_dead->setInitialState(deathAnimation); +} + +void LifeCycle::start() +{ + m_machine->start(); +} + +void LifeCycle::connectByAnimation(QState *s1, QAbstractState *s2, + QAbstractTransition *transition) +{ + if (transition == 0) { + transition = s1->addTransition(s2); + } else { + transition->setTargetState(s2); + s1->addTransition(transition); + } + transition->addAnimation(m_animationGroup); +} + +void LifeCycle::addActivity(const QString &fileName, Qt::Key key) +{ + QState *state = makeState(m_alive, fileName); + connectByAnimation(m_alive, state, new KeyPressTransition(m_keyReceiver, key)); +} + +QState *LifeCycle::makeState(QState *parentState, const QString &animationFileName) +{ + QState *topLevel = new QState(parentState); + + Animation animation; + { + QFile file(animationFileName); + if (file.open(QIODevice::ReadOnly)) + animation.load(&file); + } + + const int frameCount = animation.totalFrames(); + QState *previousState = 0; + for (int i=0; i<frameCount; ++i) { + QState *frameState = new QState(topLevel); + frameState->setObjectName(QString::fromLatin1("frame %0").arg(i)); + + animation.setCurrentFrame(i); + const int nodeCount = animation.nodeCount(); + for (int j=0; j<nodeCount; ++j) + frameState->assignProperty(m_stickMan->node(j), "position", animation.nodePos(j)); + + if (previousState == 0) { + topLevel->setInitialState(frameState); + } else { + connectByAnimation(previousState, frameState, + new QSignalTransition(m_machine, SIGNAL(animationsFinished()))); + } + previousState = frameState; + } + + // Loop + connectByAnimation(previousState, topLevel->initialState(), + new QSignalTransition(m_machine, SIGNAL(animationsFinished()))); + + return topLevel; + +} + +LifeCycle::~LifeCycle() +{ + delete m_machine; + delete m_animationGroup; +} diff --git a/examples/animation/stickman/lifecycle.h b/examples/animation/stickman/lifecycle.h new file mode 100644 index 0000000..437e935 --- /dev/null +++ b/examples/animation/stickman/lifecycle.h @@ -0,0 +1,40 @@ +#ifndef LIFECYCLE_H +#define LIFECYCLE_H + +#include <Qt> + +class StickMan; +class QStateMachine; +class QAnimationGroup; +class QState; +class QAbstractState; +class QAbstractTransition; +class GraphicsView; +class LifeCycle +{ +public: + LifeCycle(StickMan *stickMan, GraphicsView *keyEventReceiver); + ~LifeCycle(); + + void setDeathAnimation(const QString &fileName); + void setResetKey(Qt::Key key); + void addActivity(const QString &fileName, Qt::Key key); + + void start(); + +private: + void connectByAnimation(QState *s1, QAbstractState *s2, + QAbstractTransition *transition = 0); + QState *makeState(QState *parentState, const QString &animationFileName); + + StickMan *m_stickMan; + QStateMachine *m_machine; + QAnimationGroup *m_animationGroup; + GraphicsView *m_keyReceiver; + + QState *m_alive; + QState *m_dead; + QState *m_idle; +}; + +#endif diff --git a/examples/animation/stickman/main.cpp b/examples/animation/stickman/main.cpp new file mode 100644 index 0000000..62860ec --- /dev/null +++ b/examples/animation/stickman/main.cpp @@ -0,0 +1,58 @@ +#include "animation.h" +#include "node.h" +#include "lifecycle.h" +#include "stickman.h" +#include "graphicsview.h" + +#include <QtCore> +#include <QtGui> + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + StickMan *stickMan = new StickMan; + stickMan->setDrawSticks(false); + + QGraphicsTextItem *textItem = new QGraphicsTextItem(); + textItem->setHtml("<font color=\"white\"><b>Stickman</b>" + "<p>" + "Tell the stickman what to do!" + "</p>" + "<p><i>" + "<li>Press <font color=\"purple\">J</font> to make the stickman jump.</li>" + "<li>Press <font color=\"purple\">D</font> to make the stickman dance.</li>" + "<li>Press <font color=\"purple\">C</font> to make him chill out.</li>" + "<li>Press <font color=\"purple\">Return</font> to make him return to his original position.</li>" + "<li>When you are done, press <font color=\"purple\">Escape</font>.</li>" + "</i></p>" + "<p>If he is unlucky, the stickman will get struck by lightning, and never jump, dance or chill out again." + "</p></font>"); + qreal w = textItem->boundingRect().width(); + QRectF stickManBoundingRect = stickMan->mapToScene(stickMan->boundingRect()).boundingRect(); + textItem->setPos(-w / 2.0, stickManBoundingRect.bottom() + 25.0); + + QGraphicsScene *scene = new QGraphicsScene(); + scene->addItem(stickMan); + scene->addItem(textItem); + scene->setBackgroundBrush(Qt::black); + + GraphicsView *view = new GraphicsView(); + view->setRenderHints(QPainter::Antialiasing); + view->setTransformationAnchor(QGraphicsView::NoAnchor); + view->setScene(scene); + view->showFullScreen(); + view->setFocus(); + view->setSceneRect(scene->sceneRect()); + + LifeCycle *cycle = new LifeCycle(stickMan, view); + cycle->setResetKey(Qt::Key_Return); + cycle->setDeathAnimation("animations/dead"); + + cycle->addActivity("animations/jumping", Qt::Key_J); + cycle->addActivity("animations/dancing", Qt::Key_D); + cycle->addActivity("animations/chilling", Qt::Key_C); + cycle->start(); + + return app.exec(); +} diff --git a/examples/animation/stickman/node.cpp b/examples/animation/stickman/node.cpp new file mode 100644 index 0000000..5932b60 --- /dev/null +++ b/examples/animation/stickman/node.cpp @@ -0,0 +1,51 @@ +#include "node.h" +#include "stickman.h" + +#include <QRectF> +#include <QPainter> +#include <QGraphicsSceneMouseEvent> + +Node::Node(const QPointF &pos, QGraphicsItem *parent) + : QGraphicsItem(parent), m_dragging(false) +{ + setPos(pos); +} + +Node::~Node() +{ +} + +QRectF Node::boundingRect() const +{ + return QRectF(-6.0, -6.0, 12.0, 12.0); +} + +void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + painter->setPen(Qt::white); + painter->drawEllipse(QPointF(0.0, 0.0), 5.0, 5.0); +} + +QVariant Node::itemChange(GraphicsItemChange change, const QVariant &value) +{ + if (change == QGraphicsItem::ItemPositionChange) + emit positionChanged(); + + return QGraphicsItem::itemChange(change, value); +} + +void Node::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + m_dragging = true; +} + +void Node::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + if (m_dragging) + setPos(mapToParent(event->pos())); +} + +void Node::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + m_dragging = false; +}
\ No newline at end of file diff --git a/examples/animation/stickman/node.h b/examples/animation/stickman/node.h new file mode 100644 index 0000000..e9e9190 --- /dev/null +++ b/examples/animation/stickman/node.h @@ -0,0 +1,31 @@ +#ifndef NODE_H +#define NODE_H + +#include <QGraphicsItem> + +class Node: public QObject, public QGraphicsItem +{ + Q_OBJECT + Q_PROPERTY(QPointF position READ pos WRITE setPos); +public: + Node(const QPointF &pos, QGraphicsItem *parent = 0); + ~Node(); + + QRectF boundingRect() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + +signals: + void positionChanged(); + +protected: + QVariant itemChange(GraphicsItemChange change, const QVariant &value); + + void mousePressEvent(QGraphicsSceneMouseEvent *); + void mouseMoveEvent(QGraphicsSceneMouseEvent *); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *); + +private: + bool m_dragging; +}; + +#endif diff --git a/examples/animation/stickman/stickman.cpp b/examples/animation/stickman/stickman.cpp new file mode 100644 index 0000000..1393836 --- /dev/null +++ b/examples/animation/stickman/stickman.cpp @@ -0,0 +1,298 @@ +#include "stickman.h" +#include "node.h" + +#include <QPainter> +#include <QTimer> + +#define _USE_MATH_DEFINES +#include <math.h> + +static const int NodeCount = 16; +static const qreal Coords[NodeCount * 2] = { + 0.0, -150.0, // head, #0 + + 0.0, -100.0, // body pentagon, top->bottom, left->right, #1 - 5 + -50.0, -50.0, + 50.0, -50.0, + -25.0, 50.0, + 25.0, 50.0, + + -100.0, 0.0, // right arm, #6 - 7 + -125.0, 50.0, + + 100.0, 0.0, // left arm, #8 - 9 + 125.0, 50.0, + + -35.0, 75.0, // lower body, #10 - 11 + 35.0, 75.0, + + -25.0, 200.0, // right leg, #12 - 13 + -30.0, 300.0, + + 25.0, 200.0, // left leg, #14 - 15 + 30.0, 300.0 + +}; + +static const int BoneCount = 24; +static const int Bones[BoneCount * 2] = { + 0, 1, // neck + + 1, 2, // body + 1, 3, + 1, 4, + 1, 5, + 2, 3, + 2, 4, + 2, 5, + 3, 4, + 3, 5, + 4, 5, + + 2, 6, // right arm + 6, 7, + + 3, 8, // left arm + 8, 9, + + 4, 10, // lower body + 4, 11, + 5, 10, + 5, 11, + 10, 11, + + 10, 12, // right leg + 12, 13, + + 11, 14, // left leg + 14, 15 + +}; + +StickMan::StickMan() +{ + m_nodes = new Node*[NodeCount]; + m_sticks = true; + m_isDead = false; + m_pixmap = QPixmap("images/head.png"); + m_penColor = Qt::white; + m_fillColor = Qt::black; + + // Set up start position of limbs + for (int i=0; i<NodeCount; ++i) { + m_nodes[i] = new Node(QPointF(Coords[i * 2], Coords[i * 2 + 1]), this); + connect(m_nodes[i], SIGNAL(positionChanged()), this, SLOT(childPositionChanged())); + } + + m_perfectBoneLengths = new qreal[BoneCount]; + for (int i=0; i<BoneCount; ++i) { + int n1 = Bones[i * 2]; + int n2 = Bones[i * 2 + 1]; + + Node *node1 = m_nodes[n1]; + Node *node2 = m_nodes[n2]; + + QPointF dist = node1->pos() - node2->pos(); + m_perfectBoneLengths[i] = sqrt(pow(dist.x(),2) + pow(dist.y(),2)); + } + + startTimer(10); +} + +StickMan::~StickMan() +{ + delete m_nodes; +} + +void StickMan::childPositionChanged() +{ + prepareGeometryChange(); +} + +void StickMan::setDrawSticks(bool on) +{ + m_sticks = on; + for (int i=0;i<nodeCount();++i) { + Node *node = m_nodes[i]; + node->setVisible(on); + } +} + +QRectF StickMan::boundingRect() const +{ + // account for head radius=50.0 plus pen which is 5.0 + return childrenBoundingRect().adjusted(-55.0, -55.0, 55.0, 55.0); +} + +int StickMan::nodeCount() const +{ + return NodeCount; +} + +Node *StickMan::node(int idx) const +{ + if (idx >= 0 && idx < NodeCount) + return m_nodes[idx]; + else + return 0; +} + +void StickMan::timerEvent(QTimerEvent *e) +{ + update(); +} + +void StickMan::stabilize() +{ + static const qreal threshold = 0.001; + + for (int i=0; i<BoneCount; ++i) { + int n1 = Bones[i * 2]; + int n2 = Bones[i * 2 + 1]; + + Node *node1 = m_nodes[n1]; + Node *node2 = m_nodes[n2]; + + QPointF pos1 = node1->pos(); + QPointF pos2 = node2->pos(); + + QPointF dist = pos1 - pos2; + qreal length = sqrt(pow(dist.x(),2) + pow(dist.y(),2)); + qreal diff = (length - m_perfectBoneLengths[i]) / length; + + QPointF p = dist * (0.5 * diff); + if (p.x() > threshold && p.y() > threshold) { + pos1 -= p; + pos2 += p; + + node1->setPos(pos1); + node2->setPos(pos2); + } + } +} + +QPointF StickMan::posFor(int idx) const +{ + return m_nodes[idx]->pos(); +} + +//#include <QTime> +void StickMan::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + /* static int frames = 0; + static QTime time; + if (frames++ % 100 == 0) { + frames = 1; + time.restart(); + } + + if (time.elapsed() > 0) { + painter->setPen(Qt::white); + painter->drawText(0, 0, QString::number(frames / (time.elapsed() / 1000.0))); + }*/ + + stabilize(); + if (m_sticks) { + painter->setPen(Qt::white); + for (int i=0; i<BoneCount; ++i) { + int n1 = Bones[i * 2]; + int n2 = Bones[i * 2 + 1]; + + Node *node1 = m_nodes[n1]; + Node *node2 = m_nodes[n2]; + + painter->drawLine(node1->pos(), node2->pos()); + } + } else { + // first bone is neck and will be used for head + + QPainterPath path; + path.moveTo(posFor(0)); + path.lineTo(posFor(1)); + + // right arm + path.lineTo(posFor(2)); + path.lineTo(posFor(6)); + path.lineTo(posFor(7)); + + // left arm + path.moveTo(posFor(3)); + path.lineTo(posFor(8)); + path.lineTo(posFor(9)); + + // body + path.moveTo(posFor(2)); + path.lineTo(posFor(4)); + path.lineTo(posFor(10)); + path.lineTo(posFor(11)); + path.lineTo(posFor(5)); + path.lineTo(posFor(3)); + path.lineTo(posFor(1)); + + // right leg + path.moveTo(posFor(10)); + path.lineTo(posFor(12)); + path.lineTo(posFor(13)); + + // left leg + path.moveTo(posFor(11)); + path.lineTo(posFor(14)); + path.lineTo(posFor(15)); + + painter->setPen(QPen(m_penColor, 5.0, Qt::SolidLine, Qt::RoundCap)); + painter->drawPath(path); + + { + int n1 = Bones[0]; + int n2 = Bones[1]; + Node *node1 = m_nodes[n1]; + Node *node2 = m_nodes[n2]; + + QPointF dist = node2->pos() - node1->pos(); + + qreal sinAngle = dist.x() / sqrt(pow(dist.x(), 2) + pow(dist.y(), 2)); + qreal angle = asin(sinAngle) * 180.0 / M_PI; + + QPointF headPos = node1->pos(); + painter->translate(headPos); + painter->rotate(-angle); + + painter->setBrush(m_fillColor); + painter->drawEllipse(QPointF(0,0), 50.0, 50.0); + + painter->setBrush(m_penColor); + painter->setPen(QPen(m_penColor, 2.5, Qt::SolidLine, Qt::RoundCap)); + + // eyes + if (m_isDead) { + painter->drawLine(-30.0, -30.0, -20.0, -20.0); + painter->drawLine(-20.0, -30.0, -30.0, -20.0); + + painter->drawLine(20.0, -30.0, 30.0, -20.0); + painter->drawLine(30.0, -30.0, 20.0, -20.0); + } else { + painter->drawChord(QRectF(-30.0, -30.0, 25.0, 70.0), 30.0*16, 120.0*16); + painter->drawChord(QRectF(5.0, -30.0, 25.0, 70.0), 30.0*16, 120.0*16); + } + + // mouth + if (m_isDead) { + painter->drawLine(-28.0, 2.0, 29.0, 2.0); + } else { + painter->setBrush(QColor(128, 0, 64 )); + painter->drawChord(QRectF(-28.0, 2.0-55.0/2.0, 57.0, 55.0), 0.0, -180.0*16); + } + + // pupils + if (!m_isDead) { + painter->setPen(QPen(m_fillColor, 1.0, Qt::SolidLine, Qt::RoundCap)); + painter->setBrush(m_fillColor); + painter->drawEllipse(QPointF(-12.0, -25.0), 5.0, 5.0); + painter->drawEllipse(QPointF(22.0, -25.0), 5.0, 5.0); + } + } + } +} + + + diff --git a/examples/animation/stickman/stickman.h b/examples/animation/stickman/stickman.h new file mode 100644 index 0000000..e659cdb --- /dev/null +++ b/examples/animation/stickman/stickman.h @@ -0,0 +1,60 @@ +#ifndef STICKMAN_H +#define STICKMAN_H + +#include <QGraphicsItem> + +const int LimbCount = 16; + +class Node; +class QTimer; +class StickMan: public QObject, public QGraphicsItem +{ + Q_OBJECT + Q_PROPERTY(QColor penColor WRITE setPenColor READ penColor) + Q_PROPERTY(QColor fillColor WRITE setFillColor READ fillColor) + Q_PROPERTY(bool isDead WRITE setIsDead READ isDead) +public: + StickMan(); + ~StickMan(); + + virtual QRectF boundingRect() const; + virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + + int nodeCount() const; + Node *node(int idx) const; + + void setDrawSticks(bool on); + bool drawSticks() const { return m_sticks; } + + QColor penColor() const { return m_penColor; } + void setPenColor(const QColor &color) { m_penColor = color; } + + QColor fillColor() const { return m_fillColor; } + void setFillColor(const QColor &color) { m_fillColor = color; } + + bool isDead() const { return m_isDead; } + void setIsDead(bool isDead) { m_isDead = isDead; } + +public slots: + void stabilize(); + void childPositionChanged(); + +protected: + void timerEvent(QTimerEvent *e); + +private: + QPointF posFor(int idx) const; + + Node **m_nodes; + qreal *m_perfectBoneLengths; + + uint m_sticks : 1; + uint m_isDead : 1; + uint m_reserved : 30; + + QPixmap m_pixmap; + QColor m_penColor; + QColor m_fillColor; +}; + +#endif // STICKMAN_H diff --git a/examples/animation/stickman/stickman.pro b/examples/animation/stickman/stickman.pro new file mode 100644 index 0000000..bfee5b0 --- /dev/null +++ b/examples/animation/stickman/stickman.pro @@ -0,0 +1,14 @@ +###################################################################### +# Automatically generated by qmake (2.01a) ti 3. feb 19:50:14 2009 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +include(editor/editor.pri) + +# Input +HEADERS += stickman.h animation.h node.h lifecycle.h graphicsview.h +SOURCES += main.cpp stickman.cpp animation.cpp node.cpp lifecycle.cpp graphicsview.cpp diff --git a/examples/animation/sub-attaq/animationmanager.cpp b/examples/animation/sub-attaq/animationmanager.cpp new file mode 100644 index 0000000..9f99426 --- /dev/null +++ b/examples/animation/sub-attaq/animationmanager.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//Own +#include "animationmanager.h" + +//Qt +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qabstractanimation.h" +#else +# include <QtCore/QAbstractAnimation> +#endif +#include <QDebug> + +// the universe's only animation manager +AnimationManager *AnimationManager::instance = 0; + +AnimationManager::AnimationManager() +{ +} + +AnimationManager *AnimationManager::self() +{ + if (!instance) + instance = new AnimationManager; + return instance; +} + +void AnimationManager::registerAnimation(QAbstractAnimation *anim) +{ + animations.append(anim); +} + +void AnimationManager::unregisterAnimation(QAbstractAnimation *anim) +{ + animations.removeAll(anim); +} + +void AnimationManager::unregisterAllAnimations() +{ + animations.clear(); +} + +void AnimationManager::pauseAll() +{ + foreach (QAbstractAnimation* animation, animations) + { + if (animation->state() == QAbstractAnimation::Running) + animation->pause(); + } +} +void AnimationManager::resumeAll() +{ + foreach (QAbstractAnimation* animation, animations) + { + if (animation->state() == QAbstractAnimation::Paused) + animation->resume(); + } +} diff --git a/examples/animation/sub-attaq/animationmanager.h b/examples/animation/sub-attaq/animationmanager.h new file mode 100644 index 0000000..fe92680 --- /dev/null +++ b/examples/animation/sub-attaq/animationmanager.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ANIMATIONMANAGER_H +#define ANIMATIONMANAGER_H + +#include <QObject> + +class QAbstractAnimation; + +class AnimationManager : public QObject +{ +Q_OBJECT +public: + AnimationManager(); + void registerAnimation(QAbstractAnimation *anim); + void unregisterAnimation(QAbstractAnimation *anim); + void unregisterAllAnimations(); + static AnimationManager *self(); + +public slots: + void pauseAll(); + void resumeAll(); + +private: + static AnimationManager *instance; + QList<QAbstractAnimation *> animations; +}; + +#endif // ANIMATIONMANAGER_H diff --git a/examples/animation/sub-attaq/boat.cpp b/examples/animation/sub-attaq/boat.cpp new file mode 100644 index 0000000..633e1b1 --- /dev/null +++ b/examples/animation/sub-attaq/boat.cpp @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//Own +#include "boat.h" +#include "boat_p.h" +#include "bomb.h" +#include "pixmapitem.h" +#include "graphicsscene.h" +#include "animationmanager.h" +#include "custompropertyanimation.h" +#include "qanimationstate.h" + +//Qt +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qpropertyanimation.h" +# include "qstatemachine.h" +# include "qhistorystate.h" +# include "qfinalstate.h" +# include "qstate.h" +#include "qsequentialanimationgroup.h" +#else +#include <QPropertyAnimation> +#include <QStateMachine> +#include <QHistoryState> +#include <QFinalState> +#include <QState> +#include <QSequentialAnimationGroup> +#endif + +static QAbstractAnimation *setupDestroyAnimation(Boat *boat) +{ + QSequentialAnimationGroup *group = new QSequentialAnimationGroup(boat); +#if QT_VERSION >=0x040500 + PixmapItem *step1 = new PixmapItem(QString("explosion/boat/step1"),GraphicsScene::Big, boat); + step1->setZValue(6); + PixmapItem *step2 = new PixmapItem(QString("explosion/boat/step2"),GraphicsScene::Big, boat); + step2->setZValue(6); + PixmapItem *step3 = new PixmapItem(QString("explosion/boat/step3"),GraphicsScene::Big, boat); + step3->setZValue(6); + PixmapItem *step4 = new PixmapItem(QString("explosion/boat/step4"),GraphicsScene::Big, boat); + step4->setZValue(6); + step1->setOpacity(0); + step2->setOpacity(0); + step3->setOpacity(0); + step4->setOpacity(0); + CustomPropertyAnimation *anim1 = new CustomPropertyAnimation(boat); + anim1->setMemberFunctions((QGraphicsItem*)step1, &QGraphicsItem::opacity, &QGraphicsItem::setOpacity); + anim1->setDuration(100); + anim1->setEndValue(1); + CustomPropertyAnimation *anim2 = new CustomPropertyAnimation(boat); + anim2->setMemberFunctions((QGraphicsItem*)step2, &QGraphicsItem::opacity, &QGraphicsItem::setOpacity); + anim2->setDuration(100); + anim2->setEndValue(1); + CustomPropertyAnimation *anim3 = new CustomPropertyAnimation(boat); + anim3->setMemberFunctions((QGraphicsItem*)step3, &QGraphicsItem::opacity, &QGraphicsItem::setOpacity); + anim3->setDuration(100); + anim3->setEndValue(1); + CustomPropertyAnimation *anim4 = new CustomPropertyAnimation(boat); + anim4->setMemberFunctions((QGraphicsItem*)step4, &QGraphicsItem::opacity, &QGraphicsItem::setOpacity); + anim4->setDuration(100); + anim4->setEndValue(1); + group->addAnimation(anim1); + group->addAnimation(anim2); + group->addAnimation(anim3); + group->addAnimation(anim4); +#else + // work around for a bug where we don't transition if the duration is zero. + QtPauseAnimation *anim = new QtPauseAnimation(group); + anim->setDuration(1); + group->addAnimation(anim); +#endif + AnimationManager::self()->registerAnimation(group); + return group; +} + + + +Boat::Boat(QGraphicsItem * parent, Qt::WindowFlags wFlags) + : QGraphicsWidget(parent,wFlags), speed(0), bombsAlreadyLaunched(0), direction(Boat::None), movementAnimation(0) +{ + pixmapItem = new PixmapItem(QString("boat"),GraphicsScene::Big, this); + setZValue(4); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable); + resize(pixmapItem->boundingRect().size()); + + //The movement animation used to animate the boat + movementAnimation = new QPropertyAnimation(this, "pos"); + AnimationManager::self()->registerAnimation(movementAnimation); + + //We setup the state machien of the boat + QStateMachine *machine = new QStateMachine(this); + QState *moving = new QState(machine->rootState()); + StopState *stopState = new StopState(this,moving); + machine->setInitialState(moving); + moving->setInitialState(stopState); + MoveStateRight *moveStateRight = new MoveStateRight(this,moving); + MoveStateLeft *moveStateLeft = new MoveStateLeft(this,moving); + LaunchStateRight *launchStateRight = new LaunchStateRight(this,machine->rootState()); + LaunchStateLeft *launchStateLeft = new LaunchStateLeft(this,machine->rootState()); + + //then setup the transitions for the rightMove state + KeyStopTransition *leftStopRight = new KeyStopTransition(this,QEvent::KeyPress,Qt::Key_Left); + leftStopRight->setTargetState(stopState); + KeyMoveTransition *leftMoveRight = new KeyMoveTransition(this,QEvent::KeyPress,Qt::Key_Left); + leftMoveRight->setTargetState(moveStateRight); + KeyMoveTransition *rightMoveRight = new KeyMoveTransition(this,QEvent::KeyPress,Qt::Key_Right); + rightMoveRight->setTargetState(moveStateRight); + KeyMoveTransition *rightMoveStop = new KeyMoveTransition(this,QEvent::KeyPress,Qt::Key_Right); + rightMoveStop->setTargetState(moveStateRight); + + //then setup the transitions for the leftMove state + KeyStopTransition *rightStopLeft = new KeyStopTransition(this,QEvent::KeyPress,Qt::Key_Right); + rightStopLeft->setTargetState(stopState); + KeyMoveTransition *rightMoveLeft = new KeyMoveTransition(this,QEvent::KeyPress,Qt::Key_Right); + rightMoveLeft->setTargetState(moveStateLeft); + KeyMoveTransition *leftMoveLeft = new KeyMoveTransition(this,QEvent::KeyPress,Qt::Key_Left); + leftMoveLeft->setTargetState(moveStateLeft); + KeyMoveTransition *leftMoveStop = new KeyMoveTransition(this,QEvent::KeyPress,Qt::Key_Left); + leftMoveStop->setTargetState(moveStateLeft); + + //We set up the right move state + moveStateRight->addTransition(leftStopRight); + moveStateRight->addTransition(leftMoveRight); + moveStateRight->addTransition(rightMoveRight); + stopState->addTransition(rightMoveStop); + + //We set up the left move state + moveStateLeft->addTransition(rightStopLeft); + moveStateLeft->addTransition(leftMoveLeft); + moveStateLeft->addTransition(rightMoveLeft); + stopState->addTransition(leftMoveStop); + + //The animation is finished, it means we reached the border of the screen, the boat is stopped so we move to the stop state + moveStateLeft->addTransition(movementAnimation, SIGNAL(finished()), stopState); + moveStateRight->addTransition(movementAnimation, SIGNAL(finished()), stopState); + + //We set up the keys for dropping bombs + KeyLaunchTransition *upFireLeft = new KeyLaunchTransition(this,QEvent::KeyPress,Qt::Key_Up); + upFireLeft->setTargetState(launchStateRight); + KeyLaunchTransition *upFireRight = new KeyLaunchTransition(this,QEvent::KeyPress,Qt::Key_Up); + upFireRight->setTargetState(launchStateRight); + KeyLaunchTransition *upFireStop = new KeyLaunchTransition(this,QEvent::KeyPress,Qt::Key_Up); + upFireStop->setTargetState(launchStateRight); + KeyLaunchTransition *downFireLeft = new KeyLaunchTransition(this,QEvent::KeyPress,Qt::Key_Down); + downFireLeft->setTargetState(launchStateLeft); + KeyLaunchTransition *downFireRight = new KeyLaunchTransition(this,QEvent::KeyPress,Qt::Key_Down); + downFireRight->setTargetState(launchStateLeft); + KeyLaunchTransition *downFireMove = new KeyLaunchTransition(this,QEvent::KeyPress,Qt::Key_Down); + downFireMove->setTargetState(launchStateLeft); + + //We set up transitions for fire up + moveStateRight->addTransition(upFireRight); + moveStateLeft->addTransition(upFireLeft); + stopState->addTransition(upFireStop); + + //We set up transitions for fire down + moveStateRight->addTransition(downFireRight); + moveStateLeft->addTransition(downFireLeft); + stopState->addTransition(downFireMove); + + //Finally the launch state should come back to its original state + QHistoryState *historyState = moving->addHistoryState(); + launchStateLeft->addTransition(historyState); + launchStateRight->addTransition(historyState); + + QFinalState *final = new QFinalState(machine->rootState()); + + //This state play the destroyed animation + QAnimationState *destroyedState = new QAnimationState(machine->rootState()); + destroyedState->setAnimation(setupDestroyAnimation(this)); + + //Play a nice animation when the boat is destroyed + moving->addTransition(this, SIGNAL(boatDestroyed()),destroyedState); + + //Transition to final state when the destroyed animation is finished + destroyedState->addTransition(destroyedState, SIGNAL(animationFinished()), final); + + //The machine has finished to be executed, then the boat is dead + connect(machine,SIGNAL(finished()),this, SIGNAL(boatExecutionFinished())); + + machine->start(); +} + +void Boat::updateBoatMovement() +{ + if (speed == 0 || direction == Boat::None) { + movementAnimation->stop(); + return; + } + + movementAnimation->stop(); + movementAnimation->setStartValue(pos()); + + if (direction == Boat::Left) { + movementAnimation->setEndValue(QPointF(0,y())); + movementAnimation->setDuration(x()/speed*15); + } + else /*if (direction == Boat::Right)*/ { + movementAnimation->setEndValue(QPointF(scene()->width()-size().width(),y())); + movementAnimation->setDuration((scene()->width()-size().width()-x())/speed*15); + } + movementAnimation->start(); +} + +void Boat::destroy() +{ + movementAnimation->stop(); + emit boatDestroyed(); +} + +int Boat::bombsLaunched() const +{ + return bombsAlreadyLaunched; +} + +void Boat::setBombsLaunched(int number) +{ + if (number > MAX_BOMB) { + qWarning("Boat::setBombsLaunched : It impossible to launch that number of bombs"); + return; + } + bombsAlreadyLaunched = number; +} + +int Boat::currentSpeed() const +{ + return speed; +} + +void Boat::setCurrentSpeed(int speed) +{ + if (speed > 3 || speed < 0) { + qWarning("Boat::setCurrentSpeed: The boat can't run on that speed"); + return; + } + this->speed = speed; +} + +enum Boat::Movement Boat::currentDirection() const +{ + return direction; +} + +void Boat::setCurrentDirection(Movement direction) +{ + this->direction = direction; +} + +int Boat::type() const +{ + return Type; +} diff --git a/examples/animation/sub-attaq/boat.h b/examples/animation/sub-attaq/boat.h new file mode 100644 index 0000000..4c4a737 --- /dev/null +++ b/examples/animation/sub-attaq/boat.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef __BOAT__H__ +#define __BOAT__H__ + +//Qt +#include <QObject> +#include <QKeyEvent> + +#include <QDebug> + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qgraphicswidget.h" +#else +# include <QtGui/QGraphicsWidget> +#endif + +class PixmapItem; +class Bomb; +class QVariantAnimation; + +class Boat : public QGraphicsWidget +{ +Q_OBJECT +Q_PROPERTY(QPointF pos READ pos WRITE setPos) +public: + enum Movement { + None = 0, + Left, + Right + }; + enum { Type = UserType + 2 }; + Boat(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0); + void destroy(); + + int bombsLaunched() const; + void setBombsLaunched(int number); + + int currentSpeed() const; + void setCurrentSpeed(int speed); + + enum Movement currentDirection() const; + void setCurrentDirection(Movement direction); + + void updateBoatMovement(); + + virtual int type() const; + +Q_SIGNALS: + void boatDestroyed(); + void boatExecutionFinished(); + +private: + int speed; + int bombsAlreadyLaunched; + Movement direction; + QVariantAnimation *movementAnimation; + PixmapItem *pixmapItem; +}; + +#endif //__BOAT__H__ diff --git a/examples/animation/sub-attaq/boat_p.h b/examples/animation/sub-attaq/boat_p.h new file mode 100644 index 0000000..8dacaba --- /dev/null +++ b/examples/animation/sub-attaq/boat_p.h @@ -0,0 +1,258 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BOAT_P_H +#define BOAT_P_H + +//Own +#include "bomb.h" +#include "graphicsscene.h" + +// Qt +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qkeyeventtransition.h" +#else +# include <QtGui/QKeyEventTransition> +#endif + +static const int MAX_BOMB = 5; + + +//These transtion test if we have to stop the boat (i.e current speed is 1) +class KeyStopTransition : public QKeyEventTransition +{ +public: + KeyStopTransition(Boat *boat, QEvent::Type type, int key) + : QKeyEventTransition(boat,type, key) + { + this->boat = boat; + this->key = key; +#if defined(Q_OS_MAC) + setModifiers(Qt::KeypadModifier); +#endif + } +protected: + virtual bool eventTest(QEvent *event) const + { + Q_UNUSED(event); + if (!QKeyEventTransition::eventTest(event)) + return false; + if (boat->currentSpeed() == 1) + return true; + else + return false; + } +private: + Boat * boat; + int key; +}; + +//These transtion test if we have to move the boat (i.e current speed was 0 or another value) + class KeyMoveTransition : public QKeyEventTransition +{ +public: + KeyMoveTransition(Boat *boat, QEvent::Type type, int key) + : QKeyEventTransition(boat,type, key) + { + this->boat = boat; + this->key = key; +#if defined(Q_OS_MAC) + setModifiers(Qt::KeypadModifier); +#endif + } +protected: + virtual bool eventTest(QEvent *event) const + { + Q_UNUSED(event); + if (!QKeyEventTransition::eventTest(event)) + return false; + if (boat->currentSpeed() >= 0) + return true; + else + return false; + + } + void onTransition() + { + //We decrease the speed if needed + if (key == Qt::Key_Left && boat->currentDirection() == Boat::Right) + boat->setCurrentSpeed(boat->currentSpeed() - 1); + else if (key == Qt::Key_Right && boat->currentDirection() == Boat::Left) + boat->setCurrentSpeed(boat->currentSpeed() - 1); + else if (boat->currentSpeed() < 3) + boat->setCurrentSpeed(boat->currentSpeed() + 1); + boat->updateBoatMovement(); + } +private: + Boat * boat; + int key; +}; + +//This transition trigger the bombs launch + class KeyLaunchTransition : public QKeyEventTransition +{ +public: + KeyLaunchTransition(Boat *boat, QEvent::Type type, int key) + : QKeyEventTransition(boat,type, key) + { + this->boat = boat; + this->key = key; +#if defined(Q_OS_MAC) + setModifiers(Qt::KeypadModifier); +#endif + } +protected: + virtual bool eventTest(QEvent *event) const + { + Q_UNUSED(event); + if (!QKeyEventTransition::eventTest(event)) + return false; + //We have enough bomb? + if (boat->bombsLaunched() < MAX_BOMB) + return true; + else + return false; + } +private: + Boat * boat; + int key; +}; + +//This state is describing when the boat is moving right +class MoveStateRight : public QState +{ +public: + MoveStateRight(Boat *boat,QState *parent = 0) : QState(parent) + { + this->boat = boat; + } +protected: + void onEntry() + { + boat->setCurrentDirection(Boat::Right); + boat->updateBoatMovement(); + } +private: + Boat * boat; +}; + + //This state is describing when the boat is moving left +class MoveStateLeft : public QState +{ +public: + MoveStateLeft(Boat *boat,QState *parent = 0) : QState(parent) + { + this->boat = boat; + } +protected: + void onEntry() + { + boat->setCurrentDirection(Boat::Left); + boat->updateBoatMovement(); + } +private: + Boat * boat; +}; + +//This state is describing when the boat is in a stand by position +class StopState : public QState +{ +public: + StopState(Boat *boat,QState *parent = 0) : QState(parent) + { + this->boat = boat; + } +protected: + void onEntry() + { + boat->setCurrentSpeed(0); + boat->setCurrentDirection(Boat::None); + boat->updateBoatMovement(); + } +private: + Boat * boat; +}; + +//This state is describing the launch of the torpedo on the right +class LaunchStateRight : public QState +{ +public: + LaunchStateRight(Boat *boat,QState *parent = 0) : QState(parent) + { + this->boat = boat; + } +protected: + void onEntry() + { + Bomb *b = new Bomb(); + b->setPos(boat->x()+boat->size().width(),boat->y()); + GraphicsScene *scene = static_cast<GraphicsScene *>(boat->scene()); + scene->addItem(b); + b->launch(Bomb::Right); + boat->setBombsLaunched(boat->bombsLaunched() + 1); + } +private: + Boat * boat; +}; + +//This state is describing the launch of the torpedo on the left +class LaunchStateLeft : public QState +{ +public: + LaunchStateLeft(Boat *boat,QState *parent = 0) : QState(parent) + { + this->boat = boat; + } +protected: + void onEntry() + { + Bomb *b = new Bomb(); + b->setPos(boat->x() - b->size().width(), boat->y()); + GraphicsScene *scene = static_cast<GraphicsScene *>(boat->scene()); + scene->addItem(b); + b->launch(Bomb::Left); + boat->setBombsLaunched(boat->bombsLaunched() + 1); + } +private: + Boat * boat; +}; + +#endif // BOAT_P_H diff --git a/examples/animation/sub-attaq/bomb.cpp b/examples/animation/sub-attaq/bomb.cpp new file mode 100644 index 0000000..04310aa --- /dev/null +++ b/examples/animation/sub-attaq/bomb.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//Own +#include "bomb.h" +#include "submarine.h" +#include "pixmapitem.h" +#include "animationmanager.h" +#include "qanimationstate.h" + +//Qt + +#if defined(QT_EXPERIMENTAL_SOLUTION) +#include "qpropertyanimation.h" +#include "qsequentialanimationgroup.h" +#include "qstatemachine.h" +#include "qfinalstate.h" +#else +#include <QtCore/QSequentialAnimationGroup> +#include <QtCore/QPropertyAnimation> +#include <QtCore/QStateMachine> +#include <QtCore/QFinalState> +#endif + +Bomb::Bomb(QGraphicsItem * parent, Qt::WindowFlags wFlags) + : QGraphicsWidget(parent,wFlags), launchAnimation(0) +{ + pixmapItem = new PixmapItem(QString("bomb"),GraphicsScene::Big, this); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + setFlags(QGraphicsItem::ItemIsMovable); + setZValue(2); + resize(pixmapItem->boundingRect().size()); +} + +void Bomb::launch(Bomb::Direction direction) +{ + launchAnimation = new QSequentialAnimationGroup(); + AnimationManager::self()->registerAnimation(launchAnimation); + qreal delta = direction == Right ? 20 : - 20; + QPropertyAnimation *anim = new QPropertyAnimation(this, "pos"); + anim->setEndValue(QPointF(x() + delta,y() - 20)); + anim->setDuration(150); + launchAnimation->addAnimation(anim); + anim = new QPropertyAnimation(this, "pos"); + anim->setEndValue(QPointF(x() + delta*2, y() )); + anim->setDuration(150); + launchAnimation->addAnimation(anim); + anim = new QPropertyAnimation(this, "pos"); + anim->setEndValue(QPointF(x() + delta*2,scene()->height())); + anim->setDuration(y()/2*60); + launchAnimation->addAnimation(anim); + connect(anim,SIGNAL(valueChanged(const QVariant &)),this,SLOT(onAnimationLaunchValueChanged(const QVariant &))); + + //We setup the state machine of the bomb + QStateMachine *machine = new QStateMachine(this); + + //This state is when the launch animation is playing + QAnimationState *launched = new QAnimationState(machine->rootState()); + launched->setAnimation(launchAnimation); + + //End + QFinalState *final = new QFinalState(machine->rootState()); + + machine->setInitialState(launched); + + //### Add a nice animation when the bomb is destroyed + launched->addTransition(this, SIGNAL(bombExplosed()),final); + + //If the animation is finished, then we move to the final state + launched->addTransition(launched, SIGNAL(animationFinished()), final); + + //The machine has finished to be executed, then the boat is dead + connect(machine,SIGNAL(finished()),this, SIGNAL(bombExecutionFinished())); + + machine->start(); + +} + +void Bomb::onAnimationLaunchValueChanged(const QVariant &) +{ + foreach (QGraphicsItem * item , collidingItems(Qt::IntersectsItemBoundingRect)) { + if (item->type() == SubMarine::Type) { + SubMarine *s = static_cast<SubMarine *>(item); + destroy(); + s->destroy(); + } + } +} + +void Bomb::destroy() +{ + launchAnimation->stop(); + emit bombExplosed(); +} diff --git a/examples/animation/sub-attaq/bomb.h b/examples/animation/sub-attaq/bomb.h new file mode 100644 index 0000000..9191e6e --- /dev/null +++ b/examples/animation/sub-attaq/bomb.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef __BOMB__H__ +#define __BOMB__H__ + +//Qt +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qanimationgroup.h" +# include "qgraphicswidget.h" +#else +# include <QtGui/QGraphicsWidget> +# include <QtCore/QAnimationGroup> +#endif + +class PixmapItem; + +class Bomb : public QGraphicsWidget +{ +Q_OBJECT +Q_PROPERTY(QPointF pos READ pos WRITE setPos) +public: + enum Direction { + Left = 0, + Right + }; + Bomb(QGraphicsItem * parent = 0, Qt::WindowFlags wFlags = 0); + void launch(Direction direction); + void destroy(); + +Q_SIGNALS: + void bombExplosed(); + void bombExecutionFinished(); + +private slots: + void onAnimationLaunchValueChanged(const QVariant &); + +private: + QAnimationGroup *launchAnimation; + PixmapItem *pixmapItem; +}; + +#endif //__BOMB__H__ diff --git a/examples/animation/sub-attaq/custompropertyanimation.cpp b/examples/animation/sub-attaq/custompropertyanimation.cpp new file mode 100644 index 0000000..45997af --- /dev/null +++ b/examples/animation/sub-attaq/custompropertyanimation.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "custompropertyanimation.h" +#include "custompropertyanimation_p.h" + +// Qt +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + + +void CustomPropertyAnimationPrivate::initDefaultStartValue() +{ + if (!animProp) + return; + QVariant def = animProp->read(); + if (def.isValid()) + convertValues(def.userType()); + if (animProp && !defaultStartValue.isValid() + && ((currentTime == 0 && (currentIteration || currentIteration == 0)) + || (currentTime == duration && currentIteration == (iterationCount - 1)))) { + setDefaultStartValue(def); + } +} + + +CustomPropertyAnimation::CustomPropertyAnimation(QObject *parent) : + QVariantAnimation(*new CustomPropertyAnimationPrivate, parent) +{ +} + +CustomPropertyAnimation::~CustomPropertyAnimation() +{ +} + +void CustomPropertyAnimation::setProperty(AbstractProperty *animProp) +{ + Q_D(CustomPropertyAnimation); + if (d->animProp == animProp) + return; + delete d->animProp; + d->animProp = animProp; +} + +/*! + \reimp + */ +void CustomPropertyAnimation::updateCurrentValue(const QVariant &value) +{ + Q_D(CustomPropertyAnimation); + if (!d->animProp || state() == QAbstractAnimation::Stopped) + return; + + d->animProp->write(value); +} + + +/*! + \reimp +*/ +void CustomPropertyAnimation::updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState) +{ + Q_D(CustomPropertyAnimation); + // Initialize start value + if (oldState == QAbstractAnimation::Stopped) + d->initDefaultStartValue(); + + QVariantAnimation::updateState(oldState, newState); +} + + + +#include "moc_custompropertyanimation.cpp" + +QT_END_NAMESPACE diff --git a/examples/animation/sub-attaq/custompropertyanimation.h b/examples/animation/sub-attaq/custompropertyanimation.h new file mode 100644 index 0000000..ba6ef55 --- /dev/null +++ b/examples/animation/sub-attaq/custompropertyanimation.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CUSTOMPROPERTYANIMATION_H +#define CUSTOMPROPERTYANIMATION_H + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qvariantanimation.h" +#else +# include <QtCore/qvariantanimation.h> +#endif + +class QGraphicsItem; +class CustomPropertyAnimationPrivate; + +struct AbstractProperty +{ + virtual QVariant read() const = 0; + virtual void write(const QVariant &value) = 0; +}; + + +class CustomPropertyAnimation : public QVariantAnimation +{ + Q_OBJECT + + template <typename Target, typename T, typename T2 = T> + class MemberFunctionProperty : public AbstractProperty + { + public: + typedef T (Target::*Getter)(void) const; + typedef void (Target::*Setter)(T2); + + MemberFunctionProperty(Target* target, Getter getter, Setter setter) + : m_target(target), m_getter(getter), m_setter(setter) {} + + virtual void write(const QVariant &value) + { + if (m_setter) (m_target->*m_setter)(qVariantValue<T>(value)); + } + + virtual QVariant read() const + { + if (m_getter) return qVariantFromValue<T>((m_target->*m_getter)()); + return QVariant(); + } + + private: + Target *m_target; + Getter m_getter; + Setter m_setter; + }; + +public: + CustomPropertyAnimation(QObject *parent = 0); + ~CustomPropertyAnimation(); + + template<class Target, typename T> + void setMemberFunctions(Target* target, T (Target::*getter)() const, void (Target::*setter)(const T& )) + { + setProperty(new MemberFunctionProperty<Target, T, const T&>(target, getter, setter)); + } + + template<class Target, typename T> + void setMemberFunctions(Target* target, T (Target::*getter)() const, void (Target::*setter)(T)) + { + setProperty(new MemberFunctionProperty<Target, T>(target, getter, setter)); + } + + void updateCurrentValue(const QVariant &value); + void updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState); + void setProperty(AbstractProperty *animProp); + +private: + Q_DISABLE_COPY(CustomPropertyAnimation); + Q_DECLARE_PRIVATE(CustomPropertyAnimation); +}; + +#endif // CUSTOMPROPERTYANIMATION_H diff --git a/examples/animation/sub-attaq/custompropertyanimation_p.h b/examples/animation/sub-attaq/custompropertyanimation_p.h new file mode 100644 index 0000000..89fc757 --- /dev/null +++ b/examples/animation/sub-attaq/custompropertyanimation_p.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CUSTOMPROPERTYANIMATION_P_H +#define CUSTOMPROPERTYANIMATION_P_H + +#ifdef QT_EXPERIMENTAL_SOLUTION +# include "qvariantanimation_p.h" +#else +# include <private/qvariantanimation_p.h> +#endif + +class CustomPropertyAnimationPrivate : public QVariantAnimationPrivate +{ + Q_DECLARE_PUBLIC(CustomPropertyAnimation) +public: + CustomPropertyAnimationPrivate() : QVariantAnimationPrivate(), animProp(0) + { + } + + void initDefaultStartValue(); + + AbstractProperty *animProp; +}; + +#endif //QTCUSTOMPROPERTYANIMATION_P_H diff --git a/examples/animation/sub-attaq/data.xml b/examples/animation/sub-attaq/data.xml new file mode 100644 index 0000000..41d4754 --- /dev/null +++ b/examples/animation/sub-attaq/data.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<subattaq> + <submarines> + <submarine type="0" points="10" name="Q1" /> + <submarine type="1" points="20" name="Q2" /> + </submarines> + <levels> + <level id="0" name="Seaman recruit"> + <subinstance type="0" nb="2"/> + </level> + <level id="1" name="Seaman apprentice"> + <subinstance type="0" nb="4"/> + </level> + </levels> +</subattaq> diff --git a/examples/animation/sub-attaq/graphicsscene.cpp b/examples/animation/sub-attaq/graphicsscene.cpp new file mode 100644 index 0000000..5dcc034 --- /dev/null +++ b/examples/animation/sub-attaq/graphicsscene.cpp @@ -0,0 +1,375 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//Own +#include "graphicsscene.h" +#include "states.h" +#include "boat.h" +#include "submarine.h" +#include "torpedo.h" +#include "bomb.h" +#include "pixmapitem.h" +#include "custompropertyanimation.h" +#include "animationmanager.h" +#include "qanimationstate.h" + +//Qt +#if defined(QT_EXPERIMENTAL_SOLUTION) +#include "qpropertyanimation.h" +#include "qsequentialanimationgroup.h" +#include "qparallelanimationgroup.h" +#include "qstatemachine.h" +#include "qfinalstate.h" +#include "qpauseanimation.h" +#else +#include <QPropertyAnimation> +#include <QSequentialAnimationGroup> +#include <QParallelAnimationGroup> +#include <QStateMachine> +#include <QFinalState> +#include <QPauseAnimation> +#endif +#include <QAction> +#include <QDir> +#include <QApplication> +#include <QMessageBox> +#include <QGraphicsView> +#include <QGraphicsSceneMouseEvent> +#include <QXmlStreamReader> + +//helper function that creates an animation for position and inserts it into group +static CustomPropertyAnimation *addGraphicsItemPosAnimation(QSequentialAnimationGroup *group, + QGraphicsItem *item, const QPointF &endPos) +{ + CustomPropertyAnimation *ret = new CustomPropertyAnimation(group); + ret->setMemberFunctions(item, &QGraphicsItem::pos, &QGraphicsItem::setPos); + ret->setEndValue(endPos); + ret->setDuration(200); + ret->setEasingCurve(QEasingCurve::OutElastic); + group->addPause(50); + return ret; +} + +//helper function that creates an animation for opacity and inserts it into group +static void addGraphicsItemFadeoutAnimation(QAnimationGroup *group, QGraphicsItem *item) +{ +#if QT_VERSION >=0x040500 + CustomPropertyAnimation *anim = new CustomPropertyAnimation(group); + anim->setMemberFunctions(item, &QGraphicsItem::opacity, &QGraphicsItem::setOpacity); + anim->setDuration(800); + anim->setEndValue(0); + anim->setEasingCurve(QEasingCurve::OutQuad); +#else + // work around for a bug where we don't transition if the duration is zero. + QtPauseAnimation *anim = new QtPauseAnimation(group); + anim->setDuration(1); +#endif +} + +GraphicsScene::GraphicsScene(int x, int y, int width, int height, Mode mode) + : QGraphicsScene(x,y,width,height), mode(mode), newAction(0), quitAction(0), boat(0) +{ + backgroundItem = new PixmapItem(QString("background"),mode); + backgroundItem->setZValue(1); + backgroundItem->setPos(0,0); + addItem(backgroundItem); + + PixmapItem *surfaceItem = new PixmapItem(QString("surface"),mode); + surfaceItem->setZValue(3); + surfaceItem->setPos(0,sealLevel() - surfaceItem->boundingRect().height()/2); + addItem(surfaceItem); + + //parse the xml that contain all data of the game + QXmlStreamReader reader; + QFile file(QDir::currentPath() + "/data.xml"); + file.open(QIODevice::ReadOnly); + reader.setDevice(&file); + LevelDescription currentLevel; + while (!reader.atEnd()) { + reader.readNext(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == "submarine") + { + SubmarineDescription desc; + desc.name = reader.attributes().value("name").toString(); + desc.points = reader.attributes().value("points").toString().toInt(); + desc.type = reader.attributes().value("type").toString().toInt(); + submarinesData.append(desc); + } + if (reader.name() == "level") + { + currentLevel.id = reader.attributes().value("id").toString().toInt(); + currentLevel.name = reader.attributes().value("name").toString(); + } + if (reader.name() == "subinstance") + { + currentLevel.submarines.append(qMakePair(reader.attributes().value("type").toString().toInt(),reader.attributes().value("nb").toString().toInt())); + } + } + if (reader.tokenType() == QXmlStreamReader::EndElement) { + if (reader.name() == "level") + { + levelsData.insert(currentLevel.id,currentLevel); + currentLevel.submarines.clear(); + } + } + } +} + +qreal GraphicsScene::sealLevel() const +{ + if (mode == Big) + return 220; + else + return 160; +} + +void GraphicsScene::setupScene(const QList<QAction *> &actions) +{ + newAction = actions.at(0); + quitAction = actions.at(1); + + QGraphicsItem *logo_s = addWelcomeItem(QPixmap(":/logo-s")); + QGraphicsItem *logo_u = addWelcomeItem(QPixmap(":/logo-u")); + QGraphicsItem *logo_b = addWelcomeItem(QPixmap(":/logo-b")); + QGraphicsItem *logo_dash = addWelcomeItem(QPixmap(":/logo-dash")); + QGraphicsItem *logo_a = addWelcomeItem(QPixmap(":/logo-a")); + QGraphicsItem *logo_t = addWelcomeItem(QPixmap(":/logo-t")); + QGraphicsItem *logo_t2 = addWelcomeItem(QPixmap(":/logo-t2")); + QGraphicsItem *logo_a2 = addWelcomeItem(QPixmap(":/logo-a2")); + QGraphicsItem *logo_q = addWelcomeItem(QPixmap(":/logo-q")); + QGraphicsItem *logo_excl = addWelcomeItem(QPixmap(":/logo-excl")); + logo_s->setZValue(3); + logo_u->setZValue(4); + logo_b->setZValue(5); + logo_dash->setZValue(6); + logo_a->setZValue(7); + logo_t->setZValue(8); + logo_t2->setZValue(9); + logo_a2->setZValue(10); + logo_q->setZValue(11); + logo_excl->setZValue(12); + logo_s->setPos(QPointF(-1000, -1000)); + logo_u->setPos(QPointF(-800, -1000)); + logo_b->setPos(QPointF(-600, -1000)); + logo_dash->setPos(QPointF(-400, -1000)); + logo_a->setPos(QPointF(1000, 2000)); + logo_t->setPos(QPointF(800, 2000)); + logo_t2->setPos(QPointF(600, 2000)); + logo_a2->setPos(QPointF(400, 2000)); + logo_q->setPos(QPointF(200, 2000)); + logo_excl->setPos(QPointF(0, 2000)); + + QSequentialAnimationGroup * lettersGroupMoving = new QSequentialAnimationGroup(this); + QParallelAnimationGroup * lettersGroupFading = new QParallelAnimationGroup(this); + + //creation of the animations for moving letters + addGraphicsItemPosAnimation(lettersGroupMoving, logo_s, QPointF(300, 150)); + addGraphicsItemPosAnimation(lettersGroupMoving, logo_u, QPointF(350, 150)); + addGraphicsItemPosAnimation(lettersGroupMoving, logo_b, QPointF(400, 120)); + addGraphicsItemPosAnimation(lettersGroupMoving, logo_dash, QPointF(460, 150)); + addGraphicsItemPosAnimation(lettersGroupMoving, logo_a, QPointF(350, 250)); + addGraphicsItemPosAnimation(lettersGroupMoving, logo_t, QPointF(400, 250)); + addGraphicsItemPosAnimation(lettersGroupMoving, logo_t2, QPointF(430, 250)); + addGraphicsItemPosAnimation(lettersGroupMoving, logo_a2, QPointF(465, 250)); + addGraphicsItemPosAnimation(lettersGroupMoving, logo_q, QPointF(510, 250)); + addGraphicsItemPosAnimation(lettersGroupMoving, logo_excl, QPointF(570, 220)); + + //creation of the animations for fading out the letters + addGraphicsItemFadeoutAnimation(lettersGroupFading, logo_s); + addGraphicsItemFadeoutAnimation(lettersGroupFading, logo_u); + addGraphicsItemFadeoutAnimation(lettersGroupFading, logo_b); + addGraphicsItemFadeoutAnimation(lettersGroupFading, logo_dash); + addGraphicsItemFadeoutAnimation(lettersGroupFading, logo_a); + addGraphicsItemFadeoutAnimation(lettersGroupFading, logo_t); + addGraphicsItemFadeoutAnimation(lettersGroupFading, logo_t2); + addGraphicsItemFadeoutAnimation(lettersGroupFading, logo_a2); + addGraphicsItemFadeoutAnimation(lettersGroupFading, logo_q); + addGraphicsItemFadeoutAnimation(lettersGroupFading, logo_excl); + connect(lettersGroupFading, SIGNAL(finished()), this, SLOT(onIntroAnimationFinished())); + + QStateMachine *machine = new QStateMachine(this); + + //This state is when the player is playing + PlayState *gameState = new PlayState(this,machine->rootState()); + + //Final state + QFinalState *final = new QFinalState(machine->rootState()); + + //Animation when the player enter in the game + QAnimationState *lettersMovingState = new QAnimationState(machine->rootState()); + lettersMovingState->setAnimation(lettersGroupMoving); + + //Animation when the welcome screen disappear + QAnimationState *lettersFadingState = new QAnimationState(machine->rootState()); + lettersFadingState->setAnimation(lettersGroupFading); + + //if new game then we fade out the welcome screen and start playing + lettersMovingState->addTransition(newAction, SIGNAL(triggered()),lettersFadingState); + lettersFadingState->addTransition(lettersFadingState, SIGNAL(animationFinished()),gameState); + + //New Game is triggered then player start playing + gameState->addTransition(newAction, SIGNAL(triggered()),gameState); + + //Wanna quit, then connect to CTRL+Q + gameState->addTransition(quitAction, SIGNAL(triggered()),final); + lettersMovingState->addTransition(quitAction, SIGNAL(triggered()),final); + + //Welcome screen is the initial state + machine->setInitialState(lettersMovingState); + + machine->start(); + + //We reach the final state, then we quit + connect(machine,SIGNAL(finished()),this, SLOT(onQuitGameTriggered())); +} + +void GraphicsScene::addItem(Bomb *bomb) +{ + bombs.insert(bomb); + connect(bomb,SIGNAL(bombExecutionFinished()),this, SLOT(onBombExecutionFinished())); + QGraphicsScene::addItem(bomb); +} + +void GraphicsScene::addItem(Torpedo *torpedo) +{ + torpedos.insert(torpedo); + connect(torpedo,SIGNAL(torpedoExecutionFinished()),this, SLOT(onTorpedoExecutionFinished())); + QGraphicsScene::addItem(torpedo); +} + +void GraphicsScene::addItem(SubMarine *submarine) +{ + submarines.insert(submarine); + connect(submarine,SIGNAL(subMarineExecutionFinished()),this, SLOT(onSubMarineExecutionFinished())); + QGraphicsScene::addItem(submarine); +} + +void GraphicsScene::addItem(QGraphicsItem *item) +{ + QGraphicsScene::addItem(item); +} + +void GraphicsScene::mousePressEvent (QGraphicsSceneMouseEvent * event) +{ + event->ignore(); +} + +void GraphicsScene::onQuitGameTriggered() +{ + qApp->closeAllWindows(); +} + +void GraphicsScene::onBombExecutionFinished() +{ + Bomb *bomb = qobject_cast<Bomb *>(sender()); + bombs.remove(bomb); + bomb->deleteLater(); + if (boat) + boat->setBombsLaunched(boat->bombsLaunched() - 1); +} + +void GraphicsScene::onTorpedoExecutionFinished() +{ + Torpedo *torpedo = qobject_cast<Torpedo *>(sender()); + torpedos.remove(torpedo); + torpedo->deleteLater(); +} + +void GraphicsScene::onSubMarineExecutionFinished() +{ + SubMarine *submarine = qobject_cast<SubMarine *>(sender()); + submarines.remove(submarine); + if (submarines.count() == 0) { + emit allSubMarineDestroyed(submarine->points()); + } else { + emit subMarineDestroyed(submarine->points()); + } + submarine->deleteLater(); +} + +int GraphicsScene::remainingSubMarines() const +{ + return submarines.count(); +} + +void GraphicsScene::clearScene() +{ + foreach (SubMarine *sub,submarines) { + sub->destroy(); + delete sub; + } + + foreach (Torpedo *torpedo,torpedos) { + torpedo->destroy(); + delete torpedo; + } + + foreach (Bomb *bomb,bombs) { + bomb->destroy(); + delete bomb; + } + + submarines.clear(); + bombs.clear(); + torpedos.clear(); + + AnimationManager::self()->unregisterAllAnimations(); + + if (boat) { + delete boat; + boat = 0; + } +} + +QGraphicsPixmapItem *GraphicsScene::addWelcomeItem(const QPixmap &pm) +{ + QGraphicsPixmapItem *item = addPixmap(pm); + welcomeItems << item; + return item; +} + +void GraphicsScene::onIntroAnimationFinished() +{ + qDeleteAll(welcomeItems); + welcomeItems.clear(); +} + diff --git a/examples/animation/sub-attaq/graphicsscene.h b/examples/animation/sub-attaq/graphicsscene.h new file mode 100644 index 0000000..875f59f --- /dev/null +++ b/examples/animation/sub-attaq/graphicsscene.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef __GRAPHICSSCENE__H__ +#define __GRAPHICSSCENE__H__ + +//Qt +#include <QGraphicsScene> +#include <QSet> + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qstate.h" +#else +# include <QState> +#endif + + +class Boat; +class SubMarine; +class Torpedo; +class Bomb; +class PixmapItem; +class QAction; + +class GraphicsScene : public QGraphicsScene +{ +Q_OBJECT +public: + enum Mode { + Big = 0, + Small + }; + + struct SubmarineDescription { + int type; + int points; + QString name; + }; + + struct LevelDescription { + int id; + QString name; + QList<QPair<int,int> > submarines; + }; + + GraphicsScene(int x, int y, int width, int height, Mode mode = Big); + qreal sealLevel() const; + void setupScene(const QList<QAction *> &actions); + void addItem(Bomb *bomb); + void addItem(Torpedo *torpedo); + void addItem(SubMarine *submarine); + void addItem(QGraphicsItem *item); + int remainingSubMarines() const; + void clearScene(); + QGraphicsPixmapItem *addWelcomeItem(const QPixmap &pm); + +Q_SIGNALS: + void subMarineDestroyed(int); + void allSubMarineDestroyed(int); + +protected: + void mousePressEvent (QGraphicsSceneMouseEvent * event); + +private slots: + void onQuitGameTriggered(); + void onBombExecutionFinished(); + void onTorpedoExecutionFinished(); + void onSubMarineExecutionFinished(); + void onIntroAnimationFinished(); + +private: + Mode mode; + PixmapItem *backgroundItem; + QAction * newAction; + QAction * quitAction; + Boat *boat; + QSet<SubMarine *> submarines; + QSet<Bomb *> bombs; + QSet<Torpedo *> torpedos; + QVector<QGraphicsPixmapItem *> welcomeItems; + QVector<SubmarineDescription> submarinesData; + QHash<int, LevelDescription> levelsData; + + friend class PauseState; + friend class PlayState; + friend class LostState; + friend class WinState; +}; + +#endif //__GRAPHICSSCENE__H__ + diff --git a/examples/animation/sub-attaq/main.cpp b/examples/animation/sub-attaq/main.cpp new file mode 100644 index 0000000..ffaa86f --- /dev/null +++ b/examples/animation/sub-attaq/main.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> + +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + Q_INIT_RESOURCE(subattaq); + + qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); + + MainWindow w; + w.show(); + + return app.exec(); +} diff --git a/examples/animation/sub-attaq/mainwindow.cpp b/examples/animation/sub-attaq/mainwindow.cpp new file mode 100644 index 0000000..c25b9ef --- /dev/null +++ b/examples/animation/sub-attaq/mainwindow.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//Own +#include "mainwindow.h" +#include "graphicsscene.h" + +#ifndef QT_NO_OPENGL + #include <QtOpenGL/QtOpenGL> +#endif +//Qt +#include <QGraphicsView> + +MainWindow::MainWindow() : QMainWindow(0) +{ + QMenuBar *menuBar = new QMenuBar; + QMenu *file = new QMenu(tr("&File"),menuBar); + + QAction *newAction = new QAction(tr("New Game"),file); + newAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_N)); + file->addAction(newAction); + QAction *quitAction = new QAction(tr("Quit"),file); + quitAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q)); + file->addAction(quitAction); + + menuBar->addMenu(file); + setMenuBar(menuBar); + + QStringList list = QApplication::arguments(); + if (list.contains("-fullscreen")) { + scene = new GraphicsScene(0, 0, 750, 400,GraphicsScene::Small); + setWindowState(Qt::WindowFullScreen); + } else { + scene = new GraphicsScene(0, 0, 880, 630); + layout()->setSizeConstraint(QLayout::SetFixedSize); + } + + view = new QGraphicsView(scene,this); + view->setAlignment(Qt::AlignLeft | Qt::AlignTop); + QList<QAction *> actions; + actions << newAction << quitAction; + scene->setupScene(actions); +#ifndef QT_NO_OPENGL + view->setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers))); +#endif + + setCentralWidget(view); + +} + +MainWindow::~MainWindow() +{ +} + diff --git a/examples/animation/sub-attaq/mainwindow.h b/examples/animation/sub-attaq/mainwindow.h new file mode 100644 index 0000000..6289b3f --- /dev/null +++ b/examples/animation/sub-attaq/mainwindow.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef __MAINWINDOW__H__ +#define __MAINWINDOW__H__ + +//Qt +#include <QMainWindow> + +class GraphicsScene; +class QGraphicsView; + +class MainWindow : public QMainWindow +{ +Q_OBJECT +public: + MainWindow(); + ~MainWindow(); + +private: + GraphicsScene *scene; + QGraphicsView *view; +}; + +#endif //__MAINWINDOW__H__ diff --git a/examples/animation/sub-attaq/pics/big/background.png b/examples/animation/sub-attaq/pics/big/background.png Binary files differnew file mode 100644 index 0000000..9f58157 --- /dev/null +++ b/examples/animation/sub-attaq/pics/big/background.png diff --git a/examples/animation/sub-attaq/pics/big/boat.png b/examples/animation/sub-attaq/pics/big/boat.png Binary files differnew file mode 100644 index 0000000..be82dff --- /dev/null +++ b/examples/animation/sub-attaq/pics/big/boat.png diff --git a/examples/animation/sub-attaq/pics/big/bomb.png b/examples/animation/sub-attaq/pics/big/bomb.png Binary files differnew file mode 100644 index 0000000..3af5f2f --- /dev/null +++ b/examples/animation/sub-attaq/pics/big/bomb.png diff --git a/examples/animation/sub-attaq/pics/big/explosion/boat/step1.png b/examples/animation/sub-attaq/pics/big/explosion/boat/step1.png Binary files differnew file mode 100644 index 0000000..c9fd8b0 --- /dev/null +++ b/examples/animation/sub-attaq/pics/big/explosion/boat/step1.png diff --git a/examples/animation/sub-attaq/pics/big/explosion/boat/step2.png b/examples/animation/sub-attaq/pics/big/explosion/boat/step2.png Binary files differnew file mode 100644 index 0000000..7528f2d --- /dev/null +++ b/examples/animation/sub-attaq/pics/big/explosion/boat/step2.png diff --git a/examples/animation/sub-attaq/pics/big/explosion/boat/step3.png b/examples/animation/sub-attaq/pics/big/explosion/boat/step3.png Binary files differnew file mode 100644 index 0000000..aae9c9c --- /dev/null +++ b/examples/animation/sub-attaq/pics/big/explosion/boat/step3.png diff --git a/examples/animation/sub-attaq/pics/big/explosion/boat/step4.png b/examples/animation/sub-attaq/pics/big/explosion/boat/step4.png Binary files differnew file mode 100644 index 0000000..d697c1b --- /dev/null +++ b/examples/animation/sub-attaq/pics/big/explosion/boat/step4.png diff --git a/examples/animation/sub-attaq/pics/big/explosion/submarine/step1.png b/examples/animation/sub-attaq/pics/big/explosion/submarine/step1.png Binary files differnew file mode 100644 index 0000000..88ca514 --- /dev/null +++ b/examples/animation/sub-attaq/pics/big/explosion/submarine/step1.png diff --git a/examples/animation/sub-attaq/pics/big/explosion/submarine/step2.png b/examples/animation/sub-attaq/pics/big/explosion/submarine/step2.png Binary files differnew file mode 100644 index 0000000..524f589 --- /dev/null +++ b/examples/animation/sub-attaq/pics/big/explosion/submarine/step2.png diff --git a/examples/animation/sub-attaq/pics/big/explosion/submarine/step3.png b/examples/animation/sub-attaq/pics/big/explosion/submarine/step3.png Binary files differnew file mode 100644 index 0000000..2cca1e8 --- /dev/null +++ b/examples/animation/sub-attaq/pics/big/explosion/submarine/step3.png diff --git a/examples/animation/sub-attaq/pics/big/explosion/submarine/step4.png b/examples/animation/sub-attaq/pics/big/explosion/submarine/step4.png Binary files differnew file mode 100644 index 0000000..82100a8 --- /dev/null +++ b/examples/animation/sub-attaq/pics/big/explosion/submarine/step4.png diff --git a/examples/animation/sub-attaq/pics/big/submarine.png b/examples/animation/sub-attaq/pics/big/submarine.png Binary files differnew file mode 100644 index 0000000..df435dc --- /dev/null +++ b/examples/animation/sub-attaq/pics/big/submarine.png diff --git a/examples/animation/sub-attaq/pics/big/surface.png b/examples/animation/sub-attaq/pics/big/surface.png Binary files differnew file mode 100644 index 0000000..4eba29e --- /dev/null +++ b/examples/animation/sub-attaq/pics/big/surface.png diff --git a/examples/animation/sub-attaq/pics/big/torpedo.png b/examples/animation/sub-attaq/pics/big/torpedo.png Binary files differnew file mode 100644 index 0000000..f9c2687 --- /dev/null +++ b/examples/animation/sub-attaq/pics/big/torpedo.png diff --git a/examples/animation/sub-attaq/pics/scalable/background-n810.svg b/examples/animation/sub-attaq/pics/scalable/background-n810.svg new file mode 100644 index 0000000..ece9f7a --- /dev/null +++ b/examples/animation/sub-attaq/pics/scalable/background-n810.svg @@ -0,0 +1,171 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.0" + width="744.09448" + height="1052.3622" + id="svg2588" + sodipodi:version="0.32" + inkscape:version="0.46" + sodipodi:docname="background-n810.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <metadata + id="metadata28"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <sodipodi:namedview + inkscape:window-height="1141" + inkscape:window-width="1920" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + guidetolerance="10.0" + gridtolerance="10.0" + objecttolerance="10.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + showgrid="false" + inkscape:zoom="1.2399902" + inkscape:cx="375" + inkscape:cy="461.074" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:current-layer="layer1" /> + <defs + id="defs2590"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 526.18109 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="744.09448 : 526.18109 : 1" + inkscape:persp3d-origin="372.04724 : 350.78739 : 1" + id="perspective30" /> + <linearGradient + id="linearGradient3746"> + <stop + id="stop3748" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop3750" + style="stop-color:#0074b7;stop-opacity:1" + offset="1" /> + </linearGradient> + <radialGradient + cx="82.966125" + cy="-178.42453" + r="526.79456" + fx="82.966125" + fy="-178.42453" + id="radialGradient3880" + xlink:href="#linearGradient3746" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.4952094,0.2388475,-0.1040669,0.3734391,-208.61982,418.216)" /> + <linearGradient + id="linearGradient3624"> + <stop + id="stop3626" + style="stop-color:#3a8daf;stop-opacity:1" + offset="0" /> + <stop + id="stop3636" + style="stop-color:#252525;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="552.98486" + y1="390.56842" + x2="549.39465" + y2="702.3479" + id="linearGradient3630" + xlink:href="#linearGradient3624" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.3373776,0,0,1.186038,-986.88716,67.776416)" /> + <linearGradient + id="linearGradient3816"> + <stop + id="stop3818" + style="stop-color:#ad8b00;stop-opacity:1" + offset="0" /> + <stop + id="stop3820" + style="stop-color:#ad8b00;stop-opacity:0" + offset="1" /> + </linearGradient> + <linearGradient + x1="573" + y1="755.46222" + x2="573" + y2="700.13464" + id="linearGradient3826" + xlink:href="#linearGradient3816" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,1.2561681,-151.5,-34.518664)" /> + <linearGradient + id="linearGradient5097"> + <stop + id="stop5099" + style="stop-color:#19a2db;stop-opacity:0" + offset="0" /> + <stop + id="stop5109" + style="stop-color:#1379a7;stop-opacity:0.49803922" + offset="0.30000001" /> + <stop + id="stop5101" + style="stop-color:#0e5173;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="590.84674" + y1="274.57559" + x2="590.84674" + y2="334.01376" + id="linearGradient5103" + xlink:href="#linearGradient5097" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-151.5,156.75229)" + spreadMethod="pad" /> + </defs> + <g + id="layer1"> + <rect + width="1053.5891" + height="206.64989" + x="-151.79456" + y="330.16019" + id="rect3638" + style="opacity:1;fill:url(#radialGradient3880);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.1880002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + width="1054.4708" + height="364.81519" + x="-152.23541" + y="533.48895" + id="rect3622" + style="opacity:1;fill:url(#linearGradient3630);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.13464069;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M -152.5,877.11847 C 120.5,865.81296 -202.86309,769.3663 109.5,871.29717 C 172.96247,892.00636 243.5,872.55334 297.5,871.29717 C 351.5,870.041 311.5,859.80335 358.5,876.13354 C 405.5,892.46372 553.5,861.09903 598.5,854.8182 C 643.5,848.53736 756.5,841.79698 795.5,853.10249 C 834.5,864.408 904.5,866.2725 904.5,866.2725 L 901.5,903.95754 L -154.5,902.70137 L -152.5,877.11847 z" + id="path3814" + style="fill:url(#linearGradient3826);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 902.20121,894.16261 C 632.01828,889.43035 756.73005,860.2801 614.20403,894.1311 C 596.58819,898.315 408.23621,883.21212 400.43291,894.1311 C 376.86263,927.11261 75.265447,868.1243 34.250926,886.79082 C 31.281885,888.14209 12.514878,884.22134 -12.264082,889.72008 C -48.555335,897.77353 -64.717178,885.62471 -103.31472,890.35697 C -141.91229,895.08922 -145.87102,891.93439 -145.87102,891.93439 L -152.79879,903.10131 L 892.3044,902.5755 L 902.20121,894.16261 z" + id="path3828" + style="fill:#ad8b00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + </g> +</svg> diff --git a/examples/animation/sub-attaq/pics/scalable/background.svg b/examples/animation/sub-attaq/pics/scalable/background.svg new file mode 100644 index 0000000..0be2680 --- /dev/null +++ b/examples/animation/sub-attaq/pics/scalable/background.svg @@ -0,0 +1,171 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.0" + width="744.09448" + height="1052.3622" + id="svg2588" + sodipodi:version="0.32" + inkscape:version="0.46" + sodipodi:docname="background.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <metadata + id="metadata28"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <sodipodi:namedview + inkscape:window-height="1141" + inkscape:window-width="1920" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + guidetolerance="10.0" + gridtolerance="10.0" + objecttolerance="10.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + showgrid="false" + inkscape:zoom="0.93884027" + inkscape:cx="473.72605" + inkscape:cy="538.63678" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:current-layer="layer1" /> + <defs + id="defs2590"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 526.18109 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="744.09448 : 526.18109 : 1" + inkscape:persp3d-origin="372.04724 : 350.78739 : 1" + id="perspective30" /> + <linearGradient + id="linearGradient3746"> + <stop + id="stop3748" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop3750" + style="stop-color:#0074b7;stop-opacity:1" + offset="1" /> + </linearGradient> + <radialGradient + cx="82.966125" + cy="-178.42453" + r="526.79456" + fx="82.966125" + fy="-178.42453" + id="radialGradient3880" + xlink:href="#linearGradient3746" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.4952094,0.3367191,-0.1040669,0.5264617,-208.61982,282.52272)" /> + <linearGradient + id="linearGradient3624"> + <stop + id="stop3626" + style="stop-color:#3a8daf;stop-opacity:1" + offset="0" /> + <stop + id="stop3636" + style="stop-color:#252525;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="552.98486" + y1="390.56842" + x2="549.39465" + y2="702.3479" + id="linearGradient3630" + xlink:href="#linearGradient3624" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.3373776,0,0,1.5004634,-986.88716,-154.07447)" /> + <linearGradient + id="linearGradient3816"> + <stop + id="stop3818" + style="stop-color:#ad8b00;stop-opacity:1" + offset="0" /> + <stop + id="stop3820" + style="stop-color:#ad8b00;stop-opacity:0" + offset="1" /> + </linearGradient> + <linearGradient + x1="573" + y1="755.46222" + x2="573" + y2="700.13464" + id="linearGradient3826" + xlink:href="#linearGradient3816" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,1.6033628,-151.5,-294.0167)" /> + <linearGradient + id="linearGradient5097"> + <stop + id="stop5099" + style="stop-color:#19a2db;stop-opacity:0" + offset="0" /> + <stop + id="stop5109" + style="stop-color:#1379a7;stop-opacity:0.49803922" + offset="0.30000001" /> + <stop + id="stop5101" + style="stop-color:#0e5173;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="590.84674" + y1="274.57559" + x2="590.84674" + y2="334.01376" + id="linearGradient5103" + xlink:href="#linearGradient5097" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-151.5,156.75229)" + spreadMethod="pad" /> + </defs> + <g + id="layer1"> + <rect + width="1053.5891" + height="291.32797" + x="-151.79456" + y="158.38464" + id="rect3638" + style="opacity:1;fill:url(#radialGradient3880);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.1880002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + width="1054.4708" + height="461.52972" + x="-152.23541" + y="435.10107" + id="rect3622" + style="opacity:1;fill:url(#linearGradient3630);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.13464069;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M -152.5,869.5896 C 120.5,855.15934 -202.86309,732.0556 109.5,862.15934 C 172.96247,888.59238 243.5,863.7627 297.5,862.15934 C 351.5,860.55598 311.5,847.48872 358.5,868.33244 C 405.5,889.17615 553.5,849.14252 598.5,841.12571 C 643.5,833.1089 756.5,824.50553 795.5,838.9358 C 834.5,853.36606 904.5,855.74589 904.5,855.74589 L 901.5,903.84677 L -154.5,902.24341 L -152.5,869.5896 z" + id="path3814" + style="fill:url(#linearGradient3826);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 902.20121,891.3446 C 632.01828,885.30439 756.73005,848.09724 614.20403,891.30439 C 596.58819,896.64468 408.23621,877.36748 400.43291,891.30439 C 376.86263,933.40172 75.265447,858.10952 34.250926,881.93531 C 31.281885,883.66006 12.514878,878.65564 -12.264082,885.67419 C -48.555335,895.95355 -64.717178,880.4469 -103.31472,886.48711 C -141.91229,892.52732 -145.87102,888.50052 -145.87102,888.50052 L -152.79879,902.75389 L 892.3044,902.08275 L 902.20121,891.3446 z" + id="path3828" + style="fill:#ad8b00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + </g> +</svg> diff --git a/examples/animation/sub-attaq/pics/scalable/boat.svg b/examples/animation/sub-attaq/pics/scalable/boat.svg new file mode 100644 index 0000000..5298821b --- /dev/null +++ b/examples/animation/sub-attaq/pics/scalable/boat.svg @@ -0,0 +1,279 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + version="1.0" + width="744.09448" + height="1052.3622" + id="svg2584"> + <defs + id="defs2666"> + <linearGradient + x1="542.5" + y1="222.59448" + x2="559" + y2="222.59448" + id="linearGradient3387" + xlink:href="#linearGradient3746" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-110.6791,190.19124)" /> + <linearGradient + id="linearGradient3167"> + <stop + id="stop3169" + style="stop-color:#464646;stop-opacity:1" + offset="0" /> + <stop + id="stop3345" + style="stop-color:#848788;stop-opacity:1" + offset="0.44021741" /> + <stop + id="stop3347" + style="stop-color:#9ca0a2;stop-opacity:1" + offset="0.56799388" /> + <stop + id="stop3171" + style="stop-color:#b5babd;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="474.23065" + y1="229.92336" + x2="474.1944" + y2="218.27365" + id="linearGradient3416" + xlink:href="#linearGradient3167" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-125.98032,185.95625)" /> + <linearGradient + id="linearGradient3692"> + <stop + id="stop3694" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop3696" + style="stop-color:#b6b6b6;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="573.5" + y1="244.2056" + x2="578.25" + y2="216.9556" + id="linearGradient3972" + xlink:href="#linearGradient3692" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-126.5541,188.56624)" /> + <linearGradient + id="linearGradient3438"> + <stop + id="stop3440" + style="stop-color:#939393;stop-opacity:1" + offset="0" /> + <stop + id="stop3444" + style="stop-color:#d6d6d6;stop-opacity:1" + offset="0.12354442" /> + <stop + id="stop3446" + style="stop-color:#dadada;stop-opacity:1" + offset="0.74055624" /> + <stop + id="stop3442" + style="stop-color:#ffffff;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="660.29303" + y1="256.53284" + x2="444.79303" + y2="255.62085" + id="linearGradient3948" + xlink:href="#linearGradient3438" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-126.5541,185.56624)" /> + <linearGradient + x1="542.5" + y1="222.59448" + x2="559" + y2="222.59448" + id="linearGradient3990" + xlink:href="#linearGradient3746" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-132.8041,190.19124)" /> + <linearGradient + id="linearGradient3746"> + <stop + id="stop3748" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop3750" + style="stop-color:#0074b7;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="542.5" + y1="222.59448" + x2="559" + y2="222.59448" + id="linearGradient3994" + xlink:href="#linearGradient3746" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-88.054101,190.19124)" /> + <linearGradient + id="linearGradient3428"> + <stop + id="stop3430" + style="stop-color:#464646;stop-opacity:1" + offset="0" /> + <stop + id="stop3432" + style="stop-color:#848788;stop-opacity:1" + offset="0.18306103" /> + <stop + id="stop3434" + style="stop-color:#9ca0a2;stop-opacity:1" + offset="0.66368055" /> + <stop + id="stop3436" + style="stop-color:#b5babd;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="592.92798" + y1="199.43727" + x2="557.05743" + y2="196.5448" + id="linearGradient3426" + xlink:href="#linearGradient3428" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-102.5217,149.09845)" /> + </defs> + <g + id="layer1"> + <g + id="boat"> + <path + d="M 296.669,434.15623 C 376.12538,436.50959 448.282,436.46711 542.42304,434.15623 C 542.42304,434.15623 544.22253,425.03531 542.42304,422.57953 C 432.90655,403.86953 296.669,418.12547 296.669,422.57953 L 296.669,434.15623 z" + id="path3469" + style="fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:#484848;stroke-width:3.4975698;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" /> + <rect + width="3.4280596" + height="29.611124" + x="647.59613" + y="173.91156" + transform="matrix(0.9327494,0.3605254,-0.3633626,0.9316478,0,0)" + id="rect3408" + style="opacity:1;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + width="48.499989" + height="8.5" + x="318.48221" + y="405.82172" + transform="matrix(0.9999952,3.0887777e-3,-3.0887777e-3,0.9999952,0,0)" + id="rect3376" + style="opacity:1;fill:url(#linearGradient3416);fill-opacity:1;fill-rule:nonzero;stroke:#484848;stroke-width:2.99999928;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 382.4459,430.66072 C 382.4459,430.66072 420.85999,388.74829 397.4459,385.66072 L 488.4459,397.66072 L 488.4459,432.66072 L 382.4459,430.66072 z" + id="path3952" + style="fill:url(#linearGradient3972);fill-opacity:1;fill-rule:evenodd;stroke:#323232;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 301.4459,429.66072 C 301.4459,429.66072 330.46329,468.66072 343.4459,468.66072 C 355.42851,471.91072 507.57644,473.70653 525.4459,465.91072 C 534.58031,461.59104 537.90602,455.58662 539.4459,429.66072 C 473.70193,439.43306 371.2651,439.78219 301.4459,429.66072 z" + id="path3938" + style="fill:url(#linearGradient3948);fill-opacity:1;fill-rule:evenodd;stroke:#545454;stroke-width:3.0999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 339.44863,416.12222 L 357.69854,416.17859 L 368.1622,427.96097 L 339.41234,427.87217 L 339.44863,416.12222 z" + id="rect3378" + style="fill:#dedede;fill-opacity:1;fill-rule:nonzero;stroke:#484848;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" /> + <rect + width="13.5" + height="17" + x="411.19589" + y="404.28574" + id="rect3974" + style="opacity:1;fill:url(#linearGradient3990);fill-opacity:1;fill-rule:nonzero;stroke:#323232;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + width="13.5" + height="17" + x="455.94589" + y="404.28574" + id="rect3992" + style="opacity:1;fill:url(#linearGradient3994);fill-opacity:1;fill-rule:nonzero;stroke:#323232;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 295.6959,421.91072 C 360.77923,430.41072 446.61257,432.91072 541.9459,421.91072 C 541.9459,421.91072 543.74902,428.6076 541.9459,430.41072 C 432.20839,444.14823 295.6959,433.68104 295.6959,430.41072 L 295.6959,421.91072 z" + id="rect2558" + style="fill:#dedede;fill-opacity:1;fill-rule:nonzero;stroke:#484848;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" /> + <rect + width="94.427879" + height="7.236649" + x="437.10614" + y="342.2645" + transform="matrix(0.9947793,0.1020501,-0.1079723,0.9941539,0,0)" + id="rect2569" + style="opacity:1;fill:#c1c1c1;fill-opacity:1;fill-rule:nonzero;stroke:#404040;stroke-width:3.0365274;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + width="13.5" + height="17" + x="433.32089" + y="404.28574" + id="rect3385" + style="opacity:1;fill:url(#linearGradient3387);fill-opacity:1;fill-rule:nonzero;stroke:#323232;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 402.86916,380.21847 L 489.80407,388.85485 L 491.52271,394.54919 L 397.58781,384.91281 L 402.86916,380.21847 z" + id="rect3466" + style="fill:#dcdcdc;fill-opacity:1;fill-rule:nonzero;stroke:#404040;stroke-width:3.03650045;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" /> + <rect + width="34.5" + height="14.5" + x="456.4783" + y="336.94293" + transform="matrix(0.997157,7.5351915e-2,-7.5351915e-2,0.997157,0,0)" + id="rect3418" + style="opacity:1;fill:url(#linearGradient3426);fill-opacity:1;fill-rule:nonzero;stroke:#494949;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <g + transform="matrix(0.9246214,0.3808874,-0.3808874,0.9246214,-13.252851,-40.129692)" + id="flag"> + <rect + width="19.75" + height="27.75" + x="193.34448" + y="-709" + transform="matrix(0,1,-1,0,0,0)" + id="rect3389" + style="opacity:1;fill:#b20000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + width="27.25" + height="5.75" + x="681.5" + y="200.59448" + id="rect3393" + style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + width="5.75" + height="19.5" + x="691.25" + y="193.59448" + id="rect3395" + style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + width="27.75" + height="2.5" + x="681.5" + y="202.34448" + id="rect3397" + style="opacity:1;fill:#000080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + width="3" + height="19.25" + x="692.5" + y="193.59448" + id="rect3399" + style="opacity:1;fill:#000080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> + </g> + </g> +</svg> diff --git a/examples/animation/sub-attaq/pics/scalable/bomb.svg b/examples/animation/sub-attaq/pics/scalable/bomb.svg new file mode 100644 index 0000000..294771a --- /dev/null +++ b/examples/animation/sub-attaq/pics/scalable/bomb.svg @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + version="1.0" + width="744.09448" + height="1052.3622" + id="svg3121"> + <defs + id="defs3123"> + <radialGradient + cx="-135.625" + cy="148.71948" + r="7.625" + fx="-135.625" + fy="148.71948" + id="radialGradient3439" + xlink:href="#linearGradient3366" + gradientUnits="userSpaceOnUse" /> + <linearGradient + x1="-132.85063" + y1="173.6969" + x2="-145.3662" + y2="177.59828" + id="linearGradient3418" + xlink:href="#linearGradient3366" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.2134297,-0.5943658,0.6658882,-0.2391126,-274.53441,123.00067)" /> + <linearGradient + x1="-141.85466" + y1="181.49153" + x2="-144.95044" + y2="175.90179" + id="linearGradient3414" + xlink:href="#linearGradient3366" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2134297,-0.5943658,-0.6658882,-0.2391126,-15.893355,122.67824)" /> + <linearGradient + x1="-149.5" + y1="177.59448" + x2="-145.7928" + y2="180.05936" + id="linearGradient3410" + xlink:href="#linearGradient3366" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.6315243,0,0,0.7075182,-227.03781,54.321514)" /> + <linearGradient + x1="-140.46242" + y1="177.40488" + x2="-147.04802" + y2="172.66473" + id="linearGradient3406" + xlink:href="#linearGradient3366" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.6315243,0,0,-0.7075182,-226.40365,274.91611)" /> + <linearGradient + x1="-147.2406" + y1="180.95567" + x2="-140.01878" + y2="175.57777" + id="linearGradient3402" + xlink:href="#linearGradient3366" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.6315243,0,0,-0.7075182,-64.045217,275.07466)" /> + <linearGradient + x1="-146.98956" + y1="174.00922" + x2="-142.60332" + y2="179.38712" + id="linearGradient3398" + xlink:href="#linearGradient3366" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.6315243,0,0,0.7075182,-62.683611,54.187362)" /> + <linearGradient + id="linearGradient3366"> + <stop + id="stop3368" + style="stop-color:#bcbcbc;stop-opacity:1" + offset="0" /> + <stop + id="stop3370" + style="stop-color:#191b1c;stop-opacity:1" + offset="1" /> + </linearGradient> + <radialGradient + cx="-208.95004" + cy="173.10576" + r="31.667252" + fx="-208.95004" + fy="173.10576" + id="radialGradient3364" + xlink:href="#linearGradient3366" + gradientUnits="userSpaceOnUse" /> + </defs> + <g + id="layer1"> + <g + transform="translate(419.4996,488.13454)" + id="mine"> + <path + d="M -167.5843,186.54079 A 31.466251,31.466251 0 1 1 -230.5168,186.54079 A 31.466251,31.466251 0 1 1 -167.5843,186.54079 z" + transform="matrix(0.6341613,0,0,0.6341613,-18.521242,45.718192)" + id="path2586" + style="opacity:1;fill:url(#radialGradient3364);fill-opacity:1;stroke:#131313;stroke-width:3.54799318;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M -155.20193,175.4167 C -157.60085,176.6451 -156.78074,184.26068 -156.78074,184.26068 C -156.78074,184.26068 -148.33787,181.58301 -148.57092,178.60053 C -148.74283,176.40051 -153.23774,174.41092 -155.20193,175.4167 z" + id="path3382" + style="fill:url(#linearGradient3398);fill-opacity:1;fill-rule:evenodd;stroke:#131313;stroke-width:2.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M -156.56354,153.84532 C -158.96246,152.61693 -158.14235,145.00135 -158.14235,145.00135 C -158.14235,145.00135 -149.69948,147.67902 -149.93253,150.66149 C -150.10444,152.86151 -154.59935,154.85111 -156.56354,153.84532 z" + id="path3400" + style="fill:url(#linearGradient3402);fill-opacity:1;fill-rule:evenodd;stroke:#131313;stroke-width:2.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M -133.88532,153.68678 C -131.48641,152.45838 -132.30652,144.8428 -132.30652,144.8428 C -132.30652,144.8428 -140.74938,147.52047 -140.51633,150.50295 C -140.34442,152.70297 -135.84951,154.69256 -133.88532,153.68678 z" + id="path3404" + style="fill:url(#linearGradient3406);fill-opacity:1;fill-rule:evenodd;stroke:#131313;stroke-width:2.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M -134.51948,175.55085 C -132.12057,176.77925 -132.94068,184.39483 -132.94068,184.39483 C -132.94068,184.39483 -141.38355,181.71716 -141.15049,178.73469 C -140.97858,176.53467 -136.48367,174.54507 -134.51948,175.55085 z" + id="path3408" + style="fill:url(#linearGradient3410);fill-opacity:1;fill-rule:evenodd;stroke:#131313;stroke-width:2.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M -161.25709,168.78221 C -163.22395,170.62484 -170.11427,165.85236 -170.11427,165.85236 C -170.11427,165.85236 -164.7408,160.23808 -162.01257,161.46538 C -160.00011,162.37068 -159.64667,167.27352 -161.25709,168.78221 z" + id="path3412" + style="fill:url(#linearGradient3414);fill-opacity:1;fill-rule:evenodd;stroke:#131313;stroke-width:2.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M -129.17068,169.10464 C -127.20382,170.94727 -120.3135,166.17478 -120.3135,166.17478 C -120.3135,166.17478 -125.68697,160.5605 -128.41519,161.7878 C -130.42766,162.69311 -130.7811,167.59595 -129.17068,169.10464 z" + id="path3416" + style="fill:url(#linearGradient3418);fill-opacity:1;fill-rule:evenodd;stroke:#131313;stroke-width:2.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M -126,151.21948 A 6.625,6.625 0 1 1 -139.25,151.21948 A 6.625,6.625 0 1 1 -126,151.21948 z" + transform="matrix(0.6341613,0,0,0.6341613,-61.039517,68.324922)" + id="path3426" + style="opacity:1;fill:url(#radialGradient3439);fill-opacity:1;stroke:#131313;stroke-width:3.54799318;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> + </g> +</svg> diff --git a/examples/animation/sub-attaq/pics/scalable/sand.svg b/examples/animation/sub-attaq/pics/scalable/sand.svg new file mode 100644 index 0000000..8af11b7 --- /dev/null +++ b/examples/animation/sub-attaq/pics/scalable/sand.svg @@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + version="1.0" + width="744.09448" + height="1052.3622" + id="svg2596"> + <defs + id="defs2598"> + <linearGradient + id="linearGradient3708"> + <stop + id="stop3710" + style="stop-color:#202020;stop-opacity:1" + offset="0" /> + <stop + id="stop3712" + style="stop-color:#ffffff;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="518.26996" + y1="497.31476" + x2="533.02924" + y2="497.31476" + id="linearGradient3794" + xlink:href="#linearGradient3708" + gradientUnits="userSpaceOnUse" /> + <linearGradient + id="linearGradient3718"> + <stop + id="stop3720" + style="stop-color:#bcbcbc;stop-opacity:0.28169015" + offset="0" /> + <stop + id="stop3722" + style="stop-color:#bcbcbc;stop-opacity:0" + offset="1" /> + </linearGradient> + <linearGradient + x1="516.89508" + y1="503.50137" + x2="516.89508" + y2="543.80646" + id="linearGradient3792" + xlink:href="#linearGradient3718" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9947644,0,0,1.3346457,2.7877039,-166.60153)" /> + <linearGradient + id="linearGradient3692"> + <stop + id="stop3694" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop3696" + style="stop-color:#b6b6b6;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="506.95975" + y1="469.73706" + x2="525.41608" + y2="469.73706" + id="linearGradient3790" + xlink:href="#linearGradient3692" + gradientUnits="userSpaceOnUse" /> + <linearGradient + id="linearGradient3816"> + <stop + id="stop3818" + style="stop-color:#ad8b00;stop-opacity:1" + offset="0" /> + <stop + id="stop3820" + style="stop-color:#ad8b00;stop-opacity:0" + offset="1" /> + </linearGradient> + <linearGradient + x1="573" + y1="755.46222" + x2="573" + y2="700.13464" + id="linearGradient3826" + xlink:href="#linearGradient3816" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,1.6033628,-150.63569,-350.3846)" /> + </defs> + <g + id="layer1"> + <path + d="M -151.63569,813.2217 C 121.3643,798.79144 -201.99878,675.6877 110.3643,805.79144 C 173.82677,832.22448 244.3643,807.3948 298.3643,805.79144 C 352.3643,804.18808 312.3643,791.12082 359.3643,811.96454 C 406.3643,832.80825 554.3643,792.77462 599.3643,784.75781 C 644.3643,776.741 757.36426,768.13763 796.36426,782.5679 C 835.36426,796.99816 905.36426,799.37799 905.36426,799.37799 L 902.36426,847.47887 L -153.63569,845.87551 L -151.63569,813.2217 z" + id="path3814" + style="fill:url(#linearGradient3826);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 908.86426,836.95812 C 635.8643,830.91791 761.87636,793.71076 617.8643,836.91791 C 600.0648,842.2582 409.74894,822.981 401.8643,836.91791 C 378.04825,879.01524 73.306465,803.72304 31.864305,827.54883 C 28.864305,829.27358 9.9016246,824.26916 -15.135695,831.28771 C -51.805335,841.56707 -68.135695,826.06042 -107.1357,832.10063 C -146.1357,838.14084 -150.13569,834.11404 -150.13569,834.11404 L -157.13569,848.36741 L 898.86426,847.69627 L 908.86426,836.95812 z" + id="path3828" + style="fill:#ad8b00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + </g> +</svg> diff --git a/examples/animation/sub-attaq/pics/scalable/see.svg b/examples/animation/sub-attaq/pics/scalable/see.svg new file mode 100644 index 0000000..0666691 --- /dev/null +++ b/examples/animation/sub-attaq/pics/scalable/see.svg @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + version="1.0" + width="744.09448" + height="1052.3622" + id="svg2650"> + <defs + id="defs2652"> + <linearGradient + id="linearGradient3624"> + <stop + id="stop3626" + style="stop-color:#3a8daf;stop-opacity:1" + offset="0" /> + <stop + id="stop3636" + style="stop-color:#252525;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="552.98486" + y1="390.56842" + x2="549.39465" + y2="702.3479" + id="linearGradient3630" + xlink:href="#linearGradient3624" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.3373776,0,0,1.5004634,-996.17287,-279.00679)" /> + </defs> + <g + id="layer1"> + <rect + width="1054.4708" + height="461.52972" + x="-161.52115" + y="310.16876" + id="rect3622" + style="opacity:1;fill:url(#linearGradient3630);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.13464069;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> +</svg> diff --git a/examples/animation/sub-attaq/pics/scalable/sky.svg b/examples/animation/sub-attaq/pics/scalable/sky.svg new file mode 100644 index 0000000..1546c08 --- /dev/null +++ b/examples/animation/sub-attaq/pics/scalable/sky.svg @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + version="1.0" + width="744.09448" + height="1052.3622" + id="svg2721"> + <defs + id="defs2723"> + <linearGradient + id="linearGradient3746"> + <stop + id="stop3748" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop3750" + style="stop-color:#0074b7;stop-opacity:1" + offset="1" /> + </linearGradient> + <radialGradient + cx="82.966125" + cy="-178.42453" + r="526.79456" + fx="82.966125" + fy="-178.42453" + id="radialGradient3880" + xlink:href="#linearGradient3746" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.4952094,0.3367191,-0.1040669,0.5264617,-235.04839,425.12197)" /> + </defs> + <g + id="layer1"> + <rect + width="1053.5891" + height="291.32797" + x="-178.22313" + y="300.98392" + id="rect3638" + style="opacity:1;fill:url(#radialGradient3880);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.1880002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> +</svg> diff --git a/examples/animation/sub-attaq/pics/scalable/sub-attaq.svg b/examples/animation/sub-attaq/pics/scalable/sub-attaq.svg new file mode 100644 index 0000000..b075179 --- /dev/null +++ b/examples/animation/sub-attaq/pics/scalable/sub-attaq.svg @@ -0,0 +1,1473 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="1052.3622" + height="744.09448" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.46" + version="1.0" + sodipodi:docname="sub-attaq.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs4"> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3366" + id="radialGradient3439" + cx="-135.625" + cy="148.71948" + fx="-135.625" + fy="148.71948" + r="7.625" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3366" + id="linearGradient3418" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.2134297,-0.5943658,0.6658882,-0.2391126,-274.53441,123.00067)" + x1="-132.85063" + y1="173.6969" + x2="-145.3662" + y2="177.59828" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3366" + id="linearGradient3414" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2134297,-0.5943658,-0.6658882,-0.2391126,-15.893355,122.67824)" + x1="-141.85466" + y1="181.49153" + x2="-144.95044" + y2="175.90179" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3366" + id="linearGradient3410" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.6315243,0,0,0.7075182,-227.03781,54.321514)" + x1="-149.5" + y1="177.59448" + x2="-145.7928" + y2="180.05936" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3366" + id="linearGradient3406" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.6315243,0,0,-0.7075182,-226.40365,274.91611)" + x1="-140.46242" + y1="177.40488" + x2="-147.04802" + y2="172.66473" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3366" + id="linearGradient3402" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.6315243,0,0,-0.7075182,-64.045217,275.07466)" + x1="-147.2406" + y1="180.95567" + x2="-140.01878" + y2="175.57777" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3366" + id="linearGradient3398" + x1="-146.98956" + y1="174.00922" + x2="-142.60332" + y2="179.38712" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.6315243,0,0,0.7075182,-62.683611,54.187362)" /> + <linearGradient + id="linearGradient3366"> + <stop + id="stop3368" + offset="0" + style="stop-color:#bcbcbc;stop-opacity:1;" /> + <stop + id="stop3370" + offset="1" + style="stop-color:#191b1c;stop-opacity:1;" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3366" + id="radialGradient3364" + cx="-208.95004" + cy="173.10576" + fx="-208.95004" + fy="173.10576" + r="31.667252" + gradientUnits="userSpaceOnUse" /> + <linearGradient + id="linearGradient5097"> + <stop + style="stop-color:#19a2db;stop-opacity:0;" + offset="0" + id="stop5099" /> + <stop + id="stop5109" + offset="0.30000001" + style="stop-color:#1379a7;stop-opacity:0.49803922;" /> + <stop + style="stop-color:#0e5173;stop-opacity:1;" + offset="1" + id="stop5101" /> + </linearGradient> + <linearGradient + id="linearGradient3523" + inkscape:collect="always"> + <stop + id="stop3525" + offset="0" + style="stop-color:#b9b9b9;stop-opacity:1" /> + <stop + id="stop3527" + offset="1" + style="stop-color:#444444;stop-opacity:0;" /> + </linearGradient> + <linearGradient + id="linearGradient3438"> + <stop + style="stop-color:#939393;stop-opacity:1;" + offset="0" + id="stop3440" /> + <stop + id="stop3444" + offset="0.12354442" + style="stop-color:#d6d6d6;stop-opacity:1;" /> + <stop + style="stop-color:#dadada;stop-opacity:1;" + offset="0.74055624" + id="stop3446" /> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="1" + id="stop3442" /> + </linearGradient> + <linearGradient + id="linearGradient3428"> + <stop + id="stop3430" + offset="0" + style="stop-color:#464646;stop-opacity:1;" /> + <stop + style="stop-color:#848788;stop-opacity:1;" + offset="0.18306103" + id="stop3432" /> + <stop + id="stop3434" + offset="0.66368055" + style="stop-color:#9ca0a2;stop-opacity:1;" /> + <stop + id="stop3436" + offset="1" + style="stop-color:#b5babd;stop-opacity:1;" /> + </linearGradient> + <linearGradient + id="linearGradient4034"> + <stop + id="stop4036" + offset="0" + style="stop-color:#ffffff;stop-opacity:1;" /> + <stop + style="stop-color:#ffffff;stop-opacity:0.49803922;" + offset="0.5" + id="stop4038" /> + <stop + id="stop4040" + offset="0.63705367" + style="stop-color:#ffffff;stop-opacity:0.24705882;" /> + <stop + style="stop-color:#ffffff;stop-opacity:0.12156863;" + offset="0.79425853" + id="stop4042" /> + <stop + id="stop4044" + offset="1" + style="stop-color:#a0a0a0;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient4016"> + <stop + style="stop-color:#283e6a;stop-opacity:1;" + offset="0" + id="stop4018" /> + <stop + style="stop-color:#283e6a;stop-opacity:0;" + offset="1" + id="stop4020" /> + </linearGradient> + <linearGradient + id="linearGradient4004"> + <stop + style="stop-color:#dbdbdb;stop-opacity:1;" + offset="0" + id="stop4010" /> + <stop + style="stop-color:#c4c9cb;stop-opacity:1;" + offset="1" + id="stop4012" /> + </linearGradient> + <linearGradient + id="linearGradient3998"> + <stop + id="stop4000" + offset="0" + style="stop-color:#adadad;stop-opacity:1;" /> + <stop + id="stop4002" + offset="1" + style="stop-color:#ffffff;stop-opacity:1;" /> + </linearGradient> + <linearGradient + id="linearGradient3864"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop3866" /> + <stop + id="stop4028" + offset="0.5" + style="stop-color:#ffffff;stop-opacity:0.49803922;" /> + <stop + style="stop-color:#ffffff;stop-opacity:0.24705882;" + offset="0.75" + id="stop4030" /> + <stop + id="stop4032" + offset="0.875" + style="stop-color:#ffffff;stop-opacity:0.12156863;" /> + <stop + style="stop-color:#a0a0a0;stop-opacity:1;" + offset="1" + id="stop3868" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient3816"> + <stop + style="stop-color:#ad8b00;stop-opacity:1;" + offset="0" + id="stop3818" /> + <stop + style="stop-color:#ad8b00;stop-opacity:0;" + offset="1" + id="stop3820" /> + </linearGradient> + <linearGradient + id="linearGradient3746"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop3748" /> + <stop + style="stop-color:#0074b7;stop-opacity:1;" + offset="1" + id="stop3750" /> + </linearGradient> + <linearGradient + id="linearGradient3718"> + <stop + style="stop-color:#bcbcbc;stop-opacity:0.28169015;" + offset="0" + id="stop3720" /> + <stop + style="stop-color:#bcbcbc;stop-opacity:0;" + offset="1" + id="stop3722" /> + </linearGradient> + <linearGradient + id="linearGradient3708"> + <stop + style="stop-color:#202020;stop-opacity:1;" + offset="0" + id="stop3710" /> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="1" + id="stop3712" /> + </linearGradient> + <linearGradient + id="linearGradient3692"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop3694" /> + <stop + style="stop-color:#b6b6b6;stop-opacity:1;" + offset="1" + id="stop3696" /> + </linearGradient> + <linearGradient + id="linearGradient3656"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop3658" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop3660" /> + </linearGradient> + <linearGradient + id="linearGradient3624"> + <stop + style="stop-color:#3a8daf;stop-opacity:1;" + offset="0" + id="stop3626" /> + <stop + id="stop3636" + offset="1" + style="stop-color:#252525;stop-opacity:1;" /> + </linearGradient> + <linearGradient + id="linearGradient3532"> + <stop + id="stop3534" + offset="0" + style="stop-color:#545454;stop-opacity:1;" /> + <stop + style="stop-color:#848788;stop-opacity:1;" + offset="0.44021741" + id="stop3536" /> + <stop + id="stop3538" + offset="0.56799388" + style="stop-color:#9ca0a2;stop-opacity:1;" /> + <stop + id="stop3540" + offset="1" + style="stop-color:#565d60;stop-opacity:1" /> + </linearGradient> + <linearGradient + id="linearGradient3345"> + <stop + id="stop3348" + offset="0" + style="stop-color:#898989;stop-opacity:1;" /> + <stop + style="stop-color:#9ea1a2;stop-opacity:1;" + offset="0.44021741" + id="stop3350" /> + <stop + id="stop3352" + offset="0.56799388" + style="stop-color:#bbbdbf;stop-opacity:1;" /> + <stop + id="stop3354" + offset="1" + style="stop-color:#f0f1f2;stop-opacity:1;" /> + </linearGradient> + <linearGradient + id="linearGradient3227"> + <stop + style="stop-color:#444444;stop-opacity:1;" + offset="0" + id="stop3229" /> + <stop + style="stop-color:#b0b0b0;stop-opacity:1;" + offset="1" + id="stop3232" /> + </linearGradient> + <linearGradient + id="linearGradient3435"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop3437" /> + <stop + style="stop-color:#c0c0c0;stop-opacity:0;" + offset="1" + id="stop3439" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient3421"> + <stop + style="stop-color:#444444;stop-opacity:1;" + offset="0" + id="stop3423" /> + <stop + style="stop-color:#444444;stop-opacity:0;" + offset="1" + id="stop3425" /> + </linearGradient> + <linearGradient + id="linearGradient3293"> + <stop + style="stop-color:#c4b434;stop-opacity:1;" + offset="0" + id="stop3295" /> + <stop + style="stop-color:#9b5500;stop-opacity:1;" + offset="1" + id="stop3297" /> + </linearGradient> + <linearGradient + id="linearGradient3229"> + <stop + style="stop-color:#125a7a;stop-opacity:1;" + offset="0" + id="stop3231" /> + <stop + style="stop-color:#308fc0;stop-opacity:1;" + offset="1" + id="stop3233" /> + </linearGradient> + <linearGradient + id="linearGradient3219"> + <stop + id="stop3221" + offset="0" + style="stop-color:#a55b00;stop-opacity:1;" /> + <stop + id="stop3223" + offset="1" + style="stop-color:#f4e45e;stop-opacity:1;" /> + </linearGradient> + <linearGradient + id="linearGradient3189"> + <stop + style="stop-color:#000000;stop-opacity:1;" + offset="0" + id="stop3191" /> + <stop + style="stop-color:#000000;stop-opacity:0;" + offset="1" + id="stop3193" /> + </linearGradient> + <linearGradient + id="linearGradient3167"> + <stop + style="stop-color:#464646;stop-opacity:1;" + offset="0" + id="stop3169" /> + <stop + id="stop3345" + offset="0.44021741" + style="stop-color:#848788;stop-opacity:1;" /> + <stop + style="stop-color:#9ca0a2;stop-opacity:1;" + offset="0.56799388" + id="stop3347" /> + <stop + style="stop-color:#b5babd;stop-opacity:1;" + offset="1" + id="stop3171" /> + </linearGradient> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 526.18109 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="744.09448 : 526.18109 : 1" + inkscape:persp3d-origin="372.04724 : 350.78739 : 1" + id="perspective10" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3167" + id="linearGradient3175" + x1="443.95602" + y1="315.31854" + x2="443.95602" + y2="247.85609" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4908502,0,0,0.4579593,350.98557,542.12189)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3219" + id="linearGradient3253" + gradientUnits="userSpaceOnUse" + x1="325.57214" + y1="280.13632" + x2="312.84424" + y2="257.60013" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3229" + id="linearGradient3255" + gradientUnits="userSpaceOnUse" + x1="310.01578" + y1="255.47881" + x2="325.92572" + y2="280.13632" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3219" + id="linearGradient3321" + gradientUnits="userSpaceOnUse" + x1="325.57214" + y1="280.13632" + x2="312.84424" + y2="257.60013" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3229" + id="linearGradient3323" + gradientUnits="userSpaceOnUse" + x1="310.01578" + y1="255.47881" + x2="325.92572" + y2="280.13632" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3219" + id="linearGradient3331" + gradientUnits="userSpaceOnUse" + x1="325.57214" + y1="280.13632" + x2="312.84424" + y2="257.60013" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3229" + id="linearGradient3333" + gradientUnits="userSpaceOnUse" + x1="310.01578" + y1="255.47881" + x2="325.92572" + y2="280.13632" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3293" + id="linearGradient3343" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.3292883,0,0,1.10796,1.5038593,-24.232315)" + x1="359.5589" + y1="258.84247" + x2="370.88239" + y2="258.84247" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3219" + id="linearGradient3365" + gradientUnits="userSpaceOnUse" + x1="325.57214" + y1="280.13632" + x2="312.84424" + y2="257.60013" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3229" + id="linearGradient3367" + gradientUnits="userSpaceOnUse" + x1="310.01578" + y1="255.47881" + x2="325.92572" + y2="280.13632" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3219" + id="linearGradient3369" + gradientUnits="userSpaceOnUse" + x1="325.57214" + y1="280.13632" + x2="312.84424" + y2="257.60013" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3229" + id="linearGradient3371" + gradientUnits="userSpaceOnUse" + x1="310.01578" + y1="255.47881" + x2="325.92572" + y2="280.13632" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3219" + id="linearGradient3379" + gradientUnits="userSpaceOnUse" + x1="325.57214" + y1="280.13632" + x2="312.84424" + y2="257.60013" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3229" + id="linearGradient3381" + gradientUnits="userSpaceOnUse" + x1="310.01578" + y1="255.47881" + x2="325.92572" + y2="280.13632" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3293" + id="linearGradient3385" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.3267302,0,0,1.1332782,-1.5786343,-29.194748)" + x1="371.79858" + y1="258.84247" + x2="364.49646" + y2="258.84247" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3293" + id="linearGradient3401" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.9807835,0,0,1.1280701,-361.45126,-28.553769)" + x1="371.79858" + y1="258.84247" + x2="364.49646" + y2="258.84247" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3421" + id="radialGradient3431" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1862613,0,0,0.3638703,-186.86143,179.02055)" + cx="432.3343" + cy="233.80295" + fx="432.3343" + fy="233.80295" + r="59.056834" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3435" + id="radialGradient3441" + cx="290.5" + cy="244.34448" + fx="290.5" + fy="244.34448" + r="37.5" + gradientTransform="matrix(0.8202102,0.8202102,-0.7960458,0.7960458,246.73838,-189.686)" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3345" + id="linearGradient3311" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.466978,0,0,0.4500435,352.00841,540.25044)" + x1="510.99884" + y1="161.99408" + x2="396.48914" + y2="161.99408" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3421" + id="radialGradient3339" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4181493,0,0,0.1282619,386.09461,620.15777)" + cx="432.3343" + cy="233.80295" + fx="432.3343" + fy="233.80295" + r="59.056834" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3219" + id="linearGradient3434" + gradientUnits="userSpaceOnUse" + x1="325.57214" + y1="280.13632" + x2="312.84424" + y2="257.60013" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3229" + id="linearGradient3436" + gradientUnits="userSpaceOnUse" + x1="310.01578" + y1="255.47881" + x2="325.92572" + y2="280.13632" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3532" + id="linearGradient3520" + x1="525" + y1="371.09448" + x2="525" + y2="395.09448" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.5865192,0,0,0.2518015,339.73218,572.99479)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3624" + id="linearGradient3630" + x1="552.98486" + y1="390.56842" + x2="549.39465" + y2="702.3479" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.3373776,0,0,1.5004634,-835.38716,-310.82676)" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3656" + id="radialGradient3662" + cx="656.19507" + cy="534.45917" + fx="656.19507" + fy="534.45917" + r="13.227922" + gradientTransform="matrix(1,0,0,1.2672781,0,-144.63884)" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3656" + id="radialGradient3668" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,1.2672781,0,-144.63884)" + cx="656.19507" + cy="534.45917" + fx="656.19507" + fy="534.45917" + r="13.227922" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3656" + id="radialGradient3672" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,1.2672781,0,-144.63884)" + cx="656.19507" + cy="534.45917" + fx="656.19507" + fy="534.45917" + r="13.227922" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3656" + id="radialGradient3676" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,1.2672781,0,-144.63884)" + cx="656.19507" + cy="534.45917" + fx="656.19507" + fy="534.45917" + r="13.227922" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3692" + id="linearGradient3772" + gradientUnits="userSpaceOnUse" + x1="506.95975" + y1="469.73706" + x2="525.41608" + y2="469.73706" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3718" + id="linearGradient3774" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9947644,0,0,1.3346457,2.7877039,-166.60153)" + x1="516.89508" + y1="503.50137" + x2="516.89508" + y2="543.80646" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3708" + id="linearGradient3776" + gradientUnits="userSpaceOnUse" + x1="518.26993" + y1="497.31477" + x2="533.02923" + y2="497.31477" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3692" + id="linearGradient3790" + gradientUnits="userSpaceOnUse" + x1="506.95975" + y1="469.73706" + x2="525.41608" + y2="469.73706" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3718" + id="linearGradient3792" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9947644,0,0,1.3346457,2.7877039,-166.60153)" + x1="516.89508" + y1="503.50137" + x2="516.89508" + y2="543.80646" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3708" + id="linearGradient3794" + gradientUnits="userSpaceOnUse" + x1="518.26993" + y1="497.31477" + x2="533.02923" + y2="497.31477" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3656" + id="radialGradient3804" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,1.2672781,0,-144.63884)" + cx="656.19507" + cy="534.45917" + fx="656.19507" + fy="534.45917" + r="13.227922" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3656" + id="radialGradient3808" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,1.2672781,0,-144.63884)" + cx="656.19507" + cy="534.45917" + fx="656.19507" + fy="534.45917" + r="13.227922" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3227" + id="linearGradient3812" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1223608,0,0,0.3849769,-17.516054,565.40983)" + x1="543.5" + y1="205.19257" + x2="587.52001" + y2="205.19257" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3816" + id="linearGradient3826" + x1="573" + y1="755.46222" + x2="573" + y2="700.13464" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,1.6033628,0,-450.76899)" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3864" + id="radialGradient3874" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.9674693,0.8647541,-0.8726553,1.0212484,-15.308759,-74.232772)" + cx="94.273849" + cy="89.893486" + fx="94.273849" + fy="89.893486" + r="74.397521" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3746" + id="radialGradient3880" + cx="82.966125" + cy="-178.42453" + fx="82.966125" + fy="-178.42453" + r="526.79456" + gradientTransform="matrix(1.4952094,0.3367191,-0.1040669,0.5264617,-57.119818,125.77043)" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3438" + id="linearGradient3948" + x1="660.29303" + y1="256.53284" + x2="444.79303" + y2="255.62085" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(0,32.526912)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3692" + id="linearGradient3972" + x1="573.5" + y1="244.2056" + x2="578.25" + y2="216.9556" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(0,35.526912)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3746" + id="linearGradient3990" + x1="542.5" + y1="222.59448" + x2="559" + y2="222.59448" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-6.25,37.151912)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3746" + id="linearGradient3994" + gradientUnits="userSpaceOnUse" + x1="542.5" + y1="222.59448" + x2="559" + y2="222.59448" + gradientTransform="translate(38.5,37.151912)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4016" + id="linearGradient4022" + x1="639" + y1="262.09448" + x2="667" + y2="262.09448" + gradientUnits="userSpaceOnUse" /> + <inkscape:perspective + id="perspective2578" + inkscape:persp3d-origin="372.04724 : 350.78739 : 1" + inkscape:vp_z="744.09448 : 526.18109 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 526.18109 : 1" + sodipodi:type="inkscape:persp3d" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3746" + id="linearGradient3387" + gradientUnits="userSpaceOnUse" + x1="542.5" + y1="222.59448" + x2="559" + y2="222.59448" + gradientTransform="translate(15.875,37.151912)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3167" + id="linearGradient3416" + x1="474.23065" + y1="229.92336" + x2="474.1944" + y2="218.27365" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(0.1004684,32.526757)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3428" + id="linearGradient3426" + x1="592.92798" + y1="199.43727" + x2="557.05743" + y2="196.5448" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(12.140805,-13.041887)" /> + <filter + inkscape:collect="always" + id="filter3507"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="3.0523171" + id="feGaussianBlur3509" /> + </filter> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3523" + id="linearGradient3521" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,-0.7291751,0,521.83983)" + x1="562.55634" + y1="285.89896" + x2="562.55634" + y2="244.09448" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5097" + id="linearGradient5103" + x1="590.84674" + y1="274.57559" + x2="590.84674" + y2="334.01376" + gradientUnits="userSpaceOnUse" + spreadMethod="pad" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3864" + id="radialGradient5107" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.9674693,0.8647541,-0.8726553,1.0212484,-15.308759,-74.232772)" + cx="94.273849" + cy="89.893486" + fx="94.273849" + fy="89.893486" + r="74.397521" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + gridtolerance="10000" + guidetolerance="10" + objecttolerance="10" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.70710678" + inkscape:cx="532.91407" + inkscape:cy="457.84365" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="1674" + inkscape:window-height="1000" + inkscape:window-x="2" + inkscape:window-y="14" + showguides="false" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <rect + style="opacity:1;fill:url(#radialGradient3880);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.1880002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3638" + width="1053.5891" + height="291.32797" + x="-0.29455566" + y="1.6323624" /> + <path + style="fill:url(#radialGradient3874);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3507)" + d="M 158.37853,75.817898 C 130.95894,49.483192 82.14552,74.615971 85.85382,95.15981 C 49.691853,94.8009 50.214842,139.36083 83.29101,132.16343 C 144.66465,163.16454 159.26268,129.80212 164.6863,136.51386 C 225.60448,157.97672 246.34362,130.65438 265.24417,127.0714 C 294.43981,137.91859 337.16986,121.78798 297.03636,102.77604 C 331.73096,64.597047 277.96882,60.229366 253.07028,70.400868 C 191.09597,33.610112 168.89234,63.292037 158.37853,75.817898 z" + id="path3872" + sodipodi:nodetypes="cccccccc" + transform="matrix(1.5062893,0,0,1.1720951,618.04001,132.36768)" /> + <rect + style="opacity:1;fill:url(#linearGradient3630);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.13464069;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3622" + width="1054.4708" + height="461.52972" + x="-0.7354126" + y="278.34879" /> + <path + style="fill:url(#linearGradient3826);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M -0.99999999,712.83731 C 272,698.40705 -51.363087,575.30331 261,705.40705 C 324.46247,731.84009 395,707.01041 449,705.40705 C 503,703.80369 463,690.73643 510,711.58015 C 557,732.42386 705,692.39023 750,684.37342 C 795,676.35661 908,667.75324 947,682.18351 C 986,696.61377 1056,698.9936 1056,698.9936 L 1053,747.09448 L -3,745.49112 L -0.99999999,712.83731 z" + id="path3814" + sodipodi:nodetypes="cssssscccc" /> + <rect + style="opacity:1;fill:url(#linearGradient3520);fill-opacity:1;fill-rule:nonzero;stroke:#1b1e1f;stroke-width:0.56879884;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3512" + width="10.557344" + height="6.0432386" + x="642.3761" + y="666.43695" /> + <use + x="0" + y="0" + xlink:href="#path2455" + id="use3258" + transform="matrix(0.869168,0,0,-0.869168,81.98751,1246.5374)" + width="1052.3622" + height="744.09448" /> + <path + style="fill:url(#linearGradient3812);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.77744257;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 593.04822,651.68104 C 593.04822,651.68104 653.65569,615.49321 639.065,637.05192 C 624.47431,658.61061 624.47431,658.61061 624.47431,658.61061 L 593.04822,651.68104 z" + id="path2455" /> + <path + style="fill:url(#linearGradient3175);fill-opacity:1;fill-rule:evenodd;stroke:#393939;stroke-width:1.90693891;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 485.26939,643.71814 C 443.15507,651.66437 458.5319,680.53556 502.21486,686.27814 C 551.68229,692.78115 568.45042,691.0115 605.34827,686.27814 C 657.60843,679.57406 657.68143,651.78445 605.34827,643.25553 C 553.98131,634.88408 516.10913,637.89923 485.26939,643.71814 z" + id="path2385" + sodipodi:nodetypes="cssss" /> + <path + style="fill:url(#radialGradient3339);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 542.18031,648.1112 C 548.56327,665.42741 608.42397,656.72745 586.93551,642.57104 C 586.93551,642.57104 543.33293,648.61096 542.18031,648.1112 z" + id="path3403" + sodipodi:nodetypes="ccc" /> + <path + style="fill:url(#linearGradient3311);fill-opacity:1;fill-rule:evenodd;stroke:#2d2d2d;stroke-width:2.07042313;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 537.39402,641.90906 C 537.39402,656.7605 583.62247,656.30589 583.62247,641.45445 L 583.62247,636.06071 C 583.62247,621.21003 537.39402,613.87461 537.39402,628.72529 L 537.39402,641.90906 z" + id="path3291" + sodipodi:nodetypes="cssss" /> + <g + id="g3235" + transform="matrix(1.4016868,0,0,1.1319742,112.22001,-99.678822)" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:url(#radialGradient3441);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.227;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3433" + sodipodi:cx="303.5" + sodipodi:cy="263.09448" + sodipodi:rx="37.5" + sodipodi:ry="40" + d="M 341,263.09448 A 37.5,40 0 1 1 266,263.09448 A 37.5,40 0 1 1 341,263.09448 z" + transform="matrix(0.692163,0,1.4106583e-2,0.289185,275.31394,582.37251)" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:#444444;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.06500006;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3458" + sodipodi:cx="369.5" + sodipodi:cy="316.09448" + sodipodi:rx="27.5" + sodipodi:ry="7" + d="M 397,316.09448 A 27.5,7 0 1 1 342,316.09448 A 27.5,7 0 1 1 397,316.09448 z" + transform="matrix(0.5642633,0,0,0.5642633,348.03095,450.47113)" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:#444444;fill-opacity:1;fill-rule:nonzero;stroke:#1b1e1f;stroke-width:4.23126984;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3510" + sodipodi:cx="369.5" + sodipodi:cy="316.09448" + sodipodi:rx="27.5" + sodipodi:ry="7" + d="M 397,316.09448 A 27.5,7 0 1 1 342,316.09448 A 27.5,7 0 1 1 397,316.09448 z" + transform="matrix(0,0.30778,-0.5642633,0,828.66499,563.5944)" /> + <use + x="0" + y="0" + xlink:href="#path3510" + id="use3544" + transform="translate(0.5000005,-17.23511)" + width="1052.3622" + height="744.09448" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:#787878;fill-opacity:1;fill-rule:nonzero;stroke:#1b1e1f;stroke-width:2.38492584;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3584" + sodipodi:cx="237.5" + sodipodi:cy="366.09448" + sodipodi:rx="8.5" + sodipodi:ry="8" + d="M 246,366.09448 A 8.5,8 0 1 1 229,366.09448 A 8.5,8 0 1 1 246,366.09448 z" + transform="matrix(1.7798114,-4.2997512e-2,1.3318941e-2,0.5513151,196.65666,476.1443)" /> + <path + style="fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:#484848;stroke-width:3.49756980000000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" + d="M 423.2231,281.1169 C 502.67948,283.47026 574.8361,283.42778 668.97714,281.1169 C 668.97714,281.1169 670.77663,271.99598 668.97714,269.5402 C 559.46065,250.8302 423.2231,265.08614 423.2231,269.5402 L 423.2231,281.1169 z" + id="path3469" + sodipodi:nodetypes="cccsc" /> + <rect + style="opacity:1;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3408" + width="3.4280596" + height="29.611124" + x="709.89148" + y="-14.462622" + transform="matrix(0.9327494,0.3605254,-0.3633626,0.9316478,0,0)" /> + <rect + style="opacity:1;fill:url(#linearGradient3416);fill-opacity:1;fill-rule:nonzero;stroke:#484848;stroke-width:2.99999928;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3376" + width="48.499989" + height="8.5" + x="444.56302" + y="252.39224" + transform="matrix(0.9999952,3.0887776e-3,-3.0887776e-3,0.9999952,0,0)" /> + <path + style="fill:url(#linearGradient3972);fill-opacity:1;fill-rule:evenodd;stroke:#323232;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 509,277.62139 C 509,277.62139 547.41409,235.70896 524,232.62139 L 615,244.62139 L 615,279.62139 L 509,277.62139 z" + id="path3952" + sodipodi:nodetypes="csccc" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:url(#radialGradient3662);fill-opacity:1;fill-rule:nonzero;stroke:#41526b;stroke-width:2.9000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3654" + sodipodi:cx="656.19507" + sodipodi:cy="541.15485" + sodipodi:rx="12.727922" + sodipodi:ry="16.263456" + d="M 668.92299,541.15485 A 12.727922,16.263456 0 1 1 643.46715,541.15485 A 12.727922,16.263456 0 1 1 668.92299,541.15485 z" + transform="matrix(0.5187874,0,0,0.3968421,374.8524,387.30025)" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:url(#radialGradient3668);fill-opacity:1;fill-rule:nonzero;stroke:#41526b;stroke-width:2.9000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3666" + sodipodi:cx="656.19507" + sodipodi:cy="541.15485" + sodipodi:rx="12.727922" + sodipodi:ry="16.263456" + d="M 668.92299,541.15485 A 12.727922,16.263456 0 1 1 643.46715,541.15485 A 12.727922,16.263456 0 1 1 668.92299,541.15485 z" + transform="matrix(0.5734968,0,0,0.4386917,316.52295,315.62837)" /> + <path + transform="matrix(0.8598866,0,0,0.5637407,192.52282,220.77351)" + d="M 668.92299,541.15485 A 12.727922,16.263456 0 1 1 643.46715,541.15485 A 12.727922,16.263456 0 1 1 668.92299,541.15485 z" + sodipodi:ry="16.263456" + sodipodi:rx="12.727922" + sodipodi:cy="541.15485" + sodipodi:cx="656.19507" + id="path3670" + style="opacity:1;fill:url(#radialGradient3672);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.90000010000000020;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:type="arc" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:url(#radialGradient3676);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.90000010000000020;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3674" + sodipodi:cx="656.19507" + sodipodi:cy="541.15485" + sodipodi:rx="12.727922" + sodipodi:ry="16.263456" + d="M 668.92299,541.15485 A 12.727922,16.263456 0 1 1 643.46715,541.15485 A 12.727922,16.263456 0 1 1 668.92299,541.15485 z" + transform="matrix(0.7435991,0,0,0.6264519,225.8301,127.83701)" /> + <g + id="g3759" + transform="matrix(0.8830571,0,0,0.8830571,104.83144,103.2985)"> + <path + d="M 523.9661,469.73706 A 7.7781744,34.648232 0 1 1 508.40975,469.73706 A 7.7781744,34.648232 0 1 1 523.9661,469.73706 z" + sodipodi:ry="34.648232" + sodipodi:rx="7.7781744" + sodipodi:cy="469.73706" + sodipodi:cx="516.18793" + id="path3682" + style="opacity:1;fill:url(#linearGradient3772);fill-opacity:1;fill-rule:nonzero;stroke:#272727;stroke-width:2.9000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:type="arc" /> + <g + id="g3754"> + <rect + style="opacity:1;fill:url(#linearGradient3774);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.20000005;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3716" + width="33.58757" + height="59.927299" + x="498.86386" + y="497.84454" /> + <path + style="fill:url(#linearGradient3776);fill-opacity:1;fill-rule:evenodd;stroke:#1f1f1f;stroke-width:1.99788344;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 523.35045,482.89424 C 523.35045,482.89424 532.31256,488.20203 532.02344,500.14638 C 531.73431,512.09072 531.73431,511.73417 531.73431,511.73417 C 531.73431,511.73417 520.70627,493.83104 519.26887,499.77636 L 523.35045,482.89424 z" + id="path3704" + sodipodi:nodetypes="cscsc" /> + <path + sodipodi:nodetypes="cscsc" + id="path3706" + d="M 508.50327,482.89424 C 508.50327,482.89424 499.54116,488.20203 499.83028,500.14638 C 500.11941,512.09072 500.11941,511.73417 500.11941,511.73417 C 500.11941,511.73417 511.14745,493.83104 512.58485,499.77636 L 508.50327,482.89424 z" + style="fill:#bcbcbc;fill-opacity:1;fill-rule:evenodd;stroke:#1f1f1f;stroke-width:1.99788344;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> + </g> + <g + transform="matrix(0.8830571,0,0,0.8830571,192.45885,-66.370546)" + id="g3778"> + <path + sodipodi:type="arc" + style="opacity:1;fill:url(#linearGradient3790);fill-opacity:1;fill-rule:nonzero;stroke:#272727;stroke-width:2.9000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3780" + sodipodi:cx="516.18793" + sodipodi:cy="469.73706" + sodipodi:rx="7.7781744" + sodipodi:ry="34.648232" + d="M 523.9661,469.73706 A 7.7781744,34.648232 0 1 1 508.40975,469.73706 A 7.7781744,34.648232 0 1 1 523.9661,469.73706 z" /> + <g + id="g3782"> + <rect + y="497.84454" + x="498.86386" + height="59.927299" + width="33.58757" + id="rect3784" + style="opacity:1;fill:url(#linearGradient3792);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.20000005;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + sodipodi:nodetypes="cscsc" + id="path3786" + d="M 523.35045,482.89424 C 523.35045,482.89424 532.31256,488.20203 532.02344,500.14638 C 531.73431,512.09072 531.73431,511.73417 531.73431,511.73417 C 531.73431,511.73417 520.70627,493.83104 519.26887,499.77636 L 523.35045,482.89424 z" + style="fill:url(#linearGradient3794);fill-opacity:1;fill-rule:evenodd;stroke:#1f1f1f;stroke-width:1.99788344;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + style="fill:#bcbcbc;fill-opacity:1;fill-rule:evenodd;stroke:#1f1f1f;stroke-width:1.99788344;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 508.50327,482.89424 C 508.50327,482.89424 499.54116,488.20203 499.83028,500.14638 C 500.11941,512.09072 500.11941,511.73417 500.11941,511.73417 C 500.11941,511.73417 511.14745,493.83104 512.58485,499.77636 L 508.50327,482.89424 z" + id="path3788" + sodipodi:nodetypes="cscsc" /> + </g> + </g> + <path + transform="matrix(0.4292897,0,0,0.3283816,384.32775,481.20689)" + d="M 668.92299,541.15485 A 12.727922,16.263456 0 1 1 643.46715,541.15485 A 12.727922,16.263456 0 1 1 668.92299,541.15485 z" + sodipodi:ry="16.263456" + sodipodi:rx="12.727922" + sodipodi:cy="541.15485" + sodipodi:cx="656.19507" + id="path3802" + style="opacity:1;fill:url(#radialGradient3804);fill-opacity:1;fill-rule:nonzero;stroke:#41526b;stroke-width:2.9000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:type="arc" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:url(#radialGradient3808);fill-opacity:1;fill-rule:nonzero;stroke:#41526b;stroke-width:2.9000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3806" + sodipodi:cx="656.19507" + sodipodi:cy="541.15485" + sodipodi:rx="12.727922" + sodipodi:ry="16.263456" + d="M 668.92299,541.15485 A 12.727922,16.263456 0 1 1 643.46715,541.15485 A 12.727922,16.263456 0 1 1 668.92299,541.15485 z" + transform="matrix(0.5842998,0,0,0.4469553,299.7804,369.91514)" /> + <path + style="fill:#ad8b00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 1059.5,736.57373 C 786.5,730.53352 912.51207,693.32637 768.5,736.53352 C 750.7005,741.87381 560.38464,722.59661 552.5,736.53352 C 528.68395,778.63085 223.94216,703.33865 182.5,727.16444 C 179.5,728.88919 160.53732,723.88477 135.5,730.90332 C 98.830356,741.18268 82.5,725.67603 43.5,731.71624 C 4.5,737.75645 0.5,733.72965 0.5,733.72965 L -6.5,747.98302 L 1049.5,747.31188 L 1059.5,736.57373 z" + id="path3828" + sodipodi:nodetypes="cssssscccc" /> + <rect + style="opacity:1;fill:url(#linearGradient5103);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3448" + width="1053.5891" + height="67.882248" + x="-0.29455566" + y="274.57559" /> + <path + sodipodi:nodetypes="ccccc" + id="path3519" + d="M 428,343.85222 C 428,343.85222 457.01739,315.41439 470,315.41439 C 481.98261,313.04457 634.13054,311.73511 652,317.41962 C 661.13441,320.56943 664.46012,324.9477 666,343.85222 C 600.25603,336.72647 497.8192,336.4719 428,343.85222 z" + style="opacity:0.43933058;fill:url(#linearGradient3521);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.0999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + style="fill:url(#linearGradient3948);fill-opacity:1;fill-rule:evenodd;stroke:#545454;stroke-width:3.0999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 428,276.62139 C 428,276.62139 457.01739,315.62139 470,315.62139 C 481.98261,318.87139 634.13054,320.6672 652,312.87139 C 661.13441,308.55171 664.46012,302.54729 666,276.62139 C 600.25603,286.39373 497.8192,286.74286 428,276.62139 z" + id="path3938" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:#dedede;fill-opacity:1;fill-rule:nonzero;stroke:#484848;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" + d="M 466.00273,263.08289 L 484.25264,263.13926 L 494.7163,274.92164 L 465.96644,274.83284 L 466.00273,263.08289 z" + id="rect3378" + sodipodi:nodetypes="ccccc" /> + <rect + style="opacity:1;fill:url(#linearGradient3990);fill-opacity:1;fill-rule:nonzero;stroke:#323232;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3974" + width="13.5" + height="17" + x="537.75" + y="251.2464" + inkscape:transform-center-x="30" /> + <rect + inkscape:transform-center-x="30" + y="251.2464" + x="582.5" + height="17" + width="13.5" + id="rect3992" + style="opacity:1;fill:url(#linearGradient3994);fill-opacity:1;fill-rule:nonzero;stroke:#323232;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + style="fill:#dedede;fill-opacity:1;fill-rule:nonzero;stroke:#484848;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" + d="M 422.25,268.87139 C 487.33333,277.37139 573.16667,279.87139 668.5,268.87139 C 668.5,268.87139 670.30312,275.56827 668.5,277.37139 C 558.76249,291.1089 422.25,280.64171 422.25,277.37139 L 422.25,268.87139 z" + id="rect2558" + sodipodi:nodetypes="cccsc" /> + <rect + style="opacity:1;fill:#c1c1c1;fill-opacity:1;fill-rule:nonzero;stroke:#404040;stroke-width:3.0365274;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect2569" + width="94.427879" + height="7.236649" + x="546.39832" + y="177.10637" + transform="matrix(0.9947793,0.1020501,-0.1079723,0.9941539,0,0)" /> + <rect + inkscape:transform-center-x="30" + y="251.2464" + x="559.875" + height="17" + width="13.5" + id="rect3385" + style="opacity:1;fill:url(#linearGradient3387);fill-opacity:1;fill-rule:nonzero;stroke:#323232;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <g + id="g3401" + transform="matrix(0.9246214,0.3808874,-0.3808874,0.9246214,113.30125,-193.16902)" + inkscape:transform-center-x="17.590385" + inkscape:transform-center-y="-15.415449"> + <rect + inkscape:transform-center-y="-43.888889" + transform="matrix(0,1,-1,0,0,0)" + style="opacity:1;fill:#b20000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3389" + width="19.75" + height="27.75" + x="193.34448" + y="-709" /> + <rect + y="200.59448" + x="681.5" + height="5.75" + width="27.25" + id="rect3393" + style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + y="193.59448" + x="691.25" + height="19.5" + width="5.75" + id="rect3395" + style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + y="202.34448" + x="681.5" + height="2.5" + width="27.75" + id="rect3397" + style="opacity:1;fill:#000080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + y="193.59448" + x="692.5" + height="19.25" + width="3" + id="rect3399" + style="opacity:1;fill:#000080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> + <path + style="fill:#dcdcdc;fill-opacity:1;fill-rule:nonzero;stroke:#404040;stroke-width:3.03650045;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" + d="M 529.42326,227.17914 L 616.35817,235.81552 L 618.07681,241.50986 L 524.14191,231.87348 L 529.42326,227.17914 z" + id="rect3466" + sodipodi:nodetypes="ccccc" /> + <rect + style="opacity:1;fill:url(#linearGradient3426);fill-opacity:1;fill-rule:nonzero;stroke:#494949;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3418" + width="34.5" + height="14.5" + x="571.14081" + y="174.8026" + transform="matrix(0.997157,7.5351915e-2,-7.5351915e-2,0.997157,0,0)" + inkscape:transform-center-x="-8" + inkscape:transform-center-y="2" /> + <g + id="mine" + transform="translate(971.11461,237.62715)" + inkscape:label="#g3441"> + <path + transform="matrix(0.6341613,0,0,0.6341613,-18.521242,45.718192)" + d="M -167.5843,186.54079 A 31.466251,31.466251 0 1 1 -230.5168,186.54079 A 31.466251,31.466251 0 1 1 -167.5843,186.54079 z" + sodipodi:ry="31.466251" + sodipodi:rx="31.466251" + sodipodi:cy="186.54079" + sodipodi:cx="-199.05055" + id="path2586" + style="opacity:1;fill:url(#radialGradient3364);fill-opacity:1;stroke:#131313;stroke-width:3.54799318;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:type="arc" /> + <path + sodipodi:nodetypes="ccss" + id="path3382" + d="M -155.20193,175.4167 C -157.60085,176.6451 -156.78074,184.26068 -156.78074,184.26068 C -156.78074,184.26068 -148.33787,181.58301 -148.57092,178.60053 C -148.74283,176.40051 -153.23774,174.41092 -155.20193,175.4167 z" + style="fill:url(#linearGradient3398);fill-opacity:1;fill-rule:evenodd;stroke:#131313;stroke-width:2.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + sodipodi:nodetypes="ccss" + id="path3400" + d="M -156.56354,153.84532 C -158.96246,152.61693 -158.14235,145.00135 -158.14235,145.00135 C -158.14235,145.00135 -149.69948,147.67902 -149.93253,150.66149 C -150.10444,152.86151 -154.59935,154.85111 -156.56354,153.84532 z" + style="fill:url(#linearGradient3402);fill-opacity:1;fill-rule:evenodd;stroke:#131313;stroke-width:2.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + sodipodi:nodetypes="ccss" + id="path3404" + d="M -133.88532,153.68678 C -131.48641,152.45838 -132.30652,144.8428 -132.30652,144.8428 C -132.30652,144.8428 -140.74938,147.52047 -140.51633,150.50295 C -140.34442,152.70297 -135.84951,154.69256 -133.88532,153.68678 z" + style="fill:url(#linearGradient3406);fill-opacity:1;fill-rule:evenodd;stroke:#131313;stroke-width:2.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + sodipodi:nodetypes="ccss" + id="path3408" + d="M -134.51948,175.55085 C -132.12057,176.77925 -132.94068,184.39483 -132.94068,184.39483 C -132.94068,184.39483 -141.38355,181.71716 -141.15049,178.73469 C -140.97858,176.53467 -136.48367,174.54507 -134.51948,175.55085 z" + style="fill:url(#linearGradient3410);fill-opacity:1;fill-rule:evenodd;stroke:#131313;stroke-width:2.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + sodipodi:nodetypes="ccss" + id="path3412" + d="M -161.25709,168.78221 C -163.22395,170.62484 -170.11427,165.85236 -170.11427,165.85236 C -170.11427,165.85236 -164.7408,160.23808 -162.01257,161.46538 C -160.00011,162.37068 -159.64667,167.27352 -161.25709,168.78221 z" + style="fill:url(#linearGradient3414);fill-opacity:1;fill-rule:evenodd;stroke:#131313;stroke-width:2.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + sodipodi:nodetypes="ccss" + id="path3416" + d="M -129.17068,169.10464 C -127.20382,170.94727 -120.3135,166.17478 -120.3135,166.17478 C -120.3135,166.17478 -125.68697,160.5605 -128.41519,161.7878 C -130.42766,162.69311 -130.7811,167.59595 -129.17068,169.10464 z" + style="fill:url(#linearGradient3418);fill-opacity:1;fill-rule:evenodd;stroke:#131313;stroke-width:2.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + transform="matrix(0.6341613,0,0,0.6341613,-61.039517,68.324922)" + d="M -126,151.21948 A 6.625,6.625 0 1 1 -139.25,151.21948 A 6.625,6.625 0 1 1 -126,151.21948 z" + sodipodi:ry="6.625" + sodipodi:rx="6.625" + sodipodi:cy="151.21948" + sodipodi:cx="-132.625" + id="path3426" + style="opacity:1;fill:url(#radialGradient3439);fill-opacity:1;stroke:#131313;stroke-width:3.54799318;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:type="arc" /> + </g> + </g> +</svg> diff --git a/examples/animation/sub-attaq/pics/scalable/submarine.svg b/examples/animation/sub-attaq/pics/scalable/submarine.svg new file mode 100644 index 0000000..8a0ffdd --- /dev/null +++ b/examples/animation/sub-attaq/pics/scalable/submarine.svg @@ -0,0 +1,214 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + version="1.0" + width="744.09448" + height="1052.3622" + id="svg2594"> + <defs + id="defs2596"> + <linearGradient + id="linearGradient3345"> + <stop + id="stop3348" + style="stop-color:#898989;stop-opacity:1" + offset="0" /> + <stop + id="stop3350" + style="stop-color:#9ea1a2;stop-opacity:1" + offset="0.44021741" /> + <stop + id="stop3352" + style="stop-color:#bbbdbf;stop-opacity:1" + offset="0.56799388" /> + <stop + id="stop3354" + style="stop-color:#f0f1f2;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="510.99884" + y1="161.99408" + x2="396.48914" + y2="161.99408" + id="linearGradient3311" + xlink:href="#linearGradient3345" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.466978,0,0,0.4500435,231.58508,159.95135)" /> + <linearGradient + id="linearGradient3532"> + <stop + id="stop3534" + style="stop-color:#545454;stop-opacity:1" + offset="0" /> + <stop + id="stop3536" + style="stop-color:#848788;stop-opacity:1" + offset="0.44021741" /> + <stop + id="stop3538" + style="stop-color:#9ca0a2;stop-opacity:1" + offset="0.56799388" /> + <stop + id="stop3540" + style="stop-color:#565d60;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="525" + y1="371.09448" + x2="525" + y2="395.09448" + id="linearGradient3520" + xlink:href="#linearGradient3532" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.5865192,0,0,0.2518015,219.30885,192.6957)" /> + <linearGradient + id="linearGradient3227"> + <stop + id="stop3229" + style="stop-color:#444444;stop-opacity:1" + offset="0" /> + <stop + id="stop3232" + style="stop-color:#b0b0b0;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="543.5" + y1="205.19257" + x2="587.52002" + y2="205.19257" + id="linearGradient3812" + xlink:href="#linearGradient3227" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1223608,0,0,0.3849769,-137.93938,185.11074)" /> + <linearGradient + id="linearGradient3167"> + <stop + id="stop3169" + style="stop-color:#464646;stop-opacity:1" + offset="0" /> + <stop + id="stop3345" + style="stop-color:#848788;stop-opacity:1" + offset="0.44021741" /> + <stop + id="stop3347" + style="stop-color:#9ca0a2;stop-opacity:1" + offset="0.56799388" /> + <stop + id="stop3171" + style="stop-color:#b5babd;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="443.95602" + y1="315.31854" + x2="443.95602" + y2="247.85609" + id="linearGradient3175" + xlink:href="#linearGradient3167" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4908502,0,0,0.4579593,230.56224,161.8228)" /> + <linearGradient + id="linearGradient3421"> + <stop + id="stop3423" + style="stop-color:#444444;stop-opacity:1" + offset="0" /> + <stop + id="stop3425" + style="stop-color:#444444;stop-opacity:0" + offset="1" /> + </linearGradient> + <radialGradient + cx="432.33429" + cy="233.80295" + r="59.056835" + fx="432.33429" + fy="233.80295" + id="radialGradient3339" + xlink:href="#linearGradient3421" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4181493,0,0,0.1282619,265.67128,239.85868)" /> + <linearGradient + id="linearGradient3435"> + <stop + id="stop3437" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop3439" + style="stop-color:#c0c0c0;stop-opacity:0" + offset="1" /> + </linearGradient> + <radialGradient + cx="290.5" + cy="244.34448" + r="37.5" + fx="290.5" + fy="244.34448" + id="radialGradient3441" + xlink:href="#linearGradient3435" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.8202102,0.8202102,-0.7960458,0.7960458,246.73838,-189.686)" /> + </defs> + <g + id="submarine"> + <rect + width="10.557344" + height="6.0432386" + x="521.95276" + y="286.13785" + id="rect3512" + style="opacity:1;fill:url(#linearGradient3520);fill-opacity:1;fill-rule:nonzero;stroke:#1b1e1f;stroke-width:0.56879884;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 472.62489,271.38195 C 472.62489,271.38195 533.23236,235.19412 518.64167,256.75283 C 504.05098,278.31152 504.05098,278.31152 504.05098,278.31152 L 472.62489,271.38195 z" + id="path2455" + style="fill:url(#linearGradient3812);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.77744257;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 364.84606,263.41905 C 322.73174,271.36528 338.10857,300.23647 381.79153,305.97905 C 431.25896,312.48206 448.02709,310.71241 484.92494,305.97905 C 537.1851,299.27497 537.2581,271.48536 484.92494,262.95644 C 433.55798,254.58499 395.6858,257.60014 364.84606,263.41905 z" + id="path2385" + style="fill:url(#linearGradient3175);fill-opacity:1;fill-rule:evenodd;stroke:#393939;stroke-width:1.90693891;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 421.75698,267.81211 C 428.13994,285.12832 488.00064,276.42836 466.51218,262.27195 C 466.51218,262.27195 422.9096,268.31187 421.75698,267.81211 z" + id="path3403" + style="fill:url(#radialGradient3339);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 416.97069,261.60997 C 416.97069,276.46141 463.19914,276.0068 463.19914,261.15536 L 463.19914,255.76162 C 463.19914,240.91094 416.97069,233.57552 416.97069,248.4262 L 416.97069,261.60997 z" + id="path3291" + style="fill:url(#linearGradient3311);fill-opacity:1;fill-rule:evenodd;stroke:#2d2d2d;stroke-width:2.07042313;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 341,263.09448 A 37.5,40 0 1 1 266,263.09448 A 37.5,40 0 1 1 341,263.09448 z" + transform="matrix(0.692163,0,1.4106583e-2,0.289185,154.89061,202.07342)" + id="path3433" + style="opacity:1;fill:url(#radialGradient3441);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.227;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 397,316.09448 A 27.5,7 0 1 1 342,316.09448 A 27.5,7 0 1 1 397,316.09448 z" + transform="matrix(0.5642633,0,0,0.5642633,227.60762,70.172035)" + id="path3458" + style="opacity:1;fill:#444444;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.06500006;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 397,316.09448 A 27.5,7 0 1 1 342,316.09448 A 27.5,7 0 1 1 397,316.09448 z" + transform="matrix(0,0.30778,-0.5642633,0,708.24166,183.29531)" + id="path3510" + style="opacity:1;fill:#444444;fill-opacity:1;fill-rule:nonzero;stroke:#1b1e1f;stroke-width:4.23126984;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <use + transform="translate(0.5000044,-17.235115)" + id="use3544" + x="0" + y="0" + width="1052.3622" + height="744.09448" + xlink:href="#path3510" /> + <path + d="M 246,366.09448 A 8.5,8 0 1 1 229,366.09448 A 8.5,8 0 1 1 246,366.09448 z" + transform="matrix(1.7798114,-4.2997512e-2,1.3318941e-2,0.5513151,76.233334,95.845205)" + id="path3584" + style="opacity:1;fill:#787878;fill-opacity:1;fill-rule:nonzero;stroke:#1b1e1f;stroke-width:2.38492584;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> +</svg> diff --git a/examples/animation/sub-attaq/pics/scalable/surface.svg b/examples/animation/sub-attaq/pics/scalable/surface.svg new file mode 100644 index 0000000..40ed239 --- /dev/null +++ b/examples/animation/sub-attaq/pics/scalable/surface.svg @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + version="1.0" + width="744.09448" + height="1052.3622" + id="svg2685"> + <defs + id="defs2687"> + <linearGradient + id="linearGradient5097"> + <stop + id="stop5099" + style="stop-color:#19a2db;stop-opacity:0" + offset="0" /> + <stop + id="stop5109" + style="stop-color:#1379a7;stop-opacity:0.49803922" + offset="0.30000001" /> + <stop + id="stop5101" + style="stop-color:#0e5173;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="590.84674" + y1="274.57559" + x2="590.84674" + y2="334.01376" + id="linearGradient5103" + xlink:href="#linearGradient5097" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-172.21428,209.55976)" + spreadMethod="pad" /> + </defs> + <g + id="layer1"> + <rect + width="1053.5891" + height="67.882248" + x="-172.50883" + y="484.13535" + id="rect3448" + style="opacity:1;fill:url(#linearGradient5103);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> +</svg> diff --git a/examples/animation/sub-attaq/pics/scalable/torpedo.svg b/examples/animation/sub-attaq/pics/scalable/torpedo.svg new file mode 100644 index 0000000..48e429d --- /dev/null +++ b/examples/animation/sub-attaq/pics/scalable/torpedo.svg @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + version="1.0" + width="744.09448" + height="1052.3622" + id="svg2584"> + <defs + id="defs2586"> + <linearGradient + id="linearGradient3708"> + <stop + id="stop3710" + style="stop-color:#202020;stop-opacity:1" + offset="0" /> + <stop + id="stop3712" + style="stop-color:#ffffff;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="518.26996" + y1="497.31476" + x2="533.02924" + y2="497.31476" + id="linearGradient3776" + xlink:href="#linearGradient3708" + gradientUnits="userSpaceOnUse" /> + <linearGradient + id="linearGradient3718"> + <stop + id="stop3720" + style="stop-color:#bcbcbc;stop-opacity:0.28169015" + offset="0" /> + <stop + id="stop3722" + style="stop-color:#bcbcbc;stop-opacity:0" + offset="1" /> + </linearGradient> + <linearGradient + x1="516.89508" + y1="503.50137" + x2="516.89508" + y2="543.80646" + id="linearGradient3774" + xlink:href="#linearGradient3718" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9947644,0,0,1.3346457,2.7877039,-166.60153)" /> + <linearGradient + id="linearGradient3692"> + <stop + id="stop3694" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop3696" + style="stop-color:#b6b6b6;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="506.95975" + y1="469.73706" + x2="525.41608" + y2="469.73706" + id="linearGradient3772" + xlink:href="#linearGradient3692" + gradientUnits="userSpaceOnUse" /> + <linearGradient + x1="506.95975" + y1="469.73706" + x2="525.41608" + y2="469.73706" + id="linearGradient2403" + xlink:href="#linearGradient3692" + gradientUnits="userSpaceOnUse" /> + <linearGradient + x1="516.89508" + y1="503.50137" + x2="516.89508" + y2="543.80646" + id="linearGradient2405" + xlink:href="#linearGradient3718" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9947644,0,0,1.3346457,2.7877039,-166.60153)" /> + <linearGradient + x1="518.26996" + y1="497.31476" + x2="533.02924" + y2="497.31476" + id="linearGradient2407" + xlink:href="#linearGradient3708" + gradientUnits="userSpaceOnUse" /> + </defs> + <g + transform="translate(-128.69958,6.6568748)" + id="torpedo"> + <g + transform="matrix(0.8830571,0,0,0.8830571,-119.78327,177.67947)" + id="g3525"> + <path + d="M 523.9661,469.73706 A 7.7781744,34.648232 0 1 1 508.40975,469.73706 A 7.7781744,34.648232 0 1 1 523.9661,469.73706 z" + id="path3682" + style="opacity:1;fill:url(#linearGradient2403);fill-opacity:1;fill-rule:nonzero;stroke:#272727;stroke-width:2.9000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <g + id="g3754"> + <rect + width="33.58757" + height="59.927299" + x="498.86386" + y="497.84454" + id="rect3716" + style="opacity:1;fill:url(#linearGradient2405);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.20000005;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 523.35045,482.89424 C 523.35045,482.89424 532.31256,488.20203 532.02344,500.14638 C 531.73431,512.09072 531.73431,511.73417 531.73431,511.73417 C 531.73431,511.73417 520.70627,493.83104 519.26887,499.77636 L 523.35045,482.89424 z" + id="path3704" + style="fill:url(#linearGradient2407);fill-opacity:1;fill-rule:evenodd;stroke:#1f1f1f;stroke-width:1.99788344;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 508.50327,482.89424 C 508.50327,482.89424 499.54116,488.20203 499.83028,500.14638 C 500.11941,512.09072 500.11941,511.73417 500.11941,511.73417 C 500.11941,511.73417 511.14745,493.83104 512.58485,499.77636 L 508.50327,482.89424 z" + id="path3706" + style="fill:#bcbcbc;fill-opacity:1;fill-rule:evenodd;stroke:#1f1f1f;stroke-width:1.99788344;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> + </g> + </g> +</svg> diff --git a/examples/animation/sub-attaq/pics/small/background.png b/examples/animation/sub-attaq/pics/small/background.png Binary files differnew file mode 100644 index 0000000..5ad3db6 --- /dev/null +++ b/examples/animation/sub-attaq/pics/small/background.png diff --git a/examples/animation/sub-attaq/pics/small/boat.png b/examples/animation/sub-attaq/pics/small/boat.png Binary files differnew file mode 100644 index 0000000..114ccc3 --- /dev/null +++ b/examples/animation/sub-attaq/pics/small/boat.png diff --git a/examples/animation/sub-attaq/pics/small/bomb.png b/examples/animation/sub-attaq/pics/small/bomb.png Binary files differnew file mode 100644 index 0000000..3af5f2f --- /dev/null +++ b/examples/animation/sub-attaq/pics/small/bomb.png diff --git a/examples/animation/sub-attaq/pics/small/submarine.png b/examples/animation/sub-attaq/pics/small/submarine.png Binary files differnew file mode 100644 index 0000000..0c0c350 --- /dev/null +++ b/examples/animation/sub-attaq/pics/small/submarine.png diff --git a/examples/animation/sub-attaq/pics/small/surface.png b/examples/animation/sub-attaq/pics/small/surface.png Binary files differnew file mode 100644 index 0000000..06d0e47 --- /dev/null +++ b/examples/animation/sub-attaq/pics/small/surface.png diff --git a/examples/animation/sub-attaq/pics/small/torpedo.png b/examples/animation/sub-attaq/pics/small/torpedo.png Binary files differnew file mode 100644 index 0000000..f9c2687 --- /dev/null +++ b/examples/animation/sub-attaq/pics/small/torpedo.png diff --git a/examples/animation/sub-attaq/pics/welcome/logo-a.png b/examples/animation/sub-attaq/pics/welcome/logo-a.png Binary files differnew file mode 100644 index 0000000..67dd76d --- /dev/null +++ b/examples/animation/sub-attaq/pics/welcome/logo-a.png diff --git a/examples/animation/sub-attaq/pics/welcome/logo-a2.png b/examples/animation/sub-attaq/pics/welcome/logo-a2.png Binary files differnew file mode 100644 index 0000000..17668b0 --- /dev/null +++ b/examples/animation/sub-attaq/pics/welcome/logo-a2.png diff --git a/examples/animation/sub-attaq/pics/welcome/logo-b.png b/examples/animation/sub-attaq/pics/welcome/logo-b.png Binary files differnew file mode 100644 index 0000000..cf6c045 --- /dev/null +++ b/examples/animation/sub-attaq/pics/welcome/logo-b.png diff --git a/examples/animation/sub-attaq/pics/welcome/logo-dash.png b/examples/animation/sub-attaq/pics/welcome/logo-dash.png Binary files differnew file mode 100644 index 0000000..219233c --- /dev/null +++ b/examples/animation/sub-attaq/pics/welcome/logo-dash.png diff --git a/examples/animation/sub-attaq/pics/welcome/logo-excl.png b/examples/animation/sub-attaq/pics/welcome/logo-excl.png Binary files differnew file mode 100644 index 0000000..8dd0a2e --- /dev/null +++ b/examples/animation/sub-attaq/pics/welcome/logo-excl.png diff --git a/examples/animation/sub-attaq/pics/welcome/logo-q.png b/examples/animation/sub-attaq/pics/welcome/logo-q.png Binary files differnew file mode 100644 index 0000000..86e588d --- /dev/null +++ b/examples/animation/sub-attaq/pics/welcome/logo-q.png diff --git a/examples/animation/sub-attaq/pics/welcome/logo-s.png b/examples/animation/sub-attaq/pics/welcome/logo-s.png Binary files differnew file mode 100644 index 0000000..7b6a36e --- /dev/null +++ b/examples/animation/sub-attaq/pics/welcome/logo-s.png diff --git a/examples/animation/sub-attaq/pics/welcome/logo-t.png b/examples/animation/sub-attaq/pics/welcome/logo-t.png Binary files differnew file mode 100644 index 0000000..b2e3526 --- /dev/null +++ b/examples/animation/sub-attaq/pics/welcome/logo-t.png diff --git a/examples/animation/sub-attaq/pics/welcome/logo-t2.png b/examples/animation/sub-attaq/pics/welcome/logo-t2.png Binary files differnew file mode 100644 index 0000000..b11a778 --- /dev/null +++ b/examples/animation/sub-attaq/pics/welcome/logo-t2.png diff --git a/examples/animation/sub-attaq/pics/welcome/logo-u.png b/examples/animation/sub-attaq/pics/welcome/logo-u.png Binary files differnew file mode 100644 index 0000000..24eede8 --- /dev/null +++ b/examples/animation/sub-attaq/pics/welcome/logo-u.png diff --git a/examples/animation/sub-attaq/pixmapitem.cpp b/examples/animation/sub-attaq/pixmapitem.cpp new file mode 100644 index 0000000..aa8b552 --- /dev/null +++ b/examples/animation/sub-attaq/pixmapitem.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//Own +#include "pixmapitem.h" + +//Qt +#include <QDir> + +PixmapItem::PixmapItem(const QString &fileName,GraphicsScene::Mode mode, QGraphicsItem * parent) : QGraphicsPixmapItem(parent),name(fileName) +{ + loadPixmap(mode); +} + +void PixmapItem::loadPixmap(GraphicsScene::Mode mode) +{ + if (mode == GraphicsScene::Big) + setPixmap(":/big/" + name); + else + setPixmap(":/small/" + name); +} diff --git a/examples/animation/sub-attaq/pixmapitem.h b/examples/animation/sub-attaq/pixmapitem.h new file mode 100644 index 0000000..f3c1a41 --- /dev/null +++ b/examples/animation/sub-attaq/pixmapitem.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef __PIXMAPITEM__H__ +#define __PIXMAPITEM__H__ + +//Own +#include "graphicsscene.h" + +//Qt +#include <QGraphicsPixmapItem> + +class PixmapItem : public QGraphicsPixmapItem +{ +public: + PixmapItem(const QString &fileName, GraphicsScene::Mode mode, QGraphicsItem * parent = 0); + +private: + void loadPixmap(GraphicsScene::Mode mode); + + QString name; + QPixmap pixmap; +}; + +#endif //__PIXMAPITEM__H__ diff --git a/examples/animation/sub-attaq/qanimationstate.cpp b/examples/animation/sub-attaq/qanimationstate.cpp new file mode 100644 index 0000000..70285a8 --- /dev/null +++ b/examples/animation/sub-attaq/qanimationstate.cpp @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qanimationstate.h" + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qstate.h" +#else +# include <QtCore/qstate.h> +#endif + +#include <private/qstate_p.h> + +QT_BEGIN_NAMESPACE + +/*! +\class QAnimationState + +\brief The QAnimationState class provides state that handle an animation and emit +a signal when this animation is finished. + +\ingroup statemachine + +QAnimationState provides a state that handle an animation. It will start this animation +when the state is entered and stop it when it is leaved. When the animation has finished the +state emit animationFinished signal. +QAnimationState is part of \l{The State Machine Framework}. + +\code +QStateMachine machine; +QAnimationState *s = new QAnimationState(machine->rootState()); +QPropertyAnimation *animation = new QPropertyAnimation(obj, "pos"); +s->setAnimation(animation); +QState *s2 = new QState(machine->rootState()); +s->addTransition(s, SIGNAL(animationFinished()), s2); +machine.start(); +\endcode + +\sa QState, {The Animation Framework} +*/ + + +#ifndef QT_NO_ANIMATION + +class QAnimationStatePrivate : public QStatePrivate +{ + Q_DECLARE_PUBLIC(QAnimationState) +public: + QAnimationStatePrivate() + : animation(0) + { + + } + ~QAnimationStatePrivate() {} + + QAbstractAnimation *animation; +}; + +/*! + Constructs a new state with the given \a parent state. +*/ +QAnimationState::QAnimationState(QState *parent) + : QState(*new QAnimationStatePrivate, parent) +{ +} + +/*! + Destroys the animation state. +*/ +QAnimationState::~QAnimationState() +{ +} + +/*! + Set an \a animation for this QAnimationState. If an animation was previously handle by this + state then it won't emit animationFinished for the old animation. The QAnimationState doesn't + take the ownership of the animation. +*/ +void QAnimationState::setAnimation(QAbstractAnimation *animation) +{ + Q_D(QAnimationState); + + if (animation == d->animation) + return; + + //Disconnect from the previous animation if exist + if(d->animation) + disconnect(d->animation, SIGNAL(finished()), this, SIGNAL(animationFinished())); + + d->animation = animation; + + if (d->animation) { + //connect the new animation + connect(d->animation, SIGNAL(finished()), this, SIGNAL(animationFinished())); + } +} + +/*! + Returns the animation handle by this animation state, or 0 if there is no animation. +*/ +QAbstractAnimation* QAnimationState::animation() const +{ + Q_D(const QAnimationState); + return d->animation; +} + +/*! + \reimp +*/ +void QAnimationState::onEntry() +{ + Q_D(QAnimationState); + if (d->animation) + d->animation->start(); +} + +/*! + \reimp +*/ +void QAnimationState::onExit() +{ + Q_D(QAnimationState); + if (d->animation) + d->animation->stop(); +} + +/*! + \reimp +*/ +bool QAnimationState::event(QEvent *e) +{ + return QState::event(e); +} + +QT_END_NAMESPACE + +#endif diff --git a/examples/animation/sub-attaq/qanimationstate.h b/examples/animation/sub-attaq/qanimationstate.h new file mode 100644 index 0000000..fda1534 --- /dev/null +++ b/examples/animation/sub-attaq/qanimationstate.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANIMATIONSTATE_H +#define QANIMATIONSTATE_H + +#ifndef QT_STATEMACHINE_SOLUTION +# include <QtCore/qstate.h> +# include <QtCore/qabstractanimation.h> +#else +# include "qstate.h" +# include "qabstractanimation.h" +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_ANIMATION + +class QAnimationStatePrivate; + +class Q_GUI_EXPORT QAnimationState : public QState +{ + Q_OBJECT +public: + QAnimationState(QState *parent = 0); + ~QAnimationState(); + + void setAnimation(QAbstractAnimation *animation); + QAbstractAnimation* animation() const; + +Q_SIGNALS: + void animationFinished(); + +protected: + void onEntry(); + void onExit(); + bool event(QEvent *e); + +private: + Q_DISABLE_COPY(QAnimationState) + Q_DECLARE_PRIVATE(QAnimationState) +}; + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QANIMATIONSTATE_H diff --git a/examples/animation/sub-attaq/states.cpp b/examples/animation/sub-attaq/states.cpp new file mode 100644 index 0000000..f476f55 --- /dev/null +++ b/examples/animation/sub-attaq/states.cpp @@ -0,0 +1,289 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//Own +#include "states.h" +#include "graphicsscene.h" +#include "boat.h" +#include "submarine.h" +#include "torpedo.h" +#include "animationmanager.h" + +//Qt +#include <QMessageBox> +#include <QGraphicsView> +#if defined(QT_EXPERIMENTAL_SOLUTION) +#include "qstatemachine.h" +#include "qkeyeventtransition.h" +#include "qsignalevent.h" +#include "qfinalstate.h" +#else +#include <QtCore/QStateMachine> +#include <QtGui/QKeyEventTransition> +#include <QtCore/QSignalEvent> +#include <QFinalState> +#endif + +PlayState::PlayState(GraphicsScene *scene, QState *parent) + : QState(parent), + scene(scene), + machine(0), + currentLevel(0), + score(0) +{ +} + +PlayState::~PlayState() +{ +} + +void PlayState::onEntry() +{ + //We are now playing? + if (machine) { + machine->stop(); + scene->clearScene(); + delete machine; + } + + machine = new QStateMachine(this); + + //This state is when player is playing + QState *playState = new QState(machine->rootState()); + + initializeLevel(); + + //This state is when the game is paused + PauseState *pauseState = new PauseState(scene, machine->rootState()); + + //We have one view, it receive the key press event + QKeyEventTransition *pressPplay = new QKeyEventTransition(scene->views().at(0), QEvent::KeyPress, Qt::Key_P); + pressPplay->setTargetState(pauseState); + QKeyEventTransition *pressPpause = new QKeyEventTransition(scene->views().at(0), QEvent::KeyPress, Qt::Key_P); + pressPpause->setTargetState(playState); + + //Pause "P" is triggered, the player pause the game + playState->addTransition(pressPplay); + + //To get back playing when the game has been paused + pauseState->addTransition(pressPpause); + + //This state is when player have lost + LostState *lostState = new LostState(scene, this, machine->rootState()); + + //This state is when player have won + WinState *winState = new WinState(scene, this, machine->rootState()); + + //The boat has been destroyed then the game is finished + playState->addTransition(scene->boat, SIGNAL(boatExecutionFinished()),lostState); + + //This transition check if we won or not + WinTransition *winTransition = new WinTransition(scene, this, winState); + + //The boat has been destroyed then the game is finished + playState->addTransition(winTransition); + + //This state is an animation when the score changed + UpdateScoreState *scoreState = new UpdateScoreState(this, machine->rootState()); + + //This transition update the score when a submarine die + UpdateScoreTransition *scoreTransition = new UpdateScoreTransition(scene, this, scoreState); + + //The boat has been destroyed then the game is finished + playState->addTransition(scoreTransition); + + //We go back to play state + scoreState->addTransition(playState); + + //We start playing!!! + machine->setInitialState(playState); + + //Final state + QFinalState *final = new QFinalState(machine->rootState()); + + //We win we should reach the final state + winState->addFinishedTransition(final); + + //We lost we should reach the final state + lostState->addFinishedTransition(final); + + machine->start(); +} + +void PlayState::initializeLevel() +{ + scene->boat = new Boat(); + scene->addItem(scene->boat); + scene->setFocusItem(scene->boat,Qt::OtherFocusReason); + scene->boat->setPos(scene->width()/2, scene->sealLevel() - scene->boat->size().height()); + + GraphicsScene::LevelDescription currentLevelDescription = scene->levelsData.value(currentLevel); + + for (int i = 0; i < currentLevelDescription.submarines.size(); ++i ) { + + QPair<int,int> subContent = currentLevelDescription.submarines.at(i); + GraphicsScene::SubmarineDescription submarineDesc = scene->submarinesData.at(subContent.first); + + for (int j = 0; j < subContent.second; ++j ) { + SubMarine *sub = new SubMarine(submarineDesc.type, submarineDesc.name, submarineDesc.points); + scene->addItem(sub); + int random = (qrand() % 15 + 1); + qreal x = random == 13 || random == 5 ? 0 : scene->width() - sub->size().width(); + qreal y = scene->height() -(qrand() % 150 + 1) - sub->size().height(); + sub->setPos(x,y); + sub->setCurrentDirection(x == 0 ? SubMarine::Right : SubMarine::Left); + sub->setCurrentSpeed(qrand() % 3 + 1); + } + } +} + +/** Pause State */ +PauseState::PauseState(GraphicsScene *scene, QState *parent) : QState(parent),scene(scene) +{ +} +void PauseState::onEntry() +{ + AnimationManager::self()->pauseAll(); + scene->boat->setEnabled(false); +} +void PauseState::onExit() +{ + AnimationManager::self()->resumeAll(); + scene->boat->setEnabled(true); + scene->boat->setFocus(); +} + +/** Lost State */ +LostState::LostState(GraphicsScene *scene, PlayState *game, QState *parent) : QState(parent), scene(scene), game(game) +{ +} + +void LostState::onEntry() +{ + //The message to display + QString message = QString("You lose on level %1. Your score is %2.").arg(game->currentLevel+1).arg(game->score); + + //We set the level back to 0 + game->currentLevel = 0; + + //We set the score back to 0 + game->score = 0; + + //We clear the scene + scene->clearScene(); + + //we have only one view + QMessageBox::information(scene->views().at(0),"You lose",message); +} + +/** Win State */ +WinState::WinState(GraphicsScene *scene, PlayState *game, QState *parent) : QState(parent), scene(scene), game(game) +{ +} + +void WinState::onEntry() +{ + //We clear the scene + scene->clearScene(); + + QString message; + if (scene->levelsData.size() - 1 != game->currentLevel) { + message = QString("You win the level %1. Your score is %2.").arg(game->currentLevel+1).arg(game->score); + //We increment the level number + game->currentLevel++; + } else { + message = QString("You finish the game on level %1. Your score is %2.").arg(game->currentLevel+1).arg(game->score); + //We set the level back to 0 + game->currentLevel = 0; + //We set the score back to 0 + game->score = 0; + } + + //we have only one view + QMessageBox::information(scene->views().at(0),"You win",message); +} + +/** UpdateScore State */ +UpdateScoreState::UpdateScoreState(PlayState *game, QState *parent) : QState(parent) +{ + this->game = game; +} +void UpdateScoreState::onEntry() +{ + //### Make a nice anim to update the score in the scene + QState::onEntry(); +} + +/** Win transition */ +UpdateScoreTransition::UpdateScoreTransition(GraphicsScene *scene, PlayState *game, QAbstractState *target) + : QSignalTransition(scene,SIGNAL(subMarineDestroyed(int)), QList<QAbstractState*>() << target), + game(game) +{ +} + +bool UpdateScoreTransition::eventTest(QEvent *event) const +{ + if (!QSignalTransition::eventTest(event)) + return false; + else { + QSignalEvent *se = static_cast<QSignalEvent*>(event); + game->score += se->arguments().at(0).toInt(); + return true; + } +} + +/** Win transition */ +WinTransition::WinTransition(GraphicsScene *scene, PlayState *game, QAbstractState *target) + : QSignalTransition(scene,SIGNAL(allSubMarineDestroyed(int)), QList<QAbstractState*>() << target), + game(game) +{ +} + +bool WinTransition::eventTest(QEvent *event) const +{ + if (!QSignalTransition::eventTest(event)) + return false; + else { + QSignalEvent *se = static_cast<QSignalEvent*>(event); + game->score += se->arguments().at(0).toInt(); + return true; + } +} diff --git a/examples/animation/sub-attaq/states.h b/examples/animation/sub-attaq/states.h new file mode 100644 index 0000000..52d4ffa --- /dev/null +++ b/examples/animation/sub-attaq/states.h @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef STATES_H +#define STATES_H + +//Qt +#if defined(QT_EXPERIMENTAL_SOLUTION) +#include "qstate.h" +#include "qsignaltransition.h" +#include "qpropertyanimation.h" +#else +#include <QState> +#include <QSignalTransition> +#include <QPropertyAnimation> +#endif +#include <QSet> + +class GraphicsScene; +class Boat; +class SubMarine; +class QStateMachine; + +class PlayState : public QState +{ +public: + PlayState(GraphicsScene *scene, QState *parent = 0); + ~PlayState(); + void initializeLevel(); + + protected: + void onEntry(); + +private : + GraphicsScene *scene; + QStateMachine *machine; + int currentLevel; + int score; + QState *parallelChild; + + friend class UpdateScoreState; + friend class UpdateScoreTransition; + friend class WinTransition; + friend class WinState; + friend class LostState; +}; + +class PauseState : public QState +{ +public: + PauseState(GraphicsScene *scene, QState *parent = 0); + +protected: + void onEntry(); + void onExit(); +private : + GraphicsScene *scene; + Boat *boat; +}; + +class LostState : public QState +{ +public: + LostState(GraphicsScene *scene, PlayState *game, QState *parent = 0); + +protected: + void onEntry(); +private : + GraphicsScene *scene; + PlayState *game; +}; + +class WinState : public QState +{ +public: + WinState(GraphicsScene *scene, PlayState *game, QState *parent = 0); + +protected: + void onEntry(); +private : + GraphicsScene *scene; + PlayState *game; +}; + +class UpdateScoreState : public QState +{ +public: + UpdateScoreState(PlayState *game, QState *parent); +protected: + void onEntry(); +private: + QPropertyAnimation *scoreAnimation; + PlayState *game; +}; + +//These transtion is used to update the score +class UpdateScoreTransition : public QSignalTransition +{ +public: + UpdateScoreTransition(GraphicsScene *scene, PlayState *game, QAbstractState *target); +protected: + virtual bool eventTest(QEvent *event) const; +private: + PlayState * game; +}; + +//These transtion test if we have won the game +class WinTransition : public QSignalTransition +{ +public: + WinTransition(GraphicsScene *scene, PlayState *game, QAbstractState *target); +protected: + virtual bool eventTest(QEvent *event) const; +private: + PlayState * game; +}; + +#endif // STATES_H diff --git a/examples/animation/sub-attaq/sub-attaq.pro b/examples/animation/sub-attaq/sub-attaq.pro new file mode 100644 index 0000000..07c4d06 --- /dev/null +++ b/examples/animation/sub-attaq/sub-attaq.pro @@ -0,0 +1,40 @@ +# ##################################################################### +# Automatically generated by qmake (2.01a) Thu Oct 9 10:53:30 2008 +# ##################################################################### +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +QT+= xml +contains(QT_CONFIG, opengl):QT += opengl + +# Input +HEADERS += boat.h \ + bomb.h \ + mainwindow.h \ + submarine.h \ + torpedo.h \ + pixmapitem.h \ + graphicsscene.h \ + animationmanager.h \ + states.h \ + boat_p.h \ + submarine_p.h \ + custompropertyanimation_p.h \ + custompropertyanimation.h \ + qanimationstate.h + +SOURCES += boat.cpp \ + bomb.cpp \ + main.cpp \ + mainwindow.cpp \ + submarine.cpp \ + torpedo.cpp \ + pixmapitem.cpp \ + graphicsscene.cpp \ + animationmanager.cpp \ + states.cpp \ + custompropertyanimation.cpp \ + qanimationstate.cpp + +RESOURCES += subattaq.qrc diff --git a/examples/animation/sub-attaq/subattaq.qrc b/examples/animation/sub-attaq/subattaq.qrc new file mode 100644 index 0000000..c76f8ef --- /dev/null +++ b/examples/animation/sub-attaq/subattaq.qrc @@ -0,0 +1,38 @@ +<RCC> + <qresource prefix="/" > + <file alias="all" >pics/scalable/sub-attaq.svg</file> + <file alias="submarine" >pics/scalable/submarine.svg</file> + <file alias="boat" >pics/scalable/boat.svg</file> + <file alias="torpedo" >pics/scalable/torpedo.svg</file> + <file alias="logo-s" >pics/welcome/logo-s.png</file> + <file alias="logo-u" >pics/welcome/logo-u.png</file> + <file alias="logo-b" >pics/welcome/logo-b.png</file> + <file alias="logo-dash" >pics/welcome/logo-dash.png</file> + <file alias="logo-a" >pics/welcome/logo-a.png</file> + <file alias="logo-t" >pics/welcome/logo-t.png</file> + <file alias="logo-t2" >pics/welcome/logo-t2.png</file> + <file alias="logo-a2" >pics/welcome/logo-a2.png</file> + <file alias="logo-q" >pics/welcome/logo-q.png</file> + <file alias="logo-excl" >pics/welcome/logo-excl.png</file> + <file alias="big/background" >pics/big/background.png</file> + <file alias="big/boat" >pics/big/boat.png</file> + <file alias="big/bomb" >pics/big/bomb.png</file> + <file alias="big/submarine" >pics/big/submarine.png</file> + <file alias="big/surface" >pics/big/surface.png</file> + <file alias="big/torpedo" >pics/big/torpedo.png</file> + <file alias="small/background" >pics/small/background.png</file> + <file alias="small/boat" >pics/small/boat.png</file> + <file alias="small/bomb" >pics/small/bomb.png</file> + <file alias="small/submarine" >pics/small/submarine.png</file> + <file alias="small/surface" >pics/small/surface.png</file> + <file alias="small/torpedo" >pics/small/torpedo.png</file> + <file alias="big/explosion/boat/step1" >pics/big/explosion/boat/step1.png</file> + <file alias="big/explosion/boat/step2" >pics/big/explosion/boat/step2.png</file> + <file alias="big/explosion/boat/step3" >pics/big/explosion/boat/step3.png</file> + <file alias="big/explosion/boat/step4" >pics/big/explosion/boat/step4.png</file> + <file alias="big/explosion/submarine/step1" >pics/big/explosion/submarine/step1.png</file> + <file alias="big/explosion/submarine/step2" >pics/big/explosion/submarine/step2.png</file> + <file alias="big/explosion/submarine/step3" >pics/big/explosion/submarine/step3.png</file> + <file alias="big/explosion/submarine/step4" >pics/big/explosion/submarine/step4.png</file> + </qresource> +</RCC> diff --git a/examples/animation/sub-attaq/submarine.cpp b/examples/animation/sub-attaq/submarine.cpp new file mode 100644 index 0000000..e64ffdd --- /dev/null +++ b/examples/animation/sub-attaq/submarine.cpp @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//Own +#include "submarine.h" +#include "submarine_p.h" +#include "torpedo.h" +#include "pixmapitem.h" +#include "graphicsscene.h" +#include "animationmanager.h" +#include "custompropertyanimation.h" +#include "qanimationstate.h" + +#if defined(QT_EXPERIMENTAL_SOLUTION) +#include "qpropertyanimation.h" +#include "qstatemachine.h" +#include "qfinalstate.h" +#include "qsequentialanimationgroup.h" +#else +#include <QPropertyAnimation> +#include <QStateMachine> +#include <QFinalState> +#include <QSequentialAnimationGroup> +#endif + +static QAbstractAnimation *setupDestroyAnimation(SubMarine *sub) +{ + QSequentialAnimationGroup *group = new QSequentialAnimationGroup(sub); +#if QT_VERSION >=0x040500 + PixmapItem *step1 = new PixmapItem(QString("explosion/submarine/step1"),GraphicsScene::Big, sub); + step1->setZValue(6); + PixmapItem *step2 = new PixmapItem(QString("explosion/submarine/step2"),GraphicsScene::Big, sub); + step2->setZValue(6); + PixmapItem *step3 = new PixmapItem(QString("explosion/submarine/step3"),GraphicsScene::Big, sub); + step3->setZValue(6); + PixmapItem *step4 = new PixmapItem(QString("explosion/submarine/step4"),GraphicsScene::Big, sub); + step4->setZValue(6); + step1->setOpacity(0); + step2->setOpacity(0); + step3->setOpacity(0); + step4->setOpacity(0); + CustomPropertyAnimation *anim1 = new CustomPropertyAnimation(sub); + anim1->setMemberFunctions((QGraphicsItem*)step1, &QGraphicsItem::opacity, &QGraphicsItem::setOpacity); + anim1->setDuration(100); + anim1->setEndValue(1); + CustomPropertyAnimation *anim2 = new CustomPropertyAnimation(sub); + anim2->setMemberFunctions((QGraphicsItem*)step2, &QGraphicsItem::opacity, &QGraphicsItem::setOpacity); + anim2->setDuration(100); + anim2->setEndValue(1); + CustomPropertyAnimation *anim3 = new CustomPropertyAnimation(sub); + anim3->setMemberFunctions((QGraphicsItem*)step3, &QGraphicsItem::opacity, &QGraphicsItem::setOpacity); + anim3->setDuration(100); + anim3->setEndValue(1); + CustomPropertyAnimation *anim4 = new CustomPropertyAnimation(sub); + anim4->setMemberFunctions((QGraphicsItem*)step4, &QGraphicsItem::opacity, &QGraphicsItem::setOpacity); + anim4->setDuration(100); + anim4->setEndValue(1); + group->addAnimation(anim1); + group->addAnimation(anim2); + group->addAnimation(anim3); + group->addAnimation(anim4); +#else + // work around for a bug where we don't transition if the duration is zero. + QtPauseAnimation *anim = new QtPauseAnimation(group); + anim->setDuration(1); + group->addAnimation(anim); +#endif + AnimationManager::self()->registerAnimation(group); + return group; +} + + +SubMarine::SubMarine(int type, const QString &name, int points, QGraphicsItem * parent, Qt::WindowFlags wFlags) + : QGraphicsWidget(parent,wFlags), subType(type), subName(name), subPoints(points), speed(0), direction(SubMarine::None) +{ + pixmapItem = new PixmapItem(QString("submarine"),GraphicsScene::Big, this); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + setZValue(5); + setFlags(QGraphicsItem::ItemIsMovable); + resize(pixmapItem->boundingRect().width(),pixmapItem->boundingRect().height()); + setTransformOrigin(boundingRect().center()); + + //We setup the state machine of the submarine + QStateMachine *machine = new QStateMachine(this); + + //This state is when the boat is moving/rotating + QState *moving = new QState(machine->rootState()); + + //This state is when the boat is moving from left to right + MovementState *movement = new MovementState(this,moving); + + //This state is when the boat is moving from left to right + ReturnState *rotation = new ReturnState(this,moving); + + //This is the initial state of the moving root state + moving->setInitialState(movement); + + movement->addTransition(this, SIGNAL(subMarineStateChanged()),moving); + + //This is the initial state of the machine + machine->setInitialState(moving); + + //End + QFinalState *final = new QFinalState(machine->rootState()); + + //If the moving animation is finished we move to the return state + movement->addTransition(movement, SIGNAL(animationFinished()), rotation); + + //If the return animation is finished we move to the moving state + rotation->addTransition(rotation, SIGNAL(animationFinished()), movement); + + //This state play the destroyed animation + QAnimationState *destroyedState = new QAnimationState(machine->rootState()); + destroyedState->setAnimation(setupDestroyAnimation(this)); + + //Play a nice animation when the submarine is destroyed + moving->addTransition(this, SIGNAL(subMarineDestroyed()),destroyedState); + + //Transition to final state when the destroyed animation is finished + destroyedState->addTransition(destroyedState, SIGNAL(animationFinished()), final); + + //The machine has finished to be executed, then the submarine is dead + connect(machine,SIGNAL(finished()),this, SIGNAL(subMarineExecutionFinished())); + + machine->start(); +} + +int SubMarine::points() +{ + return subPoints; +} + +void SubMarine::setCurrentDirection(SubMarine::Movement direction) +{ + if (this->direction == direction) + return; + if (direction == SubMarine::Right && this->direction == SubMarine::None) { + setYRotation(180); + } + this->direction = direction; +} + +enum SubMarine::Movement SubMarine::currentDirection() const +{ + return direction; +} + +void SubMarine::setCurrentSpeed(int speed) +{ + if (speed < 0 || speed > 3) { + qWarning("SubMarine::setCurrentSpeed : The speed is invalid"); + } + this->speed = speed; + emit subMarineStateChanged(); +} + +int SubMarine::currentSpeed() const +{ + return speed; +} + +void SubMarine::launchTorpedo(int speed) +{ + Torpedo * torp = new Torpedo(); + GraphicsScene *scene = static_cast<GraphicsScene *>(this->scene()); + scene->addItem(torp); + torp->setPos(x(), y()); + torp->setCurrentSpeed(speed); + torp->launch(); +} + +void SubMarine::destroy() +{ + emit subMarineDestroyed(); +} + +int SubMarine::type() const +{ + return Type; +} diff --git a/examples/animation/sub-attaq/submarine.h b/examples/animation/sub-attaq/submarine.h new file mode 100644 index 0000000..562b4cf --- /dev/null +++ b/examples/animation/sub-attaq/submarine.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef __SUBMARINE__H__ +#define __SUBMARINE__H__ + +//Qt +#if defined(QT_EXPERIMENTAL_SOLUTION) +#include "qvariantanimation.h" +#include "qgraphicswidget.h" +#else +#include <QVariantAnimation> +#include <QGraphicsWidget> +#endif + +class PixmapItem; + +class Torpedo; + +class SubMarine : public QGraphicsWidget +{ +Q_OBJECT +public: + enum Movement { + None = 0, + Left, + Right + }; + enum { Type = UserType + 1 }; + SubMarine(int type, const QString &name, int points, QGraphicsItem * parent = 0, Qt::WindowFlags wFlags = 0); + + int points(); + + void setCurrentDirection(Movement direction); + enum Movement currentDirection() const; + + void setCurrentSpeed(int speed); + int currentSpeed() const; + + void launchTorpedo(int speed); + void destroy(); + + virtual int type() const; + +Q_SIGNALS: + void subMarineDestroyed(); + void subMarineExecutionFinished(); + void subMarineStateChanged(); + +private: + int subType; + QString subName; + int subPoints; + int speed; + Movement direction; + PixmapItem *pixmapItem; +}; + +#endif //__SUBMARINE__H__ diff --git a/examples/animation/sub-attaq/submarine_p.h b/examples/animation/sub-attaq/submarine_p.h new file mode 100644 index 0000000..354a406 --- /dev/null +++ b/examples/animation/sub-attaq/submarine_p.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SUBMARINE_P_H +#define SUBMARINE_P_H + +//Own +#include "animationmanager.h" +#include "submarine.h" +#include "qanimationstate.h" + +//Qt +#if defined(QT_EXPERIMENTAL_SOLUTION) +#include "qpropertyanimation.h" +#else +#include <QPropertyAnimation> +#endif +#include <QGraphicsScene> + +//This state is describing when the boat is moving right +class MovementState : public QAnimationState +{ +Q_OBJECT +public: + MovementState(SubMarine *submarine, QState *parent = 0) : QAnimationState(parent) + { + movementAnimation = new QPropertyAnimation(submarine, "pos"); + connect(movementAnimation,SIGNAL(valueChanged(const QVariant &)),this,SLOT(onAnimationMovementValueChanged(const QVariant &))); + setAnimation(movementAnimation); + AnimationManager::self()->registerAnimation(movementAnimation); + this->submarine = submarine; + } + +protected slots: + void onAnimationMovementValueChanged(const QVariant &) + { + if (qrand() % 200 + 1 == 3) + submarine->launchTorpedo(qrand() % 3 + 1); + } + +protected: + void onEntry() + { + if (submarine->currentDirection() == SubMarine::Left) { + movementAnimation->setEndValue(QPointF(0,submarine->y())); + movementAnimation->setDuration(submarine->x()/submarine->currentSpeed()*12); + } + else /*if (submarine->currentDirection() == SubMarine::Right)*/ { + movementAnimation->setEndValue(QPointF(submarine->scene()->width()-submarine->size().width(),submarine->y())); + movementAnimation->setDuration((submarine->scene()->width()-submarine->size().width()-submarine->x())/submarine->currentSpeed()*12); + } + movementAnimation->setStartValue(submarine->pos()); + QAnimationState::onEntry(); + } + +private: + SubMarine *submarine; + QPropertyAnimation *movementAnimation; +}; + +//This state is describing when the boat is moving right +class ReturnState : public QAnimationState +{ +public: + ReturnState(SubMarine *submarine, QState *parent = 0) : QAnimationState(parent) + { + returnAnimation = new QPropertyAnimation(submarine, "yRotation"); + AnimationManager::self()->registerAnimation(returnAnimation); + setAnimation(returnAnimation); + this->submarine = submarine; + } + +protected: + void onEntry() + { + returnAnimation->stop(); + returnAnimation->setStartValue(submarine->yRotation()); + returnAnimation->setEndValue(submarine->currentDirection() == SubMarine::Right ? 360. : 180.); + returnAnimation->setDuration(500); + QAnimationState::onEntry(); + } + + void onExit() + { + submarine->currentDirection() == SubMarine::Right ? submarine->setCurrentDirection(SubMarine::Left) : submarine->setCurrentDirection(SubMarine::Right); + QAnimationState::onExit(); + } + +private: + SubMarine *submarine; + QPropertyAnimation *returnAnimation; +}; + +#endif // SUBMARINE_P_H diff --git a/examples/animation/sub-attaq/torpedo.cpp b/examples/animation/sub-attaq/torpedo.cpp new file mode 100644 index 0000000..b24948d --- /dev/null +++ b/examples/animation/sub-attaq/torpedo.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//Own +#include "torpedo.h" +#include "pixmapitem.h" +#include "boat.h" +#include "graphicsscene.h" +#include "animationmanager.h" +#include "qanimationstate.h" + +#if defined(QT_EXPERIMENTAL_SOLUTION) +#include "qpropertyanimation.h" +#include "qstatemachine.h" +#include "qfinalstate.h" +#else +#include <QPropertyAnimation> +#include <QStateMachine> +#include <QFinalState> +#endif + +Torpedo::Torpedo(QGraphicsItem * parent, Qt::WindowFlags wFlags) + : QGraphicsWidget(parent,wFlags), currentSpeed(0), launchAnimation(0) +{ + pixmapItem = new PixmapItem(QString::fromLatin1("torpedo"),GraphicsScene::Big, this); + setZValue(2); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + setFlags(QGraphicsItem::ItemIsMovable); + resize(pixmapItem->boundingRect().size()); +} + +void Torpedo::launch() +{ + launchAnimation = new QPropertyAnimation(this, "pos"); + AnimationManager::self()->registerAnimation(launchAnimation); + launchAnimation->setEndValue(QPointF(x(),qobject_cast<GraphicsScene *>(scene())->sealLevel() - 15)); + launchAnimation->setEasingCurve(QEasingCurve::InQuad); + launchAnimation->setDuration(y()/currentSpeed*10); + connect(launchAnimation,SIGNAL(valueChanged(const QVariant &)),this,SLOT(onAnimationLaunchValueChanged(const QVariant &))); + + //We setup the state machine of the torpedo + QStateMachine *machine = new QStateMachine(this); + + //This state is when the launch animation is playing + QAnimationState *launched = new QAnimationState(machine->rootState()); + launched->setAnimation(launchAnimation); + + //End + QFinalState *final = new QFinalState(machine->rootState()); + + machine->setInitialState(launched); + + //### Add a nice animation when the torpedo is destroyed + launched->addTransition(this, SIGNAL(torpedoExplosed()),final); + + //If the animation is finished, then we move to the final state + launched->addTransition(launched, SIGNAL(animationFinished()), final); + + //The machine has finished to be executed, then the boat is dead + connect(machine,SIGNAL(finished()),this, SIGNAL(torpedoExecutionFinished())); + + machine->start(); +} + +void Torpedo::setCurrentSpeed(int speed) +{ + if (speed < 0) { + qWarning("Torpedo::setCurrentSpeed : The speed is invalid"); + return; + } + currentSpeed = speed; +} + +void Torpedo::onAnimationLaunchValueChanged(const QVariant &) +{ + foreach (QGraphicsItem *item , collidingItems(Qt::IntersectsItemBoundingRect)) { + if (item->type() == Boat::Type) { + Boat *b = static_cast<Boat *>(item); + b->destroy(); + } + } +} + +void Torpedo::destroy() +{ + launchAnimation->stop(); + emit torpedoExplosed(); +} diff --git a/examples/animation/sub-attaq/torpedo.h b/examples/animation/sub-attaq/torpedo.h new file mode 100644 index 0000000..33bbc04 --- /dev/null +++ b/examples/animation/sub-attaq/torpedo.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef __TORPEDO__H__ +#define __TORPEDO__H__ + +//Qt +#include <QObject> + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qvariantanimation.h" +# include "qgraphicswidget.h" +#else +# include <QVariantAnimation> +# include <QtGui/QGraphicsWidget> +#endif + +class PixmapItem; + +class Torpedo : public QGraphicsWidget +{ +Q_OBJECT +Q_PROPERTY(QPointF pos READ pos WRITE setPos) +public: + Torpedo(QGraphicsItem * parent = 0, Qt::WindowFlags wFlags = 0); + void launch(); + void setCurrentSpeed(int speed); + void destroy(); + +Q_SIGNALS: + void torpedoExplosed(); + void torpedoExecutionFinished(); + +private slots: + void onAnimationLaunchValueChanged(const QVariant &); + +private: + int currentSpeed; + PixmapItem *pixmapItem; + QVariantAnimation *launchAnimation; +}; + +#endif //__TORPEDO__H__ diff --git a/examples/examples.pro b/examples/examples.pro index 41501a0..332a712 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -14,6 +14,7 @@ SUBDIRS = \ qtconcurrent \ richtext \ sql \ + statemachine \ threads \ tools \ tutorials \ @@ -24,6 +25,7 @@ SUBDIRS = \ contains(QT_CONFIG, phonon):!static: SUBDIRS += phonon contains(QT_CONFIG, webkit): SUBDIRS += webkit +contains(QT_CONFIG, animation): SUBDIRS += animation embedded:SUBDIRS += qws !wince*: { !contains(QT_EDITION, Console):contains(QT_BUILD_PARTS, tools):SUBDIRS += designer diff --git a/examples/statemachine/README b/examples/statemachine/README new file mode 100644 index 0000000..2879e67 --- /dev/null +++ b/examples/statemachine/README @@ -0,0 +1,36 @@ +Qt is provided with a powerful hierchical finite state machine through +the Qt State Machine classes. + +The example launcher provided with Qt can be used to explore each of the +examples in this directory. + +Documentation for these examples can be found via the Tutorial and Examples +link in the main Qt documentation. + + +Finding the Qt Examples and Demos launcher +========================================== + +On Windows: + +The launcher can be accessed via the Windows Start menu. Select the menu +entry entitled "Qt Examples and Demos" entry in the submenu containing +the Qt tools. + +On Mac OS X: + +For the binary distribution, the qtdemo executable is installed in the +/Developer/Applications/Qt directory. For the source distribution, it is +installed alongside the other Qt tools on the path specified when Qt is +configured. + +On Unix/Linux: + +The qtdemo executable is installed alongside the other Qt tools on the path +specified when Qt is configured. + +On all platforms: + +The source code for the launcher can be found in the demos/qtdemo directory +in the Qt package. This example is built at the same time as the Qt libraries, +tools, examples, and demonstrations. diff --git a/examples/statemachine/citizenquartz/citizenquartz.pro b/examples/statemachine/citizenquartz/citizenquartz.pro new file mode 100644 index 0000000..58039a0 --- /dev/null +++ b/examples/statemachine/citizenquartz/citizenquartz.pro @@ -0,0 +1,20 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += main.cpp \ + clock.cpp \ + clockbutton.cpp \ + clockdisplay.cpp \ + propertyaddstate.cpp \ + +HEADERS += clock.h \ + clockbutton.h \ + clockdisplay.h \ + propertyaddstate.h \ + timeperiod.h \ + +RESOURCES += citizenquartz.qrc +CONFIG += console diff --git a/examples/statemachine/citizenquartz/citizenquartz.qrc b/examples/statemachine/citizenquartz/citizenquartz.qrc new file mode 100644 index 0000000..fc09ef9 --- /dev/null +++ b/examples/statemachine/citizenquartz/citizenquartz.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>sound/alarm.wav</file> + <file>images/alarm.png</file> +</qresource> +</RCC>
\ No newline at end of file diff --git a/examples/statemachine/citizenquartz/clock.cpp b/examples/statemachine/citizenquartz/clock.cpp new file mode 100644 index 0000000..1b2b21e --- /dev/null +++ b/examples/statemachine/citizenquartz/clock.cpp @@ -0,0 +1,390 @@ +#include "clock.h" +#include "clockbutton.h" +#include "clockdisplay.h" +#include "propertyaddstate.h" +#include "timeperiod.h" + +#include <QStateMachine> +#include <QState> +#include <QHistoryState> + +#include <QPainter> +#include <QTimer> +#include <QSound> + +Clock::Clock(QGraphicsItem *parent) + : QGraphicsItem(parent), + m_stateMachine(0), + m_clockDisplay(0), + m_buttonA(0), + m_buttonB(0), + m_buttonC(0), + m_buttonD(0), + m_alarmState(0), + m_timeState(0), + m_updateState(0), + m_regularState(0), + m_displaysHistoryState(0), + m_alarmSound(new QSound(":/sound/alarm.wav", this)) +{ +} + +void Clock::initializeUi() +{ + QPainterPath path = shape(); + QPointF pap; + qreal aap; + + m_buttonA = new ClockButton("Button A", this); + pap = path.pointAtPercent(0.05); + aap = path.angleAtPercent(0.05); + m_buttonA->translate(pap.x(), pap.y()); + m_buttonA->rotate(-aap); + connect(m_buttonA, SIGNAL(pressed()), this, SIGNAL(anyButtonPressed())); + + m_buttonB = new ClockButton("Button B", this); + pap = path.pointAtPercent(0.77); + aap = path.angleAtPercent(0.77); + m_buttonB->translate(pap.x(), pap.y()); + m_buttonB->rotate(-aap); + connect(m_buttonB, SIGNAL(pressed()), this, SIGNAL(anyButtonPressed())); + + m_buttonC = new ClockButton("Button C", this); + pap = path.pointAtPercent(0.67); + aap = path.angleAtPercent(0.67); + m_buttonC->translate(pap.x(), pap.y()); + m_buttonC->rotate(-aap); + connect(m_buttonC, SIGNAL(pressed()), this, SIGNAL(anyButtonPressed())); + + m_buttonD = new ClockButton("Button D", this); + pap = path.pointAtPercent(0.57); + aap = path.angleAtPercent(0.57); + m_buttonD->translate(pap.x(), pap.y()); + m_buttonD->rotate(-aap); + connect(m_buttonD, SIGNAL(pressed()), this, SIGNAL(anyButtonPressed())); + + QGraphicsSimpleTextItem *label = new QGraphicsSimpleTextItem(this); + label->setText("CITIZEN"); + label->setPos(0.0 - label->boundingRect().width() / 2.0, -100.0); + + m_clockDisplay = new ClockDisplay(this); + m_clockDisplay->setCurrentTime(QDateTime::currentDateTime()); + + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(updateTime())); + timer->setInterval(1); + timer->start(); + m_time.start(); +} + +void Clock::initializeStateMachine() +{ + m_stateMachine = new QStateMachine; + + QState *displays = new QState(m_stateMachine->rootState()); + displays->setObjectName("displays"); + initializeDisplaysState(displays); + + QState *alarmsBeepState = new QState(m_stateMachine->rootState()); + alarmsBeepState->setObjectName("alarmsBeep"); + alarmsBeepState->invokeMethodOnEntry(this, "playSound"); + alarmsBeepState->invokeMethodOnExit(this, "stopSound"); + + QTimer *alarmTimeOut = new QTimer(alarmsBeepState); + alarmTimeOut->setInterval(30000); + alarmsBeepState->invokeMethodOnEntry(alarmTimeOut, "start"); + alarmsBeepState->invokeMethodOnExit(alarmTimeOut, "stop"); + + displays->addTransition(m_clockDisplay, SIGNAL(alarmTriggered()), alarmsBeepState); + alarmsBeepState->addTransition(this, SIGNAL(anyButtonPressed()), m_displaysHistoryState); + alarmsBeepState->addTransition(alarmTimeOut, SIGNAL(timeout()), m_displaysHistoryState); + + m_stateMachine->setInitialState(displays); + m_stateMachine->start(); +} + +void Clock::initializeUpdateState(QState *updateState) +{ + QState *sec = new QState(updateState); + sec->setObjectName("sec"); + sec->setPropertyOnEntry(m_clockDisplay, "displayMode", ClockDisplay::EditSecondMode); + updateState->setInitialState(sec); + + PropertyAddState *secIncrease = new PropertyAddState(updateState); + secIncrease->setObjectName("sec ++"); + secIncrease->addToProperty(m_clockDisplay, "currentTime", TimePeriod().setSeconds(1)); + sec->addTransition(m_buttonD, SIGNAL(pressed()), secIncrease); + secIncrease->addTransition(sec); + + QState *oneMin = new QState(updateState); + oneMin->setObjectName("1 min"); + oneMin->setPropertyOnEntry(m_clockDisplay, "displayMode", ClockDisplay::EditMinuteMode); + sec->addTransition(m_buttonC, SIGNAL(pressed()), oneMin); + + PropertyAddState *oneMinIncrease = new PropertyAddState(updateState); + oneMinIncrease->setObjectName("1 min ++"); + oneMinIncrease->addToProperty(m_clockDisplay, "currentTime", TimePeriod().setMinutes(1)); + oneMin->addTransition(m_buttonD, SIGNAL(pressed()), oneMinIncrease); + oneMinIncrease->addTransition(oneMin); + + QState *tenMin = new QState(updateState); + tenMin->setObjectName("10 min"); + tenMin->setPropertyOnEntry(m_clockDisplay, "displayMode", ClockDisplay::EditMinuteMode); + oneMin->addTransition(m_buttonC, SIGNAL(pressed()), tenMin); + + PropertyAddState *tenMinIncrease = new PropertyAddState(updateState); + tenMinIncrease->setObjectName("10 min ++"); + tenMinIncrease->addToProperty(m_clockDisplay, "currentTime", TimePeriod().setMinutes(10)); + tenMin->addTransition(m_buttonD, SIGNAL(pressed()), tenMinIncrease); + tenMinIncrease->addTransition(tenMin); + + QState *hr = new QState(updateState); + hr->setObjectName("hr"); + hr->setPropertyOnEntry(m_clockDisplay, "displayMode", ClockDisplay::EditHourMode); + tenMin->addTransition(m_buttonC, SIGNAL(pressed()), hr); + + PropertyAddState *hrIncrease = new PropertyAddState(updateState); + hrIncrease->setObjectName("hr ++"); + hrIncrease->addToProperty(m_clockDisplay, "currentTime", TimePeriod().setHours(1)); + hr->addTransition(m_buttonD, SIGNAL(pressed()), hrIncrease); + hrIncrease->addTransition(hr); + + QState *mon = new QState(updateState); + mon->setObjectName("mon"); + mon->setPropertyOnEntry(m_clockDisplay, "displayMode", ClockDisplay::EditMonthMode); + hr->addTransition(m_buttonC, SIGNAL(pressed()), mon); + + PropertyAddState *monIncrease = new PropertyAddState(updateState); + monIncrease->setObjectName("mon ++"); + monIncrease->addToProperty(m_clockDisplay, "currentTime", TimePeriod().setMonths(1)); + mon->addTransition(m_buttonD, SIGNAL(pressed()), monIncrease); + monIncrease->addTransition(mon); + + QState *day = new QState(updateState); + day->setObjectName("day"); + day->setPropertyOnEntry(m_clockDisplay, "displayMode", ClockDisplay::EditDayMode); + mon->addTransition(m_buttonC, SIGNAL(pressed()), day); + + PropertyAddState *dayIncrease = new PropertyAddState(updateState); + dayIncrease->setObjectName("day ++"); + dayIncrease->addToProperty(m_clockDisplay, "currentTime", TimePeriod().setDays(1)); + day->addTransition(m_buttonD, SIGNAL(pressed()), dayIncrease); + dayIncrease->addTransition(day); + + QState *year = new QState(updateState); + year->setObjectName("year"); + year->setPropertyOnEntry(m_clockDisplay, "displayMode", ClockDisplay::EditYearMode); + day->addTransition(m_buttonC, SIGNAL(pressed()), year); + + PropertyAddState *yearIncrease = new PropertyAddState(updateState); + yearIncrease->setObjectName("year ++"); + yearIncrease->addToProperty(m_clockDisplay, "currentTime", TimePeriod().setYears(1)); + year->addTransition(m_buttonD, SIGNAL(pressed()), yearIncrease); + yearIncrease->addTransition(year); + year->addTransition(m_buttonC, SIGNAL(pressed()), m_timeState); +} + +void Clock::initializeRegularState(QState *regular) +{ + m_timeState = new QState(regular); + m_timeState->setObjectName("time"); + m_timeState->setPropertyOnEntry(m_clockDisplay, "displayMode", ClockDisplay::CurrentTimeMode); + + QState *date = new QState(regular); + date->setObjectName("date"); + date->setPropertyOnEntry(m_clockDisplay, "displayMode", ClockDisplay::CurrentDateMode); + + QState *dateTimerState = new QState(date); + dateTimerState->setObjectName("dateTimerState"); + + // Date state exits after 2 m + QTimer *dateTimer = new QTimer(dateTimerState); + dateTimer->setSingleShot(true); + dateTimer->setInterval(2*60000); + dateTimerState->invokeMethodOnEntry(dateTimer, "start"); + dateTimerState->invokeMethodOnExit(dateTimer, "stop"); + + date->setInitialState(dateTimerState); + + m_updateState = new QState(regular); + m_updateState->setObjectName("update"); + + // Update state exits after 2 m + QTimer *updateTimer = new QTimer(m_updateState); + updateTimer->setSingleShot(true); + updateTimer->setInterval(2 * 60000); + m_updateState->invokeMethodOnEntry(updateTimer, "start"); + m_updateState->invokeMethodOnExit(updateTimer, "stop"); + + initializeUpdateState(m_updateState); + + m_timeState->addTransition(m_buttonD, SIGNAL(pressed()), date); + date->addTransition(m_buttonD, SIGNAL(pressed()), m_timeState); + date->addTransition(dateTimer, SIGNAL(timeout()), m_timeState); + + m_updateState->addTransition(updateTimer, SIGNAL(timeout()), m_timeState); + m_updateState->addTransition(m_buttonB, SIGNAL(pressed()), m_timeState); + + regular->setInitialState(m_timeState); +} + +void Clock::initializeAlarmUpdateState(QState *update) +{ + QState *hr = new QState(update); + hr->setObjectName("hr"); + hr->setPropertyOnEntry(m_clockDisplay, "displayMode", ClockDisplay::EditAlarmHourMode); + + PropertyAddState *hrInc = new PropertyAddState(update); + hrInc->setObjectName("hr ++"); + hrInc->addToProperty(m_clockDisplay, "alarm", TimePeriod().setHours(1)); + hr->addTransition(m_buttonD, SIGNAL(pressed()), hrInc); + hrInc->addTransition(hr); + + QState *tenMin = new QState(update); + tenMin->setObjectName("10 min"); + tenMin->setPropertyOnEntry(m_clockDisplay, "displayMode", ClockDisplay::EditAlarmTenMinuteMode); + hr->addTransition(m_buttonC, SIGNAL(pressed()), tenMin); + + PropertyAddState *tenMinInc = new PropertyAddState(update); + tenMinInc->setObjectName("10 min ++"); + tenMinInc->addToProperty(m_clockDisplay, "alarm", TimePeriod().setMinutes(10)); + tenMin->addTransition(m_buttonD, SIGNAL(pressed()), tenMinInc); + tenMinInc->addTransition(tenMin); + + QState *oneMin = new QState(update); + oneMin->setObjectName("1 min"); + oneMin->setPropertyOnEntry(m_clockDisplay, "displayMode", ClockDisplay::EditAlarmMinuteMode); + tenMin->addTransition(m_buttonC, SIGNAL(pressed()), oneMin); + + PropertyAddState *oneMinInc = new PropertyAddState(update); + oneMinInc->setObjectName("1 min ++"); + oneMinInc->addToProperty(m_clockDisplay, "alarm", TimePeriod().setMinutes(1)); + oneMin->addTransition(m_buttonD, SIGNAL(pressed()), oneMinInc); + oneMinInc->addTransition(oneMin); + + oneMin->addTransition(m_buttonC, SIGNAL(pressed()), m_alarmState); + m_alarmState->addTransition(m_buttonC, SIGNAL(pressed()), hr); +} + +void Clock::initializeOutState(QState *out) +{ + m_alarmState = new QState(out); + m_alarmState->setObjectName("alarmState"); + m_alarmState->setPropertyOnEntry(m_clockDisplay, "displayMode", ClockDisplay::AlarmMode); + initializeAlarmState(m_alarmState); + out->setInitialState(m_alarmState); + + QState *alarmUpdate = new QState(out); + alarmUpdate->setObjectName("alarmUpdate"); + initializeAlarmUpdateState(alarmUpdate); + + alarmUpdate->addTransition(m_buttonB, SIGNAL(pressed()), m_alarmState); + m_alarmState->addTransition(m_buttonA, SIGNAL(pressed()), m_regularState); +} + +void Clock::initializeDisplaysState(QState *displays) +{ + m_regularState = new QState(displays); + m_regularState->setObjectName("regular"); + initializeRegularState(m_regularState); + displays->setInitialState(m_regularState); + + QState *out = new QState(displays); + out->setObjectName("out"); + + QTimer *outTimer = new QTimer(out); + outTimer->setSingleShot(true); + outTimer->setInterval(2 * 60000); + out->invokeMethodOnEntry(outTimer, "start"); + out->invokeMethodOnExit(outTimer, "stop"); + + initializeOutState(out); + + QState *wait = new QState(displays); + wait->setObjectName("wait"); + + QTimer *waitTimer = new QTimer(wait); + waitTimer->setSingleShot(true); + waitTimer->setInterval(2000); + wait->invokeMethodOnEntry(waitTimer, "start"); + wait->invokeMethodOnExit(waitTimer, "stop"); + + m_displaysHistoryState = displays->addHistoryState(QState::DeepHistory); + + m_timeState->addTransition(m_buttonC, SIGNAL(pressed()), wait); + wait->addTransition(waitTimer, SIGNAL(timeout()), m_updateState); + wait->addTransition(m_buttonC, SIGNAL(released()), m_timeState); + m_timeState->addTransition(m_buttonA, SIGNAL(pressed()), m_alarmState); + out->addTransition(outTimer, SIGNAL(timeout()), m_regularState); +} + +void Clock::initializeAlarmState(QState *alarmState) +{ + QState *offState = new QState(alarmState); + offState->setObjectName("alarmOff"); + offState->setPropertyOnEntry(m_clockDisplay, "alarmEnabled", false); + + QState *onState = new QState(alarmState); + onState->setObjectName("alarmOn"); + onState->setPropertyOnEntry(m_clockDisplay, "alarmEnabled", true); + + QHistoryState *history = alarmState->addHistoryState(); + history->setObjectName("alarmHistory"); + history->setDefaultState(offState); + + offState->addTransition(m_buttonD, SIGNAL(pressed()), onState); + onState->addTransition(m_buttonD, SIGNAL(pressed()), offState); + + QState *intermediate = new QState(alarmState); + intermediate->addTransition(history); + + alarmState->setInitialState(intermediate); +} + +QRectF Clock::boundingRect() const +{ + return shape().boundingRect(); +} + +QPainterPath Clock::shape() const +{ + QPainterPath path; + path.addRoundedRect(QRectF(-140.0, -100.0, 280.0, 200.0), 50.0, 50.0, Qt::RelativeSize); + + return path; +} + +void Clock::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + painter->setPen(Qt::black); + painter->setBrush(Qt::NoBrush); + + // Clock face + painter->drawPath(shape()); +} + +void Clock::updateTime() +{ + int elapsed = m_time.elapsed(); + if (elapsed > 0) { + m_time.restart(); + QDateTime currentTime = m_clockDisplay->currentTime(); + m_clockDisplay->setCurrentTime(currentTime.addMSecs(elapsed)); + + update(); + } +} + +void Clock::playSound() +{ + qDebug("playing sound"); + m_alarmSound->stop(); + m_alarmSound->play(); +} + +void Clock::stopSound() +{ + qDebug("stopping sound"); + m_alarmSound->stop(); +} + diff --git a/examples/statemachine/citizenquartz/clock.h b/examples/statemachine/citizenquartz/clock.h new file mode 100644 index 0000000..afd6433 --- /dev/null +++ b/examples/statemachine/citizenquartz/clock.h @@ -0,0 +1,62 @@ +#ifndef CLOCK_H +#define CLOCK_H + +#include <QGraphicsItem> +#include <QTime> + +class ClockButton ; +class ClockDisplay ; +class QStateMachine ; +class QState ; +class QTimerState ; +class QSound ; +class QHistoryState ; + +class Clock: public QObject, public QGraphicsItem +{ + Q_OBJECT +public: + Clock(QGraphicsItem *parent = 0); + + void initializeUi(); + void initializeStateMachine(); + + virtual QRectF boundingRect() const; + virtual QPainterPath shape() const; + virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + +signals: + void anyButtonPressed(); + +public slots: + void updateTime(); + void playSound(); + void stopSound(); + +private: + void initializeDisplaysState(QState *displays); + void initializeAlarmState(QState *alarmState); + void initializeRegularState(QState *regular); + void initializeUpdateState(QState *update); + void initializeOutState(QState *out); + void initializeAlarmUpdateState(QState *update); + + QStateMachine *m_stateMachine; + ClockDisplay *m_clockDisplay; + ClockButton *m_buttonA; + ClockButton *m_buttonB; + ClockButton *m_buttonC; + ClockButton *m_buttonD; + + QState *m_alarmState; + QState *m_timeState; + QState *m_updateState; + QState *m_regularState; + + QHistoryState *m_displaysHistoryState; + + QSound *m_alarmSound; + QTime m_time; +}; + +#endif // CLOCK_H diff --git a/examples/statemachine/citizenquartz/clockbutton.cpp b/examples/statemachine/citizenquartz/clockbutton.cpp new file mode 100644 index 0000000..7b86891 --- /dev/null +++ b/examples/statemachine/citizenquartz/clockbutton.cpp @@ -0,0 +1,39 @@ +#include "clockbutton.h" + +#include <QPainter> + +ClockButton::ClockButton(const QString &name, QGraphicsItem *parent) : QGraphicsItem(parent) +{ + setObjectName(name); + setToolTip(name); + setAcceptedMouseButtons(Qt::LeftButton); +} + +QRectF ClockButton::boundingRect() const +{ + return QRectF(-10.0, -10.0, 20.0, 20.0); +} + +QPainterPath ClockButton::shape() const +{ + QPainterPath path; + path.addRoundedRect(boundingRect(), 15.0, 15.0, Qt::RelativeSize); + + return path; +} + +void ClockButton::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + painter->setBrush(Qt::black); + painter->drawPath(shape()); +} + +void ClockButton::mousePressEvent(QGraphicsSceneMouseEvent *) +{ + emit pressed(); +} + +void ClockButton::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + emit released(); +}
\ No newline at end of file diff --git a/examples/statemachine/citizenquartz/clockbutton.h b/examples/statemachine/citizenquartz/clockbutton.h new file mode 100644 index 0000000..02a7ccd --- /dev/null +++ b/examples/statemachine/citizenquartz/clockbutton.h @@ -0,0 +1,25 @@ +#ifndef CLOCKBUTTON_H +#define CLOCKBUTTON_H + +#include <QGraphicsItem> + +class ClockButton: public QObject, public QGraphicsItem +{ + Q_OBJECT +public: + ClockButton(const QString &name, QGraphicsItem *parent = 0); + + virtual QRectF boundingRect() const; + virtual QPainterPath shape() const; + virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *); + +protected: + virtual void mousePressEvent(QGraphicsSceneMouseEvent *); + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + +signals: + void pressed(); + void released(); +}; + +#endif //CLOCKBUTTON_H diff --git a/examples/statemachine/citizenquartz/clockdisplay.cpp b/examples/statemachine/citizenquartz/clockdisplay.cpp new file mode 100644 index 0000000..db026f5 --- /dev/null +++ b/examples/statemachine/citizenquartz/clockdisplay.cpp @@ -0,0 +1,139 @@ +#include "clockdisplay.h" + +#include <QPainter> +#include <QTimer> + +ClockDisplay::ClockDisplay(QGraphicsItem *parent) + : QGraphicsItem(parent), + m_displayMode(CurrentTimeMode), + m_currentTime(QDate(1970, 1, 1), QTime(0, 0)), + m_alarm(0, 0), + m_alarmEnabled(false), + m_blink(false) + +{ + m_text = new QGraphicsTextItem(this); + + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(toggleBlinkFlag())); + timer->start(500); + + QFont font = m_text->font(); + font.setPointSizeF(20.0); + m_text->setFont(font); + + m_text->translate(-100.0, -20.0); + + QPixmap pm(":/images/alarm.png"); + m_alarmSymbol = new QGraphicsPixmapItem(pm, this); + m_alarmSymbol->translate(-100.0, -60.0); + m_alarmSymbol->setOffset(5.0, 5.0); + m_alarmSymbol->scale(0.5, 0.5); +} + +ClockDisplay::~ClockDisplay() +{ +} + +void ClockDisplay::toggleBlinkFlag() +{ + m_blink = !m_blink; + update(); +} + +void ClockDisplay::updateText() +{ + switch (m_displayMode) { + case EditSecondMode: + if (m_blink) { + m_text->setHtml(m_currentTime.toString("hh:mm:'<font color=\"transparent\">'ss'</font>'")); + break; + } + // fall throoough + case EditMinuteMode: + if (m_blink) { + m_text->setHtml(m_currentTime.toString("hh:'<font color=\"transparent\">'mm'</font>':ss")); + break; + } + + // fall throoough + case EditHourMode: + if (m_blink) { + m_text->setHtml(m_currentTime.toString("'<font color=\"transparent\">'hh'</font>':mm:ss")); + break; + } + + // fall throoough + case CurrentTimeMode: + m_text->setHtml(m_currentTime.toString("hh:mm:ss")); + break; + + case EditMonthMode: + if (m_blink) { + m_text->setHtml(m_currentTime.toString("yyyy.'<font color=\"transparent\">'MM'</font>'.dd")); + break; + } + + // fall throoough + case EditDayMode: + if (m_blink) { + m_text->setHtml(m_currentTime.toString("yyyy.MM.'<font color=\"transparent\">'dd'</font>'")); + break; + } + + // fall throoough + case EditYearMode: + if (m_blink) { + m_text->setHtml(m_currentTime.toString("'<font color=\"transparent\">'yyyy'</font>'.MM.dd")); + break; + } + + // fall throoough + case CurrentDateMode: + m_text->setHtml(m_currentTime.toString("yyyy.MM.dd")); + break; + + case EditAlarmHourMode: + if (m_blink) { + m_text->setHtml(m_alarm.toString("'<font color=\"transparent\">'hh'</font>':mm")); + break; + } + + // fall throooough + case EditAlarmTenMinuteMode: + case EditAlarmMinuteMode: + if (m_blink) { + m_text->setHtml(m_alarm.toString("hh:'<font color=\"transparent\">'mm'</font>'")); + break; + } + + // fall throoough + case AlarmMode: + m_text->setHtml(m_alarm.toString("hh:mm")); + break; + + default: + m_text->setHtml("Not implemented"); + }; +} + +QRectF ClockDisplay::boundingRect() const +{ + return QRectF(-100.0, -60.0, 200.0, 120.0); +} + +void ClockDisplay::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + m_alarmSymbol->setVisible(m_alarmEnabled); + + updateText(); + + // Screen + painter->drawRoundedRect(boundingRect(), 15.0, 15.0, Qt::RelativeSize); + + // Button grid + painter->drawLine(QPointF(-100.0, -20.0), QPointF(100.0, -20.0)); + painter->drawLine(QPointF(-50.0, -60.0), QPointF(-50.0, -20.0)); + painter->drawLine(QPointF(0.0, -60.0), QPointF(0.0, -20.0)); + painter->drawLine(QPointF(50.0, -60.0), QPointF(50.0, -20.0)); +} diff --git a/examples/statemachine/citizenquartz/clockdisplay.h b/examples/statemachine/citizenquartz/clockdisplay.h new file mode 100644 index 0000000..ec86509 --- /dev/null +++ b/examples/statemachine/citizenquartz/clockdisplay.h @@ -0,0 +1,89 @@ +#ifndef CLOCKDISPLAY_H +#define CLOCKDISPLAY_H + +#include <QObject> +#include <QGraphicsItem> +#include <QDateTime> + +class ClockDisplay: public QObject, public QGraphicsItem +{ + Q_OBJECT + Q_ENUMS(DisplayMode) + Q_PROPERTY(DisplayMode displayMode READ displayMode WRITE setDisplayMode) + Q_PROPERTY(QDateTime currentTime READ currentTime WRITE setCurrentTime) + Q_PROPERTY(QTime alarm READ alarm WRITE setAlarm) + Q_PROPERTY(bool alarmEnabled READ alarmEnabled WRITE setAlarmEnabled) +public: + enum DisplayMode { + CurrentTimeMode, + EditSecondMode, + EditMinuteMode, + EditHourMode, + + CurrentDateMode, + EditMonthMode, + EditDayMode, + EditYearMode, + + AlarmMode, + EditAlarmHourMode, + EditAlarmTenMinuteMode, + EditAlarmMinuteMode, + + ChimeMode, + StopWatchMode, + ModeCount + }; + + ClockDisplay(QGraphicsItem *parent = 0); + virtual ~ClockDisplay(); + + void setDisplayMode(DisplayMode displayMode) { m_displayMode = displayMode; update(); } + DisplayMode displayMode() const { return m_displayMode; } + + QDateTime currentTime() const { return m_currentTime; } + void setCurrentTime(const QDateTime &time) + { + if (m_alarmEnabled && !alarmMatches(m_currentTime) && alarmMatches(time)) + emit alarmTriggered(); + m_currentTime = time; + update(); + } + + QTime alarm() const { return m_alarm; } + void setAlarm(const QTime &time) { m_alarm = time; update(); } + + bool alarmEnabled() const { return m_alarmEnabled; } + void setAlarmEnabled(bool enabled) { m_alarmEnabled = enabled; update(); } + + virtual QRectF boundingRect() const; + virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *); + +signals: + void alarmTriggered(); + +private slots: + void toggleBlinkFlag(); + +private: + void updateText(); + + bool alarmMatches(const QDateTime &dt) + { + return (dt.time().hour() == m_alarm.hour() && dt.time().minute() == m_alarm.minute()); + } + + DisplayMode m_displayMode; + + QDateTime m_currentTime; + QTime m_alarm; + + QGraphicsTextItem *m_text; + QGraphicsPixmapItem *m_alarmSymbol; + + uint m_alarmEnabled : 1; + uint m_blink : 1; + uint m_reserved : 30; +}; + +#endif diff --git a/examples/statemachine/citizenquartz/images/alarm.png b/examples/statemachine/citizenquartz/images/alarm.png Binary files differnew file mode 100644 index 0000000..a19778d --- /dev/null +++ b/examples/statemachine/citizenquartz/images/alarm.png diff --git a/examples/statemachine/citizenquartz/main.cpp b/examples/statemachine/citizenquartz/main.cpp new file mode 100644 index 0000000..2c6b14c --- /dev/null +++ b/examples/statemachine/citizenquartz/main.cpp @@ -0,0 +1,23 @@ +#include <QApplication> +#include <QGraphicsScene> +#include <QGraphicsView> + +#include "clock.h" + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + QGraphicsScene scene; + + Clock *clock = new Clock; + clock->initializeUi(); + clock->initializeStateMachine(); + scene.addItem(clock); + + QGraphicsView view(&scene); + view.setRenderHint(QPainter::Antialiasing); + view.show(); + + return app.exec(); +} diff --git a/examples/statemachine/citizenquartz/propertyaddstate.cpp b/examples/statemachine/citizenquartz/propertyaddstate.cpp new file mode 100644 index 0000000..dd23948 --- /dev/null +++ b/examples/statemachine/citizenquartz/propertyaddstate.cpp @@ -0,0 +1,46 @@ +#include "PropertyAddState.h" +#include "timeperiod.h" + +#include <QDateTime> +#include <QDebug> + +PropertyAddState::PropertyAddState(QState *parent) + : QState(parent) +{ +} + +void PropertyAddState::addToProperty(QObject *object, const char *propertyName, + const QVariant &valueToAdd) +{ + m_propertyAdditions.append(PropertyAdder(object, propertyName, valueToAdd)); +} + +QVariant PropertyAddState::addProperties(const QVariant ¤t, const QVariant &toAdd) const +{ + QVariant result; + switch (current.type()) { + case QVariant::DateTime: + result = current.toDateTime() + qvariant_cast<TimePeriod>(toAdd); + break; + case QVariant::Time: + result = current.toTime() + qvariant_cast<TimePeriod>(toAdd); + break; + default: + qWarning("PropertyAddState::addProperties: QVariant type '%s' not supported", + current.typeName()); + }; + + return result; +} + +void PropertyAddState::onEntry() +{ + foreach (PropertyAdder propertyAdder, m_propertyAdditions) { + QObject *object = propertyAdder.object; + QByteArray propertyName = propertyAdder.propertyName; + QVariant toAdd = propertyAdder.valueToAdd; + QVariant current = object->property(propertyName); + + object->setProperty(propertyName, addProperties(current, toAdd)); + } +} diff --git a/examples/statemachine/citizenquartz/propertyaddstate.h b/examples/statemachine/citizenquartz/propertyaddstate.h new file mode 100644 index 0000000..4d28055 --- /dev/null +++ b/examples/statemachine/citizenquartz/propertyaddstate.h @@ -0,0 +1,33 @@ +#ifndef PropertyAddState_H +#define PropertyAddState_H + +#include <qstate.h> + +#include <QVariant> +#include <QList> + +class PropertyAddState: public QState +{ +public: + PropertyAddState(QState *parent = 0); + + void addToProperty(QObject *object, const char *propertyName, const QVariant &valueToAdd); + virtual void onEntry(); + +private: + QVariant addProperties(const QVariant ¤t, const QVariant &toAdd) const; + + struct PropertyAdder { + PropertyAdder(QObject *_object, const char *_propertyName, const QVariant &_valueToAdd) + : object(_object), propertyName(_propertyName), valueToAdd(_valueToAdd) + { + } + + QObject *object; + QByteArray propertyName; + QVariant valueToAdd; + }; + QList<PropertyAdder> m_propertyAdditions; +}; + +#endif // PropertyAddState_H diff --git a/examples/statemachine/citizenquartz/sound/alarm.wav b/examples/statemachine/citizenquartz/sound/alarm.wav Binary files differnew file mode 100644 index 0000000..1d9486f --- /dev/null +++ b/examples/statemachine/citizenquartz/sound/alarm.wav diff --git a/examples/statemachine/citizenquartz/timeperiod.h b/examples/statemachine/citizenquartz/timeperiod.h new file mode 100644 index 0000000..c5a3a16 --- /dev/null +++ b/examples/statemachine/citizenquartz/timeperiod.h @@ -0,0 +1,84 @@ +#ifndef TIMEPERIOD_H +#define TIMEPERIOD_H + +#include <QDateTime> +#include <QTime> +#include <QDebug> + +class TimePeriod +{ +public: + TimePeriod() : m_seconds(0), m_minutes(0), m_hours(0), m_days(0), m_months(0), m_years(0) + { + } + + TimePeriod &setSeconds(int seconds) { m_seconds = seconds; return *this; } + int seconds() const { return m_seconds; } + + TimePeriod &setMinutes(int minutes) { m_minutes = minutes; return *this; } + int minutes() const { return m_minutes; } + + TimePeriod &setHours(int hours) { m_hours = hours; return *this; } + int hours() const { return m_hours; } + + TimePeriod &setDays(int days) { m_days = days; return *this; } + int days() const { return m_days; } + + TimePeriod &setMonths(int months) { m_months = months; return *this; } + int months() const { return m_months; } + + TimePeriod &setYears(int years) { m_years = years; return *this; } + int years() const { return m_years; } + + operator QVariant() const + { + QVariant v; + qVariantSetValue<TimePeriod>(v, *this); + return v; + } + +private: + int m_seconds; + int m_minutes; + int m_hours; + int m_days; + int m_months; + int m_years; +}; + +inline void operator+=(QDateTime &dateTime, const TimePeriod &timePeriod) +{ + dateTime = dateTime.addSecs(timePeriod.seconds()); + dateTime = dateTime.addSecs(timePeriod.minutes() * 60); + dateTime = dateTime.addSecs(timePeriod.hours() * 3600); + dateTime = dateTime.addDays(timePeriod.days()); + dateTime = dateTime.addMonths(timePeriod.months()); + dateTime = dateTime.addYears(timePeriod.years()); +} + +inline QDateTime operator+(const QDateTime &dateTime, const TimePeriod &timePeriod) +{ + QDateTime result(dateTime); + result += timePeriod; + + return result; +} + +inline void operator+=(QTime &time, const TimePeriod &timePeriod) +{ + time = time.addSecs(timePeriod.seconds()); + time = time.addSecs(timePeriod.minutes() * 60); + time = time.addSecs(timePeriod.hours() * 3600); +} + +inline QTime operator+(const QTime &time, const TimePeriod &timePeriod) +{ + QTime result(time); + result += timePeriod; + + return result; +} + +Q_DECLARE_METATYPE(TimePeriod) + +#endif \\ TIMEPERIOD_H diff --git a/examples/statemachine/clockticking/clockticking.pro b/examples/statemachine/clockticking/clockticking.pro new file mode 100644 index 0000000..bff9cb8 --- /dev/null +++ b/examples/statemachine/clockticking/clockticking.pro @@ -0,0 +1,10 @@ +QT = core +TEMPLATE = app +TARGET = +win32: CONFIG += console +mac:CONFIG -= app_bundle +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += main.cpp diff --git a/examples/statemachine/clockticking/main.cpp b/examples/statemachine/clockticking/main.cpp new file mode 100644 index 0000000..d01822f --- /dev/null +++ b/examples/statemachine/clockticking/main.cpp @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore> +#include <stdio.h> +#ifdef QT_STATEMACHINE_SOLUTION +#include <qstatemachine.h> +#include <qstate.h> +#include <qtransition.h> +#endif + +class ClockEvent : public QEvent +{ +public: + ClockEvent() : QEvent(QEvent::Type(QEvent::User+2)) + {} +}; + +class ClockState : public QState +{ +public: + ClockState(QStateMachine *machine, QState *parent) + : QState(parent), m_machine(machine) {} + +protected: + virtual void onEntry() + { + fprintf(stdout, "ClockState entered; posting the initial tick\n"); + m_machine->postEvent(new ClockEvent()); + } + +private: + QStateMachine *m_machine; +}; + +class ClockTransition : public QAbstractTransition +{ +public: + ClockTransition(QStateMachine *machine) + : QAbstractTransition(), m_machine(machine) { } + +protected: + virtual bool eventTest(QEvent *e) const { + return (e->type() == QEvent::User+2); + } + virtual void onTransition() + { + fprintf(stdout, "ClockTransition triggered; posting another tick with a delay of 1 second\n"); + m_machine->postEvent(new ClockEvent(), 1000); + } + +private: + QStateMachine *m_machine; +}; + +class ClockListener : public QAbstractTransition +{ +public: + ClockListener() + : QAbstractTransition() {} + +protected: + virtual bool eventTest(QEvent *e) const { + return (e->type() == QEvent::User+2); + } + virtual void onTransition() + { + fprintf(stdout, "ClockListener heard a tick!\n"); + } +}; + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + QStateMachine machine; + QState *group = new QState(QState::ParallelGroup); + group->setObjectName("group"); + + ClockState *clock = new ClockState(&machine, group); + clock->setObjectName("clock"); + clock->addTransition(new ClockTransition(&machine)); + + QState *listener = new QState(group); + listener->setObjectName("listener"); + listener->addTransition(new ClockListener()); + + machine.addState(group); + machine.setInitialState(group); + machine.start(); + + return app.exec(); +} diff --git a/examples/statemachine/composition/composition.pro b/examples/statemachine/composition/composition.pro new file mode 100644 index 0000000..6a976cb --- /dev/null +++ b/examples/statemachine/composition/composition.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += main.cpp diff --git a/examples/statemachine/composition/main.cpp b/examples/statemachine/composition/main.cpp new file mode 100644 index 0000000..24b823c --- /dev/null +++ b/examples/statemachine/composition/main.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#ifdef QT_STATEMACHINE_SOLUTION +#include <qstatemachine.h> +#include <qstate.h> +#include <qfinalstate.h> +#endif + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + QLabel label; + label.setAlignment(Qt::AlignCenter); + + QStateMachine machine; + + QState *s1 = new QState(); + s1->setObjectName("s1"); + s1->assignProperty(&label, "text", "In S1, hang on..."); + s1->assignProperty(&label, "geometry", QRect(100, 100, 200, 100)); + + QState *s1_timer = new QState(s1); + s1_timer->setObjectName("s1_timer"); + QTimer t1; + t1.setInterval(2000); + s1_timer->invokeMethodOnEntry(&t1, "start"); + QFinalState *s1_done = new QFinalState(s1); + s1_done->setObjectName("s1_done"); + s1_timer->addTransition(&t1, SIGNAL(timeout()), s1_done); + s1->setInitialState(s1_timer); + + QState *s2 = new QState(); + s2->setObjectName("s2"); + s2->assignProperty(&label, "text", "In S2, I'm gonna quit on you..."); + s2->assignProperty(&label, "geometry", QRect(300, 300, 300, 100)); +// s2->invokeMethodOnEntry(&label, "setNum", QList<QVariant>() << 123); +// s2->invokeMethodOnEntry(&label, "showMaximized"); + + QState *s2_timer = new QState(s2); + s2_timer->setObjectName("s2_timer"); + QTimer t2; + t2.setInterval(2000); + s2_timer->invokeMethodOnEntry(&t2, "start"); + QFinalState *s2_done = new QFinalState(s2); + s2_done->setObjectName("s2_done"); + s2_timer->addTransition(&t2, SIGNAL(timeout()), s2_done); + s2->setInitialState(s2_timer); + + s1->addFinishedTransition(s2); + + QFinalState *s3 = new QFinalState(); + s3->setObjectName("s3"); + s2->addFinishedTransition(s3); + + machine.addState(s1); + machine.addState(s2); + machine.addState(s3); + machine.setInitialState(s1); + QObject::connect(&machine, SIGNAL(finished()), &app, SLOT(quit())); + machine.start(); + + label.show(); + return app.exec(); +} diff --git a/examples/statemachine/eventtransitions/eventtransitions.pro b/examples/statemachine/eventtransitions/eventtransitions.pro new file mode 100644 index 0000000..6a976cb --- /dev/null +++ b/examples/statemachine/eventtransitions/eventtransitions.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += main.cpp diff --git a/examples/statemachine/eventtransitions/main.cpp b/examples/statemachine/eventtransitions/main.cpp new file mode 100644 index 0000000..f564b7e --- /dev/null +++ b/examples/statemachine/eventtransitions/main.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#ifdef QT_STATEMACHINE_SOLUTION +#include <qstatemachine.h> +#include <qstate.h> +#include <qeventtransition.h> +#endif + +class Window : public QWidget +{ +public: + Window(QWidget *parent = 0) + : QWidget(parent) + { + QPushButton *button = new QPushButton(this); + button->setGeometry(QRect(100, 100, 100, 100)); + + QStateMachine *machine = new QStateMachine(this); + + QState *s1 = new QState(); + s1->assignProperty(button, "text", "Outside"); + + QState *s2 = new QState(); + s2->assignProperty(button, "text", "Inside"); + + QEventTransition *enterTransition = new QEventTransition(button, QEvent::Enter); + enterTransition->setTargetState(s2); + s1->addTransition(enterTransition); + + QEventTransition *leaveTransition = new QEventTransition(button, QEvent::Leave); + leaveTransition->setTargetState(s1); + s2->addTransition(leaveTransition); + + QState *s3 = new QState(); + s3->assignProperty(button, "text", "Pressing..."); + + QEventTransition *pressTransition = new QEventTransition(button, QEvent::MouseButtonPress); + pressTransition->setTargetState(s3); + s2->addTransition(pressTransition); + + QEventTransition *releaseTransition = new QEventTransition(button, QEvent::MouseButtonRelease); + releaseTransition->setTargetState(s2); + s3->addTransition(releaseTransition); + + machine->addState(s1); + machine->addState(s2); + machine->addState(s3); + machine->setInitialState(s1); + QObject::connect(machine, SIGNAL(finished()), qApp, SLOT(quit())); + machine->start(); + } +}; + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + Window window; + window.resize(300, 300); + window.show(); + + return app.exec(); +} diff --git a/examples/statemachine/factorial/factorial.pro b/examples/statemachine/factorial/factorial.pro new file mode 100644 index 0000000..ac79117 --- /dev/null +++ b/examples/statemachine/factorial/factorial.pro @@ -0,0 +1,10 @@ +TEMPLATE = app +TARGET = +QT = core +win32: CONFIG += console +mac:CONFIG -= app_bundle +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += main.cpp diff --git a/examples/statemachine/factorial/main.cpp b/examples/statemachine/factorial/main.cpp new file mode 100644 index 0000000..9e39ced --- /dev/null +++ b/examples/statemachine/factorial/main.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore> +#include <stdio.h> +#ifdef QT_STATEMACHINE_SOLUTION +#include <qstatemachine.h> +#include <qstate.h> +#include <qsignaltransition.h> +#include <qfinalstate.h> +#endif + +class Factorial : public QObject +{ + Q_OBJECT + Q_PROPERTY(int x READ x WRITE setX) + Q_PROPERTY(int fac READ fac WRITE setFac) +public: + Factorial(QObject *parent = 0) + : QObject(parent) + { + m_fac = 1; + m_x = -1; + } + + int x() const + { + return m_x; + } + + void setX(int x) + { + if (x == m_x) + return; + m_x = x; + emit xChanged(); + } + + int fac() const + { + return m_fac; + } + + void setFac(int fac) + { + m_fac = fac; + } + +Q_SIGNALS: + void xChanged(); + +private: + int m_x; + int m_fac; +}; + +class FactorialLoopTransition : public QSignalTransition +{ +public: + FactorialLoopTransition(Factorial *fact) + : QSignalTransition(fact, SIGNAL(xChanged())), m_fact(fact) + {} + + virtual bool eventTest(QEvent *) const + { + return m_fact->property("x").toInt() > 1; + } + + virtual void onTransition() + { + int x = m_fact->property("x").toInt(); + int fac = m_fact->property("fac").toInt(); + m_fact->setProperty("fac", x * fac); + m_fact->setProperty("x", x - 1); + } + +private: + Factorial *m_fact; +}; + +class FactorialDoneTransition : public QSignalTransition +{ +public: + FactorialDoneTransition(Factorial *fact) + : QSignalTransition(fact, SIGNAL(xChanged())), m_fact(fact) + {} + + virtual bool eventTest(QEvent *) const + { + return m_fact->property("x").toInt() <= 1; + } + + virtual void onTransition() + { + fprintf(stdout, "%d\n", m_fact->property("fac").toInt()); + } + +private: + Factorial *m_fact; +}; + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + Factorial factorial; + + QStateMachine machine; + + QState *computing = new QState(machine.rootState()); + computing->addTransition(new FactorialLoopTransition(&factorial)); + + QFinalState *done = new QFinalState(machine.rootState()); + FactorialDoneTransition *doneTransition = new FactorialDoneTransition(&factorial); + doneTransition->setTargetState(done); + computing->addTransition(doneTransition); + + QState *initialize = new QState(machine.rootState()); + initialize->assignProperty(&factorial, "x", 6); + FactorialLoopTransition *enterLoopTransition = new FactorialLoopTransition(&factorial); + enterLoopTransition->setTargetState(computing); + initialize->addTransition(enterLoopTransition); + + QObject::connect(&machine, SIGNAL(finished()), &app, SLOT(quit())); + + machine.setInitialState(initialize); + machine.start(); + + return app.exec(); +} + +#include "main.moc" diff --git a/examples/statemachine/helloworld/helloworld.pro b/examples/statemachine/helloworld/helloworld.pro new file mode 100644 index 0000000..ac79117 --- /dev/null +++ b/examples/statemachine/helloworld/helloworld.pro @@ -0,0 +1,10 @@ +TEMPLATE = app +TARGET = +QT = core +win32: CONFIG += console +mac:CONFIG -= app_bundle +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += main.cpp diff --git a/examples/statemachine/helloworld/main.cpp b/examples/statemachine/helloworld/main.cpp new file mode 100644 index 0000000..13486d4 --- /dev/null +++ b/examples/statemachine/helloworld/main.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore> +#ifdef QT_STATEMACHINE_SOLUTION +#include <qstatemachine.h> +#include <qstate.h> +#include <qfinalstate.h> +#endif + +class S0 : public QState +{ +public: + S0(QState *parent = 0) + : QState(parent) {} + + virtual void onEntry() + { + fprintf(stdout, "Hello world!\n"); + } +}; + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + QStateMachine machine; + QState *s0 = new S0(); + QFinalState *s1 = new QFinalState(); + s0->addTransition(s1); + + machine.addState(s0); + machine.addState(s1); + machine.setInitialState(s0); + + QObject::connect(&machine, SIGNAL(finished()), QCoreApplication::instance(), SLOT(quit())); + machine.start(); + + return app.exec(); +} diff --git a/examples/statemachine/pauseandresume/main.cpp b/examples/statemachine/pauseandresume/main.cpp new file mode 100644 index 0000000..e0ebbff --- /dev/null +++ b/examples/statemachine/pauseandresume/main.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#ifdef QT_STATEMACHINE_SOLUTION +#include <qstatemachine.h> +#include <qstate.h> +#include <qfinalstate.h> +#include <qhistorystate.h> +#endif + +class Window : public QWidget +{ +public: + Window(QWidget *parent = 0) + : QWidget(parent) + { + QPushButton *pb = new QPushButton("Go"); + QPushButton *pauseButton = new QPushButton("Pause"); + QPushButton *quitButton = new QPushButton("Quit"); + QVBoxLayout *vbox = new QVBoxLayout(this); + vbox->addWidget(pb); + vbox->addWidget(pauseButton); + vbox->addWidget(quitButton); + + QStateMachine *machine = new QStateMachine(this); + + QState *process = new QState(machine->rootState()); + process->setObjectName("process"); + + QState *s1 = new QState(process); + s1->setObjectName("s1"); + QState *s2 = new QState(process); + s2->setObjectName("s2"); + s1->addTransition(pb, SIGNAL(clicked()), s2); + s2->addTransition(pb, SIGNAL(clicked()), s1); + + QHistoryState *h = process->addHistoryState(); + h->setDefaultState(s1); + + QState *interrupted = new QState(machine->rootState()); + interrupted->setObjectName("interrupted"); + QFinalState *terminated = new QFinalState(machine->rootState()); + terminated->setObjectName("terminated"); + interrupted->addTransition(pauseButton, SIGNAL(clicked()), h); + interrupted->addTransition(quitButton, SIGNAL(clicked()), terminated); + + process->addTransition(pauseButton, SIGNAL(clicked()), interrupted); + process->addTransition(quitButton, SIGNAL(clicked()), terminated); + + process->setInitialState(s1); + machine->setInitialState(process); + QObject::connect(machine, SIGNAL(finished()), QApplication::instance(), SLOT(quit())); + machine->start(); + } +}; + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + Window win; + win.show(); + return app.exec(); +} diff --git a/examples/statemachine/pauseandresume/pauseandresume.pro b/examples/statemachine/pauseandresume/pauseandresume.pro new file mode 100644 index 0000000..6a976cb --- /dev/null +++ b/examples/statemachine/pauseandresume/pauseandresume.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += main.cpp diff --git a/examples/statemachine/pingpong/main.cpp b/examples/statemachine/pingpong/main.cpp new file mode 100644 index 0000000..3272524 --- /dev/null +++ b/examples/statemachine/pingpong/main.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore> +#include <stdio.h> +#ifdef QT_STATEMACHINE_SOLUTION +#include <qstate.h> +#include <qstatemachine.h> +#include <qtransition.h> +#endif + +class PingEvent : public QEvent +{ +public: + PingEvent() : QEvent(QEvent::Type(QEvent::User+2)) + {} +}; + +class PongEvent : public QEvent +{ +public: + PongEvent() : QEvent(QEvent::Type(QEvent::User+3)) + {} +}; + +class Pinger : public QState +{ +public: + Pinger(QStateMachine *machine, QState *parent) + : QState(parent), m_machine(machine) {} + +protected: + virtual void onEntry() + { + m_machine->postEvent(new PingEvent()); + fprintf(stdout, "ping?\n"); + } + +private: + QStateMachine *m_machine; +}; + +class PongTransition : public QAbstractTransition +{ +public: + PongTransition(QStateMachine *machine) + : QAbstractTransition(), m_machine(machine) {} + +protected: + virtual bool eventTest(QEvent *e) const { + return (e->type() == QEvent::User+3); + } + virtual void onTransition() + { + m_machine->postEvent(new PingEvent(), 500); + fprintf(stdout, "ping?\n"); + } + +private: + QStateMachine *m_machine; +}; + +class PingTransition : public QAbstractTransition +{ +public: + PingTransition(QStateMachine *machine) + : QAbstractTransition(), m_machine(machine) {} + +protected: + virtual bool eventTest(QEvent *e) const { + return (e->type() == QEvent::User+2); + } + virtual void onTransition() + { + m_machine->postEvent(new PongEvent(), 500); + fprintf(stdout, "pong!\n"); + } + +private: + QStateMachine *m_machine; +}; + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + QStateMachine machine; + QState *group = new QState(QState::ParallelGroup); + group->setObjectName("group"); + + Pinger *pinger = new Pinger(&machine, group); + pinger->setObjectName("pinger"); + pinger->addTransition(new PongTransition(&machine)); + + QState *ponger = new QState(group); + ponger->setObjectName("ponger"); + ponger->addTransition(new PingTransition(&machine)); + + machine.addState(group); + machine.setInitialState(group); + machine.start(); + + return app.exec(); +} diff --git a/examples/statemachine/pingpong/pingpong.pro b/examples/statemachine/pingpong/pingpong.pro new file mode 100644 index 0000000..bff9cb8 --- /dev/null +++ b/examples/statemachine/pingpong/pingpong.pro @@ -0,0 +1,10 @@ +QT = core +TEMPLATE = app +TARGET = +win32: CONFIG += console +mac:CONFIG -= app_bundle +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += main.cpp diff --git a/examples/statemachine/statemachine.pro b/examples/statemachine/statemachine.pro new file mode 100644 index 0000000..ba32c12 --- /dev/null +++ b/examples/statemachine/statemachine.pro @@ -0,0 +1,17 @@ +TEMPLATE = subdirs +SUBDIRS = \ + clockticking \ + composition \ + eventtransitions \ + factorial \ + helloworld \ + pauseandresume \ + pingpong \ + trafficlight \ + twowaybutton + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/statemachine +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS statemachine.pro README +sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine +INSTALLS += target sources diff --git a/examples/statemachine/trafficlight/main.cpp b/examples/statemachine/trafficlight/main.cpp new file mode 100644 index 0000000..528ed00 --- /dev/null +++ b/examples/statemachine/trafficlight/main.cpp @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#ifdef QT_STATEMACHINE_SOLUTION +#include <qstate.h> +#include <qstatemachine.h> +#include <qfinalstate.h> +#endif + +//! [0] +class LightWidget : public QWidget +{ + Q_OBJECT + Q_PROPERTY(bool on READ isOn WRITE setOn) +public: + LightWidget(const QColor &color, QWidget *parent = 0) + : QWidget(parent), m_color(color), m_on(false) {} + + bool isOn() const + { return m_on; } + void setOn(bool on) + { + if (on == m_on) + return; + m_on = on; + update(); + } + +public slots: + void turnOff() { setOn(false); } + void turnOn() { setOn(true); } + +protected: + virtual void paintEvent(QPaintEvent *) + { + if (!m_on) + return; + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + painter.setBrush(m_color); + painter.drawEllipse(0, 0, width(), height()); + } + +private: + QColor m_color; + bool m_on; +}; +//! [0] + +//! [1] +class LightState : public QState +{ +public: + LightState(LightWidget *light, int duration, QState *parent = 0) + : QState(parent) + { + QTimer *timer = new QTimer(this); + timer->setInterval(duration); + timer->setSingleShot(true); + QState *timing = new QState(this); + timing->invokeMethodOnEntry(light, "turnOn"); + timing->invokeMethodOnEntry(timer, "start"); + timing->invokeMethodOnExit(light, "turnOff"); + QFinalState *done = new QFinalState(this); + timing->addTransition(timer, SIGNAL(timeout()), done); + setInitialState(timing); + } +}; +//! [1] + +//! [2] +class TrafficLightWidget : public QWidget +{ +public: + TrafficLightWidget(QWidget *parent = 0) + : QWidget(parent) + { + QVBoxLayout *vbox = new QVBoxLayout(this); + m_red = new LightWidget(Qt::red); + vbox->addWidget(m_red); + m_yellow = new LightWidget(Qt::yellow); + vbox->addWidget(m_yellow); + m_green = new LightWidget(Qt::green); + vbox->addWidget(m_green); + QPalette pal = palette(); + pal.setColor(QPalette::Background, Qt::black); + setPalette(pal); + setAutoFillBackground(true); + } + + LightWidget *redLight() const + { return m_red; } + LightWidget *yellowLight() const + { return m_yellow; } + LightWidget *greenLight() const + { return m_green; } + +private: + LightWidget *m_red; + LightWidget *m_yellow; + LightWidget *m_green; +}; +//! [2] + +//! [3] +class TrafficLight : public QWidget +{ +public: + TrafficLight(QWidget *parent = 0) + : QWidget(parent) + { + QVBoxLayout *vbox = new QVBoxLayout(this); + TrafficLightWidget *widget = new TrafficLightWidget(); + vbox->addWidget(widget); + + QStateMachine *machine = new QStateMachine(this); + LightState *redGoingYellow = new LightState(widget->redLight(), 3000); + redGoingYellow->setObjectName("redGoingYellow"); + LightState *yellowGoingGreen = new LightState(widget->yellowLight(), 1000); + yellowGoingGreen->setObjectName("yellowGoingGreen"); + redGoingYellow->addFinishedTransition(yellowGoingGreen); + LightState *greenGoingYellow = new LightState(widget->greenLight(), 3000); + greenGoingYellow->setObjectName("greenGoingYellow"); + yellowGoingGreen->addFinishedTransition(greenGoingYellow); + LightState *yellowGoingRed = new LightState(widget->yellowLight(), 1000); + yellowGoingRed->setObjectName("yellowGoingRed"); + greenGoingYellow->addFinishedTransition(yellowGoingRed); + yellowGoingRed->addFinishedTransition(redGoingYellow); + + machine->addState(redGoingYellow); + machine->addState(yellowGoingGreen); + machine->addState(greenGoingYellow); + machine->addState(yellowGoingRed); + machine->setInitialState(redGoingYellow); + machine->start(); + } +}; +//! [3] + +//! [4] +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + TrafficLight widget; + widget.resize(120, 300); + widget.show(); + + return app.exec(); +} +//! [4] + +#include "main.moc" diff --git a/examples/statemachine/trafficlight/trafficlight.pro b/examples/statemachine/trafficlight/trafficlight.pro new file mode 100644 index 0000000..730bd75 --- /dev/null +++ b/examples/statemachine/trafficlight/trafficlight.pro @@ -0,0 +1,12 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +SOURCES += main.cpp + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/trafficlight +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS trafficlight.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/trafficlight +INSTALLS += target sources diff --git a/examples/statemachine/twowaybutton/main.cpp b/examples/statemachine/twowaybutton/main.cpp new file mode 100644 index 0000000..61a0f32 --- /dev/null +++ b/examples/statemachine/twowaybutton/main.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#ifdef QT_STATEMACHINE_SOLUTION +#include <qstate.h> +#include <qstatemachine.h> +#endif + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + QPushButton button; + + QStateMachine machine; + QState *first = new QState(); + first->setObjectName("first"); + + QState *off = new QState(); + off->assignProperty(&button, "text", "Off"); + off->setObjectName("off"); + first->addTransition(off); + + QState *on = new QState(); + on->setObjectName("on"); + on->assignProperty(&button, "text", "On"); + off->addTransition(&button, SIGNAL(clicked()), on); + on->addTransition(&button, SIGNAL(clicked()), off); + + machine.addState(first); + machine.addState(off); + machine.addState(on); + machine.setInitialState(first); + machine.start(); + + button.resize(100, 50); + button.show(); + return app.exec(); +} diff --git a/examples/statemachine/twowaybutton/twowaybutton.pro b/examples/statemachine/twowaybutton/twowaybutton.pro new file mode 100644 index 0000000..6a976cb --- /dev/null +++ b/examples/statemachine/twowaybutton/twowaybutton.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += main.cpp diff --git a/mkspecs/features/qt.prf b/mkspecs/features/qt.prf index 0c6e09a..3ef864e 100644 --- a/mkspecs/features/qt.prf +++ b/mkspecs/features/qt.prf @@ -36,7 +36,7 @@ INCLUDEPATH = $$QMAKE_INCDIR_QT $$INCLUDEPATH #prepending prevents us from picki win32:INCLUDEPATH += $$QMAKE_INCDIR_QT/ActiveQt # As order does matter for static libs, we reorder the QT variable here -TMPLIBS = webkit phonon dbus testlib script scripttools svg qt3support sql xmlpatterns xml opengl gui network core +TMPLIBS = webkit phonon dbus testlib script scripttools svg qt3support sql xmlpatterns xml opengl experimental-animation gui network core for(QTLIB, $$list($$TMPLIBS)) { contains(QT, $$QTLIB): QT_ORDERED += $$QTLIB } @@ -147,6 +147,7 @@ for(QTLIB, $$list($$lower($$unique(QT)))) { else:isEqual(QTLIB, svg):qlib = QtSvg else:isEqual(QTLIB, script):qlib = QtScript else:isEqual(QTLIB, scripttools):qlib = QtScriptTools + else:isEqual(QTLIB, experimental-animation):qlib = QtAnimation else:isEqual(QTLIB, testlib):qlib = QtTest else:isEqual(QTLIB, dbus):qlib = QtDBus else:isEqual(QTLIB, phonon):qlib = phonon diff --git a/mkspecs/win32-icc/qmake.conf b/mkspecs/win32-icc/qmake.conf index 03582fc..78c947d 100644 --- a/mkspecs/win32-icc/qmake.conf +++ b/mkspecs/win32-icc/qmake.conf @@ -16,7 +16,7 @@ QMAKE_LEX = flex QMAKE_LEXFLAGS = QMAKE_YACC = byacc QMAKE_YACCFLAGS = -d -QMAKE_CFLAGS = -nologo -Zm200 /Qprec +QMAKE_CFLAGS = -nologo -Zm200 /Qprec /Qwd1744,1738 QMAKE_CFLAGS_WARN_ON = -W3 /Qwd673 QMAKE_CFLAGS_WARN_OFF = -W0 /Qwd673 QMAKE_CFLAGS_RELEASE = -O2 -MD diff --git a/src/3rdparty/easing/easing.cpp b/src/3rdparty/easing/easing.cpp new file mode 100644 index 0000000..6a98c98 --- /dev/null +++ b/src/3rdparty/easing/easing.cpp @@ -0,0 +1,669 @@ +/* +Disclaimer for Robert Penner's Easing Equations license: + +TERMS OF USE - EASING EQUATIONS + +Open source under the BSD License. + +Copyright © 2001 Robert Penner +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <QtCore/qmath.h> +#include <math.h> +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_PI_2 +#define M_PI_2 (M_PI / 2) +#endif + + +/** + * Easing equation function for a simple linear tweening, with no easing. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeNone(qreal progress) +{ + return progress; +} + +/** + * Easing equation function for a quadratic (t^2) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInQuad(qreal t) +{ + return t*t; +} + +/** +* Easing equation function for a quadratic (t^2) easing out: decelerating to zero velocity. +* +* @param t Current time (in frames or seconds). +* @return The correct value. +*/ +static qreal easeOutQuad(qreal t) +{ + return -t*(t-2); +} + +/** + * Easing equation function for a quadratic (t^2) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInOutQuad(qreal t) +{ + t*=2.0; + if (t < 1) { + return t*t/qreal(2); + } else { + --t; + return -0.5 * (t*(t-2) - 1); + } +} + +/** + * Easing equation function for a quadratic (t^2) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutInQuad(qreal t) +{ + if (t < 0.5) return easeOutQuad (t*2)/2; + return easeInQuad((2*t)-1)/2 + 0.5; +} + +/** + * Easing equation function for a cubic (t^3) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInCubic(qreal t) +{ + return t*t*t; +} + +/** + * Easing equation function for a cubic (t^3) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutCubic(qreal t) +{ + t-=1.0; + return t*t*t + 1; +} + +/** + * Easing equation function for a cubic (t^3) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInOutCubic(qreal t) +{ + t*=2.0; + if(t < 1) { + return 0.5*t*t*t; + } else { + t -= qreal(2.0); + return 0.5*(t*t*t + 2); + } +} + +/** + * Easing equation function for a cubic (t^3) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutInCubic(qreal t) +{ + if (t < 0.5) return easeOutCubic (2*t)/2; + return easeInCubic(2*t - 1)/2 + 0.5; +} + +/** + * Easing equation function for a quartic (t^4) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInQuart(qreal t) +{ + return t*t*t*t; +} + +/** + * Easing equation function for a quartic (t^4) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutQuart(qreal t) +{ + t-= qreal(1.0); + return - (t*t*t*t- 1); +} + +/** + * Easing equation function for a quartic (t^4) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInOutQuart(qreal t) +{ + t*=2; + if (t < 1) return 0.5*t*t*t*t; + else { + t -= 2.0f; + return -0.5 * (t*t*t*t- 2); + } +} + +/** + * Easing equation function for a quartic (t^4) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutInQuart(qreal t) +{ + if (t < 0.5) return easeOutQuart (2*t)/2; + return easeInQuart(2*t-1)/2 + 0.5; +} + +/** + * Easing equation function for a quintic (t^5) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInQuint(qreal t) +{ + return t*t*t*t*t; +} + +/** + * Easing equation function for a quintic (t^5) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutQuint(qreal t) +{ + t-=1.0; + return t*t*t*t*t + 1; +} + +/** + * Easing equation function for a quintic (t^5) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInOutQuint(qreal t) +{ + t*=2.0; + if (t < 1) return 0.5*t*t*t*t*t; + else { + t -= 2.0; + return 0.5*(t*t*t*t*t + 2); + } +} + +/** + * Easing equation function for a quintic (t^5) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutInQuint(qreal t) +{ + if (t < 0.5) return easeOutQuint (2*t)/2; + return easeInQuint(2*t - 1)/2 + 0.5; +} + +/** + * Easing equation function for a sinusoidal (sin(t)) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInSine(qreal t) +{ + return (t == 1.0) ? 1.0 : -::cos(t * M_PI_2) + 1.0; +} + +/** + * Easing equation function for a sinusoidal (sin(t)) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutSine(qreal t) +{ + return ::sin(t* M_PI_2); +} + +/** + * Easing equation function for a sinusoidal (sin(t)) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInOutSine(qreal t) +{ + return -0.5 * (::cos(M_PI*t) - 1); +} + +/** + * Easing equation function for a sinusoidal (sin(t)) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutInSine(qreal t) +{ + if (t < 0.5) return easeOutSine (2*t)/2; + return easeInSine(2*t - 1)/2 + 0.5; +} + +/** + * Easing equation function for an exponential (2^t) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInExpo(qreal t) +{ + return (t==0 || t == 1.0) ? t : ::qPow(2.0, 10 * (t - 1)) - qreal(0.001); +} + +/** + * Easing equation function for an exponential (2^t) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutExpo(qreal t) +{ + return (t==1.0) ? 1.0 : 1.001 * (-::qPow(2.0f, -10 * t) + 1); +} + +/** + * Easing equation function for an exponential (2^t) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInOutExpo(qreal t) +{ + if (t==0.0) return qreal(0.0); + if (t==1.0) return qreal(1.0); + t*=2.0; + if (t < 1) return 0.5 * ::qPow(qreal(2.0), 10 * (t - 1)) - 1.0 * 0.0005; + return 0.5 * 1.0005 * (-::qPow(qreal(2.0), -10 * (t - 1)) + 2); +} + +/** + * Easing equation function for an exponential (2^t) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutInExpo(qreal t) +{ + if (t < 0.5) return easeOutExpo (2*t)/2; + return easeInExpo(2*t - 1)/2 + 0.5; +} + +/** + * Easing equation function for a circular (sqrt(1-t^2)) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInCirc(qreal t) +{ + return -(::sqrt(1 - t*t) - 1); +} + +/** + * Easing equation function for a circular (sqrt(1-t^2)) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutCirc(qreal t) +{ + t-= qreal(1.0); + return ::sqrt(1 - t* t); +} + +/** + * Easing equation function for a circular (sqrt(1-t^2)) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInOutCirc(qreal t) +{ + t*=qreal(2.0); + if (t < 1) { + return -0.5 * (::sqrt(1 - t*t) - 1); + } else { + t -= qreal(2.0); + return 0.5 * (::sqrt(1 - t*t) + 1); + } +} + +/** + * Easing equation function for a circular (sqrt(1-t^2)) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutInCirc(qreal t) +{ + if (t < 0.5) return easeOutCirc (2*t)/2; + return easeInCirc(2*t - 1)/2 + 0.5; +} + +static qreal easeInElastic_helper(qreal t, qreal b, qreal c, qreal d, qreal a, qreal p) +{ + if (t==0) return b; + qreal t_adj = (qreal)t / (qreal)d; + if (t_adj==1) return b+c; + + qreal s; + if(a < ::fabs(c)) { + a = c; + s = p / 4.0f; + } else { + s = p / (2 * M_PI) * ::asin(c / a); + } + + t_adj -= 1.0f; + return -(a*::qPow(2.0f,10*t_adj) * ::sin( (t_adj*d-s)*(2*M_PI)/p )) + b; +} + +/** + * Easing equation function for an elastic (exponentially decaying sine wave) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param a Amplitude. + * @param p Period. + * @return The correct value. + */ +static qreal easeInElastic(qreal t, qreal a, qreal p) +{ + return easeInElastic_helper(t, 0, 1, 1, a, p); +} + +static qreal easeOutElastic_helper(qreal t, qreal /*b*/, qreal c, qreal /*d*/, qreal a, qreal p) +{ + if (t==0) return 0; + if (t==1) return c; + + qreal s; + if(a < c) { + a = c; + s = p / 4.0f; + } else { + s = p / (2 * M_PI) * ::asin(c / a); + } + + return (a*::qPow(2.0f,-10*t) * ::sin( (t-s)*(2*M_PI)/p ) + c); +} + +/** + * Easing equation function for an elastic (exponentially decaying sine wave) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param a Amplitude. + * @param p Period. + * @return The correct value. + */ +static qreal easeOutElastic(qreal t, qreal a, qreal p) +{ + return easeOutElastic_helper(t, 0, 1, 1, a, p); +} + +/** + * Easing equation function for an elastic (exponentially decaying sine wave) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @param a Amplitude. + * @param p Period. + * @return The correct value. + */ +static qreal easeInOutElastic(qreal t, qreal a, qreal p) +{ + if (t==0) return 0.0; + t*=2.0; + if (t==2) return 1.0; + + qreal s; + if(a < 1.0) { + a = 1.0; + s = p / 4.0f; + } else { + s = p / (2 * M_PI) * ::asin(1.0 / a); + } + + if (t < 1) return -.5*(a*::qPow(2.0f,10*(t-1)) * ::sin( ((t-1)*1.0-s)*(2*M_PI)/p )); + return a*::qPow(2.0f,-10*(t-1)) * ::sin( ((t-1)*1.0-s)*(2*M_PI)/p )*.5 + 1.0; +} + +/** + * Easing equation function for an elastic (exponentially decaying sine wave) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @param a Amplitude. + * @param p Period. + * @return The correct value. + */ +static qreal easeOutInElastic(qreal t, qreal a, qreal p) +{ + if (t < 0.5) return easeOutElastic_helper(t*2, 0, 0.5, 1.0, a, p); + return easeInElastic_helper(2*t - 1.0, 0.5, 0.5, 1.0, a, p); +} + +/** + * Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param s Overshoot ammount: higher s means greater overshoot (0 produces cubic easing with no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent). + * @return The correct value. + */ +static qreal easeInBack(qreal t, qreal s) +{ + return t*t*((s+1)*t - s); +} + +/** + * Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param s Overshoot ammount: higher s means greater overshoot (0 produces cubic easing with no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent). + * @return The correct value. + */ +static qreal easeOutBack(qreal t, qreal s) +{ + t-= qreal(1.0); + return t*t*((s+1)*t+ s) + 1; +} + +/** + * Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @param s Overshoot ammount: higher s means greater overshoot (0 produces cubic easing with no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent). + * @return The correct value. + */ +static qreal easeInOutBack(qreal t, qreal s) +{ + t *= 2.0; + if (t < 1) { + s *= 1.525f; + return 0.5*(t*t*((s+1)*t - s)); + } else { + t -= 2; + s *= 1.525f; + return 0.5*(t*t*((s+1)*t+ s) + 2); + } +} + +/** + * Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @param s Overshoot ammount: higher s means greater overshoot (0 produces cubic easing with no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent). + * @return The correct value. + */ +static qreal easeOutInBack(qreal t, qreal s) +{ + if (t < 0.5) return easeOutBack (2*t, s)/2; + return easeInBack(2*t - 1, s)/2 + 0.5; +} + +static qreal easeOutBounce_helper(qreal t, qreal b, qreal c, qreal a) +{ + if (t == 1.0) return 1.0; + if (t < (1/2.75)) { + return c*(7.5625*t*t) + b; + } else if (t < (2/2.75)) { + t -= (1.5f/2.75f); + return -a * (1. - (7.5625*t*t + .75)) + (b + c); + } else if (t < (2.5/2.75)) { + t -= (2.25f/2.75f); + return -a * (1. - (7.5625*t*t + .9375)) + (b + c); + } else { + t -= (2.65f/2.75f); + return -a * (1. - (7.5625*t*t + .984375)) + (b + c); + } +} + +/** + * Easing equation function for a bounce (exponentially decaying parabolic bounce) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param a Amplitude. + * @return The correct value. + */ +static qreal easeOutBounce(qreal t, qreal a) +{ + return easeOutBounce_helper(t, 0, 1, a); +} + +/** + * Easing equation function for a bounce (exponentially decaying parabolic bounce) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param a Amplitude. + * @return The correct value. + */ +static qreal easeInBounce(qreal t, qreal a) +{ + return 1.0 - easeOutBounce_helper(1.0-t, 0, 1.0, a); +} + + +/** + * Easing equation function for a bounce (exponentially decaying parabolic bounce) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @param a Amplitude. + * @return The correct value. + */ +static qreal easeInOutBounce(qreal t, qreal a) +{ + if (t < 0.5) return easeInBounce (2*t, a)/2; + else return (t == 1.0) ? 1.0 : easeOutBounce (2*t - 1, a)/2 + 0.5; +} + +/** + * Easing equation function for a bounce (exponentially decaying parabolic bounce) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @param a Amplitude. + * @return The correct value. + */ +static qreal easeOutInBounce(qreal t, qreal a) +{ + if (t < 0.5) return easeOutBounce_helper(t*2, 0, 0.5, a); + return 1.0 - easeOutBounce_helper (2.0-2*t, 0.0, 0.5, a); +} + +static inline qreal qt_sinProgress(qreal value) +{ + return qSin((value * M_PI) - M_PI_2) / 2 + qreal(0.5); +} + +static inline qreal qt_smoothBeginEndMixFactor(qreal value) +{ + return qMin(qMax(1 - value * 2 + qreal(0.3), qreal(0.0)), qreal(1.0)); +} + +// SmoothBegin blends Smooth and Linear Interpolation. +// Progress 0 - 0.3 : Smooth only +// Progress 0.3 - ~ 0.5 : Mix of Smooth and Linear +// Progress ~ 0.5 - 1 : Linear only + +/** + * Easing function that starts growing slowly, then increases in speed. At the end of the curve the speed will be constant. + */ +static qreal easeInCurve(qreal t) +{ + const qreal sinProgress = qt_sinProgress(t); + const qreal mix = qt_smoothBeginEndMixFactor(t); + return sinProgress * mix + t * (1 - mix); +} + +/** + * Easing function that starts growing steadily, then ends slowly. The speed will be constant at the beginning of the curve. + */ +static qreal easeOutCurve(qreal t) +{ + const qreal sinProgress = qt_sinProgress(t); + const qreal mix = qt_smoothBeginEndMixFactor(1 - t); + return sinProgress * mix + t * (1 - mix); +} + +/** + * Easing function where the value grows sinusoidally. Note that the calculated end value will be 0 rather than 1. + */ +static qreal easeSineCurve(qreal t) +{ + return (qSin(((t * M_PI * 2)) - M_PI_2) + 1) / 2; +} + +/** + * Easing function where the value grows cosinusoidally. Note that the calculated start value will be 0.5 and the end value will be 0.5 + * contrary to the usual 0 to 1 easing curve. + */ +static qreal easeCosineCurve(qreal t) +{ + return (qCos(((t * M_PI * 2)) - M_PI_2) + 1) / 2; +} + diff --git a/src/3rdparty/easing/legal.qdoc b/src/3rdparty/easing/legal.qdoc new file mode 100644 index 0000000..25f67e1 --- /dev/null +++ b/src/3rdparty/easing/legal.qdoc @@ -0,0 +1,35 @@ +/*! +\page legal-easing.html +\title Easing Equations by Robert Penner +\ingroup animation + +\legalese +\code +Copyright (c) 2001 Robert Penner +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the author nor the names of contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\endcode +\endlegalese +*/ diff --git a/src/corelib/animation/animation.pri b/src/corelib/animation/animation.pri new file mode 100644 index 0000000..cb7850c --- /dev/null +++ b/src/corelib/animation/animation.pri @@ -0,0 +1,25 @@ +# Qt core animation module + +HEADERS += \ + animation/qabstractanimation.h \ + animation/qabstractanimation_p.h \ + animation/qvariantanimation.h \ + animation/qvariantanimation_p.h \ + animation/qpropertyanimation.h \ + animation/qpropertyanimation_p.h \ + animation/qanimationgroup.h \ + animation/qanimationgroup_p.h \ + animation/qsequentialanimationgroup.h \ + animation/qsequentialanimationgroup_p.h \ + animation/qparallelanimationgroup.h \ + animation/qparallelanimationgroup_p.h \ + animation/qpauseanimation.h + +SOURCES += \ + animation/qabstractanimation.cpp \ + animation/qvariantanimation.cpp \ + animation/qpropertyanimation.cpp \ + animation/qanimationgroup.cpp \ + animation/qsequentialanimationgroup.cpp \ + animation/qparallelanimationgroup.cpp \ + animation/qpauseanimation.cpp diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp new file mode 100644 index 0000000..2b9e77d --- /dev/null +++ b/src/corelib/animation/qabstractanimation.cpp @@ -0,0 +1,747 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QAbstractAnimation + \ingroup animation + \brief The QAbstractAnimation class provides an abstract base class for animations. + \since 4.5 + \preliminary + + This class is part of \l{The Animation Framework}. It serves as a base class + for standard animations and groups, with functions for shared + functionality, and it also makes it easy for you to define custom + animations that plug into the rest of the animation framework. + + If you want to create an animation, you should look at the two subclasses, + QVariantAnimation and QAnimationGroup, instead. + + QAbstractAnimation provides an interface for the current time and + duration, the iteration count, and the state of an animation. These properties + define the base functionality common to all animations in Qt. The virtual + duration() function returns the local duration of the animation; i.e., for + how long the animation should update the current time before + looping. Subclasses can implement this function differently; for example, + QVariantAnimation returns the duration of a simple animated property, whereas + QAnimationGroup returns the duration of a set or sequence of + animations. You can also set a loop count by calling setIterationCount(); a + iteration count of 2 will let the animation run twice (the default value is + 1). + + Like QTimeLine, QAbstractAnimation also provides an interface for starting + and stopping an animation, and for tracking its progress. You can call the + start() slot to start the animation. When the animation starts, the + stateChanged() signal is emitted, and state() returns Running. If you call the + stop() slot, the stateChanged() signal is emitted, and state() returns + Stopped. If you call the pause() slot, the stateChanged() signal is emitted + and state() returns Paused. If the animation reaches the end, the finished() + signal is emitted. You can check the current state by calling state(). + + QAbstractAnimation provides two functions that are pure virtual, and must + be reimplemented in a subclass: duration(), and updateCurrentTime(). The + duration() function lets you report a duration for the animation (a return + value of -1 signals that the animation runs forever until explicitly + stopped). The current time is delivered by the framework through calls to + updateCurrentTime(). By reimplementing this function, you can track the + animation progress and update your target objects accordingly. By + reimplementing updateState(), you can track the animation's state + changes, which is particularily useful for animations that are not driven + by time. + + \sa QVariantAnimation, QAnimationGroup, {The Animation Framework} +*/ + +/*! + \enum QAbstractAnimation::DeletionPolicy + + \value KeepWhenStopped The animation will not be deleted when stopped. + \value DeleteWhenStopped The animation will be automatically deleted when + stopped. +*/ + +/*! + \fn QAbstractAnimation::finished() + + QAbstractAnimation emits this signal after the animation has stopped and + has reached the end. + + This signal is emitted after stateChanged(). + + \sa stateChanged() +*/ + +/*! + \fn QAbstractAnimation::stateChanged(QAbstractAnimation::State oldState, QAbstractAnimation::State newState) + + QAbstractAnimation emits this signal whenever the state of the animation has + changed from \a oldState to \a newState. This signal is emitted after the virtual + updateState() function is called. + + \sa updateState() +*/ + +/*! + \fn QAbstractAnimation::currentIterationChanged(int currentIteration) + + QAbstractAnimation emits this signal whenever the current iteration + changes. \a currentIteration is the current iteration. + + \sa currentIteration(), iterationCount() +*/ + +/*! + \fn QAbstractAnimation::directionChanged(QAbstractAnimation::Direction newDirection); + + QAbstractAnimation emits this signal whenever the direction has been + changed. \a newDirection is the new direction. + + \sa direction +*/ + +#ifndef QT_NO_ANIMATION + +#include "qabstractanimation.h" +#include "qanimationgroup.h" +#include <QtCore/qdebug.h> + +#include "qabstractanimation_p.h" + +#include <QtCore/qmath.h> +#include <QtCore/qthreadstorage.h> +#include <QtCore/qcoreevent.h> +#include <QtCore/qpointer.h> + +#define TIMER_INTERVAL 16 + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(QThreadStorage<QUnifiedTimer *>, unifiedTimer); + +QUnifiedTimer::QUnifiedTimer() : QObject(), lastTick(0) +{ +} + +QUnifiedTimer *QUnifiedTimer::instance() +{ + QUnifiedTimer *inst; + if (!unifiedTimer()->hasLocalData()) { + inst = new QUnifiedTimer; + unifiedTimer()->setLocalData(inst); + } else { + inst = unifiedTimer()->localData(); + } + return inst; +} + +void QUnifiedTimer::updateRecentlyStartedAnimations() +{ + if (animationsToStart.isEmpty()) + return; + + animations += animationsToStart; + updateTimer(); //we make sure we start the timer there + + animationsToStart.clear(); +} + +void QUnifiedTimer::timerEvent(QTimerEvent *event) +{ + //this is simply the time we last received a tick + int oldLastTick = lastTick; + if (time.isValid()) + lastTick = time.elapsed(); + + //we transfer the waiting animations into the "really running" state + updateRecentlyStartedAnimations(); + + if (event->timerId() == startStopAnimationTimer.timerId()) { + startStopAnimationTimer.stop(); + if (animations.isEmpty()) { + animationTimer.stop(); + time = QTime(); + } else { + animationTimer.start(TIMER_INTERVAL, this); + lastTick = 0; + time.start(); + } + } else if (event->timerId() == animationTimer.timerId()) { + const int delta = lastTick - oldLastTick; + for (int i = 0; i < animations.count(); ++i) { + QAbstractAnimation *animation = animations.at(i); + int elapsed = QAbstractAnimationPrivate::get(animation)->totalCurrentTime + + (animation->direction() == QAbstractAnimation::Forward ? delta : -delta); + animation->setCurrentTime(elapsed); + } + } +} + +void QUnifiedTimer::updateTimer() +{ + //we delay the call to start and stop for the animation timer so that if you + //stop and start animations in batch you don't stop/start the timer too often. + if (!startStopAnimationTimer.isActive()) + startStopAnimationTimer.start(0, this); // we delay the actual start of the animation +} + +void QUnifiedTimer::registerAnimation(QAbstractAnimation *animation) +{ + if (animations.contains(animation) ||animationsToStart.contains(animation)) + return; + animationsToStart << animation; + updateTimer(); +} + +void QUnifiedTimer::unregisterAnimation(QAbstractAnimation *animation) +{ + animations.removeAll(animation); + animationsToStart.removeAll(animation); + updateTimer(); +} + + +void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState) +{ + Q_Q(QAbstractAnimation); + if (state == newState) + return; + + QAbstractAnimation::State oldState = state; + int oldCurrentTime = currentTime; + int oldCurrentIteration = currentIteration; + QAbstractAnimation::Direction oldDirection = direction; + + state = newState; + + QPointer<QAbstractAnimation> guard(q); + + guard->updateState(oldState, newState); + + //this is to be safe if updateState changes the state + if (state == oldState) + return; + + // Notify state change + if (guard) + emit guard->stateChanged(oldState, newState); + + // Enter running state. + switch (state) + { + case QAbstractAnimation::Paused: + case QAbstractAnimation::Running: + { + // Rewind + if (oldState == QAbstractAnimation::Stopped) { + if (guard) { + if (direction == QAbstractAnimation::Forward) + q->setCurrentTime(0); + else + q->setCurrentTime(iterationCount == -1 ? q->duration() : q->totalDuration()); + } + + // Check if the setCurrentTime() function called stop(). + // This can happen for a 0-duration animation + if (state == QAbstractAnimation::Stopped) + return; + } + + // Register timer if our parent is not running. + if (state == QAbstractAnimation::Running && guard) { + if (!group || group->state() == QAbstractAnimation::Stopped) { + QUnifiedTimer::instance()->registerAnimation(q); + } + } else { + //new state is paused + QUnifiedTimer::instance()->unregisterAnimation(q); + } + } + break; + case QAbstractAnimation::Stopped: + // Leave running state. + int dura = q->duration(); + if (deleteWhenStopped && guard) + q->deleteLater(); + + QUnifiedTimer::instance()->unregisterAnimation(q); + + if (dura == -1 || iterationCount < 0 + || (oldDirection == QAbstractAnimation::Forward && (oldCurrentTime * (oldCurrentIteration + 1)) == (dura * iterationCount)) + || (oldDirection == QAbstractAnimation::Backward && oldCurrentTime == 0)) { + if (guard) + emit q->finished(); + } + break; + } + +} + +/*! + Constructs the QAbstractAnimation base class, and passes \a parent to + QObject's constructor. + + \sa QVariantAnimation, QAnimationGroup +*/ +#ifdef QT_EXPERIMENTAL_SOLUTION +QAbstractAnimation::QAbstractAnimation(QObject *parent) + : d_ptr(new QAbstractAnimationPrivate) +{ + // Allow auto-add on reparent + setParent(parent); + d_ptr->q_ptr = this; +} +#else +QAbstractAnimation::QAbstractAnimation(QObject *parent) + : QObject(*new QAbstractAnimationPrivate, 0) +{ + // Allow auto-add on reparent + setParent(parent); +} +#endif + +/*! + \internal +*/ +#ifdef QT_EXPERIMENTAL_SOLUTION +QAbstractAnimation::QAbstractAnimation(QAbstractAnimationPrivate &dd, QObject *parent) + : d_ptr(&dd) +{ + // Allow auto-add on reparent + setParent(parent); + d_ptr->q_ptr = this; +} +#else +QAbstractAnimation::QAbstractAnimation(QAbstractAnimationPrivate &dd, QObject *parent) + : QObject(dd, 0) +{ + // Allow auto-add on reparent + setParent(parent); +} +#endif + +/*! + Stops the animation if it's running, then destroys the + QAbstractAnimation. If the animation is part of a QAnimationGroup, it is + automatically removed before it's destroyed. +*/ +QAbstractAnimation::~QAbstractAnimation() +{ + Q_D(QAbstractAnimation); + //we can't call stop here. Otherwise we get pure virtual calls + if (d->state != Stopped) { + QAbstractAnimation::State oldState = d->state; + d->state = Stopped; + emit stateChanged(oldState, d->state); + QUnifiedTimer::instance()->unregisterAnimation(this); + } +} + +/*! + \property QAbstractAnimation::state + \brief state of the animation. + + This property describes the current state of the animation. When the + animation state changes, QAbstractAnimation emits the stateChanged() + signal. +*/ +QAbstractAnimation::State QAbstractAnimation::state() const +{ + Q_D(const QAbstractAnimation); + return d->state; +} + +/*! + If this animation is part of a QAnimationGroup, this function returns a + pointer to the group; otherwise, it returns 0. + + \sa QAnimationGroup::addAnimation() +*/ +QAnimationGroup *QAbstractAnimation::group() const +{ + Q_D(const QAbstractAnimation); + return d->group; +} + +/*! + \enum QAbstractAnimation::State + + This enum describes the state of the animation. + + \value Stopped The animation is not running. This is the initial state + of QAbstractAnimation, and the state QAbstractAnimation reenters when finished. The current + time remain unchanged until either setCurrentTime() is + called, or the animation is started by calling start(). + + \value Paused The animation is paused (i.e., temporarily + suspended). Calling resume() will resume animation activity. + + \value Running The animation is running. While control is in the event + loop, QAbstractAnimation will update its current time at regular intervals, + calling updateCurrentTime() when appropriate. + + \sa state(), stateChanged() +*/ + +/*! + \enum QAbstractAnimation::Direction + + This enum describes the direction of the animation when in \l Running state. + + \value Forward The current time of the animation increases with time (i.e., + moves from 0 and towards the end / duration). + + \value Backward The current time of the animation decreases with time (i.e., + moves from the end / duration and towards 0). + + \sa direction +*/ + +/*! + \property QAbstractAnimation::direction + \brief the direction of the animation when it is in \l Running + state. + + This direction indicates whether the time moves from 0 towards the + animation duration, or from the value of the duration and towards 0 after + start() has been called. + + By default, this property is set to \l Forward. +*/ +QAbstractAnimation::Direction QAbstractAnimation::direction() const +{ + Q_D(const QAbstractAnimation); + return d->direction; +} +void QAbstractAnimation::setDirection(Direction direction) +{ + Q_D(QAbstractAnimation); + if (d->direction == direction) + return; + + d->direction = direction; + if (state() == Stopped) { + if (direction == Backward) { + d->currentTime = duration(); + d->currentIteration = d->iterationCount - 1; + } else { + d->currentTime = 0; + d->currentIteration = 0; + } + } + updateDirection(direction); + emit directionChanged(direction); +} + +/*! + \property QAbstractAnimation::duration + \brief the duration of the animation. + + If the duration is -1, it means that the duration is undefined. + In this case, iterationCount is ignored. +*/ + +/*! + \property QAbstractAnimation::iterationCount + \brief the iteration count of the animation + + This property describes the iteration count of the animation as an integer. + By default this value is 1, indicating that the animation + should run once only, and then stop. By changing it you can let the + animation loop several times. With a value of 0, the animation will not + run at all, and with a value of -1, the animation will loop forever + until stopped. + It is not supported to have loop on an animation that has an undefined + duration. It will only run once. +*/ +int QAbstractAnimation::iterationCount() const +{ + Q_D(const QAbstractAnimation); + return d->iterationCount; +} +void QAbstractAnimation::setIterationCount(int iterationCount) +{ + Q_D(QAbstractAnimation); + d->iterationCount = iterationCount; +} + +/*! + \property QAbstractAnimation::currentIteration + \brief the current iteration of the animation + + This property describes the current iteration of the animation. By default, + the animation's iteration count is 1, and so the current iteration will + always be 0. If the iteration count is 2 and the animation runs past its + duration, it will automatically rewind and restart at current time 0, and + current iteration 1, and so on. + + When the current iteration changes, QAbstractAnimation emits the + currentIterationChanged() signal. +*/ +int QAbstractAnimation::currentIteration() const +{ + Q_D(const QAbstractAnimation); + return d->currentIteration; +} + +/*! + \fn virtual int QAbstractAnimation::duration() const = 0 + + This pure virtual function returns the duration of the animation, and + defines for how long QAbstractAnimation should update the current + time. This duration is local, and does not include the iteration count. + + A return value of -1 indicates that the animation has no defined duration; + the animation should run forever until stopped. This is useful for + animations that are not time driven, or where you cannot easily predict + its duration (e.g., event driven audio playback in a game). + + If the animation is a parallel QAnimationGroup, the duration will be the longest + duration of all its animations. If the animation is a sequential QAnimationGroup, + the duration will be the sum of the duration of all its animations. + \sa iterationCount +*/ + +/*! + Returns the total and effective duration of the animation, including the + iteration count. + + \sa duration(), currentTime +*/ +int QAbstractAnimation::totalDuration() const +{ + Q_D(const QAbstractAnimation); + if (d->iterationCount < 0) + return -1; + int dura = duration(); + if (dura == -1) + return -1; + return dura * d->iterationCount; +} + +/*! + \property QAbstractAnimation::currentTime + \brief the current time and progress of the animation + + This property describes the animation's current time. You can change the + current time by calling setCurrentTime, or you can call start() and let + the animation run, setting the current time automatically as the animation + progresses. + + The animation's current time starts at 0, and ends at duration(). If the + animation's iterationCount is larger than 1, the current time will rewind and + start at 0 again for the consecutive loops. If the animation has a pause. + currentTime will also include the duration of the pause. + + \sa iterationCount + */ +int QAbstractAnimation::currentTime() const +{ + Q_D(const QAbstractAnimation); + return d->currentTime; +} +void QAbstractAnimation::setCurrentTime(int msecs) +{ + Q_D(QAbstractAnimation); + msecs = qMax(msecs, 0); + + // Calculate new time and loop. + int dura = duration(); + int totalDura = (d->iterationCount < 0 || dura == -1) ? -1 : dura * d->iterationCount; + if (totalDura != -1) + msecs = qMin(totalDura, msecs); + d->totalCurrentTime = msecs; + + // Update new values. + int oldLoop = d->currentIteration; + d->currentIteration = ((dura <= 0) ? 0 : (msecs / dura)); + if (d->currentIteration == d->iterationCount) { + //we're at the end + d->currentTime = qMax(0, dura); + d->currentIteration = qMax(0, d->iterationCount - 1); + } else { + if (d->direction == Forward) { + d->currentTime = (dura <= 0) ? msecs : (msecs % dura); + } else { + d->currentTime = (dura <= 0) ? msecs : ((msecs - 1) % dura) + 1; + if (d->currentTime == dura) + --d->currentIteration; + } + } + + updateCurrentTime(msecs); + if (d->currentIteration != oldLoop) + emit currentIterationChanged(d->currentIteration); + + // All animations are responsible for stopping the animation when their + // own end state is reached; in this case the animation is time driven, + // and has reached the end. + if ((d->direction == Forward && d->totalCurrentTime == totalDura) + || (d->direction == Backward && d->totalCurrentTime == 0)) { + stop(); + } +} + +/*! + Starts the animation. The \a policy argument says whether or not the + animation should be deleted when it's done. When the animation starts, the + stateChanged() signal is emitted, and state() returns Running. When control + reaches the event loop, the animation will run by itself, periodically + calling updateCurrentTime() as the animation progresses. + + If the animation is currently stopped or has already reached the end, + calling start() will rewind the animation and start again from the beginning. + When the animation reaches the end, the animation will either stop, or + if the loop level is more than 1, it will rewind and continue from the beginning. + + If the animation is already running, this function does nothing. + + \sa stop(), state() +*/ +void QAbstractAnimation::start(DeletionPolicy policy) +{ + Q_D(QAbstractAnimation); + if (d->state == Running) + return; + d->setState(Running); + d->deleteWhenStopped = policy; +} + +/*! + Stops the animation. When the animation is stopped, it emits the stateChanged() + signal, and state() returns Stopped. The current time is not changed. + + If the animation stops by itself after reaching the end (i.e., + currentTime() == duration() and currentIteration() > iterationCount() - 1), the + finished() signal is emitted. + + \sa start(), state() + */ +void QAbstractAnimation::stop() +{ + Q_D(QAbstractAnimation); + + d->setState(Stopped); +} + +/*! + Pauses the animation. When the animation is paused, state() returns Paused. + The currenttime will remain unchanged until resume() or start() is called. + If you want to continue from the current time, call resume(). + + + \sa start(), state(), resume() + */ +void QAbstractAnimation::pause() +{ + Q_D(QAbstractAnimation); + if (d->state == Stopped) { + qWarning("QAbstractAnimation::pause: Cannot pause a stopped animation"); + return; + } + + d->setState(Paused); +} + +/*! + Resumes the animation after it was paused. When the animation is resumed, + it emits the resumed() and stateChanged() signals. The currenttime is not + changed. + + \sa start(), pause(), state() + */ +void QAbstractAnimation::resume() +{ + Q_D(QAbstractAnimation); + if (d->state != Paused) { + qWarning("QAbstractAnimation::resume: " + "Cannot resume an animation that is not paused"); + return; + } + + d->setState(Running); +} + +/*! + \reimp +*/ +bool QAbstractAnimation::event(QEvent *event) +{ + return QObject::event(event); +} + +/*! + \fn virtual void QAbstractAnimation::updateCurrentTime(int msecs) = 0; + + This pure virtual function is called every time the animation's current + time changes. The \a msecs argument is the current time. + + \sa updateState() +*/ + +/*! + This virtual function is called by QAbstractAnimation when the state + of the animation is changed from \a oldState to \a newState. + + \sa start(), stop(), pause(), resume() +*/ +void QAbstractAnimation::updateState(QAbstractAnimation::State oldState, + QAbstractAnimation::State newState) +{ + Q_UNUSED(oldState); + Q_UNUSED(newState); +} + +/*! + This virtual function is called by QAbstractAnimation when the direction + of the animation is changed. The \a direction argument is the new direction. + + \sa setDirection(), direction() +*/ +void QAbstractAnimation::updateDirection(QAbstractAnimation::Direction direction) +{ + Q_UNUSED(direction); +} + + +QT_END_NAMESPACE + +#include "moc_qabstractanimation.cpp" + +#endif //QT_NO_ANIMATION diff --git a/src/corelib/animation/qabstractanimation.h b/src/corelib/animation/qabstractanimation.h new file mode 100644 index 0000000..d6260cd --- /dev/null +++ b/src/corelib/animation/qabstractanimation.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTANIMATION_H +#define QABSTRACTANIMATION_H + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_ANIMATION + +class QAnimationGroup; +class QSequentialAnimationGroup; + +class QAbstractAnimationPrivate; +class Q_CORE_EXPORT QAbstractAnimation : public QObject +{ + Q_OBJECT + Q_PROPERTY(State state READ state NOTIFY stateChanged) + Q_PROPERTY(int iterationCount READ iterationCount WRITE setIterationCount) + Q_PROPERTY(int currentTime READ currentTime WRITE setCurrentTime) + Q_PROPERTY(int currentIteration READ currentIteration NOTIFY currentIterationChanged) + Q_PROPERTY(Direction direction READ direction WRITE setDirection NOTIFY directionChanged) + Q_PROPERTY(int duration READ duration) + +public: + enum Direction { + Forward, + Backward + }; + + enum State { + Stopped, + Paused, + Running + }; + + enum DeletionPolicy { + KeepWhenStopped = 0, + DeleteWhenStopped + }; + + QAbstractAnimation(QObject *parent = 0); + virtual ~QAbstractAnimation(); + + State state() const; + + QAnimationGroup *group() const; + + Direction direction() const; + void setDirection(Direction direction); + + int iterationCount() const; + void setIterationCount(int iterationCount); + int currentIteration() const; + + virtual int duration() const = 0; + int totalDuration() const; + + int currentTime() const; + +Q_SIGNALS: + void finished(); + void stateChanged(QAbstractAnimation::State oldState, QAbstractAnimation::State newState); + void currentIterationChanged(int currentIteration); + void directionChanged(QAbstractAnimation::Direction); + +public Q_SLOTS: + void start(QAbstractAnimation::DeletionPolicy policy = KeepWhenStopped); + void pause(); + void resume(); + void stop(); + void setCurrentTime(int msecs); + +protected: + QAbstractAnimation(QAbstractAnimationPrivate &dd, QObject *parent = 0); + bool event(QEvent *event); + + virtual void updateCurrentTime(int msecs) = 0; + virtual void updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState); + virtual void updateDirection(QAbstractAnimation::Direction direction); + +#ifdef QT_EXPERIMENTAL_SOLUTION + QAbstractAnimationPrivate *d_ptr; +#endif + +private: + Q_DISABLE_COPY(QAbstractAnimation) + Q_DECLARE_PRIVATE(QAbstractAnimation) +}; + +#endif //QT_NO_ANIMATION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QABSTRACTANIMATION_H diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h new file mode 100644 index 0000000..28d7d71 --- /dev/null +++ b/src/corelib/animation/qabstractanimation_p.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTANIMATION_P_H +#define QABSTRACTANIMATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QIODevice. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qbasictimer.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qtimer.h> +#ifndef QT_EXPERIMENTAL_SOLUTION +#include <private/qobject_p.h> +#endif + +QT_BEGIN_NAMESPACE + +class QAnimationGroup; +class QAbstractAnimation; +#ifdef QT_EXPERIMENTAL_SOLUTION +class QAbstractAnimationPrivate +#else +class QAbstractAnimationPrivate : public QObjectPrivate +#endif +{ +public: + QAbstractAnimationPrivate() + : state(QAbstractAnimation::Stopped), + direction(QAbstractAnimation::Forward), + deleteWhenStopped(false), + totalCurrentTime(0), + currentTime(0), + iterationCount(1), + currentIteration(0), + group(0) + { + } + + virtual ~QAbstractAnimationPrivate() {} + + static QAbstractAnimationPrivate *get(QAbstractAnimation *q) + { + return q->d_func(); + } + + QAbstractAnimation::State state; + QAbstractAnimation::Direction direction; + bool deleteWhenStopped; + void setState(QAbstractAnimation::State state); + + int totalCurrentTime; + int currentTime; + int iterationCount; + int currentIteration; + + QAnimationGroup *group; +#ifdef QT_EXPERIMENTAL_SOLUTION + QAbstractAnimation *q_ptr; +#endif + +private: + Q_DECLARE_PUBLIC(QAbstractAnimation) +}; + + +class QUnifiedTimer : public QObject +{ +private: + QUnifiedTimer(); + +public: + static QUnifiedTimer *instance(); + + void timerEvent(QTimerEvent *); + void updateTimer(); + void registerAnimation(QAbstractAnimation *animation); + void unregisterAnimation(QAbstractAnimation *animation); + + +private: + void updateRecentlyStartedAnimations(); + + QBasicTimer animationTimer, startStopAnimationTimer; + QTime time; + int lastTick; + QList<QAbstractAnimation*> animations, animationsToStart; +}; + +QT_END_NAMESPACE +#endif diff --git a/src/corelib/animation/qanimationgroup.cpp b/src/corelib/animation/qanimationgroup.cpp new file mode 100644 index 0000000..f39738b --- /dev/null +++ b/src/corelib/animation/qanimationgroup.cpp @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QAnimationGroup + \brief The QAnimationGroup is an abstract base class for group of animations. + \since 4.5 + \ingroup animation + \preliminary + + QAnimationGroup represents a group of animations, such as parallel or sequential, + and lets you combine different animations into one. The group manages any animation + that inherits QAbstractAnimation. By combining groups, you can easily construct + complex animation graphs. + + The QAnimationGroup base class provides methods for adding and retrieving animations. + Besides that, you can remove animations by calling remove(), and clear the animation + group by calling clearAnimations(). You may keep track of changes in the group's animations by + listening to QEvent::ChildAdded and QEvent::ChildRemoved events. + + QAnimationGroup takes ownership of the animations it manages, and ensures that they are + deleted when the animation group is deleted. + + \sa QAbstractAnimation, QVariantAnimation, {The Animation Framework} +*/ + +#ifndef QT_NO_ANIMATION + +#include "qanimationgroup.h" +#include <QtCore/qdebug.h> +#include <QtCore/qcoreevent.h> +#include "qanimationgroup_p.h" + +QT_BEGIN_NAMESPACE + + +/*! + Constructs a QAnimationGroup. + \a parent is passed to QObject's constructor. +*/ +QAnimationGroup::QAnimationGroup(QObject *parent) + : QAbstractAnimation(*new QAnimationGroupPrivate, parent) +{ +} + +/*! + \internal +*/ +QAnimationGroup::QAnimationGroup(QAnimationGroupPrivate &dd, QObject *parent) + : QAbstractAnimation(dd, parent) +{ +} + +/*! + Destroys the animation group. It will also destroy all its animations. +*/ +QAnimationGroup::~QAnimationGroup() +{ +} + +/*! + Returns a pointer to the animation at \a index in this group. This + function is useful when you need access to a particular animation. \a + index is between 0 and animationCount() - 1. + + \sa animationCount(), indexOfAnimation() +*/ +QAbstractAnimation *QAnimationGroup::animationAt(int index) const +{ + Q_D(const QAnimationGroup); + + if (index < 0 || index >= d->animations.size()) { + qWarning("QAnimationGroup::animationAt: index is out of bounds"); + return 0; + } + + return d->animations.at(index); +} + + +/*! + Returns the number of animations managed by this group. + + \sa indexOfAnimation(), addAnimation(), animationAt() +*/ +int QAnimationGroup::animationCount() const +{ + Q_D(const QAnimationGroup); + return d->animations.size(); +} + +/*! + Returns the index of \a animation. The returned index can be passed + to the other functions that take an index as an argument. + + \sa insertAnimationAt() animationAt(), takeAnimationAt() +*/ +int QAnimationGroup::indexOfAnimation(QAbstractAnimation *animation) const +{ + Q_D(const QAnimationGroup); + return d->animations.indexOf(animation); +} + +/*! + Adds \a animation to this group. This will call insertAnimationAt with + index equals to animationCount() +*/ +void QAnimationGroup::addAnimation(QAbstractAnimation *animation) +{ + Q_D(QAnimationGroup); + insertAnimationAt(d->animations.count(), animation); +} + +/*! + Inserts \a animation into this animation group at \a index. + If \a index is 0 the animation is inserted at the beginning. + If \a index is animationCount(), the animation is inserted at the end. + \sa takeAnimationAt(), addAnimation(), indexOfAnimation() +*/ +void QAnimationGroup::insertAnimationAt(int index, QAbstractAnimation *animation) +{ + Q_D(QAnimationGroup); + + if (index < 0 || index > d->animations.size()) { + qWarning("QAnimationGroup::insertAnimationAt: index is out of bounds"); + return; + } + + d->animations.insert(index, animation); + if (QAnimationGroup *oldGroup = animation->group()) + oldGroup->removeAnimation(animation); + QAbstractAnimationPrivate::get(animation)->group = this; + // this will make sure that ChildAdded event is sent to 'this' + animation->setParent(this); + d->animationInsertedAt(index); +} + +/*! + Removes \a animation from this group. The ownership of \a animation is + transferred to the caller. + + \sa takeAnimationAt(), insertAnimationAt(), addAnimation() +*/ +void QAnimationGroup::removeAnimation(QAbstractAnimation *animation) +{ + Q_D(QAnimationGroup); + + if (!animation) { + qWarning("QAnimationGroup::remove: cannot remove null animation"); + return; + } + int index = d->animations.indexOf(animation); + if (index == -1) { + qWarning("QAnimationGroup::remove: animation is not part of this group"); + return; + } + + takeAnimationAt(index); +} + +/*! + Removes the animation at \a index from this animation group. The ownership + of the animation is transferred to the caller, and a pointer to the removed + animation is returned. + + \sa addAnimation() +*/ +QAbstractAnimation *QAnimationGroup::takeAnimationAt(int index) +{ + Q_D(QAnimationGroup); + if (index < 0 || index >= d->animations.size()) { + qWarning("QAnimationGroup::takeAnimationAt: no animation at index %d", index); + return 0; + } + QAbstractAnimation *animation = d->animations.at(index); + QAbstractAnimationPrivate::get(animation)->group = 0; + // ### removing from list before doing setParent to avoid inifinite recursion + // in ChildRemoved event + d->animations.removeAt(index); + animation->setParent(0); + d->animationRemovedAt(index); + return animation; +} + +/*! + Removes and deletes all animations in this animation group, and resets the current + time to 0. + + \sa addAnimation(), removeAnimation() +*/ +void QAnimationGroup::clearAnimations() +{ + Q_D(QAnimationGroup); + qDeleteAll(d->animations); +} + +/*! + \reimp +*/ +bool QAnimationGroup::event(QEvent *event) +{ + Q_D(QAnimationGroup); + if (event->type() == QEvent::ChildAdded) { + QChildEvent *childEvent = static_cast<QChildEvent *>(event); + if (QAbstractAnimation *a = qobject_cast<QAbstractAnimation *>(childEvent->child())) { + if (a->group() != this) + addAnimation(a); + } + } else if (event->type() == QEvent::ChildRemoved) { + QChildEvent *childEvent = static_cast<QChildEvent *>(event); + QAbstractAnimation *a = static_cast<QAbstractAnimation *>(childEvent->child()); + // You can only rely on the child being a QObject because in the QEvent::ChildRemoved + // case it might be called from the destructor. + int index = d->animations.indexOf(a); + if (index != -1) + takeAnimationAt(index); + } + return QAbstractAnimation::event(event); +} + + +void QAnimationGroupPrivate::animationRemovedAt(int index) +{ + Q_Q(QAnimationGroup); + Q_UNUSED(index); + if (animations.isEmpty()) { + currentTime = 0; + q->stop(); + } +} + +QT_END_NAMESPACE + +#include "moc_qanimationgroup.cpp" + +#endif //QT_NO_ANIMATION diff --git a/src/corelib/animation/qanimationgroup.h b/src/corelib/animation/qanimationgroup.h new file mode 100644 index 0000000..7dee070 --- /dev/null +++ b/src/corelib/animation/qanimationgroup.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANIMATIONGROUP_H +#define QANIMATIONGROUP_H + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qabstractanimation.h" +#else +# include <QtCore/qabstractanimation.h> +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_ANIMATION + +class QAnimationGroupPrivate; +class Q_CORE_EXPORT QAnimationGroup : public QAbstractAnimation +{ + Q_OBJECT + +public: + QAnimationGroup(QObject *parent = 0); + ~QAnimationGroup(); + + QAbstractAnimation *animationAt(int index) const; + int animationCount() const; + int indexOfAnimation(QAbstractAnimation *animation) const; + void addAnimation(QAbstractAnimation *animation); + void insertAnimationAt(int index, QAbstractAnimation *animation); + void removeAnimation(QAbstractAnimation *animation); + QAbstractAnimation *takeAnimationAt(int index); + void clearAnimations(); + +protected: + QAnimationGroup(QAnimationGroupPrivate &dd, QObject *parent); + bool event(QEvent *event); + +private: + Q_DISABLE_COPY(QAnimationGroup) + Q_DECLARE_PRIVATE(QAnimationGroup) +}; + +#endif //QT_NO_ANIMATION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QANIMATIONGROUP_H diff --git a/src/corelib/animation/qanimationgroup_p.h b/src/corelib/animation/qanimationgroup_p.h new file mode 100644 index 0000000..a7bd0fa --- /dev/null +++ b/src/corelib/animation/qanimationgroup_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANIMATIONGROUP_P_H +#define QANIMATIONGROUP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QIODevice. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qanimationgroup.h" + +#include <QtCore/qlist.h> + +#include "qabstractanimation_p.h" + +QT_BEGIN_NAMESPACE + +class QAnimationGroupPrivate : public QAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QAnimationGroup) +public: + QAnimationGroupPrivate() + { } + + virtual void animationInsertedAt(int index) { Q_UNUSED(index) }; + virtual void animationRemovedAt(int index); + + QList<QAbstractAnimation *> animations; +}; + +QT_END_NAMESPACE + +#endif //QANIMATIONGROUP_P_H diff --git a/src/corelib/animation/qparallelanimationgroup.cpp b/src/corelib/animation/qparallelanimationgroup.cpp new file mode 100644 index 0000000..993c577 --- /dev/null +++ b/src/corelib/animation/qparallelanimationgroup.cpp @@ -0,0 +1,298 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QParallelAnimationGroup + \brief The QParallelAnimationGroup class provides a parallel group of animations. + \since 4.5 + \ingroup animation + \preliminary + + The animations are all started at the same time, and run in parallel. The animation group + finishes when the longest lasting animation has finished. +*/ + +#ifndef QT_NO_ANIMATION + +#include "qparallelanimationgroup.h" +#include "qparallelanimationgroup_p.h" +//#define QANIMATION_DEBUG +QT_BEGIN_NAMESPACE + +/*! + Constructs a QParallelAnimationGroup. + \a parent is passed to QObject's constructor. +*/ +QParallelAnimationGroup::QParallelAnimationGroup(QObject *parent) + : QAnimationGroup(*new QParallelAnimationGroupPrivate, parent) +{ +} + +/*! + \internal +*/ +QParallelAnimationGroup::QParallelAnimationGroup(QParallelAnimationGroupPrivate &dd, + QObject *parent) + : QAnimationGroup(dd, parent) +{ +} + +/*! + Destroys the animation group. It will also destroy all its animations. +*/ +QParallelAnimationGroup::~QParallelAnimationGroup() +{ +} + +/*! + \reimp +*/ +int QParallelAnimationGroup::duration() const +{ + Q_D(const QParallelAnimationGroup); + int ret = 0; + + for (int i = 0; i < d->animations.size(); ++i) { + QAbstractAnimation *animation = d->animations.at(i); + const int currentDuration = animation->totalDuration(); + if (currentDuration == -1) + return -1; // Undetermined length + + ret = qMax(ret, currentDuration); + } + + return ret; +} + +/*! + \reimp +*/ +void QParallelAnimationGroup::updateCurrentTime(int) +{ + Q_D(QParallelAnimationGroup); + if (d->animations.isEmpty()) + return; + + if (d->currentIteration > d->lastIteration) { + // simulate completion of the loop + int dura = duration(); + if (dura > 0) { + foreach (QAbstractAnimation *animation, d->animations) { + animation->setCurrentTime(dura); // will stop + } + } + } else if (d->currentIteration < d->lastIteration) { + // simulate completion of the loop seeking backwards + foreach (QAbstractAnimation *animation, d->animations) { + animation->setCurrentTime(0); + animation->stop(); + } + } + + bool timeFwd = ((d->currentIteration == d->lastIteration && d->currentTime >= d->lastCurrentTime) + || d->currentIteration > d->lastIteration); +#ifdef QANIMATION_DEBUG + qDebug("QParallellAnimationGroup %5d: setCurrentTime(%d), loop:%d, last:%d, timeFwd:%d, lastcurrent:%d, %d", + __LINE__, d->currentTime, d->currentIteration, d->lastIteration, timeFwd, d->lastCurrentTime, state()); +#endif + // finally move into the actual time of the current loop + foreach (QAbstractAnimation *animation, d->animations) { + const int dura = animation->totalDuration(); + if (dura == -1 && d->isUncontrolledAnimationFinished(animation)) + continue; + if (dura == -1 || (d->currentTime <= dura && dura != 0) + || (dura == 0 && d->currentIteration != d->lastIteration)) { + switch (state()) { + case Running: + animation->start(); + break; + case Paused: + animation->pause(); + break; + case Stopped: + default: + break; + } + } + + if (dura <= 0) { + if (dura == -1) + animation->setCurrentTime(d->currentTime); + continue; + } + + if ((timeFwd && d->lastCurrentTime <= dura) + || (!timeFwd && d->currentTime <= dura)) + animation->setCurrentTime(d->currentTime); + if (d->currentTime > dura) + animation->stop(); + } + d->lastIteration = d->currentIteration; + d->lastCurrentTime = d->currentTime; +} + +/*! + \reimp +*/ +void QParallelAnimationGroup::updateState(QAbstractAnimation::State oldState, + QAbstractAnimation::State newState) +{ + Q_D(QParallelAnimationGroup); + QAnimationGroup::updateState(oldState, newState); + + switch (newState) { + case Stopped: + foreach (QAbstractAnimation *animation, d->animations) + animation->stop(); + d->disconnectUncontrolledAnimations(); + break; + case Paused: + foreach (QAbstractAnimation *animation, d->animations) + animation->pause(); + break; + case Running: + d->connectUncontrolledAnimations(); + foreach (QAbstractAnimation *animation, d->animations) { + animation->stop(); + animation->setDirection(d->direction); + animation->start(); + } + break; + } +} + +void QParallelAnimationGroupPrivate::_q_uncontrolledAnimationFinished() +{ + Q_Q(QParallelAnimationGroup); + + QAbstractAnimation *animation = qobject_cast<QAbstractAnimation *>(q->sender()); + Q_ASSERT(animation); + + int uncontrolledRunningCount = 0; + if (animation->duration() == -1 || animation->iterationCount() < 0) { + QHash<QAbstractAnimation *, int>::iterator it = uncontrolledFinishTime.begin(); + while (it != uncontrolledFinishTime.end()) { + if (it.key() == animation) { + *it = animation->currentTime(); + } + if (it.value() == -1) + ++uncontrolledRunningCount; + ++it; + } + } + + if (uncontrolledRunningCount > 0) + return; + + int maxDuration = 0; + foreach (QAbstractAnimation *a, animations) + maxDuration = qMax(maxDuration, a->totalDuration()); + + if (currentTime >= maxDuration) + q->stop(); +} + +void QParallelAnimationGroupPrivate::disconnectUncontrolledAnimations() +{ + Q_Q(QParallelAnimationGroup); + + QHash<QAbstractAnimation *, int>::iterator it = uncontrolledFinishTime.begin(); + while (it != uncontrolledFinishTime.end()) { + QObject::disconnect(it.key(), SIGNAL(finished()), q, SLOT(_q_uncontrolledAnimationFinished())); + ++it; + } + + uncontrolledFinishTime.clear(); +} + +void QParallelAnimationGroupPrivate::connectUncontrolledAnimations() +{ + Q_Q(QParallelAnimationGroup); + + foreach (QAbstractAnimation *animation, animations) { + if (animation->duration() == -1 || animation->iterationCount() < 0) { + uncontrolledFinishTime[animation] = -1; + QObject::connect(animation, SIGNAL(finished()), q, SLOT(_q_uncontrolledAnimationFinished())); + } + } +} + +bool QParallelAnimationGroupPrivate::isUncontrolledAnimationFinished(QAbstractAnimation *anim) const +{ + return uncontrolledFinishTime.value(anim, -1) >= 0; +} + +/*! + \reimp +*/ +void QParallelAnimationGroup::updateDirection(QAbstractAnimation::Direction direction) +{ + Q_D(QParallelAnimationGroup); + //we need to update the direction of the current animation + if (state() != Stopped) { + foreach(QAbstractAnimation *anim, d->animations) { + anim->setDirection(direction); + } + } else { + if (direction == Forward) { + d->lastIteration = 0; + d->lastCurrentTime = 0; + } else { + // Looping backwards with iterationCount == -1 does not really work well... + d->lastIteration = (d->iterationCount == -1 ? 0 : d->iterationCount - 1); + d->lastCurrentTime = duration(); + } + } +} + +/*! + \reimp +*/ +bool QParallelAnimationGroup::event(QEvent *event) +{ + return QAnimationGroup::event(event); +} + +QT_END_NAMESPACE + +#include "moc_qparallelanimationgroup.cpp" + +#endif //QT_NO_ANIMATION diff --git a/src/corelib/animation/qparallelanimationgroup.h b/src/corelib/animation/qparallelanimationgroup.h new file mode 100644 index 0000000..48d66a3 --- /dev/null +++ b/src/corelib/animation/qparallelanimationgroup.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPARALLELANIMATIONGROUP_H +#define QPARALLELANIMATIONGROUP_H + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qanimationgroup.h" +#else +# include <QtCore/qanimationgroup.h> +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_ANIMATION + +class QParallelAnimationGroupPrivate; +class Q_CORE_EXPORT QParallelAnimationGroup : public QAnimationGroup +{ + Q_OBJECT + +public: + QParallelAnimationGroup(QObject *parent = 0); + ~QParallelAnimationGroup(); + + int duration() const; + +protected: + QParallelAnimationGroup(QParallelAnimationGroupPrivate &dd, QObject *parent); + bool event(QEvent *event); + + void updateCurrentTime(int msecs); + void updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState); + void updateDirection(QAbstractAnimation::Direction direction); + +private: + Q_DISABLE_COPY(QParallelAnimationGroup) + Q_DECLARE_PRIVATE(QParallelAnimationGroup) + Q_PRIVATE_SLOT(d_func(), void _q_uncontrolledAnimationFinished()) +}; + +#endif //QT_NO_ANIMATION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPARALLELANIMATIONGROUP diff --git a/src/corelib/animation/qparallelanimationgroup_p.h b/src/corelib/animation/qparallelanimationgroup_p.h new file mode 100644 index 0000000..8a4dc1b --- /dev/null +++ b/src/corelib/animation/qparallelanimationgroup_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPARALLELANIMATIONGROUP_P_H +#define QPARALLELANIMATIONGROUP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QIODevice. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qparallelanimationgroup.h" +#include "qanimationgroup_p.h" +#include <QtCore/QHash> + +QT_BEGIN_NAMESPACE + +class QParallelAnimationGroupPrivate : public QAnimationGroupPrivate +{ + Q_DECLARE_PUBLIC(QParallelAnimationGroup) +public: + QParallelAnimationGroupPrivate() + : lastIteration(0), lastCurrentTime(0) + { + } + + QHash<QAbstractAnimation*, int> uncontrolledFinishTime; + int lastIteration; + int lastCurrentTime; + + bool isUncontrolledAnimationFinished(QAbstractAnimation *anim) const; + void connectUncontrolledAnimations(); + void disconnectUncontrolledAnimations(); + + // private slot + void _q_uncontrolledAnimationFinished(); +}; + +QT_END_NAMESPACE + +#endif //QPARALLELANIMATIONGROUP_P_H diff --git a/src/corelib/animation/qpauseanimation.cpp b/src/corelib/animation/qpauseanimation.cpp new file mode 100644 index 0000000..30ea92c --- /dev/null +++ b/src/corelib/animation/qpauseanimation.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QPauseAnimation + \brief The QPauseAnimation class provides a pause for QSequentialAnimationGroup. + \since 4.5 + \ingroup animation + \preliminary +*/ + +#ifndef QT_NO_ANIMATION + +#include "qpauseanimation.h" +#include "qabstractanimation_p.h" + + +QT_BEGIN_NAMESPACE + +class QPauseAnimationPrivate : public QAbstractAnimationPrivate +{ +public: + QPauseAnimationPrivate() : QAbstractAnimationPrivate(), duration(0) + { + } + + int duration; +}; + +/*! + Constructs a QPauseAnimation. + \a parent is passed to QObject's constructor. + The default duration is 0. +*/ + +QPauseAnimation::QPauseAnimation(QObject *parent) : QAbstractAnimation(*new QPauseAnimationPrivate, parent) +{ +} + +/*! + Constructs a QPauseAnimation. + \a msecs is the duration of the pause. + \a parent is passed to QObject's constructor. +*/ + +QPauseAnimation::QPauseAnimation(int msecs, QObject *parent) : QAbstractAnimation(*new QPauseAnimationPrivate, parent) +{ + setDuration(msecs); +} + +/*! + Destroys the pause animation. +*/ +QPauseAnimation::~QPauseAnimation() +{ +} + +/*! + \property QPauseAnimation::duration + \brief the duration of the pause. + + The duration of the pause. The duration should not be negative. +*/ +int QPauseAnimation::duration() const +{ + Q_D(const QPauseAnimation); + return d->duration; +} + +void QPauseAnimation::setDuration(int msecs) +{ + if (msecs < 0) { + qWarning("QPauseAnimation::setDuration: cannot set a negative duration"); + return; + } + Q_D(QPauseAnimation); + d->duration = msecs; +} + +/*! + \reimp + */ +bool QPauseAnimation::event(QEvent *e) +{ + return QAbstractAnimation::event(e); +} + +/*! + \reimp + */ +void QPauseAnimation::updateCurrentTime(int msecs) +{ + Q_UNUSED(msecs); +} + + +QT_END_NAMESPACE + +#include "moc_qpauseanimation.cpp" + +#endif //QT_NO_ANIMATION diff --git a/src/corelib/animation/qpauseanimation.h b/src/corelib/animation/qpauseanimation.h new file mode 100644 index 0000000..595f2d0 --- /dev/null +++ b/src/corelib/animation/qpauseanimation.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPAUSEANIMATION_P_H +#define QPAUSEANIMATION_P_H + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qanimationgroup.h" +#else +# include <QtCore/qanimationgroup.h> +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_ANIMATION + +class QPauseAnimationPrivate; + +class Q_CORE_EXPORT QPauseAnimation : public QAbstractAnimation +{ + Q_OBJECT + Q_PROPERTY(int duration READ duration WRITE setDuration) +public: + QPauseAnimation(QObject *parent = 0); + QPauseAnimation(int msecs, QObject *parent = 0); + ~QPauseAnimation(); + + int duration() const; + void setDuration(int msecs); + +protected: + bool event(QEvent *e); + void updateCurrentTime(int msecs); + +private: + Q_DISABLE_COPY(QPauseAnimation) + Q_DECLARE_PRIVATE(QPauseAnimation) +}; + +#endif //QT_NO_ANIMATION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPAUSEANIMATION_P_H diff --git a/src/corelib/animation/qpropertyanimation.cpp b/src/corelib/animation/qpropertyanimation.cpp new file mode 100644 index 0000000..adf3527 --- /dev/null +++ b/src/corelib/animation/qpropertyanimation.cpp @@ -0,0 +1,256 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QPropertyAnimation + \brief The QPropertyAnimation class animates properties for QObject(and QWidget) + \ingroup animation + \preliminary + + This class is part of {The Animation Framework}. You can use QPropertyAnimation + by itself as a simple animation class, or as part of more complex + animations through QAnimationGroup. + + The most common way to use QPropertyAnimation is to construct an instance + of it by passing a pointer to a QObject or a QWidget, and the name of the + property you would like to animate to QPropertyAnimation's constructor. + + The start value of the animation is optional. If you do not set any start + value, the animation will operate on the target's current property value + at the point when the animation was started. You can call setStartValue() + to set the start value, and setEndValue() to set the target value for + the animated property. + + Animations can operate on QObjects and QWidgets. You can choose to assign a + target object by either calling setTargetObject() or by passing a QObject + pointer to QPropertyAnimation's constructor. + + \sa QVariantAnimation, QAnimationGroup, {The Animation Framework} +*/ + +#ifndef QT_NO_ANIMATION + +#include "qpropertyanimation.h" +#include "qanimationgroup.h" +#include <QtCore/qdebug.h> + +#include "qpropertyanimation_p.h" + +#include <QtCore/qmath.h> +#include <QtCore/qmutex.h> + +QT_BEGIN_NAMESPACE + +typedef QPair<QObject *, QByteArray> QPropertyAnimationPair; +typedef QHash<QPropertyAnimationPair, QPropertyAnimation*> QPropertyAnimationHash; +Q_GLOBAL_STATIC(QPropertyAnimationHash, _q_runningAnimations); +Q_GLOBAL_STATIC_WITH_ARGS(QMutex, guardHashLock, (QMutex::Recursive) ) + +void QPropertyAnimationPrivate::updateMetaProperty() +{ + if (!target || propertyName.isEmpty()) + return; + + if (hasMetaProperty == 0 && !property.isValid()) { + const QMetaObject *mo = target->metaObject(); + propertyIndex = mo->indexOfProperty(propertyName); + if (propertyIndex != -1) { + hasMetaProperty = 1; + property = mo->property(propertyIndex); + propertyType = property.userType(); + } else { + hasMetaProperty = 2; + } + } + + if (property.isValid()) + convertValues(propertyType); +} + +void QPropertyAnimationPrivate::updateProperty(const QVariant &newValue) +{ + if (!target || state == QAbstractAnimation::Stopped) + return; + + if (hasMetaProperty == 1) { + if (newValue.userType() == propertyType) { + //no conversion is needed, we directly call the QObject::qt_metacall + void *data = const_cast<void*>(newValue.constData()); + target->qt_metacall(QMetaObject::WriteProperty, propertyIndex, &data); + } else { + property.write(target, newValue); + } + } else { + target->setProperty(propertyName.constData(), newValue); + } +} + +/*! + Construct a QPropertyAnimation object. \a parent is passed to QObject's + constructor. +*/ +QPropertyAnimation::QPropertyAnimation(QObject *parent) + : QVariantAnimation(*new QPropertyAnimationPrivate, parent) +{ +} + +/*! + Construct a QPropertyAnimation object. \a parent is passed to QObject's + constructor. The animation changes the property \a propertyName on \a + target. The default duration is 250ms. + + \sa targetObject, propertyName +*/ +QPropertyAnimation::QPropertyAnimation(QObject *target, const QByteArray &propertyName, QObject *parent) + : QVariantAnimation(*new QPropertyAnimationPrivate, parent) +{ + setTargetObject(target); + setPropertyName(propertyName); +} + +/*! + Destroys the QPropertyAnimation instance. + */ +QPropertyAnimation::~QPropertyAnimation() +{ + stop(); +} + +/*! + \property QPropertyAnimation::targetObject + \brief the target QObject for this animation. + + This property defines the target QObject for this animation. + */ +QObject *QPropertyAnimation::targetObject() const +{ + Q_D(const QPropertyAnimation); + return d->target; +} +void QPropertyAnimation::setTargetObject(QObject *target) +{ + Q_D(QPropertyAnimation); + if (d->target == target) + return; + + d->target = target; + d->hasMetaProperty = 0; + d->updateMetaProperty(); +} + +/*! + \property QPropertyAnimation::propertyName + \brief the target property name for this animation + + This property defines the target property name for this animation. The + property name is required for the animation to operate. + */ +QByteArray QPropertyAnimation::propertyName() const +{ + Q_D(const QPropertyAnimation); + return d->propertyName; +} +void QPropertyAnimation::setPropertyName(const QByteArray &propertyName) +{ + Q_D(QPropertyAnimation); + d->propertyName = propertyName; + d->hasMetaProperty = 0; + d->updateMetaProperty(); +} + + +/*! + \reimp + */ +bool QPropertyAnimation::event(QEvent *event) +{ + return QVariantAnimation::event(event); +} + +/*! + This virtual function is called by QVariantAnimation whenever the current value + changes. \a value is the new, updated value. It updates the current value + of the property on the target object. + + \sa currentValue, currentTime + */ +void QPropertyAnimation::updateCurrentValue(const QVariant &value) +{ + Q_D(QPropertyAnimation); + d->updateProperty(value); +} + +/*! + \reimp +*/ +void QPropertyAnimation::updateState(QAbstractAnimation::State oldState, + QAbstractAnimation::State newState) +{ + Q_D(QPropertyAnimation); + QVariantAnimation::updateState(oldState, newState); + QMutexLocker locker(guardHashLock()); + QPropertyAnimationHash * hash = _q_runningAnimations(); + QPropertyAnimationPair key(d->target, d->propertyName); + if (newState == Running) { + d->updateMetaProperty(); + QPropertyAnimation *oldAnim = hash->value(key, 0); + if (oldAnim) { + //we try to stop the top level group + QAbstractAnimation *current = oldAnim; + while(current->group() && current->state() != Stopped) current = current->group(); + current->stop(); + } + hash->insert(key, this); + // Initialize start value + if (d->target && !d->defaultStartValue.isValid() && (d->atBeginning() || d->atEnd())) { + d->setDefaultStartValue(d->target->property(d->propertyName.constData())); + } + } else if (hash->value(key) == this) { + hash->remove(key); + } + +} + +#include "moc_qpropertyanimation.cpp" + +QT_END_NAMESPACE + +#endif //QT_NO_ANIMATION diff --git a/src/corelib/animation/qpropertyanimation.h b/src/corelib/animation/qpropertyanimation.h new file mode 100644 index 0000000..e5d5305 --- /dev/null +++ b/src/corelib/animation/qpropertyanimation.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPROPERTYANIMATION_H +#define QPROPERTYANIMATION_H + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qvariantanimation.h" +#else +# include <QtCore/qvariantanimation.h> +#endif +#include <QtCore/qvariant.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_ANIMATION + +class QPropertyAnimationPrivate; +class Q_CORE_EXPORT QPropertyAnimation : public QVariantAnimation +{ + Q_OBJECT + Q_PROPERTY(QByteArray propertyName READ propertyName WRITE setPropertyName) + Q_PROPERTY(QObject* targetObject READ targetObject WRITE setTargetObject) + +public: + QPropertyAnimation(QObject *parent = 0); + QPropertyAnimation(QObject *target, const QByteArray &propertyName, QObject *parent = 0); + ~QPropertyAnimation(); + + QObject *targetObject() const; + void setTargetObject(QObject *target); + + QByteArray propertyName() const; + void setPropertyName(const QByteArray &propertyName); + +protected: + bool event(QEvent *event); + + void updateCurrentValue(const QVariant &value); + void updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState); +private: + Q_DISABLE_COPY(QPropertyAnimation) + Q_DECLARE_PRIVATE(QPropertyAnimation) +}; + +#endif //QT_NO_ANIMATION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPROPERTYANIMATION_H diff --git a/src/corelib/animation/qpropertyanimation_p.h b/src/corelib/animation/qpropertyanimation_p.h new file mode 100644 index 0000000..ed3666d --- /dev/null +++ b/src/corelib/animation/qpropertyanimation_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPROPERTYANIMATION_P_H +#define QPROPERTYANIMATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QIODevice. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qpropertyanimation.h" +#include <QtCore/qmetaobject.h> +#include <QtCore/qpointer.h> + +#include "qvariantanimation_p.h" + +QT_BEGIN_NAMESPACE + +class QPropertyAnimationPrivate : public QVariantAnimationPrivate +{ + Q_DECLARE_PUBLIC(QPropertyAnimation) +public: + QPropertyAnimationPrivate() + : target(0), propertyType(0), propertyIndex(0), hasMetaProperty(false) + { + } + + + QPointer<QObject> target; + + //for the QProperty + QMetaProperty property; + int propertyType; + int propertyIndex; + + int hasMetaProperty; + QByteArray propertyName; + void updateProperty(const QVariant &); + void updateMetaProperty(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/animation/qsequentialanimationgroup.cpp b/src/corelib/animation/qsequentialanimationgroup.cpp new file mode 100644 index 0000000..879532c --- /dev/null +++ b/src/corelib/animation/qsequentialanimationgroup.cpp @@ -0,0 +1,572 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QSequentialAnimationGroup + \brief The QSequentialAnimationGroup class provides a sequential group of animations. + \since 4.5 + \ingroup animation + \preliminary + + The first animation in the group is started first, and when it finishes, the next animation + is started, and so on. The animation group finishes when the last animation has finished. + + At each moment there is at most one animation that is active in the group, called currentAnimation. + An empty group has no current animation. + + You can call addPause() or insertPause() to add a pause to a sequential animation group. +*/ + +#ifndef QT_NO_ANIMATION + +#include "qsequentialanimationgroup.h" +#include "qsequentialanimationgroup_p.h" + +#include "qpauseanimation.h" + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + + + +bool QSequentialAnimationGroupPrivate::atEnd() const +{ + // we try to detect if we're at the end of the group + //this is true if the following conditions are true: + // 1. we're in the last loop + // 2. the direction is forward + // 3. the current animation is the last one + // 4. the current animation has reached its end + const int animTotalCurrentTime = QAbstractAnimationPrivate::get(currentAnimation)->totalCurrentTime; + return (currentIteration == iterationCount - 1 + && direction == QAbstractAnimation::Forward + && currentAnimation == animations.last() + && animTotalCurrentTime == animationActualTotalDuration(currentAnimationIndex)); +} + +int QSequentialAnimationGroupPrivate::animationActualTotalDuration(int index) const +{ + QAbstractAnimation *anim = animations.at(index); + int ret = anim->totalDuration(); + if (ret == -1 && actualDuration.size() > index) + ret = actualDuration.at(index); //we can try the actual duration there + return ret; +} + +QSequentialAnimationGroupPrivate::AnimationIndex QSequentialAnimationGroupPrivate::indexForTime(int msecs) const +{ + Q_Q(const QSequentialAnimationGroup); + Q_ASSERT(!animations.isEmpty()); + + AnimationIndex ret; + int duration = 0; + + // in case duration is -1, currentLoop will always be 0 + ret.timeOffset = currentIteration * q->duration(); + + for (int i = 0; i < animations.size(); ++i) { + duration = animationActualTotalDuration(i); + + // 'animation' is the current animation if one of these reasons is true: + // 1. it's duration is undefined + // 2. it ends after msecs + // 3. it is the last animation (this can happen in case there is at least 1 uncontrolled animation) + // 4. it ends exactly in msecs and the direction is backwards + if (duration == -1 || msecs < (ret.timeOffset + duration) + || (msecs == (ret.timeOffset + duration) && direction == QAbstractAnimation::Backward)) { + ret.index = i; + return ret; + } + + // 'animation' has a non-null defined duration and is not the one at time 'msecs'. + ret.timeOffset += duration; + } + + // this can only happen when one of those conditions is true: + // 1. the duration of the group is undefined and we passed its actual duration + // 2. there are only 0-duration animations in the group + ret.timeOffset -= duration; + ret.index = animations.size() - 1; + return ret; +} + +void QSequentialAnimationGroupPrivate::restart() +{ + // restarting the group by making the first/last animation the current one + if (direction == QAbstractAnimation::Forward) { + lastIteration = 0; + if (currentAnimationIndex == 0) + activateCurrentAnimation(); + else + setCurrentAnimation(0); + } else { // direction == QAbstractAnimation::Backward + lastIteration = iterationCount - 1; + int index = animations.size() - 1; + if (currentAnimationIndex == index) + activateCurrentAnimation(); + else + setCurrentAnimation(index); + } +} + +/*! + \internal + This manages advancing the execution of a group running forwards (time has gone forward), + which is the same behaviour for rewinding the execution of a group running backwards + (time has gone backward). +*/ +void QSequentialAnimationGroupPrivate::advanceForwards(const AnimationIndex &newAnimationIndex) +{ + if (lastIteration < currentIteration) { + // we need to fast forward to the end + for (int i = currentAnimationIndex; i < animations.size(); ++i) { + QAbstractAnimation *anim = animations.at(i); + setCurrentAnimation(i, true); + anim->setCurrentTime(animationActualTotalDuration(i)); + } + // this will make sure the current animation is reset to the beginning + if (animations.size() == 1) + // we need to force activation because setCurrentAnimation will have no effect + activateCurrentAnimation(); + else + setCurrentAnimation(0, true); + } + + // and now we need to fast forward from the current position to + for (int i = currentAnimationIndex; i < newAnimationIndex.index; ++i) { //### WRONG, + QAbstractAnimation *anim = animations.at(i); + setCurrentAnimation(i, true); + anim->setCurrentTime(animationActualTotalDuration(i)); + } + // setting the new current animation will happen later +} + +/*! + \internal + This manages rewinding the execution of a group running forwards (time has gone forward), + which is the same behaviour for advancing the execution of a group running backwards + (time has gone backward). +*/ +void QSequentialAnimationGroupPrivate::rewindForwards(const AnimationIndex &newAnimationIndex) +{ + if (lastIteration > currentIteration) { + // we need to fast rewind to the beginning + for (int i = currentAnimationIndex; i >= 0 ; --i) { + QAbstractAnimation *anim = animations.at(i); + setCurrentAnimation(i, true); + anim->setCurrentTime(0); + } + // this will make sure the current animation is reset to the end + if (animations.size() == 1) + // we need to force activation because setCurrentAnimation will have no effect + activateCurrentAnimation(); + else + setCurrentAnimation(animations.count() - 1, true); + } + + // and now we need to fast rewind from the current position to + for (int i = currentAnimationIndex; i > newAnimationIndex.index; --i) { + QAbstractAnimation *anim = animations.at(i); + setCurrentAnimation(i, true); + anim->setCurrentTime(0); + } + // setting the new current animation will happen later +} + +/*! + \fn QSequentialAnimationGroup::currentAnimationChanged(QAbstractAnimation *current) + + QSequentialAnimationGroup emits this signal when currentAnimation + has been changed. \a current is the current animation. + + \sa currentAnimation() +*/ + + +/*! + Constructs a QSequentialAnimationGroup. + \a parent is passed to QObject's constructor. +*/ +QSequentialAnimationGroup::QSequentialAnimationGroup(QObject *parent) + : QAnimationGroup(*new QSequentialAnimationGroupPrivate, parent) +{ +} + +/*! + \internal +*/ +QSequentialAnimationGroup::QSequentialAnimationGroup(QSequentialAnimationGroupPrivate &dd, + QObject *parent) + : QAnimationGroup(dd, parent) +{ +} + +/*! + Destroys the animation group. It will also destroy all its animations. +*/ +QSequentialAnimationGroup::~QSequentialAnimationGroup() +{ +} + +/*! + Adds a pause of \a msecs to this animation group. + The pause is considered as a special type of animation, thus count() will be + increased by one. + \sa insertPauseAt(), QAnimationGroup::addAnimation() +*/ +QPauseAnimation *QSequentialAnimationGroup::addPause(int msecs) +{ + QPauseAnimation *pause = new QPauseAnimation(msecs); + addAnimation(pause); + return pause; +} + +/*! + Inserts a pause of \a msecs milliseconds at \a index in this animation + group. + + \sa addPause(), QAnimationGroup::insertAnimationAt() +*/ +QPauseAnimation *QSequentialAnimationGroup::insertPauseAt(int index, int msecs) +{ + Q_D(const QSequentialAnimationGroup); + + if (index < 0 || index > d->animations.size()) { + qWarning("QSequentialAnimationGroup::insertPauseAt: index is out of bounds"); + return 0; + } + + QPauseAnimation *pause = new QPauseAnimation(msecs); + insertAnimationAt(index, pause); + return pause; +} + + +/*! + \property QSequentialAnimationGroup::currentAnimation + Returns the animation in the current time. + + \sa currentAnimationChanged() +*/ +QAbstractAnimation *QSequentialAnimationGroup::currentAnimation() const +{ + Q_D(const QSequentialAnimationGroup); + return d->currentAnimation; +} + +/*! + \reimp +*/ +int QSequentialAnimationGroup::duration() const +{ + Q_D(const QSequentialAnimationGroup); + int ret = 0; + + for (int i = 0; i < d->animations.size(); ++i) { + QAbstractAnimation *animation = d->animations.at(i); + const int currentDuration = animation->totalDuration(); + if (currentDuration == -1) + return -1; // Undetermined length + + ret += currentDuration; + } + + return ret; +} + +/*! + \reimp +*/ +void QSequentialAnimationGroup::updateCurrentTime(int msecs) +{ + Q_D(QSequentialAnimationGroup); + if (!d->currentAnimation) + return; + + const QSequentialAnimationGroupPrivate::AnimationIndex newAnimationIndex = d->indexForTime(msecs); + + // remove unneeded animations from actualDuration list + while (newAnimationIndex.index < d->actualDuration.size()) + d->actualDuration.removeLast(); + + // newAnimationIndex.index is the new current animation + if (d->lastIteration < d->currentIteration + || (d->lastIteration == d->currentIteration && d->currentAnimationIndex < newAnimationIndex.index)) { + // advancing with forward direction is the same as rewinding with backwards direction + d->advanceForwards(newAnimationIndex); + } else if (d->lastIteration > d->currentIteration + || (d->lastIteration == d->currentIteration && d->currentAnimationIndex > newAnimationIndex.index)) { + // rewinding with forward direction is the same as advancing with backwards direction + d->rewindForwards(newAnimationIndex); + } + + d->setCurrentAnimation(newAnimationIndex.index); + + const int newCurrentTime = msecs - newAnimationIndex.timeOffset; + + if (d->currentAnimation) { + d->currentAnimation->setCurrentTime(newCurrentTime); + if (d->atEnd()) { + //we make sure that we don't exceed the duration here + d->currentTime += QAbstractAnimationPrivate::get(d->currentAnimation)->totalCurrentTime - newCurrentTime; + stop(); + } + } else { + //the only case where currentAnimation could be null + //is when all animations have been removed + Q_ASSERT(d->animations.isEmpty()); + d->currentTime = 0; + stop(); + } + + d->lastIteration = d->currentIteration; +} + +/*! + \reimp +*/ +void QSequentialAnimationGroup::updateState(QAbstractAnimation::State oldState, + QAbstractAnimation::State newState) +{ + Q_D(QSequentialAnimationGroup); + QAnimationGroup::updateState(oldState, newState); + + if (!d->currentAnimation) + return; + + switch (newState) { + case Stopped: + d->currentAnimation->stop(); + break; + case Paused: + if (oldState == d->currentAnimation->state() + && oldState == QSequentialAnimationGroup::Running) { + d->currentAnimation->pause(); + } + else + d->restart(); + break; + case Running: + if (oldState == d->currentAnimation->state() + && oldState == QSequentialAnimationGroup::Paused) + d->currentAnimation->start(); + else + d->restart(); + break; + } +} + +/*! + \reimp +*/ +void QSequentialAnimationGroup::updateDirection(QAbstractAnimation::Direction direction) +{ + Q_D(QSequentialAnimationGroup); + // we need to update the direction of the current animation + if (state() != Stopped && d->currentAnimation) + d->currentAnimation->setDirection(direction); +} + +/*! + \reimp +*/ +bool QSequentialAnimationGroup::event(QEvent *event) +{ + return QAnimationGroup::event(event); +} + +void QSequentialAnimationGroupPrivate::setCurrentAnimation(int index, bool intermediate) +{ + Q_Q(QSequentialAnimationGroup); + + index = qMin(index, animations.count() - 1); + + if (index == -1) { + Q_ASSERT(animations.isEmpty()); + currentAnimationIndex = -1; + currentAnimation = 0; + return; + } + + // need these two checks below because this func can be called after the current animation + // has been removed + if (index == currentAnimationIndex && animations.at(index) == currentAnimation) + return; + + // stop the old current animation + if (currentAnimation) + currentAnimation->stop(); + + currentAnimation = animations.at(index); + currentAnimationIndex = index; + + emit q->currentAnimationChanged(currentAnimation); + + activateCurrentAnimation(intermediate); +} + +void QSequentialAnimationGroupPrivate::activateCurrentAnimation(bool intermediate) +{ + Q_Q(QSequentialAnimationGroup); + + if (!currentAnimation) + return; + + if (state == QSequentialAnimationGroup::Stopped) + return; + + currentAnimation->stop(); + + // we ensure the direction is consistent with the group's direction + currentAnimation->setDirection(direction); + + // connects to the finish signal of uncontrolled animations + if (currentAnimation->totalDuration() == -1) + QObject::connect(currentAnimation, SIGNAL(finished()), q, SLOT(_q_uncontrolledAnimationFinished())); + + currentAnimation->start(); + if (!intermediate && state == QSequentialAnimationGroup::Paused) + currentAnimation->pause(); +} + +void QSequentialAnimationGroupPrivate::_q_uncontrolledAnimationFinished() +{ + Q_Q(QSequentialAnimationGroup); + Q_ASSERT(qobject_cast<QAbstractAnimation *>(q->sender()) == currentAnimation); + + // we trust the duration returned by the animation + while (actualDuration.size() < (currentAnimationIndex + 1)) + actualDuration.append(-1); + actualDuration[currentAnimationIndex] = currentAnimation->currentTime(); + + QObject::disconnect(currentAnimation, SIGNAL(finished()), q, SLOT(_q_uncontrolledAnimationFinished())); + + if ((direction == QAbstractAnimation::Forward && currentAnimation == animations.last()) + || (direction == QAbstractAnimation::Backward && currentAnimationIndex == 0)) { + // we don't handle looping of a group with undefined duration + q->stop(); + } else if (direction == QAbstractAnimation::Forward) { + // set the current animation to be the next one + setCurrentAnimation(currentAnimationIndex + 1); + } else { + // set the current animation to be the previous one + setCurrentAnimation(currentAnimationIndex - 1); + } +} + +/*! + \internal + This method is called whenever an animation is added to + the group at index \a index. + Note: We only support insertion after the current animation +*/ +void QSequentialAnimationGroupPrivate::animationInsertedAt(int index) +{ + if (currentAnimation == 0) + setCurrentAnimation(0); // initialize the current animation + + if (currentAnimationIndex == index + && currentAnimation->currentTime() == 0 && currentAnimation->currentIteration() == 0) { + //in this case we simply insert an animation before the current one has actually started + setCurrentAnimation(index); + } + + //we update currentAnimationIndex in case it has changed (the animation pointer is still valid) + currentAnimationIndex = animations.indexOf(currentAnimation); + + if (index < currentAnimationIndex || currentIteration != 0) { + qWarning("QSequentialGroup::insertAnimationAt only supports to add animations after the current one."); + return; //we're not affected because it is added after the current one + } +} + +/*! + \internal + This method is called whenever an animation is removed from + the group at index \a index. The animation is no more listed when this + method is called. +*/ +void QSequentialAnimationGroupPrivate::animationRemovedAt(int index) +{ + Q_Q(QSequentialAnimationGroup); + QAnimationGroupPrivate::animationRemovedAt(index); + + Q_ASSERT(currentAnimation); // currentAnimation should always be set + + if (actualDuration.size() > index) + actualDuration.removeAt(index); + + const int currentIndex = animations.indexOf(currentAnimation); + if (currentIndex == -1) { + //we're removing the current animation, let's update it to another one + if (index < animations.count()) + setCurrentAnimation(index); //let's try to take the next one + else if (index > 0) + setCurrentAnimation(index - 1); + else// case all animations were removed + setCurrentAnimation(-1); + } else if (currentAnimationIndex > index) { + currentAnimationIndex--; + } + + // duration of the previous animations up to the current animation + currentTime = 0; + for (int i = 0; i < currentAnimationIndex; ++i) { + const int current = animationActualTotalDuration(i); + currentTime += current; + } + + if (currentIndex != -1) { + //the current animation is not the one being removed + //so we add its current time to the current time of this group + currentTime += QAbstractAnimationPrivate::get(currentAnimation)->totalCurrentTime; + } + + //let's also update the total current time + totalCurrentTime = currentTime + iterationCount * q->duration(); +} + +QT_END_NAMESPACE + +#include "moc_qsequentialanimationgroup.cpp" + +#endif //QT_NO_ANIMATION diff --git a/src/corelib/animation/qsequentialanimationgroup.h b/src/corelib/animation/qsequentialanimationgroup.h new file mode 100644 index 0000000..4c52d1b --- /dev/null +++ b/src/corelib/animation/qsequentialanimationgroup.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSEQUENTIALANIMATIONGROUP_H +#define QSEQUENTIALANIMATIONGROUP_H + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qanimationgroup.h" +#else +# include <QtCore/qanimationgroup.h> +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_ANIMATION + +class QPauseAnimation; +class QSequentialAnimationGroupPrivate; + +class Q_CORE_EXPORT QSequentialAnimationGroup : public QAnimationGroup +{ + Q_OBJECT + Q_PROPERTY(QAbstractAnimation* currentAnimation READ currentAnimation NOTIFY currentAnimationChanged) + +public: + QSequentialAnimationGroup(QObject *parent = 0); + ~QSequentialAnimationGroup(); + + QPauseAnimation *addPause(int msecs); + QPauseAnimation *insertPauseAt(int index, int msecs); + + QAbstractAnimation *currentAnimation() const; + int duration() const; + +Q_SIGNALS: + void currentAnimationChanged(QAbstractAnimation *current); + +protected: + QSequentialAnimationGroup(QSequentialAnimationGroupPrivate &dd, QObject *parent); + bool event(QEvent *event); + + void updateCurrentTime(int msecs); + void updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState); + void updateDirection(QAbstractAnimation::Direction direction); + +private: + Q_DISABLE_COPY(QSequentialAnimationGroup) + Q_DECLARE_PRIVATE(QSequentialAnimationGroup) + Q_PRIVATE_SLOT(d_func(), void _q_uncontrolledAnimationFinished()) +}; + +#endif //QT_NO_ANIMATION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QSEQUENTIALANIMATIONGROUP_H diff --git a/src/corelib/animation/qsequentialanimationgroup_p.h b/src/corelib/animation/qsequentialanimationgroup_p.h new file mode 100644 index 0000000..05d2a40 --- /dev/null +++ b/src/corelib/animation/qsequentialanimationgroup_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSEQUENTIALANIMATIONGROUP_P_H +#define QSEQUENTIALANIMATIONGROUP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QIODevice. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qsequentialanimationgroup.h" +#include "qanimationgroup_p.h" + + +QT_BEGIN_NAMESPACE + +class QSequentialAnimationGroupPrivate : public QAnimationGroupPrivate +{ + Q_DECLARE_PUBLIC(QSequentialAnimationGroup) +public: + QSequentialAnimationGroupPrivate() + : currentAnimation(0), currentAnimationIndex(-1), lastIteration(0) + { } + + + struct AnimationIndex + { + AnimationIndex() : index(0), timeOffset(0) {} + // index points to the animation at timeOffset, skipping 0 duration animations. + // Note that the index semantic is slightly different depending on the direction. + int index; // the index of the animation in timeOffset + int timeOffset; // time offset when the animation at index starts. + }; + + int animationActualTotalDuration(int index) const; + AnimationIndex indexForTime(int msecs) const; + + void setCurrentAnimation(int index, bool intermediate = false); + void activateCurrentAnimation(bool intermediate = false); + + void animationInsertedAt(int index); + void animationRemovedAt(int index); + + bool atEnd() const; + + QAbstractAnimation *currentAnimation; + int currentAnimationIndex; + + // this is the actual duration of uncontrolled animations + // it helps seeking and even going forward + QList<int> actualDuration; + + void restart(); + int lastIteration; + + // handle time changes + void rewindForwards(const AnimationIndex &newAnimationIndex); + void advanceForwards(const AnimationIndex &newAnimationIndex); + + // private slot + void _q_uncontrolledAnimationFinished(); +}; + +QT_END_NAMESPACE + +#endif //QSEQUENTIALANIMATIONGROUP_P_H diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp new file mode 100644 index 0000000..6b162ae --- /dev/null +++ b/src/corelib/animation/qvariantanimation.cpp @@ -0,0 +1,599 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_ANIMATION + +#include "qvariantanimation.h" +#include "qvariantanimation_p.h" + +#include <QtCore/QRect> +#include <QtCore/QLineF> +#include <QtCore/QLine> +#include <QtCore/QReadWriteLock> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QVariantAnimation + \ingroup animation + \brief The QVariantAnimation class provides an abstract base class for animations. + \since 4.5 + \preliminary + + This class is part of {The Animation Framework}. It serves as a base class + for property and item animations, with functions for shared functionality. + + If you want to create an animation, you should look at QPropertyAnimation instead. + + You can then set start and end values for the property by calling + setStartValue() and setEndValue(), and finally call start() to + start the animation. When control goes back to the event loop, QVariantAnimation + will interpolate the property of the target object and emit the valueChanged + signal. To react to a change in the current value you have to reimplement the + updateCurrentValue virtual method. + + There are two ways to affect how QVariantAnimation interpolates the values. + You can set an easing curve by calling setEasingCurve(), and configure the + duration by calling setDuration(). You can change how the QVariants are + interpolated by creating a subclass of QVariantAnimation, and reimplementing the + virtual interpolated() function. + + + \sa QPropertyAnimation, {The Animation Framework} +*/ + +/*! + \fn void QVariantAnimation::valueChanged(const QVariant &value) + + QVariantAnimation emits this signal whenever the current \a value changes. + + \sa currentValue, startValue, endValue +*/ + +static bool animationValueLessThan(const QVariantAnimation::KeyValue &p1, const QVariantAnimation::KeyValue &p2) +{ + return p1.first < p2.first; +} + +template<> Q_INLINE_TEMPLATE QRect _q_interpolate(const QRect &f, const QRect &t, qreal progress) +{ + QRect ret; + ret.setCoords(_q_interpolate(f.left(), t.left(), progress), + _q_interpolate(f.top(), t.top(), progress), + _q_interpolate(f.right(), t.right(), progress), + _q_interpolate(f.bottom(), t.bottom(), progress)); + return ret; +} + +template<> Q_INLINE_TEMPLATE QRectF _q_interpolate(const QRectF &f, const QRectF &t, qreal progress) +{ + qreal x1, y1, w1, h1; + f.getRect(&x1, &y1, &w1, &h1); + qreal x2, y2, w2, h2; + t.getRect(&x2, &y2, &w2, &h2); + return QRectF( _q_interpolate(x1, x2, progress), _q_interpolate(y1, y2, progress), + _q_interpolate(w1, w2, progress), _q_interpolate(h1, h2, progress)); +} + +template<> Q_INLINE_TEMPLATE QLine _q_interpolate(const QLine &f, const QLine &t, qreal progress) +{ + return QLine( _q_interpolate(f.p1(), t.p1(), progress), _q_interpolate(f.p2(), t.p2(), progress)); +} + +template<> Q_INLINE_TEMPLATE QLineF _q_interpolate(const QLineF &f, const QLineF &t, qreal progress) +{ + return QLineF( _q_interpolate(f.p1(), t.p1(), progress), _q_interpolate(f.p2(), t.p2(), progress)); +} + +void QVariantAnimationPrivate::convertValues(int t) +{ + //this ensures that all the keyValues are of type t + for (int i = 0; i < keyValues.count(); ++i) { + QVariantAnimation::KeyValue &pair = keyValues[i]; + if (pair.second.userType() != t) + pair.second.convert(static_cast<QVariant::Type>(t)); + } + currentInterval.start.first = 2; // this will force the refresh + interpolator = 0; // if the type changed we need to update the interpolator +} + +/*! + \fn void QVariantAnimation::updateCurrentValue(const QVariant &value) = 0; + This pure virtual function is called when the animated value is changed. + \a value is the new value. +*/ + +void QVariantAnimationPrivate::updateCurrentValue() +{ + Q_Q(QVariantAnimation); + const qreal progress = easing.valueForProgress(((duration == 0) ? qreal(1) : qreal(currentTime) / qreal(duration))); + + if (progress < currentInterval.start.first || progress > currentInterval.end.first) { + //let's update currentInterval + QVariantAnimation::KeyValues::const_iterator itStart = qLowerBound(keyValues.constBegin(), keyValues.constEnd(), qMakePair(progress, QVariant()), animationValueLessThan); + QVariantAnimation::KeyValues::const_iterator itEnd = itStart; + + // If we are at the end we should continue to use the last keyValues in case of extrapolation (progress > 1.0). + // This is because the easing function can return a value slightly outside the range [0, 1] + if (itStart != keyValues.constEnd()) { + + //this can't happen because we always prepend the default start value there + if (itStart == keyValues.begin()) { + ++itEnd; + if (itEnd == keyValues.constEnd()) + return; //there is no upper bound + } else { + --itStart; + } + + //update all the values of the currentInterval + currentInterval.start = *itStart; + currentInterval.end = *itEnd; + } + } + + const qreal startProgress = currentInterval.start.first, + endProgress = currentInterval.end.first; + const qreal localProgress = (progress - startProgress) / (endProgress - startProgress); + + bool changed = false; + { + //we do that here in a limited scope so that ret is dereferenced and frees memory + //and the call to updateCurrentValue can recreate QVariant of the same type (for ex. in + //QGraphicsItem::setPos + QVariant ret = q->interpolated(currentInterval.start.second, + currentInterval.end.second, + localProgress); + if (currentValue != ret) { + changed = true; + qSwap(currentValue, ret); + } + } + + if (changed) { + //the value has changed + q->updateCurrentValue(currentValue); +#ifndef QT_EXPERIMENTAL_SOLUTION + if (connectedSignals & changedSignalMask) +#endif + emit q->valueChanged(currentValue); + } +} + + +QVariant QVariantAnimationPrivate::valueAt(qreal step) const +{ + QVariantAnimation::KeyValues::const_iterator result = + qBinaryFind(keyValues.begin(), keyValues.end(), qMakePair(step, QVariant()), animationValueLessThan); + if (result != keyValues.constEnd()) + return result->second; + + return QVariant(); +} + + +void QVariantAnimationPrivate::setValueAt(qreal step, const QVariant &value) +{ + if (step < qreal(0.0) || step > qreal(1.0)) { + qWarning("QVariantAnimation::setValueAt: invalid step = %f", step); + return; + } + + QVariantAnimation::KeyValue pair(step, value); + + QVariantAnimation::KeyValues::iterator result = qLowerBound(keyValues.begin(), keyValues.end(), pair, animationValueLessThan); + if (result == keyValues.end() || result->first != step) { + keyValues.insert(result, pair); + } else { + if (value.isValid()) + result->second = value; //remove the previous value + else if (step == 0 && !hasStartValue && defaultStartValue.isValid()) + result->second = defaultStartValue; //we reset to the default start value + else + keyValues.erase(result); //replace the previous value + } + + currentInterval.start.first = 2; // this will force the refresh + updateCurrentValue(); +} + +void QVariantAnimationPrivate::setDefaultStartValue(const QVariant &value) +{ + defaultStartValue = value; + if (!hasStartValue) + setValueAt(0, value); +} + +/*! + Construct a QVariantAnimation object. \a parent is passed to QAbstractAnimation's + constructor. +*/ +QVariantAnimation::QVariantAnimation(QObject *parent) : QAbstractAnimation(*new QVariantAnimationPrivate, parent) +{ + d_func()->init(); +} + +/*! + \internal +*/ +QVariantAnimation::QVariantAnimation(QVariantAnimationPrivate &dd, QObject *parent) : QAbstractAnimation(dd, parent) +{ + d_func()->init(); +} + +/*! + Destroys the animation. +*/ +QVariantAnimation::~QVariantAnimation() +{ +} + +/*! + \property QVariantAnimation::easingCurve + \brief the easing curve of the animation + + This property defines the easing curve of the animation. By default, a + linear easing curve is used, resulting in linear interpolation of the + end property. For many animations, it's useful to try different easing + curves, including QEasingCurve::InCirc, which provides a circular entry curve, + and QEasingCurve::InOutElastic, which provides an elastic effect on the values + of the interpolated property. + + The easing curve is used with the interpolator, the interpolated() virtual + function, the animation's duration, and iterationCount, to control how the + current value changes as the animation progresses. +*/ +QEasingCurve QVariantAnimation::easingCurve() const +{ + Q_D(const QVariantAnimation); + return d->easing; +} + +void QVariantAnimation::setEasingCurve(const QEasingCurve &easing) +{ + Q_D(QVariantAnimation); + d->easing = easing; + d->updateCurrentValue(); +} + +Q_GLOBAL_STATIC(QVector<QVariantAnimation::Interpolator>, registeredInterpolators) +Q_GLOBAL_STATIC(QReadWriteLock, registeredInterpolatorsLock) + +/*! + \fn void qRegisterAnimationInterpolator(QVariant (*func)(const T &from, const T &to, qreal progress)) + \relates QVariantAnimation + \threadsafe + + Registers a custom interpolator \a func for the template type \c{T}. + The interpolator has to be registered before the animation is constructed. + To unregister (and use the default interpolator) set \a func to 0. + */ + +/*! + \internal + \typedef QVariantAnimation::Interpolator + + This is a typedef for a pointer to a function with the following + signature: + \code + QVariant myInterpolator(const QVariant &from, const QVariant &to, qreal progress); + \endcode + +*/ + +/*! \internal + * Registers a custom interpolator \a func for the specific \a interpolationType. + * The interpolator has to be registered before the animation is constructed. + * To unregister (and use the default interpolator) set \a func to 0. + */ +void QVariantAnimation::registerInterpolator(QVariantAnimation::Interpolator func, int interpolationType) +{ + // will override any existing interpolators + QWriteLocker locker(registeredInterpolatorsLock()); + if (int(interpolationType) >= registeredInterpolators()->count()) + registeredInterpolators()->resize(int(interpolationType) + 1); + registeredInterpolators()->replace(interpolationType, func); +} + + +template<typename T> static inline QVariantAnimation::Interpolator castToInterpolator(QVariant (*func)(const T &from, const T &to, qreal progress)) +{ + return reinterpret_cast<QVariantAnimation::Interpolator>(func); +} + +static QVariantAnimation::Interpolator getInterpolator(int interpolationType) +{ + QReadLocker locker(registeredInterpolatorsLock()); + QVariantAnimation::Interpolator ret = 0; + if (interpolationType < registeredInterpolators()->count()) { + ret = registeredInterpolators()->at(interpolationType); + if (ret) return ret; + } + + switch(interpolationType) + { + case QMetaType::Int: + return castToInterpolator(_q_interpolateVariant<int>); + case QMetaType::Double: + return castToInterpolator(_q_interpolateVariant<double>); + case QMetaType::Float: + return castToInterpolator(_q_interpolateVariant<float>); + case QMetaType::QLine: + return castToInterpolator(_q_interpolateVariant<QLine>); + case QMetaType::QLineF: + return castToInterpolator(_q_interpolateVariant<QLineF>); + case QMetaType::QPoint: + return castToInterpolator(_q_interpolateVariant<QPoint>); + case QMetaType::QPointF: + return castToInterpolator(_q_interpolateVariant<QPointF>); + case QMetaType::QSize: + return castToInterpolator(_q_interpolateVariant<QSize>); + case QMetaType::QSizeF: + return castToInterpolator(_q_interpolateVariant<QSizeF>); + case QMetaType::QRect: + return castToInterpolator(_q_interpolateVariant<QRect>); + case QMetaType::QRectF: + return castToInterpolator(_q_interpolateVariant<QRectF>); + default: + return 0; //this type is not handled + } +} + +/*! + \property QVariantAnimation::duration + \brief the duration of the animation + + This property describes the duration of the animation. The default + duration is 250 milliseconds. + + \sa QAbstractAnimation::duration() + */ +int QVariantAnimation::duration() const +{ + Q_D(const QVariantAnimation); + return d->duration; +} + +void QVariantAnimation::setDuration(int msecs) +{ + Q_D(QVariantAnimation); + if (msecs < 0) { + qWarning("QVariantAnimation::setDuration: cannot set a negative duration"); + return; + } + if (d->duration == msecs) + return; + d->duration = msecs; + d->updateCurrentValue(); +} + +/*! + \property QVariantAnimation::startValue + \brief the optional start value of the animation + + This property describes the optional start value of the animation. If + omitted, or if a null QVariant is assigned as the start value, the + animation will use the current position of the end when the animation + is started. + + \sa endValue +*/ +QVariant QVariantAnimation::startValue() const +{ + return keyValueAt(0); +} + +void QVariantAnimation::setStartValue(const QVariant &value) +{ + setKeyValueAt(0, value); +} + +/*! + \property QVariantAnimation::endValue + \brief the end value of the animation + + This property describes the end value of the animation. + + \sa startValue + */ +QVariant QVariantAnimation::endValue() const +{ + return keyValueAt(1); +} + +void QVariantAnimation::setEndValue(const QVariant &value) +{ + setKeyValueAt(1, value); +} + + +/*! + Returns the key frame value for the given \a step. The given \a step + must be in the range 0 to 1. If there is no KeyValue for \a step, + it returns an invalid QVariant. + + \sa keyValues(), setKeyValueAt() +*/ +QVariant QVariantAnimation::keyValueAt(qreal step) const +{ + Q_D(const QVariantAnimation); + if (step == 0 && !d->hasStartValue) + return QVariant(); //special case where we don't have an explicit startValue + + return d->valueAt(step); +} + +/*! + \typedef QVariantAnimation::KeyValue + + This is a typedef for QPair<qreal, QVariant>. +*/ +/*! + \typedef QVariantAnimation::KeyValues + + This is a typedef for QVector<KeyValue> +*/ + +/*! + Creates a key frame at the given \a step with the given \a value. + The given \a step must be in the range 0 to 1. + + \sa setKeyValues(), keyValueAt() +*/ +void QVariantAnimation::setKeyValueAt(qreal step, const QVariant &value) +{ + Q_D(QVariantAnimation); + if (step == 0) + d->hasStartValue = value.isValid(); + d->setValueAt(step, value); +} + +/*! + Returns the key frames of this animation. + + \sa keyValueAt(), setKeyValues() +*/ +QVariantAnimation::KeyValues QVariantAnimation::keyValues() const +{ + Q_D(const QVariantAnimation); + QVariantAnimation::KeyValues ret = d->keyValues; + //in case we added the default start value, we remove it + if (!d->hasStartValue && !ret.isEmpty() && ret.at(0).first == 0) + ret.remove(0); + return ret; +} + +/*! + Replaces the current set of key frames with the given \a keyValues. + the step of the key frames must be in the range 0 to 1. + + \sa keyValues(), keyValueAt() +*/ +void QVariantAnimation::setKeyValues(const KeyValues &keyValues) +{ + Q_D(QVariantAnimation); + d->keyValues = keyValues; + qSort(d->keyValues.begin(), d->keyValues.end(), animationValueLessThan); + d->currentInterval.start.first = 2; // this will force the refresh + d->hasStartValue = !d->keyValues.isEmpty() && d->keyValues.at(0).first == 0; +} + +/*! + \property QVariantAnimation::currentValue + \brief the current value of the animation + + This property describes the current value; an interpolation between the + start value and the end value, using the current time for progress. + + QVariantAnimation calls the virtual updateCurrentValue() function when the + current value changes. This is particularily useful for subclasses that + need to track updates. + + \sa startValue, endValue + */ +QVariant QVariantAnimation::currentValue() const +{ + Q_D(const QVariantAnimation); + if (!d->currentValue.isValid()) + const_cast<QVariantAnimationPrivate*>(d)->updateCurrentValue(); + return d->currentValue; +} + +/*! + \reimp + */ +bool QVariantAnimation::event(QEvent *event) +{ + return QAbstractAnimation::event(event); +} + +/*! + \reimp +*/ +void QVariantAnimation::updateState(QAbstractAnimation::State oldState, + QAbstractAnimation::State newState) +{ + Q_UNUSED(oldState); + Q_UNUSED(newState); + Q_D(QVariantAnimation); + d->currentValue = QVariant(); // this will force the refresh +} + +/*! + This virtual function returns the linear interpolation between variants \a + from and \a to, at \a progress, usually a value between 0 and 1. You can reimplement + this function in a subclass of QVariantAnimation to provide your own interpolation + algorithm. Note that in order for the interpolation to work with a QEasingCurve + that return a value smaller than 0 or larger than 1 (such as QEasingCurve::InBack) + you should make sure that it can extrapolate. If the semantic of the datatype + does not allow extrapolation this function should handle that gracefully. + + \sa QEasingCurve + */ +QVariant QVariantAnimation::interpolated(const QVariant &from, const QVariant &to, qreal progress) const +{ + Q_D(const QVariantAnimation); + if (d->interpolator == 0) { + if (from.userType() == to.userType()) + d->interpolator = getInterpolator(from.userType()); + if (d->interpolator == 0) //no interpolator found + return QVariant(); + } + + return d->interpolator(from.constData(), to.constData(), progress); +} + +/*! + \reimp + */ +void QVariantAnimation::updateCurrentTime(int msecs) +{ + Q_D(QVariantAnimation); + Q_UNUSED(msecs); + d->updateCurrentValue(); +} + +QT_END_NAMESPACE + +#include "moc_qvariantanimation.cpp" + +#endif //QT_NO_ANIMATION diff --git a/src/corelib/animation/qvariantanimation.h b/src/corelib/animation/qvariantanimation.h new file mode 100644 index 0000000..7ce597f --- /dev/null +++ b/src/corelib/animation/qvariantanimation.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANIMATION_H +#define QANIMATION_H + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qabstractanimation.h" +# include "qeasingcurve.h" +#else +# include <QtCore/qeasingcurve.h> +# include <QtCore/qabstractanimation.h> +#endif +#include <QtCore/qlist.h> +#include <QtCore/qpoint.h> +#include <QtCore/qvariant.h> +#include <QtCore/qpair.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_ANIMATION + +class QVariantAnimationPrivate; +class Q_CORE_EXPORT QVariantAnimation : public QAbstractAnimation +{ + Q_OBJECT + Q_PROPERTY(QVariant startValue READ startValue WRITE setStartValue) + Q_PROPERTY(QVariant endValue READ endValue WRITE setEndValue) + Q_PROPERTY(QVariant currentValue READ currentValue NOTIFY currentValueChanged) + Q_PROPERTY(int duration READ duration WRITE setDuration) + Q_PROPERTY(QEasingCurve easingCurve READ easingCurve WRITE setEasingCurve) + +public: + typedef QPair<qreal, QVariant> KeyValue; + typedef QVector<KeyValue> KeyValues; + + QVariantAnimation(QObject *parent = 0); + ~QVariantAnimation(); + + QVariant startValue() const; + void setStartValue(const QVariant &value); + + QVariant endValue() const; + void setEndValue(const QVariant &value); + + QVariant keyValueAt(qreal step) const; + void setKeyValueAt(qreal step, const QVariant &value); + + KeyValues keyValues() const; + void setKeyValues(const KeyValues &values); + + QVariant currentValue() const; + + int duration() const; + void setDuration(int msecs); + + QEasingCurve easingCurve() const; + void setEasingCurve(const QEasingCurve &easing); + + typedef QVariant (*Interpolator)(const void *from, const void *to, qreal progress); + +Q_SIGNALS: + void valueChanged(const QVariant &value); + +protected: + QVariantAnimation(QVariantAnimationPrivate &dd, QObject *parent = 0); + bool event(QEvent *event); + + void updateCurrentTime(int msecs); + void updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState); + + virtual void updateCurrentValue(const QVariant &value) = 0; + virtual QVariant interpolated(const QVariant &from, const QVariant &to, qreal progress) const; + +private: + template <typename T> friend void qRegisterAnimationInterpolator(QVariant (*func)(const T &, const T &, qreal)); + static void registerInterpolator(Interpolator func, int interpolationType); + + Q_DISABLE_COPY(QVariantAnimation) + Q_DECLARE_PRIVATE(QVariantAnimation) +}; + +template <typename T> +static void qRegisterAnimationInterpolator(QVariant (*func)(const T &from, const T &to, qreal progress)) { + QVariantAnimation::registerInterpolator(reinterpret_cast<QVariantAnimation::Interpolator>(func), qMetaTypeId<T>()); +} + +#endif //QT_NO_ANIMATION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QANIMATION_H diff --git a/src/corelib/animation/qvariantanimation_p.h b/src/corelib/animation/qvariantanimation_p.h new file mode 100644 index 0000000..e468ac9 --- /dev/null +++ b/src/corelib/animation/qvariantanimation_p.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANIMATION_P_H +#define QANIMATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QIODevice. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qvariantanimation.h" +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qeasingcurve.h" +#else +# include <QtCore/qeasingcurve.h> +#endif +#include <QtCore/qmetaobject.h> +#include <QtCore/qvector.h> + +#include "qabstractanimation_p.h" + +QT_BEGIN_NAMESPACE + +class Q_CORE_EXPORT QVariantAnimationPrivate : public QAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QVariantAnimation) +public: + + QVariantAnimationPrivate() : duration(250), hasStartValue(false) + { + } + + void init() + { + //we keep the mask so that we emit valueChanged only when needed (for performance reasons) + changedSignalMask = (1 << q_func()->metaObject()->indexOfSignal("valueChanged(QVariant)")); + currentInterval.start.first = currentInterval.end.first = 2; //will force the initial refresh + interpolator = 0; + } + + static QVariantAnimationPrivate *get(QVariantAnimation *q) + { + return q->d_func(); + } + + + //some helper functions + bool atBeginning() const + { + return currentTime == 0; + } + + bool atEnd() const + { + return currentTime == duration && currentIteration == (iterationCount - 1); + } + + void setDefaultStartValue(const QVariant &value); + + int duration; + QEasingCurve easing; + + QVariantAnimation::KeyValues keyValues; + QVariant currentValue; + QVariant defaultStartValue; + bool hasStartValue; + + //this is used to keep track of the KeyValue interval in which we currently are + struct + { + QVariantAnimation::KeyValue start, end; + } currentInterval; + + mutable QVariantAnimation::Interpolator interpolator; + + quint32 changedSignalMask; + + void updateCurrentValue(); + void setValueAt(qreal, const QVariant &); + QVariant valueAt(qreal step) const; + void convertValues(int t); +}; + +//this should make the interpolation faster +template<typename T> inline T _q_interpolate(const T &f, const T &t, qreal progress) +{ + return T(f + (t - f) * progress); +} + +template<typename T > inline QVariant _q_interpolateVariant(const T &from, const T &to, qreal progress) +{ + return _q_interpolate(from, to, progress); +} + + +QT_END_NAMESPACE + +#endif //QANIMATION_P_H diff --git a/src/corelib/arch/qatomic_bootstrap.h b/src/corelib/arch/qatomic_bootstrap.h index 7584b84..af76903 100644 --- a/src/corelib/arch/qatomic_bootstrap.h +++ b/src/corelib/arch/qatomic_bootstrap.h @@ -92,6 +92,20 @@ Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetOrdered(T *expectedValu return false; } +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreRelaxed(T *newValue) +{ + T *currentValue = _q_value; + _q_value = newValue; + return currentValue; +} + +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetRelaxed(T *expectedValue, T *newValue) +{ + return testAndSetOrdered(expectedValue, newValue); +} + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/corelib/corelib.pro b/src/corelib/corelib.pro index f99d57f..db51d43 100644 --- a/src/corelib/corelib.pro +++ b/src/corelib/corelib.pro @@ -5,6 +5,7 @@ DEFINES += QT_BUILD_CORE_LIB QT_NO_USING_NAMESPACE win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x67000000 include(../qbase.pri) +include(animation/animation.pri) include(arch/arch.pri) include(concurrent/concurrent.pri) include(global/global.pri) @@ -14,6 +15,7 @@ include(io/io.pri) include(plugin/plugin.pri) include(kernel/kernel.pri) include(codecs/codecs.pri) +include(statemachine/statemachine.pri) include(xml/xml.pri) mac|darwin:LIBS += -framework ApplicationServices diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 36d87e2..a0ea68c 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -128,6 +128,22 @@ namespace QT_NAMESPACE {} #endif /* __cplusplus */ +/** + * For animation framework to work seamlessly as a solution + */ +#if 0 +# define QT_EXPERIMENTAL_BEGIN_NAMESPACE namespace QtExperimental { +# define QT_EXPERIMENTAL_END_NAMESPACE } +# define QT_EXPERIMENTAL_USE_NAMESPACE using namespace QtExperimental; +# define QT_EXPERIMENTAL_PREPEND_NAMESPACE(name) QtExperimental::name +namespace QtExperimental {} +#else +# define QT_EXPERIMENTAL_BEGIN_NAMESPACE +# define QT_EXPERIMENTAL_END_NAMESPACE +# define QT_EXPERIMENTAL_USE_NAMESPACE +# define QT_EXPERIMENTAL_PREPEND_NAMESPACE(name) name +#endif + #if defined(Q_OS_MAC) && !defined(Q_CC_INTEL) #define QT_BEGIN_HEADER extern "C++" { #define QT_END_HEADER } diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri index d90ecae..b34e59d 100644 --- a/src/corelib/kernel/kernel.pri +++ b/src/corelib/kernel/kernel.pri @@ -12,7 +12,7 @@ HEADERS += \ kernel/qcoreevent.h \ kernel/qmetaobject.h \ kernel/qmetatype.h \ - kernel/qmimedata.h \ + kernel/qmimedata.h \ kernel/qobject.h \ kernel/qobjectdefs.h \ kernel/qsignalmapper.h \ @@ -27,8 +27,8 @@ HEADERS += \ kernel/qvariant_p.h \ kernel/qmetaobject_p.h \ kernel/qobject_p.h \ - kernel/qcoreglobaldata_p.h \ - kernel/qsharedmemory.h \ + kernel/qcoreglobaldata_p.h \ + kernel/qsharedmemory.h \ kernel/qsharedmemory_p.h \ kernel/qsystemsemaphore.h \ kernel/qsystemsemaphore_p.h \ @@ -43,7 +43,7 @@ SOURCES += \ kernel/qcoreevent.cpp \ kernel/qmetaobject.cpp \ kernel/qmetatype.cpp \ - kernel/qmimedata.cpp \ + kernel/qmimedata.cpp \ kernel/qobject.cpp \ kernel/qobjectcleanuphandler.cpp \ kernel/qsignalmapper.cpp \ @@ -51,10 +51,10 @@ SOURCES += \ kernel/qtimer.cpp \ kernel/qtranslator.cpp \ kernel/qvariant.cpp \ - kernel/qcoreglobaldata.cpp \ - kernel/qsharedmemory.cpp \ - kernel/qsystemsemaphore.cpp - + kernel/qcoreglobaldata.cpp \ + kernel/qsharedmemory.cpp \ + kernel/qsystemsemaphore.cpp + win32 { SOURCES += \ kernel/qeventdispatcher_win.cpp \ diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index fa472e6..5e76976 100644 --- a/src/corelib/kernel/qcoreevent.h +++ b/src/corelib/kernel/qcoreevent.h @@ -44,6 +44,7 @@ #include <QtCore/qnamespace.h> #include <QtCore/qbytearray.h> +#include <QtCore/qobjectdefs.h> QT_BEGIN_HEADER @@ -54,7 +55,9 @@ QT_MODULE(Core) class QEventPrivate; class Q_CORE_EXPORT QEvent // event base class { + Q_GADGET QDOC_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) + Q_ENUMS(Type) public: enum Type { /* @@ -266,7 +269,11 @@ public: CocoaRequestModal = 190, // Internal for requesting an application modal Cocoa Window MacGLClearDrawable = 191, // Internal Cocoa, the window has changed, so we must clear - // 512 reserved for Qt Jambi's MetaCall event + Signal = 191, + StateFinished = 192, + Bound = 193, + + // 512 reserved for Qt Jambi's MetaCall event // 513 reserved for Qt Jambi's DeleteOnMainThread event User = 1000, // first user event id diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h index 727a390..0764fe3 100644 --- a/src/corelib/kernel/qvariant_p.h +++ b/src/corelib/kernel/qvariant_p.h @@ -60,6 +60,8 @@ QT_BEGIN_NAMESPACE +extern Q_CORE_EXPORT const QVariant::Handler *qExtendedVariantHandler; + #ifdef Q_CC_SUN // Sun CC picks the wrong overload, so introduce awful hack template <typename T> @@ -68,7 +70,7 @@ inline T *v_cast(const QVariant::Private *nd, T * = 0) QVariant::Private *d = const_cast<QVariant::Private *>(nd); return ((sizeof(T) > sizeof(QVariant::Private::Data)) ? static_cast<T *>(d->data.shared->ptr) - : static_cast<T *>(static_cast<void *>(&d->data.c))); + : reinterpret_cast<T*>(&d->data.c)); } #else // every other compiler in this world @@ -78,7 +80,7 @@ inline const T *v_cast(const QVariant::Private *d, T * = 0) { return ((sizeof(T) > sizeof(QVariant::Private::Data)) ? static_cast<const T *>(d->data.shared->ptr) - : static_cast<const T *>(static_cast<const void *>(&d->data.c))); + : reinterpret_cast<const T *>(&d->data.c)); } template <typename T> @@ -86,37 +88,56 @@ inline T *v_cast(QVariant::Private *d, T * = 0) { return ((sizeof(T) > sizeof(QVariant::Private::Data)) ? static_cast<T *>(d->data.shared->ptr) - : static_cast<T *>(static_cast<void *>(&d->data.c))); + : reinterpret_cast<T *>(&d->data.c)); } #endif +//a simple template that avoids to allocate 2 buffers when creating a QVariant +template <class T> class QVariantPrivateSharedEx : public QVariant::PrivateShared +{ +public: + QVariantPrivateSharedEx(const T&t) : QVariant::PrivateShared(&m_t), m_t(t) { } + +private: + T m_t; +}; + // constructs a new variant if copy is 0, otherwise copy-constructs template <class T> -inline void v_construct(QVariant::Private *x, const void *copy, T * = 0) +inline void v_construct(QVariant::Private *x, const T& t) { + x->type = qMetaTypeId<T>(reinterpret_cast<T *>(0)); if (sizeof(T) > sizeof(QVariant::Private::Data)) { - x->data.shared = copy ? new QVariant::PrivateShared(new T(*static_cast<const T *>(copy))) - : new QVariant::PrivateShared(new T); + x->data.shared = new QVariantPrivateSharedEx<T>(t); x->is_shared = true; } else { - if (copy) - new (&x->data.ptr) T(*static_cast<const T *>(copy)); - else - new (&x->data.ptr) T; + x->is_shared = false; + new (&x->data.ptr) T(t); + } +} + +template <class T> +inline void v_construct(QVariant::Private *x, const void *copy, T * = 0) +{ + if (copy) { + v_construct<T>(x, *reinterpret_cast<const T*>(copy)); + } else { + T t; + v_construct<T>(x, t); } } + // deletes the internal structures template <class T> inline void v_clear(QVariant::Private *d, T* = 0) { - if (sizeof(T) > sizeof(QVariant::Private::Data)) { - delete v_cast<T>(d); + //now we need to call the destructor in any case + //because QVariant::PrivateShared doesn't have a virtual destructor + v_cast<T>(d)->~T(); + if (sizeof(T) > sizeof(QVariant::Private::Data)) delete d->data.shared; - } else { - v_cast<T>(d)->~T(); - } } QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qabstractstate.cpp b/src/corelib/statemachine/qabstractstate.cpp new file mode 100644 index 0000000..1795bd6 --- /dev/null +++ b/src/corelib/statemachine/qabstractstate.cpp @@ -0,0 +1,265 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qabstractstate.h" +#include "qabstractstate_p.h" +#include "qstatemachine.h" +#include "qstatemachine_p.h" +#include "qstate.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QAbstractState + + \brief The QAbstractState class is the base class of states of a QStateMachine. + + \ingroup statemachine + + The QAbstractState class is the abstract base class of states that are part + of a QStateMachine. It defines the interface that all state objects have in + common. QAbstractState is part of \l{The State Machine Framework}. + + The assignProperty() function is used for defining property assignments that + should be performed when a state is entered. + + The parentState() function returns the state's parent state. + + \section1 Subclassing + + The onEntry() function is called when the state is entered; reimplement this + function to perform custom processing when the state is entered. + + The onExit() function is called when the state is exited; reimplement this + function to perform custom processing when the state is exited. +*/ + +/*! + \enum QAbstractState::RestorePolicy + + This enum specifies the restore policy type for a state. The restore policy + takes effect when the machine enters a state which sets one or more + properties. If the restore policy of the state is set to RestoreProperties, + the state machine will save the original value of the property before the + new value is set. + + Later, when the machine either enters a state which has its restore policy + set to DoNotRestoreProperties or when it enters a state which does not set + a value for the given property, the property will automatically be restored + to its initial value. + + Only one initial value will be saved for any given property. If a value for a property has + already been saved by the state machine, it will not be overwritten until the property has been + successfully restored. Once the property has been restored, the state machine will clear the + initial value until it enters a new state which sets the property and which has RestoreProperties + as its restore policy. + + \value GlobalRestorePolicy The restore policy for the state should be retrieved using + QStateMachine::globalRestorePolicy() + \value DoNotRestoreProperties The state machine should not save the initial values of properties + set in the state and restore them later. + \value RestoreProperties The state machine should save the initial values of properties + set in the state and restore them later. + + + \sa setRestorePolicy(), restorePolicy() +*/ + +/*! + \property QAbstractState::restorePolicy + + \brief the restore policy of this state +*/ + +QAbstractStatePrivate::QAbstractStatePrivate() + : restorePolicy(QAbstractState::GlobalRestorePolicy) +{ +} + +QAbstractStatePrivate *QAbstractStatePrivate::get(QAbstractState *q) +{ + return q->d_func(); +} + +const QAbstractStatePrivate *QAbstractStatePrivate::get(const QAbstractState *q) +{ + return q->d_func(); +} + +QStateMachine *QAbstractStatePrivate::machine() const +{ + Q_Q(const QAbstractState); + QObject *par = q->parent(); + while (par != 0) { + if (QStateMachine *mach = qobject_cast<QStateMachine*>(par)) + return mach; + par = par->parent(); + } + return 0; +} + +void QAbstractStatePrivate::callOnEntry() +{ + Q_Q(QAbstractState); + q->onEntry(); +} + +void QAbstractStatePrivate::callOnExit() +{ + Q_Q(QAbstractState); + q->onExit(); +} + +/*! + Constructs a new state with the given \a parent state. +*/ +QAbstractState::QAbstractState(QState *parent) + : QObject( +#ifndef QT_STATEMACHINE_SOLUTION + *new QAbstractStatePrivate, +#endif + parent) +#ifdef QT_STATEMACHINE_SOLUTION + , d_ptr(new QAbstractStatePrivate) +#endif +{ +#ifdef QT_STATEMACHINE_SOLUTION + d_ptr->q_ptr = this; +#endif +} + +/*! + \internal +*/ +QAbstractState::QAbstractState(QAbstractStatePrivate &dd, QState *parent) + : QObject( +#ifndef QT_STATEMACHINE_SOLUTION + dd, +#endif + parent) +#ifdef QT_STATEMACHINE_SOLUTION + , d_ptr(&dd) +#endif +{ +#ifdef QT_STATEMACHINE_SOLUTION + d_ptr->q_ptr = this; +#endif +} + +/*! + Destroys this state. +*/ +QAbstractState::~QAbstractState() +{ +#ifdef QT_STATEMACHINE_SOLUTION + delete d_ptr; +#endif +} + +/*! + Returns this state's parent state, or 0 if the state has no parent state. +*/ +QState *QAbstractState::parentState() const +{ + return qobject_cast<QState*>(parent()); +} + +/*! + Instructs this state to set the property with the given \a name of the given + \a object to the given \a value when the state is entered. +*/ +void QAbstractState::assignProperty(QObject *object, const char *name, + const QVariant &value) +{ + Q_D(QAbstractState); + for (int i = 0; i < d->propertyAssignments.size(); ++i) { + QPropertyAssignment &assn = d->propertyAssignments[i]; + if ((assn.object == object) && (assn.propertyName == name)) { + assn.value = value; + return; + } + } + d->propertyAssignments.append(QPropertyAssignment(object, name, value)); +} + +/*! + Sets the restore policy of this state to \a restorePolicy. + + The default restore policy is QAbstractState::GlobalRestorePolicy. +*/ +void QAbstractState::setRestorePolicy(RestorePolicy restorePolicy) +{ + Q_D(QAbstractState); + d->restorePolicy = restorePolicy; +} + +/*! + Returns the restore policy for this state. +*/ +QAbstractState::RestorePolicy QAbstractState::restorePolicy() const +{ + Q_D(const QAbstractState); + return d->restorePolicy; +} + +/*! + \fn QAbstractState::onExit() + + This function is called when the state is exited. Reimplement this function + to perform custom processing when the state is exited. +*/ + +/*! + \fn QAbstractState::onEntry() + + This function is called when the state is entered. Reimplement this function + to perform custom processing when the state is entered. +*/ + +/*! + \reimp +*/ +bool QAbstractState::event(QEvent *e) +{ + return QObject::event(e); +} + +QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qabstractstate.h b/src/corelib/statemachine/qabstractstate.h new file mode 100644 index 0000000..b788a88 --- /dev/null +++ b/src/corelib/statemachine/qabstractstate.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTSTATE_H +#define QABSTRACTSTATE_H + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QState; + +class QAbstractStatePrivate; +class Q_CORE_EXPORT QAbstractState : public QObject +{ + Q_OBJECT + Q_ENUMS(RestorePolicy) + Q_PROPERTY(RestorePolicy restorePolicy READ restorePolicy WRITE setRestorePolicy) +public: + enum RestorePolicy { + GlobalRestorePolicy, + DoNotRestoreProperties, + RestoreProperties + }; + + ~QAbstractState(); + + QState *parentState() const; + + void assignProperty(QObject *object, const char *name, + const QVariant &value); + + void setRestorePolicy(RestorePolicy restorePolicy); + RestorePolicy restorePolicy() const; + +protected: + QAbstractState(QState *parent = 0); + + virtual void onEntry() = 0; + virtual void onExit() = 0; + + bool event(QEvent *e); + +protected: +#ifdef QT_STATEMACHINE_SOLUTION + QAbstractStatePrivate *d_ptr; +#endif + QAbstractState(QAbstractStatePrivate &dd, QState *parent); + +private: + Q_DISABLE_COPY(QAbstractState) + Q_DECLARE_PRIVATE(QAbstractState) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qabstractstate_p.h b/src/corelib/statemachine/qabstractstate_p.h new file mode 100644 index 0000000..bf44116 --- /dev/null +++ b/src/corelib/statemachine/qabstractstate_p.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTSTATE_P_H +#define QABSTRACTSTATE_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. +// + +#ifndef QT_STATEMACHINE_SOLUTION +#include <private/qobject_p.h> +#endif + +#include <QtCore/qlist.h> + +QT_BEGIN_NAMESPACE + +class QAbstractTransition; +class QHistoryState; +class QStateMachine; + +struct QPropertyAssignment +{ + QPropertyAssignment(QObject *o, const QByteArray &n, + const QVariant &v, bool es = true) + : object(o), propertyName(n), value(v), explicitlySet(es) + {} + QObject *object; + QByteArray propertyName; + QVariant value; + bool explicitlySet; +}; + +class QAbstractState; +class Q_CORE_EXPORT QAbstractStatePrivate +#ifndef QT_STATEMACHINE_SOLUTION + : public QObjectPrivate +#endif +{ + Q_DECLARE_PUBLIC(QAbstractState) + +public: + QAbstractStatePrivate(); + + static QAbstractStatePrivate *get(QAbstractState *q); + static const QAbstractStatePrivate *get(const QAbstractState *q); + + QStateMachine *machine() const; + + void callOnEntry(); + void callOnExit(); + + QAbstractState::RestorePolicy restorePolicy; + QList<QPropertyAssignment> propertyAssignments; + +#ifdef QT_STATEMACHINE_SOLUTION + QAbstractState *q_ptr; +#endif +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qabstracttransition.cpp b/src/corelib/statemachine/qabstracttransition.cpp new file mode 100644 index 0000000..36de7f5 --- /dev/null +++ b/src/corelib/statemachine/qabstracttransition.cpp @@ -0,0 +1,361 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qabstracttransition.h" +#include "qabstracttransition_p.h" +#include "qabstractstate.h" +#include "qstate.h" +#include "qstatemachine.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QAbstractTransition + + \brief The QAbstractTransition class is the base class of transitions between QAbstractState objects. + + \ingroup statemachine + + The QAbstractTransition class is the abstract base class of transitions + between states (QAbstractState objects) of a + QStateMachine. QAbstractTransition is part of \l{The State Machine + Framework}. + + The QTransition class provides a default (action-based) implementation of + the QAbstractTransition interface. + + The sourceState() function returns the source of the transition. The + targetStates() function returns the targets of the transition. + + Transitions can cause animations to be played. Use the addAnimation() + function to add an animation to the transition. + + \section1 Subclassing + + The eventTest() function is called by the state machine to determine whether + an event should trigger the transition. In your reimplementation you + typically check the event type and cast the event object to the proper type, + and check that one or more properties of the event meet your criteria. + + The onTransition() function is called when the transition is triggered; + reimplement this function to perform custom processing for the transition. +*/ + +/*! + \property QAbstractTransition::source + + \brief the source state (parent) of this transition +*/ + +/*! + \property QAbstractTransition::target + + \brief the target state of this transition +*/ + +/*! + \property QAbstractTransition::targets + + \brief the target states of this transition + + If multiple states are specified, all must be descendants of the same + parallel group state. +*/ + +QAbstractTransitionPrivate::QAbstractTransitionPrivate() +{ +} + +QAbstractTransitionPrivate *QAbstractTransitionPrivate::get(QAbstractTransition *q) +{ + return q->d_func(); +} + +const QAbstractTransitionPrivate *QAbstractTransitionPrivate::get(const QAbstractTransition *q) +{ + return q->d_func(); +} + +QStateMachine *QAbstractTransitionPrivate::machine() const +{ + Q_Q(const QAbstractTransition); + QObject *par = q->parent(); + while (par != 0) { + if (QStateMachine *mach = qobject_cast<QStateMachine*>(par)) + return mach; + par = par->parent(); + } + return 0; +} + +bool QAbstractTransitionPrivate::callEventTest(QEvent *e) const +{ + Q_Q(const QAbstractTransition); + return q->eventTest(e); +} + +void QAbstractTransitionPrivate::callOnTransition() +{ + Q_Q(QAbstractTransition); + q->onTransition(); +} + +QState *QAbstractTransitionPrivate::sourceState() const +{ + Q_Q(const QAbstractTransition); + return qobject_cast<QState*>(q->parent()); +} + +/*! + Constructs a new QAbstractTransition object with the given \a sourceState. +*/ +QAbstractTransition::QAbstractTransition(QState *sourceState) + : QObject( +#ifndef QT_STATEMACHINE_SOLUTION + *new QAbstractTransitionPrivate, +#endif + sourceState) +#ifdef QT_STATEMACHINE_SOLUTION + , d_ptr(new QAbstractTransitionPrivate) +#endif +{ +#ifdef QT_STATEMACHINE_SOLUTION + d_ptr->q_ptr = this; +#endif +} + +/*! + Constructs a new QAbstractTransition object with the given \a targets and \a + sourceState. +*/ +QAbstractTransition::QAbstractTransition(const QList<QAbstractState*> &targets, + QState *sourceState) + : QObject( +#ifndef QT_STATEMACHINE_SOLUTION + *new QAbstractTransitionPrivate, +#endif + sourceState) +#ifdef QT_STATEMACHINE_SOLUTION + , d_ptr(new QAbstractTransitionPrivate) +#endif +{ +#ifdef QT_STATEMACHINE_SOLUTION + d_ptr->q_ptr = this; +#endif + Q_D(QAbstractTransition); + d->targetStates = targets; +} + +/*! + \internal +*/ +QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, + QState *parent) + : QObject( +#ifndef QT_STATEMACHINE_SOLUTION + dd, +#endif + parent) +#ifdef QT_STATEMACHINE_SOLUTION + , d_ptr(&dd) +#endif +{ +#ifdef QT_STATEMACHINE_SOLUTION + d_ptr->q_ptr = this; +#endif +} + +/*! + \internal +*/ +QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, + const QList<QAbstractState*> &targets, + QState *parent) + : QObject( +#ifndef QT_STATEMACHINE_SOLUTION + dd, +#endif + parent) +#ifdef QT_STATEMACHINE_SOLUTION + , d_ptr(&dd) +#endif +{ +#ifdef QT_STATEMACHINE_SOLUTION + d_ptr->q_ptr = this; +#endif + Q_D(QAbstractTransition); + d->targetStates = targets; +} + +/*! + Destroys this transition. +*/ +QAbstractTransition::~QAbstractTransition() +{ +#ifdef QT_STATEMACHINE_SOLUTION + delete d_ptr; +#endif +} + +/*! + Returns the source state of this transition, or 0 if this transition has no + source state. +*/ +QState *QAbstractTransition::sourceState() const +{ + Q_D(const QAbstractTransition); + return d->sourceState(); +} + +/*! + Returns the target state of this transition, or 0 if the transition has no + target. +*/ +QAbstractState *QAbstractTransition::targetState() const +{ + Q_D(const QAbstractTransition); + if (d->targetStates.isEmpty()) + return 0; + return d->targetStates.first(); +} + +/*! + Sets the \a target state of this transition. +*/ +void QAbstractTransition::setTargetState(QAbstractState* target) +{ + Q_D(QAbstractTransition); + if (!target) + d->targetStates.clear(); + else + d->targetStates = QList<QAbstractState*>() << target; +} + +/*! + Returns the target states of this transition, or an empty list if this + transition has no target states. +*/ +QList<QAbstractState*> QAbstractTransition::targetStates() const +{ + Q_D(const QAbstractTransition); + return d->targetStates; +} + +/*! + Sets the target states of this transition to be the given \a targets. +*/ +void QAbstractTransition::setTargetStates(const QList<QAbstractState*> &targets) +{ + Q_D(QAbstractTransition); + d->targetStates = targets; +} + +#ifndef QT_NO_ANIMATION + +/*! + Adds the given \a animation to this transition. + The transition does not take ownership of the animation. + + \sa removeAnimation(), animations() +*/ +void QAbstractTransition::addAnimation(QAbstractAnimation *animation) +{ + Q_D(QAbstractTransition); + if (!animation) { + qWarning("QAbstractTransition::addAnimation: cannot add null animation"); + return; + } + d->animations.append(animation); +} + +/*! + Removes the given \a animation from this transition. + + \sa addAnimation() +*/ +void QAbstractTransition::removeAnimation(QAbstractAnimation *animation) +{ + Q_D(QAbstractTransition); + if (!animation) { + qWarning("QAbstractTransition::removeAnimation: cannot remove null animation"); + return; + } + d->animations.removeOne(animation); +} + +/*! + Returns the list of animations associated with this transition, or an empty + list if it has no animations. + + \sa addAnimation() +*/ +QList<QAbstractAnimation*> QAbstractTransition::animations() const +{ + Q_D(const QAbstractTransition); + return d->animations; +} + +#endif + +/*! + \fn QAbstractTransition::eventTest(QEvent *event) const + + This function is called to determine whether the given \a event should cause + this transition to trigger. Reimplement this function and return true if the + event should trigger the transition, otherwise return false. +*/ + +/*! + \fn QAbstractTransition::onTransition() + + This function is called when the transition is triggered. Reimplement this + function to perform custom processing when the transition is triggered. +*/ + +/*! + \reimp +*/ +bool QAbstractTransition::event(QEvent *e) +{ + return QObject::event(e); +} + +QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qabstracttransition.h b/src/corelib/statemachine/qabstracttransition.h new file mode 100644 index 0000000..c49731f --- /dev/null +++ b/src/corelib/statemachine/qabstracttransition.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTTRANSITION_H +#define QABSTRACTTRANSITION_H + +#include <QtCore/qobject.h> + +#include <QtCore/qlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QEvent; +class QAbstractState; +class QState; + +#ifndef QT_NO_ANIMATION +class QAbstractAnimation; +#endif + +class QAbstractTransitionPrivate; +class Q_CORE_EXPORT QAbstractTransition : public QObject +{ + Q_OBJECT + Q_PROPERTY(QState* source READ sourceState) + Q_PROPERTY(QAbstractState* target READ targetState WRITE setTargetState) + Q_PROPERTY(QList<QAbstractState*> targets READ targetStates WRITE setTargetStates) +public: + QAbstractTransition(QState *sourceState = 0); + QAbstractTransition(const QList<QAbstractState*> &targets, QState *sourceState = 0); + virtual ~QAbstractTransition(); + + QState *sourceState() const; + QAbstractState *targetState() const; + void setTargetState(QAbstractState* target); + QList<QAbstractState*> targetStates() const; + void setTargetStates(const QList<QAbstractState*> &targets); + +#ifndef QT_NO_ANIMATION + void addAnimation(QAbstractAnimation *animation); + void removeAnimation(QAbstractAnimation *animation); + QList<QAbstractAnimation*> animations() const; +#endif + +protected: + virtual bool eventTest(QEvent *event) const = 0; + + virtual void onTransition() = 0; + + bool event(QEvent *e); + +protected: +#ifdef QT_STATEMACHINE_SOLUTION + QAbstractTransitionPrivate *d_ptr; +#endif + QAbstractTransition(QAbstractTransitionPrivate &dd, QState *parent); + QAbstractTransition(QAbstractTransitionPrivate &dd, + const QList<QAbstractState*> &targets, QState *parent); + +private: + Q_DISABLE_COPY(QAbstractTransition) + Q_DECLARE_PRIVATE(QAbstractTransition) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qabstracttransition_p.h b/src/corelib/statemachine/qabstracttransition_p.h new file mode 100644 index 0000000..a48a09c --- /dev/null +++ b/src/corelib/statemachine/qabstracttransition_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTTRANSITION_P_H +#define QABSTRACTTRANSITION_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. +// + +#ifndef QT_STATEMACHINE_SOLUTION +#include <private/qobject_p.h> +#endif + +#include <QtCore/qlist.h> + +QT_BEGIN_NAMESPACE + +class QAbstractState; +class QState; +class QStateMachine; + +class QAbstractTransition; +class Q_CORE_EXPORT QAbstractTransitionPrivate +#ifndef QT_STATEMACHINE_SOLUTION + : public QObjectPrivate +#endif +{ + Q_DECLARE_PUBLIC(QAbstractTransition) +public: + QAbstractTransitionPrivate(); + + static QAbstractTransitionPrivate *get(QAbstractTransition *q); + static const QAbstractTransitionPrivate *get(const QAbstractTransition *q); + + bool callEventTest(QEvent *e) const; + void callOnTransition(); + QState *sourceState() const; + QStateMachine *machine() const; + + QList<QAbstractState*> targetStates; + +#ifndef QT_NO_ANIMATION + QList<QAbstractAnimation*> animations; +#endif + +#ifdef QT_STATEMACHINE_SOLUTION + QAbstractTransition *q_ptr; +#endif +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qactionstate.cpp b/src/corelib/statemachine/qactionstate.cpp new file mode 100644 index 0000000..77fd036 --- /dev/null +++ b/src/corelib/statemachine/qactionstate.cpp @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qactionstate.h" +#include "qactionstate_p.h" +#include "qstateaction.h" +#include "qstateaction_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QActionState + + \brief The QActionState class provides an action-based state. + + \ingroup statemachine + + QActionState executes \l{QStateAction}{state actions} when the state is + entered and exited. QActionState is part of \l{The State Machine Framework}. + + You can add actions to a state with the addEntryAction() and addExitAction() + functions. The state executes the actions when the state is entered and + exited, respectively. + + The invokeMethodOnEntry() and invokeMethodOnExit() functions are used for + defining method invocations that should be performed when a state is entered + and exited, respectively. + + \code + QState *s2 = new QState(); + s2->invokeMethodOnEntry(&label, "showMaximized"); + machine.addState(s2); + \endcode + + \sa QStateAction +*/ + +QActionStatePrivate::QActionStatePrivate() +{ +} + +QActionStatePrivate::~QActionStatePrivate() +{ +} + +QActionStatePrivate *QActionStatePrivate::get(QActionState *q) +{ + return q->d_func(); +} + +const QActionStatePrivate *QActionStatePrivate::get(const QActionState *q) +{ + return q->d_func(); +} + +QList<QStateAction*> QActionStatePrivate::entryActions() const +{ + QList<QStateAction*> result; + QList<QObject*>::const_iterator it; +#ifdef QT_STATEMACHINE_SOLUTION + const QObjectList &children = q_func()->children(); +#endif + for (it = children.constBegin(); it != children.constEnd(); ++it) { + QStateAction *act = qobject_cast<QStateAction*>(*it); + if (act && (QStateActionPrivate::get(act)->when == QStateActionPrivate::ExecuteOnEntry)) + result.append(act); + } + return result; +} + +QList<QStateAction*> QActionStatePrivate::exitActions() const +{ + QList<QStateAction*> result; + QList<QObject*>::const_iterator it; +#ifdef QT_STATEMACHINE_SOLUTION + const QObjectList &children = q_func()->children(); +#endif + for (it = children.constBegin(); it != children.constEnd(); ++it) { + QStateAction *act = qobject_cast<QStateAction*>(*it); + if (act && (QStateActionPrivate::get(act)->when == QStateActionPrivate::ExecuteOnExit)) + result.append(act); + } + return result; +} + +/*! + Constructs a new action state with the given \a parent state. +*/ +QActionState::QActionState(QState *parent) + : QAbstractState(*new QActionStatePrivate, parent) +{ +} + +/*! + \internal +*/ +QActionState::QActionState(QActionStatePrivate &dd, + QState *parent) + : QAbstractState(dd, parent) +{ +} + +/*! + Destroys this action state. +*/ +QActionState::~QActionState() +{ +} + +/*! + Instructs this state to invoke the given \a method of the given \a object + with the given \a arguments when the state is entered. This function will + create a QStateInvokeMethodAction object and add it to the entry actions of + the state. + + \sa invokeMethodOnExit(), setPropertyOnEntry(), addEntryAction() +*/ +void QActionState::invokeMethodOnEntry(QObject *object, const char *method, + const QList<QVariant> &arguments) +{ + addEntryAction(new QStateInvokeMethodAction(object, method, arguments)); +} + +/*! + Instructs this state to invoke the given \a method of the given \a object + with the given \a arguments when the state is exited. This function will + create a QStateInvokeMethodAction object and add it to the exit actions of + the state. + + \sa invokeMethodOnEntry(), addExitAction() +*/ +void QActionState::invokeMethodOnExit(QObject *object, const char *method, + const QList<QVariant> &arguments) +{ + addExitAction(new QStateInvokeMethodAction(object, method, arguments)); +} + +/*! + Adds the given \a action to this state. The action will be executed when + this state is entered. The state takes ownership of the action. + + \sa addExitAction(), removeEntryAction() +*/ +void QActionState::addEntryAction(QStateAction *action) +{ + if (!action) { + qWarning("QActionState::addEntryAction: cannot add null action"); + return; + } + action->setParent(this); + QStateActionPrivate::get(action)->when = QStateActionPrivate::ExecuteOnEntry; +} + +/*! + Adds the given \a action to this state. The action will be executed when + this state is exited. The state takes ownership of the action. + + \sa addEntryAction(), removeExitAction() +*/ +void QActionState::addExitAction(QStateAction *action) +{ + if (!action) { + qWarning("QActionState::addExitAction: cannot add null action"); + return; + } + action->setParent(this); + QStateActionPrivate::get(action)->when = QStateActionPrivate::ExecuteOnExit; +} + +/*! + Removes the given entry \a action from this state. The state releases + ownership of the action. + + \sa addEntryAction() +*/ +void QActionState::removeEntryAction(QStateAction *action) +{ + if (!action) { + qWarning("QActionState::removeEntryAction: cannot remove null action"); + return; + } + if (action->parent() == this) + action->setParent(0); +} + +/*! + Removes the given exit \a action from this state. The state releases + ownership of the action. + + \sa addExitAction() +*/ +void QActionState::removeExitAction(QStateAction *action) +{ + if (!action) { + qWarning("QActionState::removeExitAction: cannot remove null action"); + return; + } + if (action->parent() == this) + action->setParent(0); +} + +/*! + Returns this state's entry actions. + + \sa addEntryAction(), exitActions() +*/ +QList<QStateAction*> QActionState::entryActions() const +{ + Q_D(const QActionState); + return d->entryActions(); +} + +/*! + Returns this state's exit actions. + + \sa addExitAction(), entryActions() +*/ +QList<QStateAction*> QActionState::exitActions() const +{ + Q_D(const QActionState); + return d->exitActions(); +} + +/*! + \reimp +*/ +void QActionState::onEntry() +{ + Q_D(QActionState); + QList<QStateAction*> actions = d->entryActions(); + for (int i = 0; i < actions.size(); ++i) + QStateActionPrivate::get(actions.at(i))->callExecute(); +} + +/*! + \reimp +*/ +void QActionState::onExit() +{ + Q_D(QActionState); + QList<QStateAction*> actions = d->exitActions(); + for (int i = 0; i < actions.size(); ++i) + QStateActionPrivate::get(actions.at(i))->callExecute(); +} + +/*! + \reimp +*/ +bool QActionState::event(QEvent *e) +{ + return QAbstractState::event(e); +} + +QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qactionstate.h b/src/corelib/statemachine/qactionstate.h new file mode 100644 index 0000000..517b4b2 --- /dev/null +++ b/src/corelib/statemachine/qactionstate.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QACTIONSTATE_H +#define QACTIONSTATE_H + +#ifndef QT_STATEMACHINE_SOLUTION +#include <QtCore/qabstractstate.h> +#else +#include "qabstractstate.h" +#endif + +#include <QtCore/qlist.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QStateAction; + +class QActionStatePrivate; +class Q_CORE_EXPORT QActionState : public QAbstractState +{ + Q_OBJECT +public: + QActionState(QState *parent = 0); + ~QActionState(); + + void invokeMethodOnEntry(QObject *object, const char *method, + const QList<QVariant> &args = QList<QVariant>()); + void invokeMethodOnExit(QObject *object, const char *method, + const QList<QVariant> &args = QList<QVariant>()); + + void addEntryAction(QStateAction *action); + void addExitAction(QStateAction *action); + + void removeEntryAction(QStateAction *action); + void removeExitAction(QStateAction *action); + + QList<QStateAction*> entryActions() const; + QList<QStateAction*> exitActions() const; + +protected: + void onEntry(); + void onExit(); + + bool event(QEvent *e); + +protected: + QActionState(QActionStatePrivate &dd, QState *parent); + +private: + Q_DISABLE_COPY(QActionState) + Q_DECLARE_PRIVATE(QActionState) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qactionstate_p.h b/src/corelib/statemachine/qactionstate_p.h new file mode 100644 index 0000000..a06dde2 --- /dev/null +++ b/src/corelib/statemachine/qactionstate_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QACTIONSTATE_P_H +#define QACTIONSTATE_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 "qabstractstate_p.h" +#include "qactionstate.h" + +#include <QtCore/qlist.h> + +QT_BEGIN_NAMESPACE + +class QStateAction; + +class QActionState; +class Q_CORE_EXPORT QActionStatePrivate : public QAbstractStatePrivate +{ + Q_DECLARE_PUBLIC(QActionState) + +public: + QActionStatePrivate(); + ~QActionStatePrivate(); + + static QActionStatePrivate *get(QActionState *q); + static const QActionStatePrivate *get(const QActionState *q); + + QList<QStateAction*> entryActions() const; + QList<QStateAction*> exitActions() const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qboundevent_p.h b/src/corelib/statemachine/qboundevent_p.h new file mode 100644 index 0000000..b641ff3 --- /dev/null +++ b/src/corelib/statemachine/qboundevent_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBOUNDEVENT_P_H +#define QBOUNDEVENT_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 <QtCore/qcoreevent.h> + +QT_BEGIN_NAMESPACE + +class QBoundEvent : public QEvent +{ +public: + QBoundEvent(QObject *object, QEvent *event) +#ifdef QT_STATEMACHINE_SOLUTION + : QEvent(QEvent::Type(QEvent::User-3)), +#else + : QEvent(QEvent::Bound), +#endif + m_object(object), m_event(event) {} + ~QBoundEvent() {} + + inline QObject *object() const { return m_object; } + inline QEvent *event() const { return m_event; } + +private: + QObject *m_object; + QEvent *m_event; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qeventtransition.cpp b/src/corelib/statemachine/qeventtransition.cpp new file mode 100644 index 0000000..b03074d --- /dev/null +++ b/src/corelib/statemachine/qeventtransition.cpp @@ -0,0 +1,291 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeventtransition.h" +#include "qeventtransition_p.h" +#include "qboundevent_p.h" +#include "qstate.h" +#include "qstate_p.h" +#include "qstatemachine.h" +#include "qstatemachine_p.h" +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QEventTransition + + \brief The QEventTransition class provides a QObject-specific transition for Qt events. + + \ingroup statemachine + + A QEventTransition object binds an event or transition to a particular + QObject. QEventTransition is part of \l{The State Machine Framework}. + + Example: + + \code + QPushButton *button = ...; + QState *s1 = ...; + QState *s2 = ...; + // If in s1 and the button receives an Enter event, transition to s2 + QEventTransition *enterTransition = new QEventTransition(button, QEvent::Enter); + enterTransition->setTargetState(s2); + s1->addTransition(enterTransition); + // If in s2 and the button receives an Exit event, transition back to s1 + QEventTransition *leaveTransition = new QEventTransition(button, QEvent::Leave); + leaveTransition->setTargetState(s1); + s2->addTransition(leaveTransition); + \endcode + + \section1 Subclassing + + Many event classes have attributes in addition to the event type itself. + The testEventCondition() function can be reimplemented to check attributes + of an event instance in order to determine whether the transition should be + triggered or not. + + \sa QState::addTransition() +*/ + +/*! + \property QEventTransition::object + + \brief the event source that this event transition is associated with +*/ + +/*! + \property QEventTransition::eventType + + \brief the type of event that this event transition is associated with +*/ +QEventTransitionPrivate::QEventTransitionPrivate() +{ + object = 0; + eventType = QEvent::None; + registered = false; +} + +QEventTransitionPrivate *QEventTransitionPrivate::get(QEventTransition *q) +{ + return q->d_func(); +} + +void QEventTransitionPrivate::invalidate() +{ + Q_Q(QEventTransition); + if (registered) { + QState *source = sourceState(); + QStatePrivate *source_d = QStatePrivate::get(source); + QStateMachinePrivate *mach = QStateMachinePrivate::get(source_d->machine()); + if (mach) { + mach->unregisterEventTransition(q); + if (mach->configuration.contains(source)) + mach->registerEventTransition(q); + } + } +} + +/*! + Constructs a new QEventTransition object with the given \a sourceState. +*/ +QEventTransition::QEventTransition(QState *sourceState) + : QTransition(*new QEventTransitionPrivate, sourceState) +{ +} + +/*! + Constructs a new QEventTransition object associated with events of the given + \a type for the given \a object, and with the given \a sourceState. +*/ +QEventTransition::QEventTransition(QObject *object, QEvent::Type type, + QState *sourceState) + : QTransition(*new QEventTransitionPrivate, sourceState) +{ + Q_D(QEventTransition); + d->registered = false; + d->object = object; + d->eventType = type; +} + +/*! + Constructs a new QEventTransition object associated with events of the given + \a type for the given \a object. The transition has the given \a targets and + \a sourceState. +*/ +QEventTransition::QEventTransition(QObject *object, QEvent::Type type, + const QList<QAbstractState*> &targets, + QState *sourceState) + : QTransition(*new QEventTransitionPrivate, targets, sourceState) +{ + Q_D(QEventTransition); + d->registered = false; + d->object = object; + d->eventType = type; +} + +/*! + \internal +*/ +QEventTransition::QEventTransition(QEventTransitionPrivate &dd, QState *parent) + : QTransition(dd, parent) +{ +} + +/*! + \internal +*/ +QEventTransition::QEventTransition(QEventTransitionPrivate &dd, QObject *object, + QEvent::Type type, QState *parent) + : QTransition(dd, parent) +{ + Q_D(QEventTransition); + d->registered = false; + d->object = object; + d->eventType = type; +} + +/*! + \internal +*/ +QEventTransition::QEventTransition(QEventTransitionPrivate &dd, QObject *object, + QEvent::Type type, const QList<QAbstractState*> &targets, + QState *parent) + : QTransition(dd, targets, parent) +{ + Q_D(QEventTransition); + d->registered = false; + d->object = object; + d->eventType = type; +} + +/*! + Destroys this QObject event transition. +*/ +QEventTransition::~QEventTransition() +{ +} + +/*! + Returns the event type that this event transition is associated with. +*/ +QEvent::Type QEventTransition::eventType() const +{ + Q_D(const QEventTransition); + return d->eventType; +} + +/*! + Sets the event \a type that this event transition is associated with. +*/ +void QEventTransition::setEventType(QEvent::Type type) +{ + Q_D(QEventTransition); + if (d->eventType == type) + return; + d->eventType = type; + d->invalidate(); +} + +/*! + Returns the event source associated with this event transition. +*/ +QObject *QEventTransition::eventSource() const +{ + Q_D(const QEventTransition); + return d->object; +} + +/*! + Sets the event source associated with this event transition to be the given + \a object. +*/ +void QEventTransition::setEventSource(QObject *object) +{ + Q_D(QEventTransition); + if (d->object == object) + return; + d->object = object; + d->invalidate(); +} + +/*! + \reimp +*/ +bool QEventTransition::eventTest(QEvent *event) const +{ + Q_D(const QEventTransition); +#ifdef QT_STATEMACHINE_SOLUTION + if (event->type() == QEvent::Type(QEvent::User-3)) { +#else + if (event->type() == QEvent::Bound) { +#endif + QBoundEvent *oe = static_cast<QBoundEvent*>(event); + return (oe->object() == d->object) + && (oe->event()->type() == d->eventType) + && testEventCondition(oe->event()); + } + return false; +} + +/*! + Tests an instance of an event associated with this event transition and + returns true if the transition should be taken, otherwise returns false. + The type of the given \a event will be eventType(). + + Reimplement this function if you have custom conditions associated with + the transition. The default implementation always returns true. +*/ +bool QEventTransition::testEventCondition(QEvent *event) const +{ + Q_UNUSED(event); + return true; +} + +/*! + \reimp +*/ +bool QEventTransition::event(QEvent *e) +{ + return QTransition::event(e); +} + +QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qeventtransition.h b/src/corelib/statemachine/qeventtransition.h new file mode 100644 index 0000000..edd7f8a --- /dev/null +++ b/src/corelib/statemachine/qeventtransition.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEVENTTRANSITION_H +#define QEVENTTRANSITION_H + +#include "qtransition.h" +#include <QtCore/qcoreevent.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QEventTransitionPrivate; +class Q_CORE_EXPORT QEventTransition : public QTransition +{ + Q_OBJECT + Q_PROPERTY(QObject* object READ eventSource WRITE setEventSource) +#ifndef QT_STATEMACHINE_SOLUTION + Q_PROPERTY(QEvent::Type eventType READ eventType WRITE setEventType) +#endif +public: + QEventTransition(QState *sourceState = 0); + QEventTransition(QObject *object, QEvent::Type type, QState *sourceState = 0); + QEventTransition(QObject *object, QEvent::Type type, + const QList<QAbstractState*> &targets, QState *sourceState = 0); + ~QEventTransition(); + + QObject *eventSource() const; + void setEventSource(QObject *object); + + QEvent::Type eventType() const; + void setEventType(QEvent::Type type); + +protected: + virtual bool testEventCondition(QEvent *event) const; // ### name + + bool eventTest(QEvent *event) const; + + bool event(QEvent *e); + +protected: + QEventTransition(QEventTransitionPrivate &dd, QState *parent); + QEventTransition(QEventTransitionPrivate &dd, QObject *object, + QEvent::Type type, QState *parent); + QEventTransition(QEventTransitionPrivate &dd, QObject *object, + QEvent::Type type, const QList<QAbstractState*> &targets, + QState *parent); + +private: + Q_DISABLE_COPY(QEventTransition) + Q_DECLARE_PRIVATE(QEventTransition) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qeventtransition_p.h b/src/corelib/statemachine/qeventtransition_p.h new file mode 100644 index 0000000..568e35e --- /dev/null +++ b/src/corelib/statemachine/qeventtransition_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEVENTTRANSITION_P_H +#define QEVENTTRANSITION_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 "qtransition_p.h" + +QT_BEGIN_NAMESPACE + +class QEventTransition; +class Q_CORE_EXPORT QEventTransitionPrivate : public QTransitionPrivate +{ + Q_DECLARE_PUBLIC(QEventTransition) +public: + QEventTransitionPrivate(); + + static QEventTransitionPrivate *get(QEventTransition *q); + + void invalidate(); + + bool registered; + QObject *object; + QEvent::Type eventType; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qfinalstate.cpp b/src/corelib/statemachine/qfinalstate.cpp new file mode 100644 index 0000000..5219b1b --- /dev/null +++ b/src/corelib/statemachine/qfinalstate.cpp @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfinalstate.h" +#include "qactionstate_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QFinalState + + \brief The QFinalState class provides a final state. + + \ingroup statemachine + + A final state is used to communicate that (part of) a QStateMachine has + finished its work. When a final top-level state is entered, the state + machine's \l{QStateMachine::finished()}{finished}() signal is emitted. In + general, when a final substate (a child of a QState) is entered, a + QStateFinishedEvent is generated for the final state's parent + state. QFinalState is part of \l{The State Machine Framework}. + + To use a final state, you create a QFinalState object and add a transition + to it from another state. Example: + + \code + QPushButton button; + + QStateMachine machine; + QState *s1 = new QState(); + QFinalState *s2 = new QFinalState(); + s1->addTransition(&button, SIGNAL(clicked()), s2); + machine.addState(s1); + machine.addState(s2); + + QObject::connect(&machine, SIGNAL(finished()), QApplication::instance(), SLOT(quit())); + machine.setInitialState(s1); + machine.start(); + \endcode + + \sa QStateFinishedTransition +*/ + +class QFinalStatePrivate : public QActionStatePrivate +{ + Q_DECLARE_PUBLIC(QFinalState) + +public: + QFinalStatePrivate(); +}; + +QFinalStatePrivate::QFinalStatePrivate() +{ +} + +/*! + Constructs a new QFinalState object with the given \a parent state. +*/ +QFinalState::QFinalState(QState *parent) + : QActionState(*new QFinalStatePrivate, parent) +{ +} + +/*! + Destroys this final state. +*/ +QFinalState::~QFinalState() +{ +} + +/*! + \reimp +*/ +void QFinalState::onEntry() +{ + QActionState::onEntry(); +} + +/*! + \reimp +*/ +void QFinalState::onExit() +{ + QActionState::onExit(); +} + +/*! + \reimp +*/ +bool QFinalState::event(QEvent *e) +{ + return QActionState::event(e); +} + +QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qfinalstate.h b/src/corelib/statemachine/qfinalstate.h new file mode 100644 index 0000000..36813f5 --- /dev/null +++ b/src/corelib/statemachine/qfinalstate.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFINALSTATE_H +#define QFINALSTATE_H + +#ifndef QT_STATEMACHINE_SOLUTION +#include <QtCore/qactionstate.h> +#else +#include "qactionstate.h" +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QFinalStatePrivate; +class Q_CORE_EXPORT QFinalState : public QActionState +{ + Q_OBJECT +public: + QFinalState(QState *parent = 0); + ~QFinalState(); + +protected: + void onEntry(); + void onExit(); + + bool event(QEvent *e); + +private: + Q_DISABLE_COPY(QFinalState) + Q_DECLARE_PRIVATE(QFinalState) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qhistorystate.cpp b/src/corelib/statemachine/qhistorystate.cpp new file mode 100644 index 0000000..a0c933c --- /dev/null +++ b/src/corelib/statemachine/qhistorystate.cpp @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhistorystate.h" +#include "qhistorystate_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QHistoryState + + \brief The QHistoryState class provides a means of returning to a previously active substate. + + \ingroup statemachine + + A history state is a pseudo-state that represents the child state that the + parent state was in the last time the parent state was exited. A transition + with a history state as its target is in fact a transition to one of the + other child states of the parent state. QHistoryState is part of \l{The + State Machine Framework}. + + Use QState::addHistoryState() to construct a history state. Use the + setDefaultState() function to set the state that should be entered if the + parent state has never been entered. Example: + + \code + QStateMachine machine; + + QState *s1 = new QState(); + QState *s11 = new QState(s1); + QState *s12 = new QState(s1); + + QState *s1h = s1->addHistoryState(); + s1h->setDefaultState(s11); + + machine.addState(s1); + + QState *s2 = new QState(); + machine.addState(s2); + + QPushButton *button = new QPushButton(); + // Clicking the button will cause the state machine to enter the child state + // that s1 was in the last time s1 was exited, or the history state's default + // state if s1 has never been entered. + s1->addTransition(button, SIGNAL(clicked()), s1h); + \endcode +*/ + +QHistoryStatePrivate::QHistoryStatePrivate() + : defaultState(0) +{ +} + +QHistoryState *QHistoryStatePrivate::create(QState::HistoryType type, + QState *parent) +{ + return new QHistoryState(type, parent); +} + +QHistoryStatePrivate *QHistoryStatePrivate::get(QHistoryState *q) +{ + return q->d_func(); +} + +const QHistoryStatePrivate *QHistoryStatePrivate::get(const QHistoryState *q) +{ + return q->d_func(); +} + +/*! + \internal + + Constructs a new history state of the given \a type, with the given \a + parent state. +*/ +QHistoryState::QHistoryState(QState::HistoryType type, QState *parent) + : QAbstractState(*new QHistoryStatePrivate, parent) +{ + Q_D(QHistoryState); + d->historyType = type; +} + +/*! + Destroys this history state. +*/ +QHistoryState::~QHistoryState() +{ +} + +/*! + Returns this history state's default state. The default state indicates the + state to transition to if the parent state has never been entered before. +*/ +QAbstractState *QHistoryState::defaultState() const +{ + Q_D(const QHistoryState); + return d->defaultState; +} + +/*! + Sets this history state's default state to be the given \a state. + \a state must be a sibling of this history state. +*/ +void QHistoryState::setDefaultState(QAbstractState *state) +{ + Q_D(QHistoryState); + if (state && state->parentState() != parentState()) { + qWarning("QHistoryState::setDefaultState: state %p does not belong " + "to this history state's group (%p)", state, parentState()); + return; + } + d->defaultState = state; +} + +/*! + \reimp +*/ +void QHistoryState::onEntry() +{ +} + +/*! + \reimp +*/ +void QHistoryState::onExit() +{ +} + +/*! + \reimp +*/ +bool QHistoryState::event(QEvent *e) +{ + return QAbstractState::event(e); +} + +QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qhistorystate.h b/src/corelib/statemachine/qhistorystate.h new file mode 100644 index 0000000..9cd7f0b --- /dev/null +++ b/src/corelib/statemachine/qhistorystate.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHISTORYSTATE_H +#define QHISTORYSTATE_H + +#ifndef QT_STATEMACHINE_SOLUTION +#include <QtCore/qstate.h> +#else +#include "qstate.h" +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QHistoryStatePrivate; +class Q_CORE_EXPORT QHistoryState : public QAbstractState +{ + Q_OBJECT +public: + ~QHistoryState(); + + QAbstractState *defaultState() const; + void setDefaultState(QAbstractState *state); + +protected: + void onEntry(); + void onExit(); + + bool event(QEvent *e); + +private: + QHistoryState(QState::HistoryType type, + QState *parent = 0); + +private: + Q_DISABLE_COPY(QHistoryState) + Q_DECLARE_PRIVATE(QHistoryState) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qhistorystate_p.h b/src/corelib/statemachine/qhistorystate_p.h new file mode 100644 index 0000000..84648b5 --- /dev/null +++ b/src/corelib/statemachine/qhistorystate_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHISTORYSTATE_P_H +#define QHISTORYSTATE_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 "qabstractstate_p.h" + +#include <QtCore/qlist.h> + +QT_BEGIN_NAMESPACE + +class QHistoryState; +class QHistoryStatePrivate : public QAbstractStatePrivate +{ + Q_DECLARE_PUBLIC(QHistoryState) + +public: + QHistoryStatePrivate(); + + static QHistoryState *create(QState::HistoryType type, + QState *parent); + + static QHistoryStatePrivate *get(QHistoryState *q); + static const QHistoryStatePrivate *get(const QHistoryState *q); + + QAbstractState *defaultState; + QState::HistoryType historyType; + QList<QAbstractState*> configuration; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qsignalevent.h b/src/corelib/statemachine/qsignalevent.h new file mode 100644 index 0000000..5dcabc4 --- /dev/null +++ b/src/corelib/statemachine/qsignalevent.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSIGNALEVENT_H +#define QSIGNALEVENT_H + +#include <QtCore/qcoreevent.h> + +#include <QtCore/qlist.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class Q_CORE_EXPORT QSignalEvent : public QEvent +{ +public: + QSignalEvent(const QObject *sender, int signalIndex, + const QList<QVariant> &arguments); + ~QSignalEvent(); + + inline const QObject *sender() const { return m_sender; } + inline int signalIndex() const { return m_signalIndex; } + inline QList<QVariant> arguments() const { return m_arguments; } + +private: + const QObject *m_sender; + int m_signalIndex; + QList<QVariant> m_arguments; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qsignaleventgenerator_p.h b/src/corelib/statemachine/qsignaleventgenerator_p.h new file mode 100644 index 0000000..d18def8 --- /dev/null +++ b/src/corelib/statemachine/qsignaleventgenerator_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSIGNALEVENTGENERATOR_P_H +#define QSIGNALEVENTGENERATOR_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 <QtCore/qobject.h> + +QT_BEGIN_NAMESPACE + +class QStateMachine; + +class QSignalEventGenerator : public QObject +{ +public: + QSignalEventGenerator( +#ifdef QT_STATEMACHINE_SOLUTION + int signalIndex, +#endif + QStateMachine *parent); + + static const QMetaObject staticMetaObject; + virtual const QMetaObject *metaObject() const; + virtual void *qt_metacast(const char *); + virtual int qt_metacall(QMetaObject::Call, int, void **argv); + +private: +#ifdef QT_STATEMACHINE_SOLUTION + int signalIndex; +#endif + Q_DISABLE_COPY(QSignalEventGenerator) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qsignaltransition.cpp b/src/corelib/statemachine/qsignaltransition.cpp new file mode 100644 index 0000000..9c03b94 --- /dev/null +++ b/src/corelib/statemachine/qsignaltransition.cpp @@ -0,0 +1,251 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsignaltransition.h" +#include "qsignaltransition_p.h" +#include "qsignalevent.h" +#include "qstate.h" +#include "qstate_p.h" +#include "qstatemachine.h" +#include "qstatemachine_p.h" +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QSignalTransition + + \brief The QSignalTransition class provides a transition based on a Qt signal. + + \ingroup statemachine + + Typically you would use the overload of QState::addTransition() that takes a + sender and signal as arguments, rather than creating QSignalTransition + objects directly. QSignalTransition is part of \l{The State Machine + Framework}. + + You can subclass QSignalTransition and reimplement eventTest() to make a + signal transition conditional; the event object passed to eventTest() will + be a QSignalEvent object. Example: + + \code + class CheckedTransition : public QSignalTransition + { + public: + CheckedTransition(QCheckBox *check) + : QSignalTransition(check, SIGNAL(stateChanged(int))) {} + protected: + bool eventTest(QEvent *e) const { + if (!QSignalTransition::eventTest(e)) + return false; + QSignalEvent *se = static_cast<QSignalEvent*>(e); + return (se->arguments().at(0).toInt() == Qt::Checked); + } + }; + + ... + + QCheckBox *check = new QCheckBox(); + check->setTristate(true); + + QState *s1 = new QState(); + QState *s2 = new QState(); + CheckedTransition *t1 = new CheckedTransition(check); + t1->setTargetState(s2); + s1->addTransition(t1); + \endcode +*/ + +/*! + \property QSignalTransition::object + + \brief the sender object that this signal transition is associated with +*/ + +/*! + \property QSignalTransition::signal + + \brief the signal that this signal transition is associated with +*/ + +QSignalTransitionPrivate::QSignalTransitionPrivate() +{ + sender = 0; + signalIndex = -1; +} + +QSignalTransitionPrivate *QSignalTransitionPrivate::get(QSignalTransition *q) +{ + return q->d_func(); +} + +void QSignalTransitionPrivate::invalidate() +{ + Q_Q(QSignalTransition); + if (signalIndex != -1) { + QState *source = sourceState(); + QStatePrivate *source_d = QStatePrivate::get(source); + QStateMachinePrivate *mach = QStateMachinePrivate::get(source_d->machine()); + if (mach) { + mach->unregisterSignalTransition(q); + if (mach->configuration.contains(source)) + mach->registerSignalTransition(q); + } + } +} + +/*! + Constructs a new signal transition with the given \a sourceState. +*/ +QSignalTransition::QSignalTransition(QState *sourceState) + : QTransition(*new QSignalTransitionPrivate, sourceState) +{ +} + +/*! + Constructs a new signal transition associated with the given \a signal of + the given \a sender, and with the given \a sourceState. +*/ +QSignalTransition::QSignalTransition(QObject *sender, const char *signal, + QState *sourceState) + : QTransition(*new QSignalTransitionPrivate, sourceState) +{ + Q_D(QSignalTransition); + d->sender = sender; + d->signal = signal; +} + +/*! + Constructs a new signal transition associated with the given \a signal of + the given \a sender. The transition has the given \a targets and \a + sourceState. +*/ +QSignalTransition::QSignalTransition(QObject *sender, const char *signal, + const QList<QAbstractState*> &targets, + QState *sourceState) + : QTransition(*new QSignalTransitionPrivate, targets, sourceState) +{ + Q_D(QSignalTransition); + d->sender = sender; + d->signal = signal; +} + +/*! + Destroys this signal transition. +*/ +QSignalTransition::~QSignalTransition() +{ +} + +/*! + Returns the sender object associated with this signal transition. +*/ +QObject *QSignalTransition::senderObject() const +{ + Q_D(const QSignalTransition); + return d->sender; +} + +/*! + Sets the \a sender object associated with this signal transition. +*/ +void QSignalTransition::setSenderObject(QObject *sender) +{ + Q_D(QSignalTransition); + if (sender == d->sender) + return; + d->sender = sender; + d->invalidate(); +} + +/*! + Returns the signal associated with this signal transition. +*/ +QByteArray QSignalTransition::signal() const +{ + Q_D(const QSignalTransition); + return d->signal; +} + +/*! + Sets the \a signal associated with this signal transition. +*/ +void QSignalTransition::setSignal(const QByteArray &signal) +{ + Q_D(QSignalTransition); + if (signal == d->signal) + return; + d->signal = signal; + d->invalidate(); +} + +/*! + \reimp + + The \a event is a QSignalEvent object. The default implementation returns + true if the event's sender and signal index match this transition, and + returns false otherwise. +*/ +bool QSignalTransition::eventTest(QEvent *event) const +{ + Q_D(const QSignalTransition); +#ifndef QT_STATEMACHINE_SOLUTION + if (event->type() == QEvent::Signal) { +#else + if (event->type() == QEvent::Type(QEvent::User-1)) { +#endif + QSignalEvent *se = static_cast<QSignalEvent*>(event); + Q_ASSERT(d->signalIndex != -1); + return (se->sender() == d->sender) + && (se->signalIndex() == d->signalIndex); + } + return false; +} + +/*! + \reimp +*/ +bool QSignalTransition::event(QEvent *e) +{ + return QTransition::event(e); +} + +QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qsignaltransition.h b/src/corelib/statemachine/qsignaltransition.h new file mode 100644 index 0000000..ba4af17 --- /dev/null +++ b/src/corelib/statemachine/qsignaltransition.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSIGNALTRANSITION_H +#define QSIGNALTRANSITION_H + +#ifndef QT_STATEMACHINE_SOLUTION +#include <QtCore/qtransition.h> +#else +#include "qtransition.h" +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QSignalTransitionPrivate; +class Q_CORE_EXPORT QSignalTransition : public QTransition +{ + Q_OBJECT + Q_PROPERTY(QObject* object READ senderObject WRITE setSenderObject) + Q_PROPERTY(QByteArray signal READ signal WRITE setSignal) +public: + QSignalTransition(QState *sourceState = 0); + QSignalTransition(QObject *sender, const char *signal, + QState *sourceState = 0); + QSignalTransition(QObject *sender, const char *signal, + const QList<QAbstractState*> &targets, + QState *sourceState = 0); + ~QSignalTransition(); + + QObject *senderObject() const; + void setSenderObject(QObject *sender); + + QByteArray signal() const; + void setSignal(const QByteArray &signal); + +protected: + bool eventTest(QEvent *event) const; + + bool event(QEvent *e); + +private: + Q_DISABLE_COPY(QSignalTransition) + Q_DECLARE_PRIVATE(QSignalTransition) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qsignaltransition_p.h b/src/corelib/statemachine/qsignaltransition_p.h new file mode 100644 index 0000000..c5fbcfc --- /dev/null +++ b/src/corelib/statemachine/qsignaltransition_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSIGNALTRANSITION_P_H +#define QSIGNALTRANSITION_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 "qtransition_p.h" + +QT_BEGIN_NAMESPACE + +class QSignalTransition; +class QSignalTransitionPrivate : public QTransitionPrivate +{ + Q_DECLARE_PUBLIC(QSignalTransition) +public: + QSignalTransitionPrivate(); + + static QSignalTransitionPrivate *get(QSignalTransition *q); + + void invalidate(); + + QObject *sender; + QByteArray signal; + int signalIndex; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp new file mode 100644 index 0000000..944ca2c --- /dev/null +++ b/src/corelib/statemachine/qstate.cpp @@ -0,0 +1,430 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qstate.h" +#include "qstate_p.h" +#include "qhistorystate.h" +#include "qhistorystate_p.h" +#include "qabstracttransition.h" +#include "qabstracttransition_p.h" +#include "qsignaltransition.h" +#include "qstatefinishedtransition.h" +#include "qstatemachine.h" +#include "qstatemachine_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QState + + \brief The QState class provides a general-purpose state for QStateMachine. + + \ingroup statemachine + + QState objects can have child states, and can have transitions to other + states. QState is part of \l{The State Machine Framework}. + + The addTransition() function adds a transition. The removeTransition() + function removes a transition. + + \section1 States with Child States + + For non-parallel state groups, the setInitialState() function must be called + to set the initial state. The child states are mutually exclusive states, + and the state machine needs to know which child state to enter when the + parent state is the target of a transition. + + The addHistoryState() function adds a history state. + + The setErrorState() sets the state's error state. The error state is the + state that the state machine will transition to if an error is detected when + attempting to enter the state (e.g. because no initial state has been set). +*/ + +/*! + \enum QState::Type + + This enum specifies the type of a state. + + \value Normal A normal state. If the state has no child states, it is an + atomic state; otherwise, the child states are mutually exclusive and an + initial state must be set by calling QState::setInitialState(). + + \value ParallelGroup The state is a parallel group state. When a parallel + group state is entered, all its child states are entered in parallel. +*/ + +/*! + \enum QState::HistoryType + + This enum specifies the type of history that a QHistoryState records. + + \value ShallowHistory Only the immediate child states of the parent state + are recorded. In this case a transition with the history state as its + target will end up in the immediate child state that the parent was in the + last time it was exited. This is the default. + + \value DeepHistory Nested states are recorded. In this case a transition + with the history state as its target will end up in the most deeply nested + descendant state the parent was in the last time it was exited. +*/ + +QStatePrivate::QStatePrivate() + : errorState(0), isParallelGroup(false), initialState(0) +{ +} + +QStatePrivate::~QStatePrivate() +{ +} + +QStatePrivate *QStatePrivate::get(QState *q) +{ + if (!q) + return 0; + return q->d_func(); +} + +const QStatePrivate *QStatePrivate::get(const QState *q) +{ + if (!q) + return 0; + return q->d_func(); +} + +/*! + Constructs a new state with the given \a parent state. +*/ +QState::QState(QState *parent) + : QActionState(*new QStatePrivate, parent) +{ +} + +/*! + Constructs a new state of the given \a type with the given \a parent state. +*/ +QState::QState(Type type, QState *parent) + : QActionState(*new QStatePrivate, parent) +{ + Q_D(QState); + d->isParallelGroup = (type == ParallelGroup); +} + +/*! + \internal +*/ +QState::QState(QStatePrivate &dd, QState *parent) + : QActionState(dd, parent) +{ +} + +/*! + Destroys this state. +*/ +QState::~QState() +{ +} + +QList<QAbstractState*> QStatePrivate::childStates() const +{ + QList<QAbstractState*> result; + QList<QObject*>::const_iterator it; +#ifdef QT_STATEMACHINE_SOLUTION + const QObjectList &children = q_func()->children(); +#endif + for (it = children.constBegin(); it != children.constEnd(); ++it) { + QAbstractState *s = qobject_cast<QAbstractState*>(*it); + if (!s || qobject_cast<QHistoryState*>(s)) + continue; + result.append(s); + } + return result; +} + +QList<QHistoryState*> QStatePrivate::historyStates() const +{ + QList<QHistoryState*> result; + QList<QObject*>::const_iterator it; +#ifdef QT_STATEMACHINE_SOLUTION + const QObjectList &children = q_func()->children(); +#endif + for (it = children.constBegin(); it != children.constEnd(); ++it) { + QHistoryState *h = qobject_cast<QHistoryState*>(*it); + if (h) + result.append(h); + } + return result; +} + +QList<QAbstractTransition*> QStatePrivate::transitions() const +{ + QList<QAbstractTransition*> result; + QList<QObject*>::const_iterator it; +#ifdef QT_STATEMACHINE_SOLUTION + const QObjectList &children = q_func()->children(); +#endif + for (it = children.constBegin(); it != children.constEnd(); ++it) { + QAbstractTransition *t = qobject_cast<QAbstractTransition*>(*it); + if (t) + result.append(t); + } + return result; +} + +/*! + Returns this state group's error state. + + \sa QStateMachine::errorState(), QStateMachine::setErrorState() +*/ +QAbstractState *QState::errorState() const +{ + Q_D(const QState); + return d->errorState; +} + +/*! + Sets this state's error state to be the given \a state. If the error state + is not set, or if it is set to 0, the state will inherit its parent's error + state recursively. + + \sa QStateMachine::setErrorState(), QStateMachine::errorState() +*/ +void QState::setErrorState(QAbstractState *state) +{ + Q_D(QState); + if (state != 0 && QAbstractStatePrivate::get(state)->machine() != d->machine()) { + qWarning("QState::setErrorState: error state cannot belong " + "to a different state machine"); + return; + } + + d->errorState = state; +} + +/*! + Adds the given \a transition. The transition has this state as the source. + This state takes ownership of the transition. +*/ +void QState::addTransition(QAbstractTransition *transition) +{ + Q_D(QState); + if (!transition) { + qWarning("QState::addTransition: cannot add null transition"); + return; + } + const QList<QAbstractState*> &targets = QAbstractTransitionPrivate::get(transition)->targetStates; + for (int i = 0; i < targets.size(); ++i) { + QAbstractState *t = targets.at(i); + if (!t) { + qWarning("QState::addTransition: cannot add transition to null state"); + return; + } + if ((QAbstractStatePrivate::get(t)->machine() != d->machine()) + && QAbstractStatePrivate::get(t)->machine() && d->machine()) { + qWarning("QState::addTransition: cannot add transition " + "to a state in a different state machine"); + return; + } + } + transition->setParent(this); +} + +/*! + Adds a transition associated with the given \a signal of the given \a sender + object, and returns the new QSignalTransition object. The transition has + this state as the source, and the given \a target as the target state. +*/ +QSignalTransition *QState::addTransition(QObject *sender, const char *signal, + QAbstractState *target) +{ + if (!sender) { + qWarning("QState::addTransition: sender cannot be null"); + return 0; + } + if (!signal) { + qWarning("QState::addTransition: signal cannot be null"); + return 0; + } + QSignalTransition *trans = new QSignalTransition(sender, signal, QList<QAbstractState*>() << target); + addTransition(trans); + return trans; +} + +/*! + Adds a transition that's triggered by the finished event of this state, and + returns the new QStateFinishedTransition object. The transition has the + given \a target state. + + \sa QStateFinishedEvent +*/ +QStateFinishedTransition *QState::addFinishedTransition(QAbstractState *target) +{ + QStateFinishedTransition *trans = new QStateFinishedTransition(this, QList<QAbstractState*>() << target); + addTransition(trans); + return trans; +} + +namespace { + +// ### Make public? +class UnconditionalTransition : public QAbstractTransition +{ +public: + UnconditionalTransition(QAbstractState *target) + : QAbstractTransition(QList<QAbstractState*>() << target) {} +protected: + void onTransition() {} + bool eventTest(QEvent *) const { return true; } +}; + +} // namespace + +/*! + Adds an unconditional transition from this state to the given \a target + state, and returns then new transition object. +*/ +QAbstractTransition *QState::addTransition(QAbstractState *target) +{ + UnconditionalTransition *trans = new UnconditionalTransition(target); + addTransition(trans); + return trans; +} + +/*! + Removes the given \a transition from this state. The state releases + ownership of the transition. + + \sa addTransition() +*/ +void QState::removeTransition(QAbstractTransition *transition) +{ + Q_D(QState); + if (!transition) { + qWarning("QState::removeTransition: cannot remove null transition"); + return; + } + if (transition->sourceState() != this) { + qWarning("QState::removeTransition: transition %p's source state (%p)" + " is different from this state (%p)", + transition, transition->sourceState(), this); + return; + } + QStateMachinePrivate *mach = QStateMachinePrivate::get(d->machine()); + if (mach) + mach->unregisterTransition(transition); + transition->setParent(0); +} + +/*! + Returns the list of transitions from this state, or an empty list if there + are no transitions from this state. + + \sa addTransition(), removeTransition() +*/ +QList<QAbstractTransition*> QState::transitions() const +{ + Q_D(const QState); + return d->transitions(); +} + +/*! + Creates a history state of the given \a type for this state and returns the + new state. The history state becomes a child of this state. +*/ +QHistoryState *QState::addHistoryState(HistoryType type) +{ + return QHistoryStatePrivate::create(type, this); +} + +/*! + \reimp +*/ +void QState::onEntry() +{ + QActionState::onEntry(); +} + +/*! + \reimp +*/ +void QState::onExit() +{ + QActionState::onExit(); +} + +/*! + Returns this state's initial state, or 0 if the state has no initial state. +*/ +QAbstractState *QState::initialState() const +{ + Q_D(const QState); + return d->initialState; +} + +/*! + Sets this state's initial state to be the given \a state. + \a state has to be a child of this state. +*/ +void QState::setInitialState(QAbstractState *state) +{ + Q_D(QState); + if (d->isParallelGroup) { + qWarning("QState::setInitialState: ignoring attempt to set initial state " + "of parallel state group %p", this); + return; + } + if (state && (state->parentState() != this)) { + qWarning("QState::setInitialState: state %p is not a child of this state (%p)", + state, this); + return; + } + d->initialState = state; +} + +/*! + \reimp +*/ +bool QState::event(QEvent *e) +{ + return QActionState::event(e); +} + +QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qstate.h b/src/corelib/statemachine/qstate.h new file mode 100644 index 0000000..4c86e02 --- /dev/null +++ b/src/corelib/statemachine/qstate.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSTATE_H +#define QSTATE_H + +#ifndef QT_STATEMACHINE_SOLUTION +#include <QtCore/qactionstate.h> +#else +#include "qactionstate.h" +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QAbstractTransition; +class QHistoryState; +class QSignalTransition; +class QStateFinishedTransition; + +class QStatePrivate; +class Q_CORE_EXPORT QState : public QActionState +{ + Q_OBJECT +public: + enum Type { + Normal, + ParallelGroup + }; + + enum HistoryType { + ShallowHistory, + DeepHistory + }; + + QState(QState *parent = 0); + QState(Type type, QState *parent = 0); + ~QState(); + + QAbstractState *errorState() const; + void setErrorState(QAbstractState *state); + + void addTransition(QAbstractTransition *transition); + QSignalTransition *addTransition(QObject *sender, const char *signal, QAbstractState *target); + QAbstractTransition *addTransition(QAbstractState *target); + QStateFinishedTransition *addFinishedTransition(QAbstractState *target); + void removeTransition(QAbstractTransition *transition); + QList<QAbstractTransition*> transitions() const; + + QHistoryState *addHistoryState(HistoryType type = ShallowHistory); + + QAbstractState *initialState() const; + void setInitialState(QAbstractState *state); + +protected: + void onEntry(); + void onExit(); + + bool event(QEvent *e); + +protected: + QState(QStatePrivate &dd, QState *parent); + +private: + Q_DISABLE_COPY(QState) + Q_DECLARE_PRIVATE(QState) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qstate_p.h b/src/corelib/statemachine/qstate_p.h new file mode 100644 index 0000000..17b312a --- /dev/null +++ b/src/corelib/statemachine/qstate_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSTATE_P_H +#define QSTATE_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 "qactionstate_p.h" + +#include <QtCore/qlist.h> + +QT_BEGIN_NAMESPACE + +class QState; +class Q_CORE_EXPORT QStatePrivate : public QActionStatePrivate +{ + Q_DECLARE_PUBLIC(QState) +public: + QStatePrivate(); + ~QStatePrivate(); + + static QStatePrivate *get(QState *q); + static const QStatePrivate *get(const QState *q); + + QList<QAbstractState*> childStates() const; + QList<QHistoryState*> historyStates() const; + QList<QAbstractTransition*> transitions() const; + + QAbstractState *errorState; + bool isParallelGroup; + QAbstractState *initialState; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qstateaction.cpp b/src/corelib/statemachine/qstateaction.cpp new file mode 100644 index 0000000..b03aa19 --- /dev/null +++ b/src/corelib/statemachine/qstateaction.cpp @@ -0,0 +1,354 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qstateaction.h" +#include "qstateaction_p.h" +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qmetaobject.h> + +QT_BEGIN_NAMESPACE + +QStateActionPrivate::QStateActionPrivate() +{ + when = ExecuteOnEntry; +} + +QStateActionPrivate::~QStateActionPrivate() +{ +} + +QStateActionPrivate *QStateActionPrivate::get(QStateAction *q) +{ + return q->d_func(); +} + +void QStateActionPrivate::callExecute() +{ + Q_Q(QStateAction); + q->execute(); +} + +/*! + \class QStateAction + + \brief The QStateAction class is the base class of QState actions. + + \ingroup statemachine + + A state action is added to a state by calling QState::addEntryAction() or + QState::addExitAction(). QStateAction is part of \l{The State Machine + Framework}. + + \section1 Subclassing + + Subclasses must implement the execute() function. +*/ + +/*! + Constructs a new QStateAction object with the given \a parent. +*/ +QStateAction::QStateAction(QObject *parent) + : QObject( +#ifndef QT_STATEMACHINE_SOLUTION + *new QStateActionPrivate, +#endif + parent) +#ifdef QT_STATEMACHINE_SOLUTION + , d_ptr(new QStateActionPrivate) +#endif +{ +#ifdef QT_STATEMACHINE_SOLUTION + d_ptr->q_ptr = this; +#endif +} + +/*! + \internal +*/ +QStateAction::QStateAction(QStateActionPrivate &dd, QObject *parent) + : QObject( +#ifndef QT_STATEMACHINE_SOLUTION + dd, +#endif + parent) +#ifdef QT_STATEMACHINE_SOLUTION + , d_ptr(&dd) +#endif +{ +#ifdef QT_STATEMACHINE_SOLUTION + d_ptr->q_ptr = this; +#endif +} + +/*! + Destroys this QStateAction object. +*/ +QStateAction::~QStateAction() +{ +#ifdef QT_STATEMACHINE_SOLUTION + delete d_ptr; +#endif +} + +/*! + \fn QStateAction::execute() + + Executes this action. +*/ + +/*! + \reimp +*/ +bool QStateAction::event(QEvent *e) +{ + return QObject::event(e); +} + +QStateInvokeMethodActionPrivate *QStateInvokeMethodActionPrivate::get(QStateInvokeMethodAction *q) +{ + return q->d_func(); +} + +/*! + \class QStateInvokeMethodAction + + \brief The QStateInvokeMethodAction class provides an invoke method action for QObjects. + + \ingroup statemachine + + The QStateInvokeMethodAction class provides an action that calls a method of + a QObject when a QState is entered or exited. QStateInvokeMethodAction is + part of \l{The State Machine Framework}. + + Typically you don't construct QStateInvokeMethodAction objects directly, but + rather call the QState::invokeMethodOnEntry() function or the + QState::invokeMethodOnExit() function. +*/ + +/*! + \property QStateInvokeMethodAction::arguments + + \brief the arguments to the method this action invokes +*/ + +/*! + \property QStateInvokeMethodAction::methodName + + \brief the name of the method this action invokes +*/ + +/*! + \property QStateInvokeMethodAction::target + + \brief the object on which this action invokes a method +*/ + +/*! + Constructs a new QStateInvokeMethodAction object for the method named \a + methodName of the given \a target object, with the given \a parent. +*/ +QStateInvokeMethodAction::QStateInvokeMethodAction( + QObject *target, const QByteArray &methodName, QObject *parent) + : QStateAction(*new QStateInvokeMethodActionPrivate, parent) +{ + Q_D(QStateInvokeMethodAction); + d->target = target; + d->methodName = methodName; + d->methodIndex = -1; +} + +/*! + Constructs a new QStateInvokeMethodAction object for the method named \a + methodName of the given \a target object, with the given arguments, \a args, + and with the given \a parent. +*/ +QStateInvokeMethodAction::QStateInvokeMethodAction( + QObject *target, const QByteArray &methodName, + const QList<QVariant> &args, QObject *parent) + : QStateAction(*new QStateInvokeMethodActionPrivate, parent) +{ + Q_D(QStateInvokeMethodAction); + d->target = target; + d->methodName = methodName; + d->methodIndex = -1; + d->args = args; +} + +/*! + Constructs a new QStateInvokeMethodAction object with the given \a parent. +*/ +QStateInvokeMethodAction::QStateInvokeMethodAction(QObject *parent) + : QStateAction(*new QStateInvokeMethodActionPrivate, parent) +{ + Q_D(QStateInvokeMethodAction); + d->target = 0; + d->methodIndex = -1; +} + +/*! + Destroys this QStateInvokeMethodAction object. +*/ +QStateInvokeMethodAction::~QStateInvokeMethodAction() +{ +} + +/*! + \reimp +*/ +void QStateInvokeMethodAction::execute() +{ + Q_D(QStateInvokeMethodAction); + if (!d->target) + return; + + if (d->methodIndex == -1) { + QVarLengthArray<char, 512> sig; + int len = d->methodName.length(); + if (len <= 0) + return; + sig.append(d->methodName, len); + sig.append('('); + + int paramCount; + for (paramCount = 0; paramCount < d->args.size() && paramCount < 10; ++paramCount) { + const char *tn = d->args.at(paramCount).typeName(); + len = qstrlen(tn); + if (len <= 0) + break; + sig.append(tn, len); + sig.append(','); + } + if (paramCount == 0) + sig.append(')'); // no parameters + else + sig[sig.size() - 1] = ')'; + sig.append('\0'); + + const QMetaObject *meta = d->target->metaObject(); + int idx = meta->indexOfMethod(sig.constData()); + if (idx < 0) { + QByteArray norm = QMetaObject::normalizedSignature(sig.constData()); + idx = meta->indexOfMethod(norm.constData()); + if ((idx < 0) || (idx >= meta->methodCount())) { + qWarning("InvokeMethodAction: unable to find method '%s' of %s(%p)", + sig.constData(), meta->className(), d->target); + return; + } + } + d->methodIndex = idx; + } + + void *param[11]; + param[0] = 0; // return value + for (int i = 0; i < 10; ++i) + param[i+1] = (i < d->args.size()) ? const_cast<void*>(d->args.at(i).constData()) : (void*)0; + (void)d->target->qt_metacall(QMetaObject::InvokeMetaMethod, d->methodIndex, param); +} + +/*! + Returns the object on which this action invokes a method. +*/ +QObject *QStateInvokeMethodAction::targetObject() const +{ + Q_D(const QStateInvokeMethodAction); + return d->target; +} + +/*! + Sets the object on which this action invokes a method. +*/ +void QStateInvokeMethodAction::setTargetObject(QObject *target) +{ + Q_D(QStateInvokeMethodAction); + d->target = target; +} + +/*! + Returns the name of the method this action will invoke. +*/ +QByteArray QStateInvokeMethodAction::methodName() const +{ + Q_D(const QStateInvokeMethodAction); + return d->methodName; +} + +/*! + Sets the name of the method this action will invoke. +*/ +void QStateInvokeMethodAction::setMethodName(const QByteArray &methodName) +{ + Q_D(QStateInvokeMethodAction); + if (methodName != d->methodName) { + d->methodName = methodName; + d->methodIndex = -1; + } +} + +/*! + Returns the arguments to the method this action will invoke. +*/ +QVariantList QStateInvokeMethodAction::arguments() const +{ + Q_D(const QStateInvokeMethodAction); + return d->args; +} + +/*! + Sets the arguments to the method this action will invoke. +*/ +void QStateInvokeMethodAction::setArguments(const QVariantList &arguments) +{ + Q_D(QStateInvokeMethodAction); + if (d->args != arguments) { + d->args = arguments; + d->methodIndex = -1; + } +} + +/*! + \reimp +*/ +bool QStateInvokeMethodAction::event(QEvent *e) +{ + return QStateAction::event(e); +} + +QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qstateaction.h b/src/corelib/statemachine/qstateaction.h new file mode 100644 index 0000000..6843080 --- /dev/null +++ b/src/corelib/statemachine/qstateaction.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSTATEACTION_H +#define QSTATEACTION_H + +#include <QtCore/qobject.h> + +#include <QtCore/qvariant.h> +#include <QtCore/qlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QStateActionPrivate; +class Q_CORE_EXPORT QStateAction : public QObject +{ + Q_OBJECT +public: + ~QStateAction(); + +protected: + QStateAction(QObject *parent = 0); + + virtual void execute() = 0; + + bool event(QEvent *e); + +protected: +#ifdef QT_STATEMACHINE_SOLUTION + QStateActionPrivate *d_ptr; +#endif + QStateAction(QStateActionPrivate &dd, QObject *parent); + +private: + Q_DISABLE_COPY(QStateAction) + Q_DECLARE_PRIVATE(QStateAction) +}; + +class QStateInvokeMethodActionPrivate; +class Q_CORE_EXPORT QStateInvokeMethodAction : public QStateAction +{ + Q_OBJECT + Q_PROPERTY(QObject* target READ targetObject WRITE setTargetObject) + Q_PROPERTY(QByteArray methodName READ methodName WRITE setMethodName) + Q_PROPERTY(QVariantList arguments READ arguments WRITE setArguments) +public: + QStateInvokeMethodAction(QObject *target, const QByteArray &methodName, + QObject *parent = 0); + QStateInvokeMethodAction(QObject *target, const QByteArray &methodName, + const QList<QVariant> &args, QObject *parent = 0); + QStateInvokeMethodAction(QObject *parent = 0); + ~QStateInvokeMethodAction(); + + QObject *targetObject() const; + void setTargetObject(QObject *target); + + QByteArray methodName() const; + void setMethodName(const QByteArray &methodName); + + QVariantList arguments() const; + void setArguments(const QVariantList &arguments); + +protected: + void execute(); + + bool event(QEvent *e); + +private: + Q_DISABLE_COPY(QStateInvokeMethodAction) + Q_DECLARE_PRIVATE(QStateInvokeMethodAction) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qstateaction_p.h b/src/corelib/statemachine/qstateaction_p.h new file mode 100644 index 0000000..4016b74 --- /dev/null +++ b/src/corelib/statemachine/qstateaction_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSTATEACTION_P_H +#define QSTATEACTION_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. +// + +#ifndef QT_STATEMACHINE_SOLUTION +#include <private/qobject_p.h> +#endif + +QT_BEGIN_NAMESPACE + +class QStateAction; +class QStateActionPrivate +#ifndef QT_STATEMACHINE_SOLUTION + : public QObjectPrivate +#endif +{ + Q_DECLARE_PUBLIC(QStateAction) +public: + QStateActionPrivate(); + ~QStateActionPrivate(); + + static QStateActionPrivate *get(QStateAction *q); + + void callExecute(); + + enum When { + ExecuteOnEntry, + ExecuteOnExit + }; + + When when; + +#ifdef QT_STATEMACHINE_SOLUTION + QStateAction *q_ptr; +#endif +}; + +class QStateInvokeMethodAction; +class QStateInvokeMethodActionPrivate : public QStateActionPrivate +{ + Q_DECLARE_PUBLIC(QStateInvokeMethodAction) +public: + QStateInvokeMethodActionPrivate() {} + ~QStateInvokeMethodActionPrivate() {} + + static QStateInvokeMethodActionPrivate *get(QStateInvokeMethodAction *q); + + QObject *target; + QByteArray methodName; + int methodIndex; + QList<QVariant> args; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qstatefinishedevent.h b/src/corelib/statemachine/qstatefinishedevent.h new file mode 100644 index 0000000..c2f81f7 --- /dev/null +++ b/src/corelib/statemachine/qstatefinishedevent.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSTATEFINISHEDEVENT_H +#define QSTATEFINISHEDEVENT_H + +#include <QtCore/qcoreevent.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QState; + +class Q_CORE_EXPORT QStateFinishedEvent : public QEvent +{ +public: + QStateFinishedEvent(QState *state); + ~QStateFinishedEvent(); + + QState *state() const; + +private: + QState *m_state; + void *d; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qstatefinishedtransition.cpp b/src/corelib/statemachine/qstatefinishedtransition.cpp new file mode 100644 index 0000000..24d2ca4 --- /dev/null +++ b/src/corelib/statemachine/qstatefinishedtransition.cpp @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qstatefinishedtransition.h" +#include "qstatefinishedevent.h" +#include "qtransition_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QStateFinishedTransition + + \brief The QStateFinishedTransition class provides a transition that triggers when a state is finished. + + \ingroup statemachine + + A state is finished when one of its final child states (a QFinalState) is + entered; this will cause a QStateFinishedEvent to be generated. The + QStateFinishedTransition class provides a way of associating a transition + with such an event. QStateFinishedTransition is part of \l{The State Machine + Framework}. + + \code + QStateMachine machine; + QState *s1 = new QState(machine.rootState()); + QState *s11 = new QState(s1); + QFinalState *s12 = new QFinalState(s1); + s11->addTransition(s12); + + QState *s2 = new QState(machine.rootState()); + QStateFinishedTransition *finishedTransition = new QStateFinishedTransition(s1); + finishedTransition->setTargetState(s2); + s1->addTransition(finishedTransition); + \endcode + + \sa QState::addFinishedTransition(), QStateFinishedEvent +*/ + +/*! + \property QStateFinishedTransition::state + + \brief the state whose QStateFinishedEvent this transition is associated with +*/ + +class QStateFinishedTransitionPrivate : public QTransitionPrivate +{ + Q_DECLARE_PUBLIC(QStateFinishedTransition) +public: + QStateFinishedTransitionPrivate(); + + static QStateFinishedTransitionPrivate *get(QStateFinishedTransition *q); + + QState *state; +}; + +QStateFinishedTransitionPrivate::QStateFinishedTransitionPrivate() +{ + state = 0; +} + +QStateFinishedTransitionPrivate *QStateFinishedTransitionPrivate::get(QStateFinishedTransition *q) +{ + return q->d_func(); +} + +/*! + Constructs a new QStateFinishedTransition object that has the given \a + sourceState. +*/ +QStateFinishedTransition::QStateFinishedTransition(QState *sourceState) + : QTransition(*new QStateFinishedTransitionPrivate, sourceState) +{ +} + +/*! + Constructs a new QStateFinishedTransition object associated with the given + \a state, and that has the given \a targets and \a sourceState. +*/ +QStateFinishedTransition::QStateFinishedTransition( + QState *state, const QList<QAbstractState*> &targets, QState *sourceState) + : QTransition(*new QStateFinishedTransitionPrivate, targets, sourceState) +{ + Q_D(QStateFinishedTransition); + d->state = state; +} + +/*! + Destroys this QStateFinishedTransition. +*/ +QStateFinishedTransition::~QStateFinishedTransition() +{ +} + +/*! + Returns the state associated with this QStateFinishedTransition. +*/ +QState *QStateFinishedTransition::state() const +{ + Q_D(const QStateFinishedTransition); + return d->state; +} + +/*! + Sets the \a state associated with this QStateFinishedTransition. +*/ +void QStateFinishedTransition::setState(QState *state) +{ + Q_D(QStateFinishedTransition); + d->state = state; +} + +/*! + \reimp +*/ +bool QStateFinishedTransition::eventTest(QEvent *event) const +{ + Q_D(const QStateFinishedTransition); +#ifndef QT_STATEMACHINE_SOLUTION + if (event->type() == QEvent::StateFinished) { +#else + if (event->type() == QEvent::Type(QEvent::User-2)) { +#endif + QStateFinishedEvent *sfe = static_cast<QStateFinishedEvent*>(event); + return (sfe->state() == d->state); + } + return false; +} + +/*! + \reimp +*/ +bool QStateFinishedTransition::event(QEvent *e) +{ + return QTransition::event(e); +} + +QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qstatefinishedtransition.h b/src/corelib/statemachine/qstatefinishedtransition.h new file mode 100644 index 0000000..eae3143 --- /dev/null +++ b/src/corelib/statemachine/qstatefinishedtransition.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSTATEFINISHEDTRANSITION_H +#define QSTATEFINISHEDTRANSITION_H + +#ifndef QT_STATEMACHINE_SOLUTION +#include <QtCore/qtransition.h> +#else +#include "qtransition.h" +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QState; + +class QStateFinishedTransitionPrivate; +class Q_CORE_EXPORT QStateFinishedTransition : public QTransition +{ + Q_OBJECT + Q_PROPERTY(QState* state READ state WRITE setState) +public: + QStateFinishedTransition(QState *sourceState = 0); + QStateFinishedTransition(QState *state, const QList<QAbstractState*> &targets, + QState *sourceState = 0); + ~QStateFinishedTransition(); + + QState *state() const; // ### name + void setState(QState *state); + +protected: + bool eventTest(QEvent *event) const; + + bool event(QEvent *e); + +private: + Q_DISABLE_COPY(QStateFinishedTransition) + Q_DECLARE_PRIVATE(QStateFinishedTransition) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp new file mode 100644 index 0000000..90932f6 --- /dev/null +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -0,0 +1,2017 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qstatemachine.h" +#include "qstatemachine_p.h" +#include "qabstracttransition.h" +#include "qabstracttransition_p.h" +#include "qsignaltransition.h" +#include "qsignaltransition_p.h" +#include "qsignalevent.h" +#include "qsignaleventgenerator_p.h" +#include "qabstractstate.h" +#include "qabstractstate_p.h" +#include "qactionstate.h" +#include "qactionstate_p.h" +#include "qfinalstate.h" +#include "qhistorystate.h" +#include "qhistorystate_p.h" +#include "qstatefinishedevent.h" +#include "qstatefinishedtransition.h" +#include "qstate.h" +#include "qstate_p.h" +#include "qstateaction.h" +#include "qstateaction_p.h" +#ifndef QT_STATEMACHINE_SOLUTION +#include "private/qobject_p.h" +#include "private/qthread_p.h" +#endif + +#ifndef QT_NO_STATEMACHINE_EVENTFILTER +#include "qeventtransition.h" +#include "qeventtransition_p.h" +#include "qboundevent_p.h" +#endif + +#ifndef QT_NO_ANIMATION +#include "qpropertyanimation.h" +#include "qanimationgroup.h" +# ifndef QT_STATEMACHINE_SOLUTION +# include <private/qvariantanimation_p.h> +# else +# include "qvariantanimation_p.h" +# endif +#endif + +#include <QtCore/qmetaobject.h> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QStateMachine + \reentrant + + \brief The QStateMachine class provides a hierarchical finite state machine. + + \ingroup statemachine + + The QStateMachine class provides a hierarchical finite state machine based + on \l{Statecharts: A visual formalism for complex systems}{Statecharts} + concepts and notation. QStateMachine is part of \l{The State Machine + Framework}. + + A state machine manages a set of states (QAbstractState objects) and + transitions (QAbstractTransition objects) between those states; the states + and the transitions collectively define a state graph. Once a state graph + has been defined, the state machine can execute it. QStateMachine's + execution algorithm is based on the \l{State Chart XML: State Machine + Notation for Control Abstraction}{State Chart XML (SCXML)} algorithm. + + The QState class provides a state that you can use to set properties and + invoke methods on QObjects when the state is entered or exited. This is + typically used in conjunction with \l{Signals and Slots}{signals}; the + signals determine the flow of the state graph, whereas the states' property + assigments and method invocations are the actions. + + Use the addState() function to add a state to the state machine; + alternatively, pass the machine's rootState() to the state constructor. Use + the removeState() function to remove a state from the state machine. + + The following snippet shows a state machine that will finish when a button + is clicked: + + \code + QPushButton button; + + QStateMachine machine; + QState *s1 = new QState(); + s1->setPropertyOnEntry(&button, "text", "Click me"); + + QFinalState *s2 = new QFinalState(); + s1->addTransition(&button, SIGNAL(clicked()), s2); + + machine.addState(s1); + machine.addState(s2); + machine.setInitialState(s1); + machine.start(); + \endcode + + The setInitialState() function sets the state machine's initial state; this + state is entered when the state machine is started. + + The start() function starts the state machine. The state machine executes + asynchronously, i.e. you need to run an event loop in order for it to make + progress. The started() signal is emitted when the state machine has entered + the initial state. + + The state machine processes events and takes transitions until a top-level + final state is entered; the state machine then emits the finished() signal. + + The stop() function stops the state machine. The stopped() signal is emitted + when the state machine has stopped. + + The postEvent() function posts an event to the state machine. This is useful + when you are using custom events to trigger transitions. + + The rootState() function returns the state machine's root state. All + top-level states have the root state as their parent. + + \sa QAbstractState, QAbstractTransition +*/ + +/*! + \property QStateMachine::rootState + + \brief the root state of this state machine +*/ + +/*! + \property QStateMachine::initialState + + \brief the initial state of this state machine +*/ + +/*! + \property QStateMachine::errorState + + \brief the error state of this state machine +*/ + +/*! + \property QStateMachine::errorString + + \brief the error string of this state machine +*/ + +// #define QSTATEMACHINE_DEBUG + +QStateMachinePrivate::QStateMachinePrivate() +{ + state = NotRunning; + processing = false; + processingScheduled = false; + stop = false; + error = QStateMachine::NoError; + globalRestorePolicy = QAbstractState::DoNotRestoreProperties; + rootState = 0; + initialErrorStateForRoot = 0; +#ifndef QT_STATEMACHINE_SOLUTION + signalEventGenerator = 0; +#endif +} + +QStateMachinePrivate::~QStateMachinePrivate() +{ + qDeleteAll(internalEventQueue); + qDeleteAll(externalEventQueue); +} + +QStateMachinePrivate *QStateMachinePrivate::get(QStateMachine *q) +{ + if (q) + return q->d_func(); + return 0; +} + +static QEvent *cloneEvent(QEvent *e) +{ + switch (e->type()) { + case QEvent::None: + return new QEvent(*e); + case QEvent::Timer: + return new QTimerEvent(*static_cast<QTimerEvent*>(e)); + default: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + } + return 0; +} + +const QStateMachinePrivate::Handler qt_kernel_statemachine_handler = { + cloneEvent +}; + +const QStateMachinePrivate::Handler *QStateMachinePrivate::handler = &qt_kernel_statemachine_handler; + +Q_CORE_EXPORT const QStateMachinePrivate::Handler *qcoreStateMachineHandler() +{ + return &qt_kernel_statemachine_handler; +} + +bool QStateMachinePrivate::stateEntryLessThan(QAbstractState *s1, QAbstractState *s2) +{ + if (s1->parent() == s2->parent()) { + return s1->children().indexOf(s1) + < s2->children().indexOf(s2); + } else if (isDescendantOf(s1, s2)) { + return false; + } else if (isDescendantOf(s2, s1)) { + return true; + } else { + // ### fixme + return s1 < s2; + } +} + +bool QStateMachinePrivate::stateExitLessThan(QAbstractState *s1, QAbstractState *s2) +{ + if (s1->parent() == s2->parent()) { + return s1->children().indexOf(s1) + < s2->children().indexOf(s2); + } else if (isDescendantOf(s1, s2)) { + return true; + } else if (isDescendantOf(s2, s1)) { + return false; + } else { + // ### fixme + return s2 < s1; + } +} + +QState *QStateMachinePrivate::findLCA(const QList<QAbstractState*> &states) +{ + if (states.isEmpty()) + return 0; + QList<QState*> ancestors = properAncestors(states.at(0), 0); + for (int i = 0; i < ancestors.size(); ++i) { + QState *anc = ancestors.at(i); + bool ok = true; + for (int j = states.size() - 1; (j > 0) && ok; --j) { + const QAbstractState *s = states.at(j); + if (!isDescendantOf(s, anc)) + ok = false; + } + if (ok) + return anc; + } + return 0; +} + +bool QStateMachinePrivate::isPreempted(const QAbstractState *s, const QSet<QAbstractTransition*> &transitions) const +{ + QSet<QAbstractTransition*>::const_iterator it; + for (it = transitions.constBegin(); it != transitions.constEnd(); ++it) { + QAbstractTransition *t = *it; + QList<QAbstractState*> lst = t->targetStates(); + if (!lst.isEmpty()) { + lst.prepend(t->sourceState()); + QAbstractState *lca = findLCA(lst); + if (isDescendantOf(s, lca)) { +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ":" << transitions << "preempts selection of a transition from" + << s << "because" << s << "is a descendant of" << lca; +#endif + return true; + } + } + } + return false; +} + +QSet<QAbstractTransition*> QStateMachinePrivate::selectTransitions(QEvent *event) const +{ + Q_Q(const QStateMachine); + QSet<QAbstractTransition*> enabledTransitions; + QSet<QAbstractState*>::const_iterator it; + const_cast<QStateMachine*>(q)->beginSelectTransitions(event); + for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) { + QAbstractState *state = *it; + if (!isAtomic(state)) + continue; + if (isPreempted(state, enabledTransitions)) + continue; + QList<QState*> lst = properAncestors(state, 0); + if (QState *grp = qobject_cast<QState*>(state)) + lst.prepend(grp); + bool found = false; + for (int j = 0; (j < lst.size()) && !found; ++j) { + QState *s = lst.at(j); + QList<QAbstractTransition*> transitions = QStatePrivate::get(s)->transitions(); + for (int k = 0; k < transitions.size(); ++k) { + QAbstractTransition *t = transitions.at(k); + if (QAbstractTransitionPrivate::get(t)->callEventTest(event)) { +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": selecting transition" << t; +#endif + enabledTransitions.insert(t); + found = true; + break; + } + } + } + } + const_cast<QStateMachine*>(q)->endSelectTransitions(event); + return enabledTransitions; +} + +void QStateMachinePrivate::microstep(const QList<QAbstractTransition*> &enabledTransitions) +{ +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": begin microstep( enabledTransitions:" << enabledTransitions << ")"; + qDebug() << q_func() << ": configuration before exiting states:" << configuration; +#endif + exitStates(enabledTransitions); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": configuration after exiting states:" << configuration; +#endif + executeTransitionContent(enabledTransitions); + QList<QAbstractState*> enteredStates = enterStates(enabledTransitions); + applyProperties(enabledTransitions, enteredStates); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": configuration after entering states:" << configuration; + qDebug() << q_func() << ": end microstep"; +#endif +} + +QList<QAbstractState*> QStateMachinePrivate::exitStates(const QList<QAbstractTransition*> &enabledTransitions) +{ +// qDebug() << "exitStates(" << enabledTransitions << ")"; + QSet<QAbstractState*> statesToExit; +// QSet<QAbstractState*> statesToSnapshot; + for (int i = 0; i < enabledTransitions.size(); ++i) { + QAbstractTransition *t = enabledTransitions.at(i); + QList<QAbstractState*> lst = t->targetStates(); + if (lst.isEmpty()) + continue; + lst.prepend(t->sourceState()); + QAbstractState *lca = findLCA(lst); + { + QSet<QAbstractState*>::const_iterator it; + for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) { + QAbstractState *s = *it; + if (isDescendantOf(s, lca)) + statesToExit.insert(s); + } + } + } + QList<QAbstractState*> statesToExit_sorted = statesToExit.toList(); + qSort(statesToExit_sorted.begin(), statesToExit_sorted.end(), stateExitLessThan); + for (int i = 0; i < statesToExit_sorted.size(); ++i) { + QAbstractState *s = statesToExit_sorted.at(i); + if (QState *grp = qobject_cast<QState*>(s)) { + QList<QHistoryState*> hlst = QStatePrivate::get(grp)->historyStates(); + for (int j = 0; j < hlst.size(); ++j) { + QHistoryState *h = hlst.at(j); + QHistoryStatePrivate::get(h)->configuration.clear(); + QSet<QAbstractState*>::const_iterator it; + for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) { + QAbstractState *s0 = *it; + if (QHistoryStatePrivate::get(h)->historyType == QState::DeepHistory) { + if (isAtomic(s0) && isDescendantOf(s0, s)) + QHistoryStatePrivate::get(h)->configuration.append(s0); + } else if (s0->parentState() == s) { + QHistoryStatePrivate::get(h)->configuration.append(s0); + } + } +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": recorded" << ((QHistoryStatePrivate::get(h)->historyType == QState::DeepHistory) ? "deep" : "shallow") + << "history for" << s << "in" << h << ":" << QHistoryStatePrivate::get(h)->configuration; +#endif + } + } + } + for (int i = 0; i < statesToExit_sorted.size(); ++i) { + QAbstractState *s = statesToExit_sorted.at(i); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": exiting" << s; +#endif + QAbstractStatePrivate::get(s)->callOnExit(); + configuration.remove(s); + } + return statesToExit_sorted; +} + +void QStateMachinePrivate::executeTransitionContent(const QList<QAbstractTransition*> &enabledTransitions) +{ + for (int i = 0; i < enabledTransitions.size(); ++i) { + QAbstractTransition *t = enabledTransitions.at(i); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": triggering" << t; +#endif + QAbstractTransitionPrivate::get(t)->callOnTransition(); + } +} + +QList<QAbstractState*> QStateMachinePrivate::enterStates(const QList<QAbstractTransition*> &enabledTransitions) +{ +#ifdef QSTATEMACHINE_DEBUG + Q_Q(QStateMachine); +#endif +// qDebug() << "enterStates(" << enabledTransitions << ")"; + QSet<QAbstractState*> statesToEnter; + QSet<QAbstractState*> statesForDefaultEntry; + + for (int i = 0; i < enabledTransitions.size(); ++i) { + QAbstractTransition *t = enabledTransitions.at(i); + QList<QAbstractState*> lst = t->targetStates(); + if (lst.isEmpty()) + continue; + lst.prepend(t->sourceState()); + QState *lca = findLCA(lst); + for (int j = 1; j < lst.size(); ++j) { + QAbstractState *s = lst.at(j); + if (QHistoryState *h = qobject_cast<QHistoryState*>(s)) { + QList<QAbstractState*> hconf = QHistoryStatePrivate::get(h)->configuration; + if (!hconf.isEmpty()) { + for (int k = 0; k < hconf.size(); ++k) { + QAbstractState *s0 = hconf.at(k); + addStatesToEnter(s0, lca, statesToEnter, statesForDefaultEntry); + } +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": restoring" + << ((QHistoryStatePrivate::get(h)->historyType == QState::DeepHistory) ? "deep" : "shallow") + << "history from" << s << ":" << hconf; +#endif + } else { + QList<QAbstractState*> hlst; + if (QHistoryStatePrivate::get(h)->defaultState) + hlst.append(QHistoryStatePrivate::get(h)->defaultState); + if (hlst.isEmpty()) { + setError(QStateMachine::NoDefaultStateInHistoryState, h); + } else { + for (int k = 0; k < hlst.size(); ++k) { + QAbstractState *s0 = hlst.at(k); + addStatesToEnter(s0, lca, statesToEnter, statesForDefaultEntry); + } +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": initial history targets for" << s << ":" << hlst; +#endif + } + } + } else { + addStatesToEnter(s, lca, statesToEnter, statesForDefaultEntry); + } + } + } + + // Did an error occur while selecting transitions? Then we enter the error state. + if (!pendingErrorStates.isEmpty()) { + statesToEnter.clear(); + statesToEnter = pendingErrorStates; + statesForDefaultEntry = pendingErrorStatesForDefaultEntry; + pendingErrorStates.clear(); + pendingErrorStatesForDefaultEntry.clear(); + } + + QList<QAbstractState*> statesToEnter_sorted = statesToEnter.toList(); + qSort(statesToEnter_sorted.begin(), statesToEnter_sorted.end(), stateEntryLessThan); + + for (int i = 0; i < statesToEnter_sorted.size(); ++i) { + QAbstractState *s = statesToEnter_sorted.at(i); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": entering" << s; +#endif + configuration.insert(s); + registerTransitions(s); + QAbstractStatePrivate::get(s)->callOnEntry(); + if (statesForDefaultEntry.contains(s)) { + // ### executeContent(s.initial.transition.children()) + } + if (isFinal(s)) { + QState *parent = s->parentState(); + if (parent) { + QState *grandparent = parent->parentState(); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": posting finished event for" << parent; +#endif + internalEventQueue.append(new QStateFinishedEvent(parent)); + if (grandparent && isParallel(grandparent)) { + bool allChildStatesFinal = true; + QList<QAbstractState*> childStates = QStatePrivate::get(grandparent)->childStates(); + for (int j = 0; j < childStates.size(); ++j) { + QAbstractState *cs = childStates.at(j); + if (!isInFinalState(cs)) { + allChildStatesFinal = false; + break; + } + } + if (allChildStatesFinal) { +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": posting finished event for" << grandparent; +#endif + internalEventQueue.append(new QStateFinishedEvent(grandparent)); + } + } + } + } + } + { + QSet<QAbstractState*>::const_iterator it; + for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) { + if (isFinal(*it) && (*it)->parentState() == rootState) { + processing = false; + stopProcessingReason = Finished; + break; + } + } + } +// qDebug() << "configuration:" << configuration.toList(); + return statesToEnter_sorted; +} + +void QStateMachinePrivate::addStatesToEnter(QAbstractState *s, QState *root, + QSet<QAbstractState*> &statesToEnter, + QSet<QAbstractState*> &statesForDefaultEntry) +{ + statesToEnter.insert(s); + if (isParallel(s)) { + QState *grp = qobject_cast<QState*>(s); + QList<QAbstractState*> lst = QStatePrivate::get(grp)->childStates(); + for (int i = 0; i < lst.size(); ++i) { + QAbstractState *child = lst.at(i); + addStatesToEnter(child, grp, statesToEnter, statesForDefaultEntry); + } + } else if (isCompound(s)) { + statesForDefaultEntry.insert(s); + QState *grp = qobject_cast<QState*>(s); + QAbstractState *initial = grp->initialState(); + if (initial != 0) { + addStatesToEnter(initial, grp, statesToEnter, statesForDefaultEntry); + } else { + setError(QStateMachine::NoInitialStateError, grp); + return; + } + } + QList<QState*> ancs = properAncestors(s, root); + for (int i = 0; i < ancs.size(); ++i) { + QState *anc = ancs.at(i); + if (!anc->parentState()) + continue; + statesToEnter.insert(anc); + if (isParallel(anc)) { + QList<QAbstractState*> lst = QStatePrivate::get(anc)->childStates(); + for (int j = 0; j < lst.size(); ++j) { + QAbstractState *child = lst.at(j); + bool hasDescendantInList = false; + QSet<QAbstractState*>::const_iterator it; + for (it = statesToEnter.constBegin(); it != statesToEnter.constEnd(); ++it) { + if (isDescendantOf(*it, child)) { + hasDescendantInList = true; + break; + } + } + if (!hasDescendantInList) + addStatesToEnter(child, anc, statesToEnter, statesForDefaultEntry); + } + } + } +} + +void QStateMachinePrivate::applyProperties(const QList<QAbstractTransition*> &transitionList, + const QList<QAbstractState*> &enteredStates) +{ +#ifndef QT_NO_ANIMATION + Q_Q(QStateMachine); + // Gracefully terminate playing animations. + for (int i = 0; i < playingAnimations.size(); ++i) + playingAnimations.at(i)->stop(); + playingAnimations.clear(); + for (int i = 0; i < resetEndValues.size(); ++i) + qobject_cast<QVariantAnimation*>(resetEndValues.at(i))->setEndValue(QVariant()); // ### generalize + resetEndValues.clear(); + + // Find the animations to use for the state change. + QList<QAbstractAnimation*> selectedAnimations; + for (int i = 0; i < transitionList.size(); ++i) + selectedAnimations << transitionList.at(i)->animations(); +#else + Q_UNUSED(transitionList); +#endif + + // Process the SetProperty definitions of the entered states. + QList<QPropertyAssignment> propertyAssignments; + QHash<RestorableId, QVariant> pendingRestorables = registeredRestorables; + for (int i = 0; i < enteredStates.size(); ++i) { + QAbstractState *s = enteredStates.at(i); + + QAbstractState::RestorePolicy restorePolicy = s->restorePolicy(); + if (restorePolicy == QAbstractState::GlobalRestorePolicy) + restorePolicy = globalRestorePolicy; + + QList<QPropertyAssignment> assignments = QAbstractStatePrivate::get(s)->propertyAssignments; + for (int j = 0; j < assignments.size(); ++j) { + const QPropertyAssignment &assn = assignments.at(j); + if (restorePolicy == QAbstractState::RestoreProperties) { + registerRestorable(assn.object, assn.propertyName); + } + pendingRestorables.remove(RestorableId(assn.object, assn.propertyName)); + propertyAssignments.append(assn); + } + } + propertyAssignments << restorablesToPropertyList(pendingRestorables); + +#ifndef QT_NO_ANIMATION + // Set the animated properties that did not finish animating and that are not + // set in the new state. + for (int i = 0; i < propertiesForAnimations.size(); ++i) { + QPropertyAssignment assn = propertiesForAnimations.at(i).second; + bool found = false; + for (int j = 0; j < propertyAssignments.size(); ++j) { + if ((propertyAssignments.at(j).object == assn.object) + && (propertyAssignments.at(j).propertyName == assn.propertyName)) { + found = true; + break; + } + } + if (!found) { + assn.object->setProperty(assn.propertyName, assn.value); + } + } + + // Initialize animations from SetProperty definitions. + propertiesForAnimations.clear(); + for (int i = 0; i < selectedAnimations.size(); ++i) { + QAbstractAnimation *anim = selectedAnimations.at(i); + QList<QPropertyAssignment>::iterator it; + for (it = propertyAssignments.begin(); it != propertyAssignments.end(); ) { + QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> > ret; + ret = initializeAnimation(anim, *it); + QList<QAbstractAnimation*> handlers = ret.first; + if (!handlers.isEmpty()) { + for (int j = 0; j < handlers.size(); ++j) + propertiesForAnimations.append(qMakePair(handlers.at(j), *it)); + it = propertyAssignments.erase(it); + } else { + ++it; + } + resetEndValues << ret.second; + } + + // We require that at least one animation is valid. + // ### generalize + QList<QVariantAnimation*> variantAnims = qFindChildren<QVariantAnimation*>(anim); + if (QVariantAnimation *va = qobject_cast<QVariantAnimation*>(anim)) + variantAnims.append(va); + bool hasValidEndValue = false; + for (int j = 0; j < variantAnims.size(); ++j) { + if (variantAnims.at(j)->endValue().isValid()) { + hasValidEndValue = true; + break; + } + } + + if (hasValidEndValue) { + QObject::disconnect(anim, SIGNAL(finished()), q, SLOT(_q_animationFinished())); + QObject::connect(anim, SIGNAL(finished()), q, SLOT(_q_animationFinished())); + anim->start(); + playingAnimations.append(anim); + } + } +#endif // !QT_NO_ANIMATION + + // Immediately set the properties that are not animated. + for (int i = 0; i < propertyAssignments.size(); ++i) { + const QPropertyAssignment &assn = propertyAssignments.at(i); + assn.object->setProperty(assn.propertyName, assn.value); + } +} + +bool QStateMachinePrivate::isFinal(const QAbstractState *s) +{ + return qobject_cast<const QFinalState*>(s) != 0; +} + +bool QStateMachinePrivate::isParallel(const QAbstractState *s) +{ + const QState *ss = qobject_cast<const QState*>(s); + return ss && QStatePrivate::get(ss)->isParallelGroup; +} + +bool QStateMachinePrivate::isCompound(const QAbstractState *s) +{ + const QState *group = qobject_cast<const QState*>(s); + if (!group) + return false; + return (!isParallel(group) && !QStatePrivate::get(group)->childStates().isEmpty()) + || (qobject_cast<QStateMachine*>(group->parent()) != 0); +} + +bool QStateMachinePrivate::isAtomic(const QAbstractState *s) +{ + const QState *ss = qobject_cast<const QState*>(s); + return (ss && !QStatePrivate::get(ss)->isParallelGroup + && QStatePrivate::get(ss)->childStates().isEmpty()) + || isFinal(s); +} + + +bool QStateMachinePrivate::isDescendantOf(const QAbstractState *state, const QAbstractState *other) +{ + Q_ASSERT(state != 0); + for (QAbstractState *s = state->parentState(); s != 0; s = s->parentState()) { + if (s == other) + return true; + } + return false; +} + +QList<QState*> QStateMachinePrivate::properAncestors(const QAbstractState *state, const QState *upperBound) +{ + Q_ASSERT(state != 0); + QList<QState*> result; + for (QState *s = state->parentState(); s && s != upperBound; s = s->parentState()) { + result.append(s); + } + return result; +} + +bool QStateMachinePrivate::isInFinalState(QAbstractState* s) const +{ + if (isCompound(s)) { + QState *grp = qobject_cast<QState*>(s); + QList<QAbstractState*> lst = QStatePrivate::get(grp)->childStates(); + for (int i = 0; i < lst.size(); ++i) { + QAbstractState *cs = lst.at(i); + if (isFinal(cs) && configuration.contains(cs)) + return true; + } + return false; + } else if (isParallel(s)) { + QState *grp = qobject_cast<QState*>(s); + QList<QAbstractState*> lst = QStatePrivate::get(grp)->childStates(); + for (int i = 0; i < lst.size(); ++i) { + QAbstractState *cs = lst.at(i); + if (!isInFinalState(cs)) + return false; + } + return true; + } + else + return false; +} + +void QStateMachinePrivate::registerRestorable(QObject *object, const QByteArray &propertyName) +{ + RestorableId id(object, propertyName); + if (!registeredRestorables.contains(id)) + registeredRestorables.insert(id, object->property(propertyName)); +} + +QList<QPropertyAssignment> QStateMachinePrivate::restorablesToPropertyList(const QHash<RestorableId, QVariant> &restorables) const +{ + QList<QPropertyAssignment> result; + QHash<RestorableId, QVariant>::const_iterator it; + for (it = restorables.constBegin(); it != restorables.constEnd(); ++it) { +// qDebug() << "restorable:" << it.key().first << it.key().second << it.value(); + result.append(QPropertyAssignment(it.key().first, it.key().second, it.value(), /*explicitlySet=*/false)); + } + return result; +} + +/*! + \internal + Returns true if the variable with the given \a id has been registered for restoration. +*/ +bool QStateMachinePrivate::hasRestorable(QObject *object, const QByteArray &propertyName) const +{ + return registeredRestorables.contains(RestorableId(object, propertyName)); +} + +QVariant QStateMachinePrivate::restorableValue(QObject *object, const QByteArray &propertyName) const +{ + return registeredRestorables.value(RestorableId(object, propertyName), QVariant()); +} + + +/*! + \internal + Unregisters the variable identified by \a id +*/ +void QStateMachinePrivate::unregisterRestorable(QObject *object, const QByteArray &propertyName) +{ +// qDebug() << "unregisterRestorable(" << object << propertyName << ")"; + RestorableId id(object, propertyName); + registeredRestorables.remove(id); +} + +QAbstractState *QStateMachinePrivate::findErrorState(QAbstractState *context) +{ + // If the user sets the root state's error state to 0, we return the initial error state + if (context == 0) + return initialErrorStateForRoot; + + // Find error state recursively in parent hierarchy if not set explicitly for context state + QAbstractState *errorState = 0; + QState *s = qobject_cast<QState*>(context); + if (s) { + errorState = s->errorState(); + if (!errorState) + errorState = findErrorState(s->parentState()); + return errorState; + } + + return errorState; +} + +void QStateMachinePrivate::setError(QStateMachine::Error errorCode, QAbstractState *currentContext) +{ + error = errorCode; + + switch (errorCode) { + case QStateMachine::NoInitialStateError: + Q_ASSERT(currentContext != 0); + + errorString = QStateMachine::tr("Missing initial state in compound state '%1'") + .arg(currentContext->objectName()); + + break; + case QStateMachine::NoDefaultStateInHistoryState: + Q_ASSERT(currentContext != 0); + + errorString = QStateMachine::tr("Missing default state in history state '%1'") + .arg(currentContext->objectName()); + break; + default: + errorString = QStateMachine::tr("Unknown error"); + }; + + pendingErrorStates.clear(); + pendingErrorStatesForDefaultEntry.clear(); + + QAbstractState *currentErrorState = findErrorState(currentContext); + + // Avoid infinite loop if the error state itself has an error + if (currentContext == currentErrorState) { + Q_ASSERT(currentContext != initialErrorStateForRoot); // RootErrorState is broken + currentErrorState = initialErrorStateForRoot; + } + + if (currentErrorState) { + QState *lca = findLCA(QList<QAbstractState*>() << currentErrorState << currentContext); + addStatesToEnter(currentErrorState, lca, pendingErrorStates, pendingErrorStatesForDefaultEntry); + } +} + +#ifndef QT_NO_ANIMATION + +QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> > +QStateMachinePrivate::initializeAnimation(QAbstractAnimation *abstractAnimation, + const QPropertyAssignment &prop) +{ + QList<QAbstractAnimation*> handledAnimations; + QList<QAbstractAnimation*> localResetEndValues; + QAnimationGroup *group = qobject_cast<QAnimationGroup*>(abstractAnimation); + if (group) { + for (int i = 0; i < group->animationCount(); ++i) { + QAbstractAnimation *animationChild = group->animationAt(i); + QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> > ret; + ret = initializeAnimation(animationChild, prop); + handledAnimations << ret.first; + localResetEndValues << ret.second; + } + } else { + QPropertyAnimation *animation = qobject_cast<QPropertyAnimation *>(abstractAnimation); + if (animation != 0 + && prop.object == animation->targetObject() + && prop.propertyName == animation->propertyName()) { + + if (!animation->startValue().isValid()) { + QByteArray propertyName = animation->propertyName(); + QVariant currentValue = animation->targetObject()->property(propertyName); + + QVariantAnimationPrivate::get(animation)->setDefaultStartValue(currentValue); + } + + // Only change end value if it is undefined + if (!animation->endValue().isValid()) { + animation->setEndValue(prop.value); + localResetEndValues.append(animation); + } + handledAnimations.append(animation); + } + } + return qMakePair(handledAnimations, localResetEndValues); +} + +static bool isAncestorOf(QObject *anc, QObject *o) +{ + for (o = o->parent() ; o != 0; o = o->parent()) { + if (o == anc) + return true; + } + return false; +} + +void QStateMachinePrivate::_q_animationFinished() +{ + Q_Q(QStateMachine); + QAbstractAnimation *animation = qobject_cast<QAbstractAnimation*>(q->sender()); + Q_ASSERT(animation != 0); + QList<QPair<QAbstractAnimation*, QPropertyAssignment> >::iterator it; + for (it = propertiesForAnimations.begin(); it != propertiesForAnimations.end(); ) { + QAbstractAnimation *a = (*it).first; + if (a == animation || isAncestorOf(animation, a)) { + QPropertyAssignment assn = (*it).second; + assn.object->setProperty(assn.propertyName, assn.value); + if (!assn.explicitlySet) + unregisterRestorable(assn.object, assn.propertyName); + it = propertiesForAnimations.erase(it); + } else { + ++it; + } + } + + playingAnimations.removeOne(animation); + if (playingAnimations.isEmpty()) + emit q->animationsFinished(); +} + +#endif // !QT_NO_ANIMATION + +namespace { + +class StartState : public QState +{ +public: + StartState(QState *parent) + : QState(parent) {} +protected: + void onEntry() {} + void onExit() {} +}; + +class InitialTransition : public QAbstractTransition +{ +public: + InitialTransition(QAbstractState *target) + : QAbstractTransition(QList<QAbstractState*>() << target) {} +protected: + virtual bool eventTest(QEvent *) const { return true; } + virtual void onTransition() {} +}; + +} // namespace + +void QStateMachinePrivate::_q_start() +{ + Q_Q(QStateMachine); + Q_ASSERT(state == Starting); + if (!rootState) { + state = NotRunning; + return; + } + QAbstractState *initial = rootState->initialState(); + if (initial == 0) + setError(QStateMachine::NoInitialStateError, rootState); + + configuration.clear(); + qDeleteAll(internalEventQueue); + internalEventQueue.clear(); + qDeleteAll(externalEventQueue); + externalEventQueue.clear(); + +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": starting"; +#endif + state = Running; + processingScheduled = true; // we call _q_process() below + emit q->started(); + + StartState *start = new StartState(rootState); + QAbstractTransition *initialTransition = new InitialTransition(initial); + start->addTransition(initialTransition); + QList<QAbstractTransition*> transitions; + transitions.append(initialTransition); + executeTransitionContent(transitions); + enterStates(transitions); + applyProperties(transitions, QList<QAbstractState*>() << initial); + delete start; + +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": initial configuration:" << configuration; +#endif + _q_process(); +} + +void QStateMachinePrivate::_q_process() +{ + Q_Q(QStateMachine); + Q_ASSERT(state == Running); + Q_ASSERT(!processing); + processing = true; + processingScheduled = false; +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": starting the event processing loop"; +#endif + while (processing) { + if (stop) { + stop = false; + processing = false; + stopProcessingReason = Stopped; + break; + } + QSet<QAbstractTransition*> enabledTransitions; + QEvent *e = new QEvent(QEvent::None); + enabledTransitions = selectTransitions(e); + if (enabledTransitions.isEmpty()) { + delete e; + e = 0; + } + if (enabledTransitions.isEmpty() && !internalEventQueue.isEmpty()) { + e = internalEventQueue.takeFirst(); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": dequeued internal event" << e << "of type" << e->type(); +#endif + enabledTransitions = selectTransitions(e); + if (enabledTransitions.isEmpty()) { + delete e; + e = 0; + } + } + if (enabledTransitions.isEmpty()) { + if (externalEventQueue.isEmpty()) { + if (internalEventQueue.isEmpty()) { + processing = false; + stopProcessingReason = EventQueueEmpty; + } + } else { + e = externalEventQueue.takeFirst(); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": dequeued external event" << e << "of type" << e->type(); +#endif + enabledTransitions = selectTransitions(e); + if (enabledTransitions.isEmpty()) { + delete e; + e = 0; + } + } + } + if (!enabledTransitions.isEmpty()) { + q->beginMicrostep(e); + microstep(enabledTransitions.toList()); + q->endMicrostep(e); + } +#ifdef QSTATEMACHINE_DEBUG + else { + qDebug() << q << ": no transitions enabled"; + } +#endif + delete e; + } +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": finished the event processing loop"; +#endif + switch (stopProcessingReason) { + case EventQueueEmpty: + break; + case Finished: + state = NotRunning; + emit q->finished(); + break; + case Stopped: + state = NotRunning; + emit q->stopped(); + break; + } +} + +void QStateMachinePrivate::scheduleProcess() +{ + if ((state != Running) || processing || processingScheduled) + return; + processingScheduled = true; + QMetaObject::invokeMethod(q_func(), "_q_process", Qt::QueuedConnection); +} + +void QStateMachinePrivate::registerTransitions(QAbstractState *state) +{ + QState *group = qobject_cast<QState*>(state); + if (!group) + return; + QList<QAbstractTransition*> transitions = QStatePrivate::get(group)->transitions(); + for (int i = 0; i < transitions.size(); ++i) { + QAbstractTransition *t = transitions.at(i); + if (QSignalTransition *st = qobject_cast<QSignalTransition*>(t)) { + registerSignalTransition(st); + } +#ifndef QT_NO_STATEMACHINE_EVENTFILTER + else if (QEventTransition *oet = qobject_cast<QEventTransition*>(t)) { + registerEventTransition(oet); + } +#endif + } +} + +void QStateMachinePrivate::unregisterTransition(QAbstractTransition *transition) +{ + if (QSignalTransition *st = qobject_cast<QSignalTransition*>(transition)) { + unregisterSignalTransition(st); + } +#ifndef QT_NO_STATEMACHINE_EVENTFILTER + else if (QEventTransition *oet = qobject_cast<QEventTransition*>(transition)) { + unregisterEventTransition(oet); + } +#endif +} + +#ifndef QT_STATEMACHINE_SOLUTION + +static int senderSignalIndex(const QObject *sender) +{ + QObjectPrivate *d = QObjectPrivate::get(const_cast<QObject*>(sender)); + QMutexLocker(&d->threadData->mutex); + if (!d->currentSender) + return -1; + + // Return -1 if d->currentSender isn't in d->senders + bool found = false; + for (int i = 0; !found && i < d->senders.count(); ++i) + found = (d->senders.at(i).sender == d->currentSender->sender); + if (!found) + return -1; + return d->currentSender->signal; +} + +#endif + +void QStateMachinePrivate::registerSignalTransition(QSignalTransition *transition) +{ + Q_Q(QStateMachine); + if (QSignalTransitionPrivate::get(transition)->signalIndex != -1) + return; // already registered + QObject *sender = QSignalTransitionPrivate::get(transition)->sender; + if (!sender) + return; + QByteArray signal = QSignalTransitionPrivate::get(transition)->signal; + if (signal.startsWith('0'+QSIGNAL_CODE)) + signal.remove(0, 1); + int signalIndex = sender->metaObject()->indexOfSignal(signal); + if (signalIndex == -1) { + qWarning("QSignalTransition: no such signal: %s::%s", + sender->metaObject()->className(), signal.constData()); + return; + } + QList<int> &connectedSignalIndexes = connections[sender]; + if (!connectedSignalIndexes.contains(signalIndex)) { +#ifndef QT_STATEMACHINE_SOLUTION + if (!signalEventGenerator) + signalEventGenerator = new QSignalEventGenerator(q); +#else + QSignalEventGenerator *signalEventGenerator = new QSignalEventGenerator(signalIndex, q); +#endif + bool ok = QMetaObject::connect(sender, signalIndex, signalEventGenerator, + signalEventGenerator->metaObject()->methodOffset()); + if (!ok) { +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": FAILED to add signal transition from" << transition->sourceState() + << ": ( sender =" << sender << ", signal =" << (signal.mid(1)) + << ", targets =" << transition->targetStates() << ")"; +#endif + return; + } + connectedSignalIndexes.append(signalIndex); + } + QSignalTransitionPrivate::get(transition)->signalIndex = signalIndex; +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": added signal transition from" << transition->sourceState() + << ": ( sender =" << sender << ", signal =" << (signal.mid(1)) + << ", targets =" << transition->targetStates() << ")"; +#endif +} + +void QStateMachinePrivate::unregisterSignalTransition(QSignalTransition *transition) +{ + int signalIndex = QSignalTransitionPrivate::get(transition)->signalIndex; + if (signalIndex == -1) + return; // not registered +#ifndef QT_STATEMACHINE_SOLUTION + const QObject *sender = QSignalTransitionPrivate::get(transition)->sender; + QList<int> &connectedSignalIndexes = connections[sender]; + Q_ASSERT(connectedSignalIndexes.contains(signalIndex)); + Q_ASSERT(signalEventGenerator != 0); + bool ok = QMetaObject::disconnect(sender, signalIndex, signalEventGenerator, + signalEventGenerator->metaObject()->methodOffset()); + if (ok) { + connectedSignalIndexes.removeOne(signalIndex); + if (connectedSignalIndexes.isEmpty()) + connections.remove(sender); + QSignalTransitionPrivate::get(transition)->signalIndex = -1; + } +#endif +} + +#ifndef QT_NO_STATEMACHINE_EVENTFILTER +void QStateMachinePrivate::registerEventTransition(QEventTransition *transition) +{ + Q_Q(QStateMachine); + if (QEventTransitionPrivate::get(transition)->registered) + return; + if (transition->eventType() >= QEvent::User) { + qWarning("QObject event transitions are not supported for custom types"); + return; + } + QObject *object = QEventTransitionPrivate::get(transition)->object; + if (!object) + return; +#ifndef QT_STATEMACHINE_SOLUTION + QObjectPrivate *od = QObjectPrivate::get(object); + if (!od->eventFilters.contains(q)) +#endif + object->installEventFilter(q); + qobjectEvents[object].insert(transition->eventType()); + QEventTransitionPrivate::get(transition)->registered = true; +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": added event transition from" << transition->sourceState() + << ": ( object =" << object << ", event =" << transition->eventType() + << ", targets =" << transition->targetStates() << ")"; +#endif +} + +void QStateMachinePrivate::unregisterEventTransition(QEventTransition *transition) +{ + Q_Q(QStateMachine); + if (!QEventTransitionPrivate::get(transition)->registered) + return; + QObject *object = QEventTransitionPrivate::get(transition)->object; + QSet<QEvent::Type> &events = qobjectEvents[object]; + events.remove(transition->eventType()); + if (events.isEmpty()) { + qobjectEvents.remove(object); + object->removeEventFilter(q); + } + QEventTransitionPrivate::get(transition)->registered = false; +} +#endif + +void QStateMachinePrivate::handleTransitionSignal(const QObject *sender, int signalIndex, + void **argv) +{ + const QList<int> &connectedSignalIndexes = connections[sender]; + Q_ASSERT(connectedSignalIndexes.contains(signalIndex)); + const QMetaObject *meta = sender->metaObject(); + QMetaMethod method = meta->method(signalIndex); + QList<QByteArray> parameterTypes = method.parameterTypes(); + int argc = parameterTypes.count(); + QList<QVariant> vargs; + for (int i = 0; i < argc; ++i) { + int type = QMetaType::type(parameterTypes.at(i)); + vargs.append(QVariant(type, argv[i+1])); + } + +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": sending signal event ( sender =" << sender + << ", signal =" << sender->metaObject()->method(signalIndex).signature() << ")"; +#endif + internalEventQueue.append(new QSignalEvent(sender, signalIndex, vargs)); + scheduleProcess(); +} + +/*! + Constructs a new state machine with the given \a parent. +*/ +QStateMachine::QStateMachine(QObject *parent) + : QObject( +#ifndef QT_STATEMACHINE_SOLUTION + *new QStateMachinePrivate, +#endif + parent) +#ifdef QT_STATEMACHINE_SOLUTION + , d_ptr(new QStateMachinePrivate) +#endif +{ +#ifdef QT_STATEMACHINE_SOLUTION + d_ptr->q_ptr = this; +#endif +} + +/*! + \internal +*/ +QStateMachine::QStateMachine(QStateMachinePrivate &dd, QObject *parent) + : QObject( +#ifndef QT_STATEMACHINE_SOLUTION + dd, +#endif + parent) +#ifdef QT_STATEMACHINE_SOLUTION + , d_ptr(&dd) +#endif +{ +#ifdef QT_STATEMACHINE_SOLUTION + d_ptr->q_ptr = this; +#endif +} + +/*! + Destroys this state machine. +*/ +QStateMachine::~QStateMachine() +{ +#ifdef QT_STATEMACHINE_SOLUTION + delete d_ptr; +#endif +} + +namespace { + +class RootErrorState : public QAbstractState +{ +public: + RootErrorState(QState *parent) + : QAbstractState(parent) + { + setObjectName(QString::fromLatin1("DefaultErrorState")); + } + + void onEntry() + { + QAbstractStatePrivate *d = QAbstractStatePrivate::get(this); + QStateMachine *machine = d->machine(); + + qWarning("Unrecoverable error detected in running state machine: %s", + qPrintable(machine->errorString())); + } + + void onExit() {} +}; + +class RootState : public QState +{ +public: + RootState(QState *parent) + : QState(parent) + { + } + + void onEntry() {} + void onExit() {} +}; + +} // namespace + +/*! + Returns this state machine's root state. +*/ +QState *QStateMachine::rootState() const +{ + Q_D(const QStateMachine); + if (!d->rootState) { + const_cast<QStateMachinePrivate*>(d)->rootState = new RootState(0); + const_cast<QStateMachinePrivate*>(d)->initialErrorStateForRoot = new RootErrorState(d->rootState); + d->rootState->setParent(const_cast<QStateMachine*>(this)); + d->rootState->setErrorState(d->initialErrorStateForRoot); + } + return d->rootState; +} + +/*! + Returns the error state of the state machine's root state. + + \sa QState::errorState() +*/ +QAbstractState *QStateMachine::errorState() const +{ + return rootState()->errorState(); +} + +/*! + Sets the error state of this state machine's root state to be \a state. When a running state + machine encounters an error which puts it in an undefined state, it will enter an error state + based on the context of the error that occurred. It will enter this state regardless of what + is currently in the event queue. + + If the erroneous state has an error state set, this will be entered by the machine. If no error + state has been set, the state machine will search the parent hierarchy recursively for an + error state. The error state of the root state can thus be seen as a global error state that + applies for the states for which a more specific error state has not been set. + + Before entering the error state, the state machine will set the error code returned by error() and + error message returned by errorString(). + + The default error state will print a warning to the console containing the information returned by + errorString(). By setting a new error state on either the state machine itself, or on specific + states, you can fine tune error handling in the state machine. + + If the root state's error state is set to 0, or if the error state selected by the machine itself + contains an error, the default error state will be used. + + \sa QState::setErrorState(), rootState() +*/ +void QStateMachine::setErrorState(QAbstractState *state) +{ + rootState()->setErrorState(state); +} + +/*! \enum QStateMachine::Error + + This enum type defines errors that can occur in the state machine at run time. When the state + machine encounters an unrecoverable error at run time, it will set the error code returned + by error(), the error message returned by errorString(), and enter an error state based on + the context of the error. + + \value NoError No error has occurred. + \value NoInitialStateError The machine has entered a QState with children which does not have an + initial state set. The context of this error is the state which is missing an initial + state. + \value NoDefaultStateInHistoryState The machine has entered a QHistoryState which does not have + a default state set. The context of this error is the QHistoryState which is missing a + default state. + + \sa setErrorState() +*/ + +/*! + Returns the error code of the last error that occurred in the state machine. +*/ +QStateMachine::Error QStateMachine::error() const +{ + Q_D(const QStateMachine); + return d->error; +} + +/*! + Returns the error string of the last error that occurred in the state machine. +*/ +QString QStateMachine::errorString() const +{ + Q_D(const QStateMachine); + return d->errorString; +} + +/*! + Clears the error string and error code of the state machine. +*/ +void QStateMachine::clearError() +{ + Q_D(QStateMachine); + d->errorString.clear(); + d->error = NoError; +} + +/*! + Returns the global restore policy of the state machine. + + \sa QActionState::restorePolicy() +*/ +QActionState::RestorePolicy QStateMachine::globalRestorePolicy() const +{ + Q_D(const QStateMachine); + return d->globalRestorePolicy; +} + +/*! + Sets the global restore policy of the state machine to \a restorePolicy. The default global + restore policy is QAbstractState::DoNotRestoreProperties. + + The global restore policy cannot be set to QAbstractState::GlobalRestorePolicy. + + \sa QAbstractState::setRestorePolicy() +*/ +void QStateMachine::setGlobalRestorePolicy(QAbstractState::RestorePolicy restorePolicy) +{ + Q_D(QStateMachine); + if (restorePolicy == QState::GlobalRestorePolicy) { + qWarning("QStateMachine::setGlobalRestorePolicy: Cannot set global restore policy to " + "GlobalRestorePolicy"); + return; + } + + d->globalRestorePolicy = restorePolicy; +} + +/*! + Returns this state machine's initial state, or 0 if no initial state has + been set. +*/ +QAbstractState *QStateMachine::initialState() const +{ + Q_D(const QStateMachine); + if (!d->rootState) + return 0; + return d->rootState->initialState(); +} + +/*! + Sets this state machine's initial \a state. +*/ +void QStateMachine::setInitialState(QAbstractState *state) +{ + Q_D(QStateMachine); + if (!d->rootState) { + if (!state) + return; + rootState()->setInitialState(state); + } + d->rootState->setInitialState(state); +} + +/*! + Adds the given \a state to this state machine. The state becomes a top-level + state (i.e. a child of the rootState()). + + If the state is already in a different machine, it will first be removed + from its old machine, and then added to this machine. + + \sa removeState(), rootState() +*/ +void QStateMachine::addState(QAbstractState *state) +{ + if (!state) { + qWarning("QStateMachine::addState: cannot add null state"); + return; + } + if (QAbstractStatePrivate::get(state)->machine() == this) { + qWarning("QStateMachine::addState: state has already been added to this machine"); + return; + } + state->setParent(rootState()); +} + +/*! + Removes the given \a state from this state machine. The state machine + releases ownership of the state. + + \sa addState() +*/ +void QStateMachine::removeState(QAbstractState *state) +{ + if (!state) { + qWarning("QStateMachine::removeState: cannot remove null state"); + return; + } + if (QAbstractStatePrivate::get(state)->machine() != this) { + qWarning("QStateMachine::removeState: state %p's machine (%p)" + " is different from this machine (%p)", + state, QAbstractStatePrivate::get(state)->machine(), this); + return; + } + state->setParent(0); +} + +/*! + Starts this state machine. + The machine will reset its configuration and transition to the initial + state. When a final top-level state is entered, the machine will emit the + finished() signal. + + \sa started(), finished(), stop(), initialState() +*/ +void QStateMachine::start() +{ + Q_D(QStateMachine); + + if (rootState()->initialState() == 0) { + qWarning("QStateMachine::start: No initial state set for machine. Refusing to start."); + return; + } + + switch (d->state) { + case QStateMachinePrivate::NotRunning: + d->state = QStateMachinePrivate::Starting; + QMetaObject::invokeMethod(this, "_q_start", Qt::QueuedConnection); + break; + case QStateMachinePrivate::Starting: + break; + case QStateMachinePrivate::Running: + qWarning("QStateMachine::start(): already running"); + break; + } +} + +/*! + Stops this state machine. + + \sa stopped() +*/ +void QStateMachine::stop() +{ + Q_D(QStateMachine); + switch (d->state) { + case QStateMachinePrivate::NotRunning: + break; + case QStateMachinePrivate::Starting: + // the machine will exit as soon as it enters the event processing loop + d->stop = true; + break; + case QStateMachinePrivate::Running: + d->stop = true; + d->scheduleProcess(); + break; + } +} + +/*! + Posts the given \a event for processing by this state machine, with a delay + of \a delay milliseconds. + + This function returns immediately. The event is added to the state machine's + event queue. Events are processed in the order posted. The state machine + takes ownership of the event and deletes it once it has been processed. + + You can only post events when the state machine is running. +*/ +void QStateMachine::postEvent(QEvent *event, int delay) +{ + Q_D(QStateMachine); + if (d->state != QStateMachinePrivate::Running) { + qWarning("QStateMachine::postEvent: cannot post event when the state machine is not running"); + return; + } +#ifdef QSTATEMACHINE_DEBUG + qDebug() << this << ": posting external event" << event << "with delay" << delay; +#endif + if (delay) { + int tid = startTimer(delay); + d->delayedEvents[tid] = event; + } else { + d->externalEventQueue.append(event); + d->scheduleProcess(); + } +} + +/*! + \internal + + Posts the given internal \a event for processing by this state machine. +*/ +void QStateMachine::postInternalEvent(QEvent *event) +{ + Q_D(QStateMachine); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << this << ": posting internal event" << event; +#endif + d->internalEventQueue.append(event); + d->scheduleProcess(); +} + +/*! + Returns the list of this state machine's states, or an empty list if the + state machine has no states. +*/ +QList<QAbstractState*> QStateMachine::states() const +{ + return QStatePrivate::get(rootState())->childStates(); +} + +/*! + \internal + + Returns the maximal consistent set of states (including parallel and final + states) that this state machine is currently in. If a state \c s is in the + configuration, it is always the case that the parent of \c s is also in + c. Note, however, that the rootState() is not an explicit member of the + configuration. +*/ +QSet<QAbstractState*> QStateMachine::configuration() const +{ + Q_D(const QStateMachine); + return d->configuration; +} + +/*! + \fn QStateMachine::started() + + This signal is emitted when the state machine has entered its initial state. + + \sa QStateMachine::finished(), QStateMachine::start() +*/ + +/*! + \fn QStateMachine::finished() + + This signal is emitted when the state machine has reached a top-level final + state. + + \sa QStateMachine::started() +*/ + +/*! + \fn QStateMachine::stopped() + + This signal is emitted when the state machine has stopped. + + \sa QStateMachine::stop() +*/ + +#ifndef QT_NO_ANIMATION + +/*! + \fn QStateMachine::animationsFinished() + + This signal is emitted when the state machine has finished playing all + animations associated with the latest transition (i.e., all properties have + reached their target values). +*/ + +#endif + +/*! + \reimp +*/ +bool QStateMachine::event(QEvent *e) +{ + Q_D(QStateMachine); + if (e->type() == QEvent::Timer) { + QTimerEvent *te = static_cast<QTimerEvent*>(e); + int tid = te->timerId(); + if (d->delayedEvents.contains(tid)) { + killTimer(tid); + QEvent *ee = d->delayedEvents.take(tid); + d->externalEventQueue.append(ee); + d->scheduleProcess(); + return true; + } + } else if (e->type() == QEvent::ChildAdded) { + QChildEvent *ce = static_cast<QChildEvent*>(e); + if (QAbstractState *state = qobject_cast<QAbstractState*>(ce->child())) { + if (state != rootState()) { + state->setParent(rootState()); + return true; + } + } + } + return QObject::event(e); +} + +#ifndef QT_NO_STATEMACHINE_EVENTFILTER +/*! + \reimp +*/ +bool QStateMachine::eventFilter(QObject *watched, QEvent *event) +{ + Q_D(QStateMachine); + Q_ASSERT(d->qobjectEvents.contains(watched)); + if (d->qobjectEvents[watched].contains(event->type())) + postEvent(new QBoundEvent(watched, d->handler->cloneEvent(event))); + return false; +} +#endif + +/*! + \internal + + This function is called when the state machine is about to select + transitions based on the given \a event. + + The default implementation does nothing. +*/ +void QStateMachine::beginSelectTransitions(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \internal + + This function is called when the state machine has finished selecting + transitions based on the given \a event. + + The default implementation does nothing. +*/ +void QStateMachine::endSelectTransitions(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \internal + + This function is called when the state machine is about to do a microstep. + + The default implementation does nothing. +*/ +void QStateMachine::beginMicrostep(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \internal + + This function is called when the state machine has finished doing a + microstep. + + The default implementation does nothing. +*/ +void QStateMachine::endMicrostep(QEvent *event) +{ + Q_UNUSED(event); +} + +static const uint qt_meta_data_QSignalEventGenerator[] = { + + // content: + 2, // revision + 0, // classname + 0, 0, // classinfo + 1, 12, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + + // slots: signature, parameters, type, tag, flags + 23, 22, 22, 22, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_QSignalEventGenerator[] = { + "QSignalEventGenerator\0\0execute()\0" +}; + +const QMetaObject QSignalEventGenerator::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_QSignalEventGenerator, + qt_meta_data_QSignalEventGenerator, 0 } +}; + +const QMetaObject *QSignalEventGenerator::metaObject() const +{ + return &staticMetaObject; +} + +void *QSignalEventGenerator::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_QSignalEventGenerator)) + return static_cast<void*>(const_cast< QSignalEventGenerator*>(this)); + return QObject::qt_metacast(_clname); +} + +int QSignalEventGenerator::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: { +#ifndef QT_STATEMACHINE_SOLUTION +// ### in Qt 4.6 we can use QObject::senderSignalIndex() + int signalIndex = senderSignalIndex(this); + Q_ASSERT(signalIndex != -1); +#endif + QStateMachine *machine = qobject_cast<QStateMachine*>(parent()); + QStateMachinePrivate::get(machine)->handleTransitionSignal(sender(), signalIndex, _a); + break; + } + default: ; + } + _id -= 1; + } + return _id; +} + +QSignalEventGenerator::QSignalEventGenerator( +#ifdef QT_STATEMACHINE_SOLUTION + int sigIdx, +#endif + QStateMachine *parent) + : QObject(parent) +#ifdef QT_STATEMACHINE_SOLUTION + , signalIndex(sigIdx) +#endif +{ +} + +/*! + \class QSignalEvent + + \brief The QSignalEvent class represents a Qt signal event. + + \ingroup statemachine + + A signal event is generated by a QStateMachine in response to a Qt + signal. The QSignalTransition class provides a transition associated with a + signal event. QSignalEvent is part of \l{The State Machine Framework}. + + The sender() function returns the object that generated the signal. The + signalIndex() function returns the index of the signal. The arguments() + function returns the arguments of the signal. + + \sa QSignalTransition +*/ + +/*! + Constructs a new QSignalEvent object with the given \a sender, \a + signalIndex and \a arguments. +*/ +QSignalEvent::QSignalEvent(const QObject *sender, int signalIndex, + const QList<QVariant> &arguments) + : +#ifndef QT_STATEMACHINE_SOLUTION + QEvent(QEvent::Signal) +#else + QEvent(QEvent::Type(QEvent::User-1)) +#endif + , m_sender(sender), m_signalIndex(signalIndex), m_arguments(arguments) +{ +} + +/*! + Destroys this QSignalEvent. +*/ +QSignalEvent::~QSignalEvent() +{ +} + +/*! + \fn QSignalEvent::sender() const + + Returns the object that emitted the signal. + + \sa QObject::sender() +*/ + +/*! + \fn QSignalEvent::signalIndex() const + + Returns the index of the signal. + + \sa QMetaObject::indexOfSignal() +*/ + +/*! + \fn QSignalEvent::arguments() const + + Returns the arguments of the signal. +*/ + +/*! + \class QStateFinishedEvent + + \brief The QStateFinishedEvent class contains parameters that describe a state that has finished. + + \ingroup statemachine + + A state is finished when one of its final child states (a QFinalState) is + entered; this will cause a QStateFinishedEvent to be generated by the state + machine. QStateFinishedEvent is part of \l{The State Machine Framework}. + + Typically you do not create QStateFinishedEvent objects yourself, but rather + use QStateFinishedTransition to create a transition that's triggered by a + state's finished event. + + \sa QStateFinishedTransition +*/ + +/*! + Constructs a new QStateFinishedEvent object associated with the given \a state. +*/ +QStateFinishedEvent::QStateFinishedEvent(QState *state) + : +#ifndef QT_STATEMACHINE_SOLUTION + QEvent(StateFinished) +#else + QEvent(QEvent::Type(QEvent::User-2)) +#endif + , m_state(state) +{ +} + +/*! + Destroys this QStateFinishedEvent. +*/ +QStateFinishedEvent::~QStateFinishedEvent() +{ +} + +/*! + Returns the state associated with this QStateFinishedEvent. +*/ +QState *QStateFinishedEvent::state() const +{ + return m_state; +} + +QT_END_NAMESPACE + +#include "moc_qstatemachine.cpp" diff --git a/src/corelib/statemachine/qstatemachine.h b/src/corelib/statemachine/qstatemachine.h new file mode 100644 index 0000000..c7de171 --- /dev/null +++ b/src/corelib/statemachine/qstatemachine.h @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSTATEMACHINE_H +#define QSTATEMACHINE_H + +#ifndef QT_STATEMACHINE_SOLUTION +# include <QtCore/qactionstate.h> +#else +# include "qactionstate.h" +#endif + +#include <QtCore/qlist.h> +#include <QtCore/qobject.h> +#include <QtCore/qset.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QEvent; +class QAbstractState; +class QState; + +class QStateMachinePrivate; +class Q_CORE_EXPORT QStateMachine : public QObject +{ + Q_OBJECT + Q_PROPERTY(QState* rootState READ rootState) + Q_PROPERTY(QAbstractState* initialState READ initialState WRITE setInitialState) + Q_PROPERTY(QAbstractState* errorState READ errorState WRITE setErrorState) + Q_PROPERTY(QString errorString READ errorString) +public: + + enum Error { + NoError, + NoInitialStateError, + NoDefaultStateInHistoryState, + }; + + QStateMachine(QObject *parent = 0); + ~QStateMachine(); + + void addState(QAbstractState *state); + void removeState(QAbstractState *state); + + QState *rootState() const; + + QAbstractState *initialState() const; + void setInitialState(QAbstractState *state); + + QAbstractState *errorState() const; + void setErrorState(QAbstractState *state); + + Error error() const; + QString errorString() const; + void clearError(); + + QAbstractState::RestorePolicy globalRestorePolicy() const; + void setGlobalRestorePolicy(QAbstractState::RestorePolicy restorePolicy); + + void postEvent(QEvent *event, int delay = 0); + + QList<QAbstractState*> states() const; + QSet<QAbstractState*> configuration() const; + +#ifndef QT_NO_STATEMACHINE_EVENTFILTER + bool eventFilter(QObject *watched, QEvent *event); +#endif + +public Q_SLOTS: + void start(); + void stop(); + +Q_SIGNALS: + void started(); + void stopped(); + void finished(); + +#ifndef QT_NO_ANIMATION + void animationsFinished(); +#endif + +protected: + void postInternalEvent(QEvent *event); + + virtual void beginSelectTransitions(QEvent *event); + virtual void endSelectTransitions(QEvent *event); + + virtual void beginMicrostep(QEvent *event); + virtual void endMicrostep(QEvent *event); + + bool event(QEvent *e); + +protected: +#ifdef QT_STATEMACHINE_SOLUTION + QStateMachinePrivate *d_ptr; +#endif + QStateMachine(QStateMachinePrivate &dd, QObject *parent); + +private: + Q_DISABLE_COPY(QStateMachine) + Q_DECLARE_PRIVATE(QStateMachine) + Q_PRIVATE_SLOT(d_func(), void _q_start()) + Q_PRIVATE_SLOT(d_func(), void _q_process()) +#ifndef QT_NO_ANIMATION + Q_PRIVATE_SLOT(d_func(), void _q_animationFinished()) +#endif +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h new file mode 100644 index 0000000..04dc71e --- /dev/null +++ b/src/corelib/statemachine/qstatemachine_p.h @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSTATEMACHINE_P_H +#define QSTATEMACHINE_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. +// + +#ifndef QT_STATEMACHINE_SOLUTION +#include <private/qobject_p.h> +#endif +#include <QtCore/qcoreevent.h> +#include <QtCore/qhash.h> +#include <QtCore/qlist.h> +#include <QtCore/qpair.h> +#include <QtCore/qset.h> + +#include "qabstractstate_p.h" + +QT_BEGIN_NAMESPACE + +class QEvent; +#ifndef QT_NO_STATEMACHINE_EVENTFILTER +class QEventTransition; +#endif +class QSignalEventGenerator; +class QSignalTransition; +class QAbstractState; +class QAbstractTransition; +class QState; + +#ifndef QT_NO_ANIMATION +class QAbstractAnimation; +#endif + +class QStateMachine; +class Q_CORE_EXPORT QStateMachinePrivate +#ifndef QT_STATEMACHINE_SOLUTION + : public QObjectPrivate +#endif +{ + Q_DECLARE_PUBLIC(QStateMachine) +public: + enum State { + NotRunning, + Starting, + Running + }; + enum StopProcessingReason { + EventQueueEmpty, + Finished, + Stopped + }; + + QStateMachinePrivate(); + ~QStateMachinePrivate(); + + static QStateMachinePrivate *get(QStateMachine *q); + + static QState *findLCA(const QList<QAbstractState*> &states); + + static bool stateEntryLessThan(QAbstractState *s1, QAbstractState *s2); + static bool stateExitLessThan(QAbstractState *s1, QAbstractState *s2); + + QAbstractState *findErrorState(QAbstractState *context); + void setError(QStateMachine::Error error, QAbstractState *currentContext); + + // private slots + void _q_start(); + void _q_process(); +#ifndef QT_NO_ANIMATION + void _q_animationFinished(); +#endif + + void microstep(const QList<QAbstractTransition*> &transitionList); + bool isPreempted(const QAbstractState *s, const QSet<QAbstractTransition*> &transitions) const; + QSet<QAbstractTransition*> selectTransitions(QEvent *event) const; + QList<QAbstractState*> exitStates(const QList<QAbstractTransition*> &transitionList); + void executeTransitionContent(const QList<QAbstractTransition*> &transitionList); + QList<QAbstractState*> enterStates(const QList<QAbstractTransition*> &enabledTransitions); + void addStatesToEnter(QAbstractState *s, QState *root, + QSet<QAbstractState*> &statesToEnter, + QSet<QAbstractState*> &statesForDefaultEntry); + + void applyProperties(const QList<QAbstractTransition*> &transitionList, + const QList<QAbstractState*> &enteredStates); + + bool isInFinalState(QAbstractState *s) const; + static bool isFinal(const QAbstractState *s); + static bool isParallel(const QAbstractState *s); + static bool isCompound(const QAbstractState *s); + static bool isAtomic(const QAbstractState *s); + static bool isDescendantOf(const QAbstractState *s, const QAbstractState *other); + static QList<QState*> properAncestors(const QAbstractState *s, const QState *upperBound); + + void registerTransitions(QAbstractState *state); + void registerSignalTransition(QSignalTransition *transition); + void unregisterSignalTransition(QSignalTransition *transition); +#ifndef QT_NO_STATEMACHINE_EVENTFILTER + void registerEventTransition(QEventTransition *transition); + void unregisterEventTransition(QEventTransition *transition); +#endif + void unregisterTransition(QAbstractTransition *transition); + void handleTransitionSignal(const QObject *sender, int signalIndex, + void **args); + void scheduleProcess(); + + typedef QPair<QObject *, QByteArray> RestorableId; + QHash<RestorableId, QVariant> registeredRestorables; + void registerRestorable(QObject *object, const QByteArray &propertyName); + void unregisterRestorable(QObject *object, const QByteArray &propertyName); + bool hasRestorable(QObject *object, const QByteArray &propertyName) const; + QVariant restorableValue(QObject *object, const QByteArray &propertyName) const; + QList<QPropertyAssignment> restorablesToPropertyList(const QHash<RestorableId, QVariant> &restorables) const; + + State state; + bool processing; + bool processingScheduled; + bool stop; + StopProcessingReason stopProcessingReason; + QState *rootState; + QSet<QAbstractState*> configuration; + QList<QEvent*> internalEventQueue; + QList<QEvent*> externalEventQueue; + + QStateMachine::Error error; + QActionState::RestorePolicy globalRestorePolicy; + + QString errorString; + QSet<QAbstractState *> pendingErrorStates; + QSet<QAbstractState *> pendingErrorStatesForDefaultEntry; + QAbstractState *initialErrorStateForRoot; + +#ifndef QT_NO_ANIMATION + QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> > + initializeAnimation(QAbstractAnimation *abstractAnimation, + const QPropertyAssignment &prop); + + QList<QPair<QAbstractAnimation*, QPropertyAssignment> > propertiesForAnimations; + QList<QAbstractAnimation*> playingAnimations; + QList<QAbstractAnimation*> resetEndValues; +#endif + +#ifndef QT_STATEMACHINE_SOLUTION + QSignalEventGenerator *signalEventGenerator; +#endif + QHash<const QObject*, QList<int> > connections; +#ifndef QT_NO_STATEMACHINE_EVENTFILTER + QHash<QObject*, QSet<QEvent::Type> > qobjectEvents; +#endif + QHash<int, QEvent*> delayedEvents; + + typedef QEvent* (*f_cloneEvent)(QEvent*); + struct Handler { + f_cloneEvent cloneEvent; + }; + + static const Handler *handler; + +#ifdef QT_STATEMACHINE_SOLUTION + QStateMachine *q_ptr; +#endif +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qtransition.cpp b/src/corelib/statemachine/qtransition.cpp new file mode 100644 index 0000000..3becc3a --- /dev/null +++ b/src/corelib/statemachine/qtransition.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtransition.h" +#include "qtransition_p.h" +#include "qstateaction.h" +#include "qstateaction_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QTransition + + \brief The QTransition class provides an action-based transition. + + \ingroup statemachine + + QTransition provides an action-based transition; you add actions with the + addAction() function. The transition executes the actions when the + transition is triggered. QTransition is part of \l{The State Machine + Framework}. + + The invokeMethodOnTransition() function is used for defining method + invocations that should be performed when a transition is taken. + + \code + QStateMachine machine; + QState *s1 = new QState(); + machine.addState(s1); + QTransition *t1 = new QTransition(); + QLabel label; + t1->invokeMethodOnTransition(&label, "clear"); + QState *s2 = new QState(); + machine.addState(s2); + t1->setTargetState(s2); + s1->addTransition(t1); + \endcode + + Actions are executed in the order in which they were added. + + \sa QState::addTransition(), QStateAction +*/ + +QTransitionPrivate::QTransitionPrivate() +{ +} + +QTransitionPrivate::~QTransitionPrivate() +{ +} + +QTransitionPrivate *QTransitionPrivate::get(QTransition *q) +{ + return q->d_func(); +} + +const QTransitionPrivate *QTransitionPrivate::get(const QTransition *q) +{ + return q->d_func(); +} + +QList<QStateAction*> QTransitionPrivate::actions() const +{ + QList<QStateAction*> result; + QList<QObject*>::const_iterator it; +#ifdef QT_STATEMACHINE_SOLUTION + const QObjectList &children = q_func()->children(); +#endif + for (it = children.constBegin(); it != children.constEnd(); ++it) { + QStateAction *s = qobject_cast<QStateAction*>(*it); + if (s) + result.append(s); + } + return result; +} + +/*! + Constructs a new QTransition object with the given \a sourceState. +*/ +QTransition::QTransition(QState *sourceState) + : QAbstractTransition(*new QTransitionPrivate, sourceState) +{ +} + +/*! + Constructs a new QTransition object with the given \a targets and \a + sourceState. +*/ +QTransition::QTransition(const QList<QAbstractState*> &targets, QState *sourceState) + : QAbstractTransition(*new QTransitionPrivate, targets, sourceState) +{ +} + +/*! + \internal +*/ +QTransition::QTransition(QTransitionPrivate &dd, QState *parent) + : QAbstractTransition(dd, parent) +{ +} + +/*! + \internal +*/ +QTransition::QTransition(QTransitionPrivate &dd, const QList<QAbstractState*> &targets, QState *parent) + : QAbstractTransition(dd, targets, parent) +{ +} + +/*! + Destroys this transition. +*/ +QTransition::~QTransition() +{ +} + +/*! + Instructs this QTransition to invoke the given \a method of the given \a + object with the given \a arguments when the transition is taken. This + function will create a QStateInvokeMethodAction object and add it to the + actions of the transition. + + \sa setPropertyOnTransition() +*/ +void QTransition::invokeMethodOnTransition(QObject *object, const char *method, + const QList<QVariant> &arguments) +{ + addAction(new QStateInvokeMethodAction(object, method, arguments)); +} + +/*! + Adds the given \a action to this transition. + The action will be executed when the transition is triggered. + The transition takes ownership of the action. + + \sa removeAction() +*/ +void QTransition::addAction(QStateAction *action) +{ + if (!action) { + qWarning("QTransition::addAction: cannot add null action"); + return; + } + action->setParent(this); +} + +/*! + Removes the given \a action from this transition. + The transition releases ownership of the action. + + \sa addAction() +*/ +void QTransition::removeAction(QStateAction *action) +{ + if (!action) { + qWarning("QTransition::removeAction: cannot remove null action"); + return; + } + action->setParent(0); +} + +/*! + Returns this transitions's actions, or an empty list if the transition has + no actions. + + \sa addAction() +*/ +QList<QStateAction*> QTransition::actions() const +{ + Q_D(const QTransition); + return d->actions(); +} + +/*! + \reimp +*/ +void QTransition::onTransition() +{ + Q_D(QTransition); + QList<QStateAction*> actions = d->actions(); + for (int i = 0; i < actions.size(); ++i) + QStateActionPrivate::get(actions.at(i))->callExecute(); +} + +/*! + \reimp +*/ +bool QTransition::event(QEvent *e) +{ + return QAbstractTransition::event(e); +} + +QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qtransition.h b/src/corelib/statemachine/qtransition.h new file mode 100644 index 0000000..7eec136 --- /dev/null +++ b/src/corelib/statemachine/qtransition.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTRANSITION_H +#define QTRANSITION_H + +#ifndef QT_STATEMACHINE_SOLUTION +#include <QtCore/qabstracttransition.h> +#else +#include "qabstracttransition.h" +#endif + +#include <QtCore/qvariant.h> +#include <QtCore/qlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QStateAction; + +class QTransitionPrivate; +class Q_CORE_EXPORT QTransition : public QAbstractTransition +{ + Q_OBJECT +public: + QTransition(QState *sourceState = 0); + QTransition(const QList<QAbstractState*> &targets, QState *sourceState = 0); + ~QTransition(); + + void invokeMethodOnTransition(QObject *object, const char *method, + const QList<QVariant> &args = QList<QVariant>()); + + void addAction(QStateAction *action); + void removeAction(QStateAction *action); + QList<QStateAction*> actions() const; + +protected: + virtual void onTransition(); + + bool event(QEvent *e); + +protected: + QTransition(QTransitionPrivate &dd, QState *parent); + QTransition(QTransitionPrivate &dd, const QList<QAbstractState*> &targets, QState *parent); + +private: + Q_DISABLE_COPY(QTransition) + Q_DECLARE_PRIVATE(QTransition) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qtransition_p.h b/src/corelib/statemachine/qtransition_p.h new file mode 100644 index 0000000..d43a4d4 --- /dev/null +++ b/src/corelib/statemachine/qtransition_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTRANSITION_P_H +#define QTRANSITION_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 "qabstracttransition_p.h" + +#include <QtCore/qlist.h> + +QT_BEGIN_NAMESPACE + +class QStateAction; + +class QTransition; +class Q_CORE_EXPORT QTransitionPrivate : public QAbstractTransitionPrivate +{ + Q_DECLARE_PUBLIC(QTransition) +public: + QTransitionPrivate(); + ~QTransitionPrivate(); + + static QTransitionPrivate *get(QTransition *q); + static const QTransitionPrivate *get(const QTransition *q); + + QList<QStateAction*> actions() const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/statemachine.pri b/src/corelib/statemachine/statemachine.pri new file mode 100644 index 0000000..9c797e2 --- /dev/null +++ b/src/corelib/statemachine/statemachine.pri @@ -0,0 +1,42 @@ +HEADERS += $$PWD/qstatemachine.h \ + $$PWD/qstatemachine_p.h \ + $$PWD/qstateaction.h \ + $$PWD/qstateaction_p.h \ + $$PWD/qsignaleventgenerator_p.h \ + $$PWD/qabstractstate.h \ + $$PWD/qabstractstate_p.h \ + $$PWD/qactionstate.h \ + $$PWD/qactionstate_p.h \ + $$PWD/qstate.h \ + $$PWD/qstate_p.h \ + $$PWD/qfinalstate.h \ + $$PWD/qhistorystate.h \ + $$PWD/qhistorystate_p.h \ + $$PWD/qabstracttransition.h \ + $$PWD/qabstracttransition_p.h \ + $$PWD/qtransition.h \ + $$PWD/qtransition_p.h \ + $$PWD/qstatefinishedevent.h \ + $$PWD/qstatefinishedtransition.h \ + $$PWD/qsignalevent.h \ + $$PWD/qsignaltransition.h \ + $$PWD/qsignaltransition_p.h + +SOURCES += $$PWD/qstatemachine.cpp \ + $$PWD/qstateaction.cpp \ + $$PWD/qabstractstate.cpp \ + $$PWD/qactionstate.cpp \ + $$PWD/qstate.cpp \ + $$PWD/qfinalstate.cpp \ + $$PWD/qhistorystate.cpp \ + $$PWD/qabstracttransition.cpp \ + $$PWD/qtransition.cpp \ + $$PWD/qstatefinishedtransition.cpp \ + $$PWD/qsignaltransition.cpp + +!contains(DEFINES, QT_NO_STATEMACHINE_EVENTFILTER) { +HEADERS += $$PWD/qboundevent_p.h \ + $$PWD/qeventtransition.h \ + $$PWD/qeventtransition_p.h +SOURCES += $$PWD/qeventtransition.cpp +} diff --git a/src/corelib/tools/qeasingcurve.cpp b/src/corelib/tools/qeasingcurve.cpp new file mode 100644 index 0000000..a1a0d1f --- /dev/null +++ b/src/corelib/tools/qeasingcurve.cpp @@ -0,0 +1,844 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + +| *property* | *Used for type* | +| period | QEasingCurve::{In,Out,InOut,OutIn}Elastic | +| amplitude | QEasingCurve::{In,Out,InOut,OutIn}Bounce, QEasingCurve::{In,Out,InOut,OutIn}Elastic | +| overshoot | QEasingCurve::{In,Out,InOut,OutIn}Back | + +*/ + + + + +/*! + \class QEasingCurve + \ingroup animation + \brief The QEasingCurve class provides easing curves for controlling animation. + + Easing curves describe a function that controls how the speed of the interpolation + between 0 and 1 should be. Easing curves allow transitions from + one value to another to appear more natural than a simple constant speed would allow. + The QEasingCurve class is usually used in conjunction with the QAnimation class, + but can be used on its own. + + To calculate the speed of the interpolation, the easing curve provides the function + valueForProgress(), where the \a progress argument specifies the progress of the + interpolation: 0 is the start value of the interpolation, 1 is the end value of the + interpolation. The returned value is the effective progress of the interpolation. + If the returned value is the same as the input value for all input values the easing + curve is a linear curve. This is the default behaviour. + + For example, + \code + QEasingCurve easing(QEasingCurve::InOutQuad); + + for(qreal t = 0.0; t < 1.0; t+=0.1) + qWarning() << "Effective progress" << t << " is + << easing.valueForProgress(t); + \endcode + will print the effective progress of the interpolation between 0 and 1. + + When using a QAnimation, the easing curve will be used to control the + progress of the interpolation between startValue and endValue: + \code + QAnimation animation; + animation.setStartValue(0); + animation.setEndValue(1000); + animation.setDuration(1000); + animation.setEasingCurve(QEasingCurve::InOutQuad); + \endcode + */ + +/*! + \enum QEasingCurve::Type + + The type of easing curve. + + \value Linear \inlineimage qeasingcurve-linear.png + \br + Easing equation function for a simple linear tweening, + with no easing. + \value InQuad \inlineimage qeasingcurve-inquad.png + \br + Easing equation function for a quadratic (t^2) easing + in: accelerating from zero velocity. + \value OutQuad \inlineimage qeasingcurve-outquad.png + \br + Easing equation function for a quadratic (t^2) easing + out: decelerating to zero velocity. + \value InOutQuad \inlineimage qeasingcurve-inoutquad.png + \br + Easing equation function for a quadratic (t^2) easing + in/out: acceleration until halfway, then deceleration. + \value OutInQuad \inlineimage qeasingcurve-outinquad.png + \br + Easing equation function for a quadratic (t^2) easing + out/in: deceleration until halfway, then acceleration. + \value InCubic \inlineimage qeasingcurve-incubic.png + \br + Easing equation function for a cubic (t^3) easing + in: accelerating from zero velocity. + \value OutCubic \inlineimage qeasingcurve-outcubic.png + \br + Easing equation function for a cubic (t^3) easing + out: decelerating from zero velocity. + \value InOutCubic \inlineimage qeasingcurve-inoutcubic.png + \br + Easing equation function for a cubic (t^3) easing + in/out: acceleration until halfway, then deceleration. + \value OutInCubic \inlineimage qeasingcurve-outincubic.png + \br + Easing equation function for a cubic (t^3) easing + out/in: deceleration until halfway, then acceleration. + \value InQuart \inlineimage qeasingcurve-inquart.png + \br + Easing equation function for a quartic (t^4) easing + in: accelerating from zero velocity. + \value OutQuart \inlineimage qeasingcurve-outquart.png + \br + Easing equation function for a quartic (t^4) easing + out: decelerating from zero velocity. + \value InOutQuart \inlineimage qeasingcurve-inoutquart.png + \br + Easing equation function for a quartic (t^4) easing + in/out: acceleration until halfway, then deceleration. + \value OutInQuart \inlineimage qeasingcurve-outinquart.png + \br + Easing equation function for a quartic (t^4) easing + out/in: deceleration until halfway, then acceleration. + \value InQuint \inlineimage qeasingcurve-inquint.png + \br + Easing equation function for a quintic (t^5) easing + in: accelerating from zero velocity. + \value OutQuint \inlineimage qeasingcurve-outquint.png + \br + Easing equation function for a quintic (t^5) easing + out: decelerating from zero velocity. + \value InOutQuint \inlineimage qeasingcurve-inoutquint.png + \br + Easing equation function for a quintic (t^5) easing + in/out: acceleration until halfway, then deceleration. + \value OutInQuint \inlineimage qeasingcurve-outinquint.png + \br + Easing equation function for a quintic (t^5) easing + out/in: deceleration until halfway, then acceleration. + \value InSine \inlineimage qeasingcurve-insine.png + \br + Easing equation function for a sinusoidal (sin(t)) easing + in: accelerating from zero velocity. + \value OutSine \inlineimage qeasingcurve-outsine.png + \br + Easing equation function for a sinusoidal (sin(t)) easing + out: decelerating from zero velocity. + \value InOutSine \inlineimage qeasingcurve-inoutsine.png + \br + Easing equation function for a sinusoidal (sin(t)) easing + in/out: acceleration until halfway, then deceleration. + \value OutInSine \inlineimage qeasingcurve-outinsine.png + \br + Easing equation function for a sinusoidal (sin(t)) easing + out/in: deceleration until halfway, then acceleration. + \value InExpo \inlineimage qeasingcurve-inexpo.png + \br + Easing equation function for an exponential (2^t) easing + in: accelerating from zero velocity. + \value OutExpo \inlineimage qeasingcurve-outexpo.png + \br + Easing equation function for an exponential (2^t) easing + out: decelerating from zero velocity. + \value InOutExpo \inlineimage qeasingcurve-inoutexpo.png + \br + Easing equation function for an exponential (2^t) easing + in/out: acceleration until halfway, then deceleration. + \value OutInExpo \inlineimage qeasingcurve-outinexpo.png + \br + Easing equation function for an exponential (2^t) easing + out/in: deceleration until halfway, then acceleration. + \value InCirc \inlineimage qeasingcurve-incirc.png + \br + Easing equation function for a circular (sqrt(1-t^2)) easing + in: accelerating from zero velocity. + \value OutCirc \inlineimage qeasingcurve-outcirc.png + \br + Easing equation function for a circular (sqrt(1-t^2)) easing + out: decelerating from zero velocity. + \value InOutCirc \inlineimage qeasingcurve-inoutcirc.png + \br + Easing equation function for a circular (sqrt(1-t^2)) easing + in/out: acceleration until halfway, then deceleration. + \value OutInCirc \inlineimage qeasingcurve-outincirc.png + \br + Easing equation function for a circular (sqrt(1-t^2)) easing + out/in: deceleration until halfway, then acceleration. + \value InElastic \inlineimage qeasingcurve-inelastic.png + \br + Easing equation function for an elastic + (exponentially decaying sine wave) easing in: + accelerating from zero velocity. The peak amplitude + can be set with the \e amplitude parameter, and the + period of decay by the \e period parameter. + \value OutElastic \inlineimage qeasingcurve-outelastic.png + \br + Easing equation function for an elastic + (exponentially decaying sine wave) easing out: + decelerating from zero velocity. The peak amplitude + can be set with the \e amplitude parameter, and the + period of decay by the \e period parameter. + \value InOutElastic \inlineimage qeasingcurve-inoutelastic.png + \br + Easing equation function for an elastic + (exponentially decaying sine wave) easing in/out: + acceleration until halfway, then deceleration. + \value OutInElastic \inlineimage qeasingcurve-outinelastic.png + \br + Easing equation function for an elastic + (exponentially decaying sine wave) easing out/in: + deceleration until halfway, then acceleration. + \value InBack \inlineimage qeasingcurve-inback.png + \br + Easing equation function for a back (overshooting + cubic easing: (s+1)*t^3 - s*t^2) easing in: + accelerating from zero velocity. + \value OutBack \inlineimage qeasingcurve-outback.png + \br + Easing equation function for a back (overshooting + cubic easing: (s+1)*t^3 - s*t^2) easing out: + decelerating from zero velocity. + \value InOutBack \inlineimage qeasingcurve-inoutback.png + \br + Easing equation function for a back (overshooting + cubic easing: (s+1)*t^3 - s*t^2) easing in/out: + acceleration until halfway, then deceleration. + \value OutInBack \inlineimage qeasingcurve-outinback.png + \br + Easing equation function for a back (overshooting + cubic easing: (s+1)*t^3 - s*t^2) easing out/in: + deceleration until halfway, then acceleration. + \value InBounce \inlineimage qeasingcurve-inbounce.png + \br + Easing equation function for a bounce (exponentially + decaying parabolic bounce) easing in: accelerating + from zero velocity. + \value OutBounce \inlineimage qeasingcurve-outbounce.png + \br + Easing equation function for a bounce (exponentially + decaying parabolic bounce) easing out: decelerating + from zero velocity. + \value InOutBounce \inlineimage qeasingcurve-inoutbounce.png + \br + Easing equation function for a bounce (exponentially + decaying parabolic bounce) easing in/out: + acceleration until halfway, then deceleration. + \value OutInBounce \inlineimage qeasingcurve-outinbounce.png + \br + Easing equation function for a bounce (exponentially + decaying parabolic bounce) easing out/in: + deceleration until halfway, then acceleration. + \omitvalue InCurve + \omitvalue OutCurve + \omitvalue SineCurve + \omitvalue CosineCurve + \value Custom This is returned if the user have specified a custom curve type with setCustomType(). Note that you cannot call setType() with this value, but type() can return it. + \omitvalue NCurveTypes +*/ + +/*! + \typedef QEasingCurve::EasingFunction + + This is a typedef for a pointer to a function with the following + signature: + + \snippet doc/src/snippets/code/src_corelib_tools_qeasingcurve.cpp 0 + +*/ + +#include "qeasingcurve.h" + +QT_BEGIN_NAMESPACE + +static bool isConfigFunction(QEasingCurve::Type type) +{ + return type >= QEasingCurve::InElastic + && type <= QEasingCurve::OutInBounce; +} + +class QEasingCurveFunction +{ +public: + enum Type { In, Out, InOut, OutIn }; + + QEasingCurveFunction(QEasingCurveFunction::Type type = In, qreal period = 0.3, qreal amplitude = 1.0, + qreal overshoot = 1.70158f) + : _t(type), _p(period), _a(amplitude), _o(overshoot) + { } + virtual ~QEasingCurveFunction() {} + virtual qreal value(qreal t); + virtual QEasingCurveFunction *copy() const; + bool operator==(const QEasingCurveFunction& other); + + Type _t; + qreal _p; + qreal _a; + qreal _o; +}; + +qreal QEasingCurveFunction::value(qreal t) +{ + return t; +} + +QEasingCurveFunction *QEasingCurveFunction::copy() const +{ + return new QEasingCurveFunction(_t, _p, _a, _o); +} + +bool QEasingCurveFunction::operator==(const QEasingCurveFunction& other) +{ + return _t == other._t && + _p == other._p && + _a == other._a && + _o == other._o; +} + +#ifdef QT_EXPERIMENTAL_SOLUTION +# include "easing.cpp" +#else +# include "../3rdparty/easing/easing.cpp" +#endif + +class QEasingCurvePrivate +{ +public: + QEasingCurvePrivate() + : type(QEasingCurve::Linear), + config(0), + func(&easeNone) + { } + void setType_helper(QEasingCurve::Type); + + QEasingCurve::Type type; + QEasingCurveFunction *config; + QEasingCurve::EasingFunction func; +}; + +struct ElasticEase : public QEasingCurveFunction +{ + ElasticEase(Type type) + : QEasingCurveFunction(type, qreal(0.3), qreal(1.0)) + { } + + QEasingCurveFunction *copy() const + { + ElasticEase *rv = new ElasticEase(_t); + rv->_p = _p; + rv->_a = _a; + return rv; + } + + qreal value(qreal t) + { + qreal p = (_p < 0) ? 0.3f : _p; + qreal a = (_a < 0) ? 1.0f : _a; + switch(_t) { + case In: + return easeInElastic(t, a, p); + case Out: + return easeOutElastic(t, a, p); + case InOut: + return easeInOutElastic(t, a, p); + case OutIn: + return easeOutInElastic(t, a, p); + default: + return t; + } + } +}; + +struct BounceEase : public QEasingCurveFunction +{ + BounceEase(Type type) + : QEasingCurveFunction(type, 0.3f, 1.0f) + { } + + QEasingCurveFunction *copy() const + { + BounceEase *rv = new BounceEase(_t); + rv->_a = _a; + return rv; + } + + qreal value(qreal t) + { + qreal a = (_a < 0) ? 1.0f : _a; + switch(_t) { + case In: + return easeInBounce(t, a); + case Out: + return easeOutBounce(t, a); + case InOut: + return easeInOutBounce(t, a); + case OutIn: + return easeOutInBounce(t, a); + default: + return t; + } + } +}; + +struct BackEase : public QEasingCurveFunction +{ + BackEase(Type type) + : QEasingCurveFunction(type, 0.3f, 1.0f, 1.70158f) + { } + + QEasingCurveFunction *copy() const + { + BackEase *rv = new BackEase(_t); + rv->_o = _o; + return rv; + } + + qreal value(qreal t) + { + qreal o = (_o < 0) ? 1.70158f : _o; + switch(_t) { + case In: + return easeInBack(t, o); + case Out: + return easeOutBack(t, o); + case InOut: + return easeInOutBack(t, o); + case OutIn: + return easeOutInBack(t, o); + default: + return t; + } + } +}; + +static QEasingCurve::EasingFunction curveToFunc(QEasingCurve::Type curve) +{ + switch(curve) { + case QEasingCurve::Linear: + return &easeNone; + case QEasingCurve::InQuad: + return &easeInQuad; + case QEasingCurve::OutQuad: + return &easeOutQuad; + case QEasingCurve::InOutQuad: + return &easeInOutQuad; + case QEasingCurve::OutInQuad: + return &easeOutInQuad; + case QEasingCurve::InCubic: + return &easeInCubic; + case QEasingCurve::OutCubic: + return &easeOutCubic; + case QEasingCurve::InOutCubic: + return &easeInOutCubic; + case QEasingCurve::OutInCubic: + return &easeOutInCubic; + case QEasingCurve::InQuart: + return &easeInQuart; + case QEasingCurve::OutQuart: + return &easeOutQuart; + case QEasingCurve::InOutQuart: + return &easeInOutQuart; + case QEasingCurve::OutInQuart: + return &easeOutInQuart; + case QEasingCurve::InQuint: + return &easeInQuint; + case QEasingCurve::OutQuint: + return &easeOutQuint; + case QEasingCurve::InOutQuint: + return &easeInOutQuint; + case QEasingCurve::OutInQuint: + return &easeOutInQuint; + case QEasingCurve::InSine: + return &easeInSine; + case QEasingCurve::OutSine: + return &easeOutSine; + case QEasingCurve::InOutSine: + return &easeInOutSine; + case QEasingCurve::OutInSine: + return &easeOutInSine; + case QEasingCurve::InExpo: + return &easeInExpo; + case QEasingCurve::OutExpo: + return &easeOutExpo; + case QEasingCurve::InOutExpo: + return &easeInOutExpo; + case QEasingCurve::OutInExpo: + return &easeOutInExpo; + case QEasingCurve::InCirc: + return &easeInCirc; + case QEasingCurve::OutCirc: + return &easeOutCirc; + case QEasingCurve::InOutCirc: + return &easeInOutCirc; + case QEasingCurve::OutInCirc: + return &easeOutInCirc; + // Internal for, compatibility with QTimeLine only ?? + case QEasingCurve::InCurve: + return &easeInCurve; + case QEasingCurve::OutCurve: + return &easeOutCurve; + case QEasingCurve::SineCurve: + return &easeSineCurve; + case QEasingCurve::CosineCurve: + return &easeCosineCurve; + default: + return 0; + }; +} + +static QEasingCurveFunction *curveToFunctionObject(QEasingCurve::Type type) +{ + QEasingCurveFunction *curveFunc = 0; + switch(type) { + case QEasingCurve::InElastic: + curveFunc = new ElasticEase(ElasticEase::In); + break; + case QEasingCurve::OutElastic: + curveFunc = new ElasticEase(ElasticEase::Out); + break; + case QEasingCurve::InOutElastic: + curveFunc = new ElasticEase(ElasticEase::InOut); + break; + case QEasingCurve::OutInElastic: + curveFunc = new ElasticEase(ElasticEase::OutIn); + break; + case QEasingCurve::OutBounce: + curveFunc = new BounceEase(BounceEase::Out); + break; + case QEasingCurve::InBounce: + curveFunc = new BounceEase(BounceEase::In); + break; + case QEasingCurve::OutInBounce: + curveFunc = new BounceEase(BounceEase::OutIn); + break; + case QEasingCurve::InOutBounce: + curveFunc = new BounceEase(BounceEase::InOut); + break; + case QEasingCurve::InBack: + curveFunc = new BackEase(BackEase::In); + break; + case QEasingCurve::OutBack: + curveFunc = new BackEase(BackEase::Out); + break; + case QEasingCurve::InOutBack: + curveFunc = new BackEase(BackEase::InOut); + break; + case QEasingCurve::OutInBack: + curveFunc = new BackEase(BackEase::OutIn); + break; + default: + curveFunc = new QEasingCurveFunction(QEasingCurveFunction::In, 0.3f, 1.0f, 1.70158f); // ### + } + + return curveFunc; +} + +/*! + Constructs an easing curve of the given \a type. + */ +QEasingCurve::QEasingCurve(Type type) + : d_ptr(new QEasingCurvePrivate) +{ + setType(type); +} + +/*! + Construct a copy of \a other. + */ +QEasingCurve::QEasingCurve(const QEasingCurve &other) +: d_ptr(new QEasingCurvePrivate) +{ + // ### non-atomic, requires malloc on shallow copy + *d_ptr = *other.d_ptr; + if(other.d_ptr->config) + d_ptr->config = other.d_ptr->config->copy(); +} + +/*! + Destructor. + */ + +QEasingCurve::~QEasingCurve() +{ + delete d_ptr; +} + +/*! + Copy \a other. + */ +QEasingCurve &QEasingCurve::operator=(const QEasingCurve &other) +{ + // ### non-atomic, requires malloc on shallow copy + if (d_ptr->config) { + delete d_ptr->config; + d_ptr->config = 0; + } + + *d_ptr = *other.d_ptr; + if(other.d_ptr->config) + d_ptr->config = other.d_ptr->config->copy(); + + return *this; +} + +/*! + Compare this easing curve with \a other and returns true if they are + equal. It will also compare the properties of a curve. + */ +bool QEasingCurve::operator==(const QEasingCurve &other) const +{ + bool res = d_ptr->func == other.d_ptr->func + && d_ptr->type == other.d_ptr->type; + if (res && d_ptr->config && other.d_ptr->config) { + // catch the config content + res = d_ptr->config->operator==(*(other.d_ptr->config)); + } + return res; +} + +/*! + \fn bool QEasingCurve::operator!=(const QEasingCurve &other) const + Compare this easing curve with \a other and returns true if they are not equal. + It will also compare the properties of a curve. + + \sa operator==() +*/ + +/*! + Returns the amplitude. This is not applicable for all curve types. + It is only applicable for bounce and elastic curves (curves of type() + QEasingCurve::InBounce, QEasingCurve::OutBounce, QEasingCurve::InOutBounce, + QEasingCurve::OutInBounce, QEasingCurve::InElastic, QEasingCurve::OutElastic, + QEasingCurve::InOutElastic or QEasingCurve::OutInElastic). + */ +qreal QEasingCurve::amplitude() const +{ + return d_ptr->config ? d_ptr->config->_a : 1.0; +} + +/*! + Sets the amplitude to \a amplitude. + + This will set the amplitude of the bounce or the amplitude of the + elastic "spring" effect. The higher the number, the higher the amplitude. + \sa amplitude() +*/ +void QEasingCurve::setAmplitude(qreal amplitude) +{ + if (!d_ptr->config) + d_ptr->config = curveToFunctionObject(d_ptr->type); + d_ptr->config->_a = amplitude; +} + +/*! + Returns the period. This is not applicable for all curve types. + It is only applicable if type() is QEasingCurve::InElastic, QEasingCurve::OutElastic, + QEasingCurve::InOutElastic or QEasingCurve::OutInElastic. + */ +qreal QEasingCurve::period() const +{ + return d_ptr->config ? d_ptr->config->_p : 0.3; +} + +/*! + Sets the period to \a period. + Setting a small period value will give a high frequency of the curve. A + large period will give it a small frequency. + + \sa period() +*/ +void QEasingCurve::setPeriod(qreal period) +{ + if (!d_ptr->config) + d_ptr->config = curveToFunctionObject(d_ptr->type); + d_ptr->config->_p = period; +} + +/*! + Returns the overshoot. This is not applicable for all curve types. + It is only applicable if type() is QEasingCurve::InBack, QEasingCurve::OutBack, + QEasingCurve::InOutBack or QEasingCurve::OutInBack. + */ +qreal QEasingCurve::overshoot() const +{ + return d_ptr->config ? d_ptr->config->_o : 1.70158f; +} + +/*! + Sets the overshoot to \a overshoot. + + 0 produces no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent. + + \sa overshoot() +*/ +void QEasingCurve::setOvershoot(qreal overshoot) +{ + if (!d_ptr->config) + d_ptr->config = curveToFunctionObject(d_ptr->type); + d_ptr->config->_o = overshoot; +} + +/*! + Returns the type of the easing curve. +*/ +QEasingCurve::Type QEasingCurve::type() const +{ + return d_ptr->type; +} + +void QEasingCurvePrivate::setType_helper(QEasingCurve::Type newType) +{ + qreal amp = -1.0; + qreal period = -1.0; + qreal overshoot = -1.0; + + if (config) { + amp = config->_a; + period = config->_p; + overshoot = config->_o; + delete config; + config = 0; + } + + if (isConfigFunction(newType) || (amp != -1.0) || (period != -1.0) || (overshoot != -1.0)) { + config = curveToFunctionObject(newType); + if (amp != -1.0) + config->_a = amp; + if (period != -1.0) + config->_p = period; + if (overshoot != -1.0) + config->_o = overshoot; + func = 0; + } else if (newType != QEasingCurve::Custom) { + func = curveToFunc(newType); + } + Q_ASSERT((func == 0) == (config != 0)); + type = newType; +} + +/*! + Sets the type of the easing curve to \a type. +*/ +void QEasingCurve::setType(Type type) +{ + if (d_ptr->type == type) + return; + if (type < Linear || type >= NCurveTypes - 1) { + qWarning("QEasingCurve: Invalid curve type %d", type); + return; + } + + d_ptr->setType_helper(type); +} + +/*! + Sets a custom easing curve that is defined by the user in the function \a func. + The signature of the function is qreal myEasingFunction(qreal progress), + where \e progress and the return value is considered to be normalized between 0 and 1. + (In some cases the return value can be outside that range) + After calling this function type() will return QEasingCurve::Custom. + \a func cannot be zero. + + \sa customType() + \sa valueForProgress() +*/ +void QEasingCurve::setCustomType(EasingFunction func) +{ + if (!func) { + qWarning("Function pointer must not be null"); + return; + } + d_ptr->func = func; + d_ptr->setType_helper(Custom); +} + +/*! + Returns the function pointer to the custom easing curve. + If type() does not return QEasingCurve::Custom, this function + will return 0. +*/ +QEasingCurve::EasingFunction QEasingCurve::customType() const +{ + return d_ptr->type == Custom ? d_ptr->func : 0; +} + +/*! + Return the effective progress for the easing curve at \a progress. + While \a progress must be between 0 and 1, the returned effective progress + can be outside those bounds. For instance, QEasingCurve::InBack will + return negative values in the beginning of the function. + */ +qreal QEasingCurve::valueForProgress(qreal progress) const +{ + progress = qBound<qreal>(0, progress, 1); + if (d_ptr->func) + return d_ptr->func(progress); + else if (d_ptr->config) + return d_ptr->config->value(progress); + else + return progress; +} + +#ifndef QT_NO_DEBUG_STREAM +#include <QtCore/qdebug.h> +#include <QtCore/QString> +QDebug operator<<(QDebug debug, const QEasingCurve &item) +{ + debug << "type:" << item.d_ptr->type + << "func:" << item.d_ptr->func; + if (item.d_ptr->config) { + debug << QString::fromAscii("period:%1").arg(item.d_ptr->config->_p, 0, 'f', 20) + << QString::fromAscii("amp:%1").arg(item.d_ptr->config->_a, 0, 'f', 20) + << QString::fromAscii("overshoot:%1").arg(item.d_ptr->config->_o, 0, 'f', 20); + } + return debug; +} +#endif + +QT_END_NAMESPACE diff --git a/src/corelib/tools/qeasingcurve.h b/src/corelib/tools/qeasingcurve.h new file mode 100644 index 0000000..a240bc0 --- /dev/null +++ b/src/corelib/tools/qeasingcurve.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEASINGCURVE_H +#define QEASINGCURVE_H + +#include <QtCore/qglobal.h> +#include <QtCore/qobjectdefs.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QEasingCurvePrivate; +class Q_CORE_EXPORT QEasingCurve +{ + Q_GADGET + Q_ENUMS(Type) +public: + enum Type { + Linear, + InQuad, OutQuad, InOutQuad, OutInQuad, + InCubic, OutCubic, InOutCubic, OutInCubic, + InQuart, OutQuart, InOutQuart, OutInQuart, + InQuint, OutQuint, InOutQuint, OutInQuint, + InSine, OutSine, InOutSine, OutInSine, + InExpo, OutExpo, InOutExpo, OutInExpo, + InCirc, OutCirc, InOutCirc, OutInCirc, + InElastic, OutElastic, InOutElastic, OutInElastic, + InBack, OutBack, InOutBack, OutInBack, + InBounce, OutBounce, InOutBounce, OutInBounce, + InCurve, OutCurve, SineCurve, CosineCurve, + Custom, NCurveTypes + }; + + QEasingCurve(Type type = Linear); + QEasingCurve(const QEasingCurve &other); + ~QEasingCurve(); + + QEasingCurve &operator=(const QEasingCurve &other); + bool operator==(const QEasingCurve &other) const; + inline bool operator!=(const QEasingCurve &other) const + { return !(this->operator==(other)); } + + qreal amplitude() const; + void setAmplitude(qreal amplitude); + + qreal period() const; + void setPeriod(qreal period); + + qreal overshoot() const; + void setOvershoot(qreal overshoot); + + Type type() const; + void setType(Type type); + typedef qreal (*EasingFunction)(qreal progress); + void setCustomType(EasingFunction func); + EasingFunction customType() const; + + qreal valueForProgress(qreal progress) const; +private: + QEasingCurvePrivate *d_ptr; + friend Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QEasingCurve &item); +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QEasingCurve &item); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/tools/qtimeline.cpp b/src/corelib/tools/qtimeline.cpp index 2979a09..78ce7bd 100644 --- a/src/corelib/tools/qtimeline.cpp +++ b/src/corelib/tools/qtimeline.cpp @@ -48,20 +48,6 @@ QT_BEGIN_NAMESPACE -static const qreal pi = qreal(3.14159265359); -static const qreal halfPi = pi / qreal(2.0); - - -static inline qreal qt_sinProgress(qreal value) -{ - return qSin((value * pi) - halfPi) / 2 + qreal(0.5); -} - -static inline qreal qt_smoothBeginEndMixFactor(qreal value) -{ - return qMin(qMax(1 - value * 2 + qreal(0.3), qreal(0.0)), qreal(1.0)); -} - class QTimeLinePrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QTimeLine) @@ -70,7 +56,7 @@ public: : startTime(0), duration(1000), startFrame(0), endFrame(0), updateInterval(1000 / 25), totalLoopCount(1), currentLoopCount(0), currentTime(0), timerId(0), - direction(QTimeLine::Forward), curveShape(QTimeLine::EaseInOutCurve), + direction(QTimeLine::Forward), easingCurve(QEasingCurve::InOutSine), state(QTimeLine::NotRunning) { } @@ -88,7 +74,7 @@ public: QTime timer; QTimeLine::Direction direction; - QTimeLine::CurveShape curveShape; + QEasingCurve easingCurve; QTimeLine::State state; inline void setState(QTimeLine::State newState) { @@ -521,12 +507,68 @@ void QTimeLine::setUpdateInterval(int interval) QTimeLine::CurveShape QTimeLine::curveShape() const { Q_D(const QTimeLine); - return d->curveShape; + switch (d->easingCurve.type()) { + default: + case QEasingCurve::InOutSine: + return EaseInOutCurve; + case QEasingCurve::InCurve: + return EaseInCurve; + case QEasingCurve::OutCurve: + return EaseOutCurve; + case QEasingCurve::Linear: + return LinearCurve; + case QEasingCurve::SineCurve: + return SineCurve; + case QEasingCurve::CosineCurve: + return CosineCurve; + } + return EaseInOutCurve; } + void QTimeLine::setCurveShape(CurveShape shape) { + switch (shape) { + default: + case EaseInOutCurve: + setEasingCurve(QEasingCurve(QEasingCurve::InOutSine)); + break; + case EaseInCurve: + setEasingCurve(QEasingCurve(QEasingCurve::InCurve)); + break; + case EaseOutCurve: + setEasingCurve(QEasingCurve(QEasingCurve::OutCurve)); + break; + case LinearCurve: + setEasingCurve(QEasingCurve(QEasingCurve::Linear)); + break; + case SineCurve: + setEasingCurve(QEasingCurve(QEasingCurve::SineCurve)); + break; + case CosineCurve: + setEasingCurve(QEasingCurve(QEasingCurve::CosineCurve)); + break; + } +} + +/*! + \property QTimeLine::easingCurve + + Specifies the easing curve that the timeline will use. + If both easing curve and curveShape are set, the last set property will + override the previous one. (If valueForTime() is reimplemented it will + override both) +*/ + +QEasingCurve QTimeLine::easingCurve() const +{ + Q_D(const QTimeLine); + return d->easingCurve; +} + +void QTimeLine::setEasingCurve(const QEasingCurve& curve) +{ Q_D(QTimeLine); - d->curveShape = shape; + d->easingCurve = curve; } /*! @@ -606,42 +648,8 @@ qreal QTimeLine::valueForTime(int msec) const Q_D(const QTimeLine); msec = qMin(qMax(msec, 0), d->duration); - // Simple linear interpolation qreal value = msec / qreal(d->duration); - - switch (d->curveShape) { - case EaseInOutCurve: - value = qt_sinProgress(value); - break; - // SmoothBegin blends Smooth and Linear Interpolation. - // Progress 0 - 0.3 : Smooth only - // Progress 0.3 - ~ 0.5 : Mix of Smooth and Linear - // Progress ~ 0.5 - 1 : Linear only - case EaseInCurve: { - const qreal sinProgress = qt_sinProgress(value); - const qreal linearProgress = value; - const qreal mix = qt_smoothBeginEndMixFactor(value); - value = sinProgress * mix + linearProgress * (1 - mix); - break; - } - case EaseOutCurve: { - const qreal sinProgress = qt_sinProgress(value); - const qreal linearProgress = value; - const qreal mix = qt_smoothBeginEndMixFactor(1 - value); - value = sinProgress * mix + linearProgress * (1 - mix); - break; - } - case SineCurve: - value = (qSin(((msec * pi * 2) / d->duration) - pi/2) + 1) / 2; - break; - case CosineCurve: - value = (qCos(((msec * pi * 2) / d->duration) - pi/2) + 1) / 2; - break; - default: - break; - } - - return value; + return d->easingCurve.valueForProgress(value); } /*! diff --git a/src/corelib/tools/qtimeline.h b/src/corelib/tools/qtimeline.h index 18c3980..314dd7c 100644 --- a/src/corelib/tools/qtimeline.h +++ b/src/corelib/tools/qtimeline.h @@ -42,8 +42,11 @@ #ifndef QTIMELINE_H #define QTIMELINE_H +#include <QtCore/qeasingcurve.h> #include <QtCore/qobject.h> +QT_EXPERIMENTAL_USE_NAMESPACE + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -60,6 +63,7 @@ class Q_CORE_EXPORT QTimeLine : public QObject Q_PROPERTY(Direction direction READ direction WRITE setDirection) Q_PROPERTY(int loopCount READ loopCount WRITE setLoopCount) Q_PROPERTY(CurveShape curveShape READ curveShape WRITE setCurveShape) + Q_PROPERTY(QEasingCurve easingCurve READ easingCurve WRITE setEasingCurve) public: enum State { NotRunning, @@ -105,6 +109,9 @@ public: CurveShape curveShape() const; void setCurveShape(CurveShape shape); + QEasingCurve easingCurve() const; + void setEasingCurve(const QEasingCurve &curve); + int currentTime() const; int currentFrame() const; qreal currentValue() const; diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index e5bf7e4..41557f0 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -11,6 +11,7 @@ HEADERS += \ tools/qcryptographichash.h \ tools/qdatetime.h \ tools/qdatetime_p.h \ + tools/qeasingcurve.h \ tools/qhash.h \ tools/qline.h \ tools/qlinkedlist.h \ @@ -46,6 +47,7 @@ SOURCES += \ tools/qcryptographichash.cpp \ tools/qdatetime.cpp \ tools/qdumper.cpp \ + tools/qeasingcurve.cpp \ tools/qhash.cpp \ tools/qline.cpp \ tools/qlinkedlist.cpp \ diff --git a/src/gui/animation/animation.pri b/src/gui/animation/animation.pri new file mode 100644 index 0000000..3092117 --- /dev/null +++ b/src/gui/animation/animation.pri @@ -0,0 +1,8 @@ +# Qt gui animation module + +HEADERS += \ + animation/qitemanimation.h \ + animation/qitemanimation_p.h + +SOURCES += \ + animation/qitemanimation.cpp diff --git a/src/gui/animation/qitemanimation.cpp b/src/gui/animation/qitemanimation.cpp new file mode 100644 index 0000000..484b386 --- /dev/null +++ b/src/gui/animation/qitemanimation.cpp @@ -0,0 +1,361 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QItemAnimation + \brief The QItemAnimation class animates properties for QGraphicsItem + \since 4.5 + \ingroup animation + \preliminary + + This class is part of {The Animation Framework}. You can use QItemAnimation + by itself as a simple animation class, or as part of more complex + animations through QAnimationGroup. + + The most common way to use QItemAnimation is to construct an instance + of it by passing a pointer to a QGraphicsItem and the property you + would like to animate to QItemAnimation's constructor. + + The start value of the animation is optional. If you do not set any start + value, the animation will operate on the target's current property value + at the point when the animation was started. You can call setStartValue() + to set the start value, and setEndValue() to set the target value for + the animated property. + + You can choose to assign a target item by either calling setTargetItem() + or by passing a QGraphicsItem pointer to QVariantAnimation's constructor. + + \sa QVariantAnimation, QAnimationGroup, {The Animation Framework} +*/ + + +#ifndef QT_NO_ANIMATION + +#include "qitemanimation.h" +#include "qitemanimation_p.h" + +#include <QtCore/QMutex> +#ifdef QT_EXPERIMENTAL_SOLUTION +#include "qanimationgroup.h" +#else +#include <QtCore/QAnimationGroup> +#endif +#include <QtGui/QGraphicsItem> + + +QT_BEGIN_NAMESPACE + +typedef QPair<QGraphicsItem *, QItemAnimation::PropertyName> QItemAnimationPair; +typedef QHash<QItemAnimationPair, QItemAnimation*> QItemAnimationHash; +Q_GLOBAL_STATIC(QItemAnimationHash, _q_runningAnimations) +Q_GLOBAL_STATIC_WITH_ARGS(QMutex, guardHashLock, (QMutex::Recursive) ) + +void QItemAnimationPrivate::initDefaultStartValue() +{ + if (target && !defaultStartValue.isValid() && (atBeginning() || atEnd())) { + switch (propertyName) + { + case QItemAnimation::Position: + setDefaultStartValue(target->pos()); + break; + case QItemAnimation::Opacity: + setDefaultStartValue(target->opacity()); + break; + case QItemAnimation::RotationX: + setDefaultStartValue(target->xRotation()); + break; + case QItemAnimation::RotationY: + setDefaultStartValue(target->yRotation()); + break; + case QItemAnimation::RotationZ: + setDefaultStartValue(target->zRotation()); + break; + case QItemAnimation::ScaleFactorX: + setDefaultStartValue(target->xScale()); + break; + case QItemAnimation::ScaleFactorY: + setDefaultStartValue(target->yScale()); + break; + default: + break; + } + } +} + + +/*! + Construct a QItemAnimation object. \a parent is passed to QObject's + constructor. +*/ + +QItemAnimation::QItemAnimation(QObject *parent) : QVariantAnimation(*new QItemAnimationPrivate, parent) +{ +} + +/*! + Construct a QItemAnimation object. \a parent is passed to QObject's + constructor. The animation changes the property \a propertyName on \a + target. The default duration is 250ms. + + \sa targetItem, propertyName +*/ + +QItemAnimation::QItemAnimation(QGraphicsItem *target, PropertyName p, QObject *parent) : QVariantAnimation(*new QItemAnimationPrivate, parent) +{ + Q_D(QItemAnimation); + d->target = target; + d->propertyName = p; +} + +/*! + Destroys the QPropertyAnimation instance. + */ +QItemAnimation::~QItemAnimation() +{ + stop(); +} + +/*! + \property QItemAnimation::targetItem + \brief the target Graphics Item for this animation. + + This property defines the target item for this animation. + + \sa targetItem + */ + +QGraphicsItem *QItemAnimation::targetItem() const +{ + Q_D(const QItemAnimation); + return d->target; +} + +void QItemAnimation::setTargetItem(QGraphicsItem *item) +{ + Q_D(QItemAnimation); + d->target = item; +} + +/*! + \property QItemAnimation::propertyName + \brief the target property for this animation + + This property defines the target property for this animation. The + property is required for the animation to operate. + */ +QItemAnimation::PropertyName QItemAnimation::propertyName() const +{ + Q_D(const QItemAnimation); + return d->propertyName; +} + +void QItemAnimation::setPropertyName(PropertyName p) +{ + Q_D(QItemAnimation); + d->propertyName = p; +} + +/*! + This static function returns the list of running animations on \a item. + If item is 0, then it returns all QItemAnimations running on all QGraphicsItem. + */ +QList<QItemAnimation*> QItemAnimation::runningAnimations(QGraphicsItem *item) +{ + QMutexLocker locker(guardHashLock()); + QList<QItemAnimation*> animList = _q_runningAnimations()->values(); + if (item == 0) + return animList; + + QList<QItemAnimation*> ret; + + for (QList<QItemAnimation*>::const_iterator it = animList.constBegin(); it != animList.constEnd(); ++it) { + if ((*it)->targetItem() == item) + ret += *it; + } + + return ret; +} + +/*! + This static function returns the running animations on \a item and on \a property. + \a prop. + */ +QItemAnimation* QItemAnimation::runningAnimation(QGraphicsItem *item, PropertyName prop) +{ + QMutexLocker locker(guardHashLock()); + return _q_runningAnimations()->value(qMakePair(item, prop), 0 /*default value*/); +} + +/*! + \reimp + */ +bool QItemAnimation::event(QEvent *event) +{ + return QVariantAnimation::event(event); +} + +/*! + \reimp + */ +void QItemAnimation::updateCurrentValue(const QVariant &value) +{ + Q_D(QItemAnimation); + if (!d->target || d->state == Stopped) + return; + + switch (d->propertyName) + { + case Position: + d->target->setPos(qVariantValue<QPointF>(value)); + break; + case Opacity: + d->target->setOpacity(qVariantValue<qreal>(value)); + break; + case RotationX: + d->target->setXRotation(qVariantValue<qreal>(value)); + break; + case RotationY: + d->target->setYRotation(qVariantValue<qreal>(value)); + break; + case RotationZ: + d->target->setZRotation(qVariantValue<qreal>(value)); + break; + case ScaleFactorX: + d->target->setXScale(qVariantValue<qreal>(value)); + break; + case ScaleFactorY: + d->target->setYScale(qVariantValue<qreal>(value)); + break; + default: + qWarning("The property you're trying to animate is not managed by the item"); + break; + } +} + + +/*! + \reimp +*/ +void QItemAnimation::updateState(QAbstractAnimation::State oldState, + QAbstractAnimation::State newState) +{ + Q_D(QItemAnimation); + QVariantAnimation::updateState(oldState, newState); + QMutexLocker locker(guardHashLock()); + QItemAnimationHash *hash = _q_runningAnimations(); + QItemAnimationPair key(d->target, d->propertyName); + + //let's try to convert start and target values according to the type of the proerty + //we're animating + if (newState != Stopped) { + int type = QVariant::Invalid; + switch (d->propertyName) + { + case Position: + type = QVariant::PointF; + break; + case Opacity: + case RotationX: + case RotationY: + case RotationZ: + case ScaleFactorX: + case ScaleFactorY: + type = qMetaTypeId<qreal>(); + break; + case None: + default: + break; + + } + if (type != QVariant::Invalid) { + d->convertValues(type); + } + } + + if (newState == Running) { + if (hash->contains(key)) { + QItemAnimation *oldAnim = hash->value(key); + if (oldAnim != this) { + //we try to stop the top level group + QAbstractAnimation *current = oldAnim; + while(current->group() && current->state() != Stopped) current = current->group(); + current->stop(); + } + } + hash->insert(key, this); + // Initialize start value + d->initDefaultStartValue(); + + } else if (hash->value(key) == this) { + hash->remove(key); + } +} + +///TODO: should be placed somewhere else (in its own file) +template<> Q_INLINE_TEMPLATE QColor _q_interpolate(const QColor &f,const QColor &t, qreal progress) +{ + return QColor(_q_interpolate(f.red(), t.red(), progress), + _q_interpolate(f.green(), t.green(), progress), + _q_interpolate(f.blue(), t.blue(), progress), + _q_interpolate(f.alpha(), t.alpha(), progress)); +} + + + +static int qRegisterGuiGetInterpolator() +{ + qRegisterAnimationInterpolator<QColor>(_q_interpolateVariant<QColor>); + return 1; +} +Q_CONSTRUCTOR_FUNCTION(qRegisterGuiGetInterpolator) + +static int qUnregisterGuiGetInterpolator() +{ + qRegisterAnimationInterpolator<QColor>(0); + return 1; +} +Q_DESTRUCTOR_FUNCTION(qUnregisterGuiGetInterpolator) + +QT_END_NAMESPACE + +#include "moc_qitemanimation.cpp" + +#endif //QT_NO_ANIMATION diff --git a/src/gui/animation/qitemanimation.h b/src/gui/animation/qitemanimation.h new file mode 100644 index 0000000..d630fe7 --- /dev/null +++ b/src/gui/animation/qitemanimation.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QITEMANIMATION_H +#define QITEMANIMATION_H + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qvariantanimation.h" +#else +# include <QtCore/qvariantanimation.h> +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_ANIMATION + +class QGraphicsItem; + +class QItemAnimationPrivate; +class Q_GUI_EXPORT QItemAnimation : public QVariantAnimation +{ +public: + enum PropertyName + { + None, //default + Position, + Opacity, + RotationX, + RotationY, + RotationZ, + ScaleFactorX, + ScaleFactorY + }; + + Q_OBJECT + Q_PROPERTY(PropertyName propertyName READ propertyName WRITE setPropertyName) + Q_PROPERTY(QGraphicsItem* targetItem READ targetItem WRITE setTargetItem) /*NOTIFY targetItemChanged*/ + +public: + QItemAnimation(QObject *parent = 0); + QItemAnimation(QGraphicsItem *target, PropertyName p = None, QObject *parent = 0); + ~QItemAnimation(); + + QGraphicsItem *targetItem() const; + void setTargetItem(QGraphicsItem *item); + + PropertyName propertyName() const; + void setPropertyName(PropertyName); + + static QList<QItemAnimation*> runningAnimations(QGraphicsItem *item = 0); + static QItemAnimation* runningAnimation(QGraphicsItem *item, PropertyName prop); + +protected: + bool event(QEvent *event); + void updateCurrentValue(const QVariant &value); + void updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState); + +private: + Q_DISABLE_COPY(QItemAnimation) + Q_DECLARE_PRIVATE(QItemAnimation) +}; + +#endif //QT_NO_ANIMATION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QITEMANIMATION_H diff --git a/src/gui/animation/qitemanimation_p.h b/src/gui/animation/qitemanimation_p.h new file mode 100644 index 0000000..027c199 --- /dev/null +++ b/src/gui/animation/qitemanimation_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QITEMANIMATION_P_H +#define QITEMANIMATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QIODevice. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qitemanimation.h" + +#if defined(QT_EXPERIMENTAL_SOLUTION) +#include "qvariantanimation_p.h" +#else +#include "private/qvariantanimation_p.h" +#endif + +QT_BEGIN_NAMESPACE + +class QItemAnimationPrivate : public QVariantAnimationPrivate +{ + Q_DECLARE_PUBLIC(QItemAnimation) +public: + QItemAnimationPrivate() : propertyName(QItemAnimation::None), + target(0) + { + } + + void initDefaultStartValue(); + + QItemAnimation::PropertyName propertyName; + QGraphicsItem *target; +}; + +QT_END_NAMESPACE + +#endif //QITEMANIMATION_P_H diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 30c15bc..201dfb8 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -873,7 +873,12 @@ QGraphicsItem::~QGraphicsItem() } if (d_ptr->scene) d_ptr->scene->d_func()->_q_removeItemLater(this); - + + if (d_ptr->hasTransform) { + delete static_cast<QGraphicsItemPrivate::TransformData *>( + qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform))); + } + delete d_ptr; qt_dataStore()->data.remove(this); @@ -2471,7 +2476,24 @@ QTransform QGraphicsItem::transform() const { if (!d_ptr->hasTransform) return QTransform(); - return qVariantValue<QTransform>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform)); + QGraphicsItemPrivate::TransformData *transformData = static_cast<QGraphicsItemPrivate::TransformData *>( + qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform))); + Q_ASSERT(transformData); + if (transformData->dirty) { + QGraphicsItem *that = const_cast<QGraphicsItem *>(this); + QTransform newTransform; + QPointF center = transformData->transformCenter; + newTransform.translate(center.x(), center.y()); + newTransform.rotate(transformData->rotationZ, Qt::ZAxis); + newTransform.rotate(transformData->rotationY, Qt::YAxis); + newTransform.rotate(transformData->rotationX, Qt::XAxis); + newTransform.scale(transformData->scaleX, transformData->scaleY); + newTransform *= transformData->baseTransform; + newTransform.translate(-center.x(), -center.y()); + transformData->transform = newTransform; + transformData->dirty = false; + } + return transformData->transform; } /*! @@ -2749,7 +2771,17 @@ QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) co */ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) { - QTransform oldTransform = this->transform(); + QGraphicsItemPrivate::TransformData *transformData = static_cast<QGraphicsItemPrivate::TransformData *>( + qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform))); + if (!transformData) { + if (matrix.isIdentity()) + return; + transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, qVariantFromValue<void *>(transformData)); + } + + QTransform oldTransform = transformData->baseTransform; + QTransform newTransform; if (!combine) newTransform = QTransform(matrix); @@ -2768,8 +2800,9 @@ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) // Update and set the new transformation. d_ptr->fullUpdateHelper(true, true); prepareGeometryChange(); - d_ptr->hasTransform = !newTransform.isIdentity(); - d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, newTransform); + transformData->baseTransform = newTransform; + transformData->dirty = true; + d_ptr->hasTransform = true; d_ptr->invalidateSceneTransformCache(); // Send post-notification. @@ -2797,7 +2830,17 @@ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) */ void QGraphicsItem::setTransform(const QTransform &matrix, bool combine) { - QTransform oldTransform = this->transform(); + QGraphicsItemPrivate::TransformData *transformData = static_cast<QGraphicsItemPrivate::TransformData *>( + qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform))); + if (!transformData) { + if (matrix.isIdentity()) + return; + transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, qVariantFromValue<void *>(transformData)); + } + + QTransform oldTransform = transformData->baseTransform; + QTransform newTransform; if (!combine) newTransform = matrix; @@ -2816,8 +2859,10 @@ void QGraphicsItem::setTransform(const QTransform &matrix, bool combine) // Update and set the new transformation. d_ptr->fullUpdateHelper(true, true); prepareGeometryChange(); - d_ptr->hasTransform = !newTransform.isIdentity(); - d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, newTransform); + transformData->baseTransform = newTransform; + transformData->dirty = true; + d_ptr->hasTransform = true; + transform(); // ### update transform, bad! d_ptr->invalidateSceneTransformCache(); // Send post-notification. @@ -2903,6 +2948,197 @@ void QGraphicsItem::translate(qreal dx, qreal dy) setTransform(QTransform::fromTranslate(dx, dy), true); } +QPointF QGraphicsItem::transformOrigin() const +{ + QGraphicsItemPrivate::TransformData *transformData = static_cast<QGraphicsItemPrivate::TransformData *>( + qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform))); + if (!transformData) + return QPointF(); + + return transformData->transformCenter; +} + +/*! + Set a center for all transformation +*/ +void QGraphicsItem::setTransformOrigin(const QPointF ¢er) +{ + QGraphicsItemPrivate::TransformData *transformData = static_cast<QGraphicsItemPrivate::TransformData *>( + qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform))); + if (!transformData) { + if (center.isNull()) + return; + transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, qVariantFromValue<void *>(transformData)); + } + if (transformData->transformCenter == center) + return; + + d_ptr->fullUpdateHelper(true); + prepareGeometryChange(); + + transformData->transformCenter = center; + + transformData->dirty = true; + d_ptr->hasTransform = true; + d_ptr->fullUpdateHelper(); + +} + +qreal QGraphicsItem::xScale() const +{ + QGraphicsItemPrivate::TransformData *transformData = static_cast<QGraphicsItemPrivate::TransformData *>( + qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform))); + if (!transformData) + return 1; + + return transformData->scaleX; +} + +void QGraphicsItem::setXScale(qreal factor) +{ + QGraphicsItemPrivate::TransformData *transformData = static_cast<QGraphicsItemPrivate::TransformData *>( + qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform))); + if (!transformData) { + if (factor == 1) + return; + transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, qVariantFromValue<void *>(transformData)); + } + if (transformData->scaleX == factor) + return; + + d_ptr->fullUpdateHelper(true); + prepareGeometryChange(); + + transformData->scaleX = factor; + transformData->dirty = true; + d_ptr->hasTransform = true; + d_ptr->invalidateSceneTransformCache(); +} + +qreal QGraphicsItem::yScale() const +{ + QGraphicsItemPrivate::TransformData *transformData = static_cast<QGraphicsItemPrivate::TransformData *>( + qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform))); + if (!transformData) + return 1; + + return transformData->scaleY; +} + +void QGraphicsItem::setYScale(qreal factor) +{ + QGraphicsItemPrivate::TransformData *transformData = static_cast<QGraphicsItemPrivate::TransformData *>( + qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform))); + if (!transformData) { + if (factor == 1) + return; + transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, qVariantFromValue<void *>(transformData)); + } + if (transformData->scaleY == factor) + return; + + d_ptr->fullUpdateHelper(true); + prepareGeometryChange(); + + transformData->scaleY = factor; + transformData->dirty = true; + d_ptr->hasTransform = true; + d_ptr->invalidateSceneTransformCache(); +} + +qreal QGraphicsItem::xRotation() const +{ + QGraphicsItemPrivate::TransformData *transformData = static_cast<QGraphicsItemPrivate::TransformData *>( + qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform))); + if (!transformData) + return 0; + return transformData->rotationX; +} + +void QGraphicsItem::setXRotation(qreal angle) +{ + QGraphicsItemPrivate::TransformData *transformData = static_cast<QGraphicsItemPrivate::TransformData *>( + qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform))); + if (!transformData) { + if (qFuzzyCompare(angle + 1, 1)) + return; + transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, qVariantFromValue<void *>(transformData)); + } + + d_ptr->fullUpdateHelper(true); + prepareGeometryChange(); + + transformData->rotationX = angle; + + transformData->dirty = true; + d_ptr->hasTransform = true; + d_ptr->invalidateSceneTransformCache(); +} + +qreal QGraphicsItem::yRotation() const +{ + QGraphicsItemPrivate::TransformData *transformData = static_cast<QGraphicsItemPrivate::TransformData *>( + qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform))); + if (!transformData) + return 0; + return transformData->rotationY; +} + +void QGraphicsItem::setYRotation(qreal angle) +{ + QGraphicsItemPrivate::TransformData *transformData = static_cast<QGraphicsItemPrivate::TransformData *>( + qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform))); + if (!transformData) { + if (qFuzzyCompare(angle + 1, 1)) + return; + transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, qVariantFromValue<void *>(transformData)); + } + + d_ptr->fullUpdateHelper(true); + prepareGeometryChange(); + + transformData->rotationY = angle; + + transformData->dirty = true; + d_ptr->hasTransform = true; + d_ptr->invalidateSceneTransformCache(); +} + +qreal QGraphicsItem::zRotation() const +{ + QGraphicsItemPrivate::TransformData *transformData = static_cast<QGraphicsItemPrivate::TransformData *>( + qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform))); + if (!transformData) + return 0; + return transformData->rotationZ; +} + +void QGraphicsItem::setZRotation(qreal angle) +{ + QGraphicsItemPrivate::TransformData *transformData = static_cast<QGraphicsItemPrivate::TransformData *>( + qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform))); + if (!transformData) { + if (qFuzzyCompare(angle + 1, 1)) + return; + transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, qVariantFromValue<void *>(transformData)); + } + + d_ptr->fullUpdateHelper(true); + prepareGeometryChange(); + + transformData->rotationZ = angle; + + transformData->dirty = true; + d_ptr->hasTransform = true; + d_ptr->invalidateSceneTransformCache(); +} + /*! This virtual function is called twice for all items by the QGraphicsScene::advance() slot. In the first phase, all items are called diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index b98882d..1fce3db 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -239,11 +239,27 @@ public: QTransform itemTransform(const QGraphicsItem *other, bool *ok = 0) const; void setTransform(const QTransform &matrix, bool combine = false); void resetTransform(); - + + // ### obsolete? void rotate(qreal angle); void scale(qreal sx, qreal sy); void shear(qreal sh, qreal sv); void translate(qreal dx, qreal dy); + + // ### experimental + QPointF transformOrigin() const; + void setTransformOrigin(const QPointF ¢er); + qreal xScale() const; + void setXScale(qreal factor); + qreal yScale() const; + void setYScale(qreal factor); + qreal xRotation() const; + void setXRotation(qreal angle); + qreal yRotation() const; + void setYRotation(qreal angle); + qreal zRotation() const; + void setZRotation(qreal angle); + virtual void advance(int phase); // Stacking order diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 9ce1bbf..e8ab63d 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -91,6 +91,15 @@ class Q_AUTOTEST_EXPORT QGraphicsItemPrivate { Q_DECLARE_PUBLIC(QGraphicsItem) public: + struct TransformData + { + TransformData() : rotationX(0),rotationY(0),rotationZ(0),scaleX(1),scaleY(1), dirty(true) {} + QTransform baseTransform; + QTransform transform; + QPointF transformCenter; + qreal rotationX,rotationY,rotationZ,scaleX,scaleY; + bool dirty; + }; enum Extra { ExtraTransform, ExtraToolTip, @@ -224,7 +233,7 @@ public: } } } - + struct ExtraStruct { ExtraStruct(Extra type, QVariant value) : type(type), value(value) @@ -236,6 +245,7 @@ public: bool operator<(Extra extra) const { return type < extra; } }; + QList<ExtraStruct> extras; QGraphicsItemCache *extraItemCache() const; diff --git a/src/gui/graphicsview/qgraphicsproxywidget.cpp b/src/gui/graphicsview/qgraphicsproxywidget.cpp index e660879..973254f 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget.cpp +++ b/src/gui/graphicsview/qgraphicsproxywidget.cpp @@ -977,6 +977,7 @@ void QGraphicsProxyWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *even } #endif // QT_NO_CONTEXTMENU +#ifndef QT_NO_DRAGANDDROP /*! \reimp */ @@ -1097,6 +1098,7 @@ void QGraphicsProxyWidget::dropEvent(QGraphicsSceneDragDropEvent *event) } #endif } +#endif /*! \reimp diff --git a/src/gui/graphicsview/qgraphicsproxywidget.h b/src/gui/graphicsview/qgraphicsproxywidget.h index b2c3c8f..ab8c9da 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget.h +++ b/src/gui/graphicsview/qgraphicsproxywidget.h @@ -90,10 +90,12 @@ protected: void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); #endif +#ifndef QT_NO_DRAGANDDROP void dragEnterEvent(QGraphicsSceneDragDropEvent *event); void dragLeaveEvent(QGraphicsSceneDragDropEvent *event); void dragMoveEvent(QGraphicsSceneDragDropEvent *event); void dropEvent(QGraphicsSceneDragDropEvent *event); +#endif void hoverEnterEvent(QGraphicsSceneHoverEvent *event); void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); diff --git a/src/gui/graphicsview/qgraphicswidget.h b/src/gui/graphicsview/qgraphicswidget.h index 34f1c5f..05195b6 100644 --- a/src/gui/graphicsview/qgraphicswidget.h +++ b/src/gui/graphicsview/qgraphicswidget.h @@ -59,6 +59,7 @@ class QGraphicsLayout; class QGraphicsSceneMoveEvent; class QGraphicsWidgetPrivate; class QGraphicsSceneResizeEvent; +class QGraphicsWidgetAnimator; class QStyle; class QStyleOption; @@ -81,6 +82,11 @@ class Q_GUI_EXPORT QGraphicsWidget : public QObject, public QGraphicsItem, publi Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity) Q_PROPERTY(QPointF pos READ pos WRITE setPos) Q_PROPERTY(QRectF geometry READ geometry WRITE setGeometry) + Q_PROPERTY(qreal xScale READ xScale WRITE setXScale) + Q_PROPERTY(qreal yScale READ yScale WRITE setYScale) + Q_PROPERTY(qreal zRotation READ zRotation WRITE setZRotation) + Q_PROPERTY(qreal xRotation READ xRotation WRITE setXRotation) + Q_PROPERTY(qreal yRotation READ yRotation WRITE setYRotation) public: QGraphicsWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0); diff --git a/src/gui/gui.pro b/src/gui/gui.pro index f224e67..2b07395 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -19,6 +19,7 @@ win32:include(kernel/win.pri) embedded:include(embedded/embedded.pri) #modules +include(animation/animation.pri) include(kernel/kernel.pri) include(image/image.pri) include(painting/painting.pri) @@ -31,6 +32,7 @@ include(itemviews/itemviews.pri) include(inputmethod/inputmethod.pri) include(graphicsview/graphicsview.pri) include(util/util.pri) +include(statemachine/statemachine.pri) embedded: QT += network diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index f3bd57b..b04d5bf 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -848,6 +848,9 @@ void QApplicationPrivate::initialize() // trigger registering of QVariant's GUI types extern int qRegisterGuiVariant(); qRegisterGuiVariant(); + // trigger registering of QStateMachine's GUI types + extern int qRegisterGuiStateMachine(); + qRegisterGuiStateMachine(); is_app_running = true; // no longer starting up @@ -1069,6 +1072,9 @@ QApplication::~QApplication() QApplicationPrivate::fade_tooltip = false; QApplicationPrivate::widgetCount = false; + // trigger unregistering of QStateMachine's GUI types + extern int qUnregisterGuiStateMachine(); + qUnregisterGuiStateMachine(); // trigger unregistering of QVariant's GUI types extern int qUnregisterGuiVariant(); qUnregisterGuiVariant(); diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 9f2831d..35d0b52 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -7258,7 +7258,7 @@ static void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer, } void qt_build_pow_tables() { - qreal smoothing = 1.7; + qreal smoothing = qreal(1.7); #ifdef Q_WS_WIN int winSmooth; @@ -7274,15 +7274,15 @@ void qt_build_pow_tables() { } #else for (int i=0; i<256; ++i) { - qt_pow_rgb_gamma[i] = uchar(qRound(pow(i / 255.0, smoothing) * 255)); - qt_pow_rgb_invgamma[i] = uchar(qRound(pow(i / 255.0, 1 / smoothing) * 255)); + qt_pow_rgb_gamma[i] = uchar(qRound(pow(i / qreal(255.0), smoothing) * 255)); + qt_pow_rgb_invgamma[i] = uchar(qRound(pow(i / qreal(255.), 1 / smoothing) * 255)); } #endif #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) const qreal gray_gamma = 2.31; for (int i=0; i<256; ++i) - qt_pow_gamma[i] = uint(qRound(pow(i / 255.0, gray_gamma) * 2047)); + qt_pow_gamma[i] = uint(qRound(pow(i / qreal(255.), gray_gamma) * 2047)); for (int i=0; i<2048; ++i) qt_pow_invgamma[i] = uchar(qRound(pow(i / 2047.0, 1 / gray_gamma) * 255)); #endif diff --git a/src/gui/statemachine/qbasickeyeventtransition.cpp b/src/gui/statemachine/qbasickeyeventtransition.cpp new file mode 100644 index 0000000..2ce428c --- /dev/null +++ b/src/gui/statemachine/qbasickeyeventtransition.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "qbasickeyeventtransition_p.h" +#include <QtGui/qevent.h> +#include <qdebug.h> + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qabstracttransition_p.h" +#else +# include <private/qabstracttransition_p.h> +#endif + + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QBasicKeyEventTransition + + \brief The QBasicKeyEventTransition class provides a transition for Qt key events. +*/ + +class QBasicKeyEventTransitionPrivate : public QAbstractTransitionPrivate +{ + Q_DECLARE_PUBLIC(QBasicKeyEventTransition) +public: + QBasicKeyEventTransitionPrivate(); + + static QBasicKeyEventTransitionPrivate *get(QBasicKeyEventTransition *q); + + QEvent::Type eventType; + int key; + Qt::KeyboardModifiers modifiers; +}; + +QBasicKeyEventTransitionPrivate::QBasicKeyEventTransitionPrivate() +{ + eventType = QEvent::None; + key = 0; + modifiers = Qt::NoModifier; +} + +QBasicKeyEventTransitionPrivate *QBasicKeyEventTransitionPrivate::get(QBasicKeyEventTransition *q) +{ + return q->d_func(); +} + +/*! + Constructs a new key event transition with the given \a sourceState. +*/ +QBasicKeyEventTransition::QBasicKeyEventTransition(QState *sourceState) + : QAbstractTransition(*new QBasicKeyEventTransitionPrivate, sourceState) +{ +} + +/*! + Constructs a new event transition for events of the given \a type for the + given \a key, with the given \a sourceState. +*/ +QBasicKeyEventTransition::QBasicKeyEventTransition(QEvent::Type type, int key, + QState *sourceState) + : QAbstractTransition(*new QBasicKeyEventTransitionPrivate, sourceState) +{ + Q_D(QBasicKeyEventTransition); + d->eventType = type; + d->key = key; +} + +/*! + Constructs a new event transition for events of the given \a type for the + given \a key, with the given \a modifiers and \a sourceState. +*/ +QBasicKeyEventTransition::QBasicKeyEventTransition(QEvent::Type type, int key, + Qt::KeyboardModifiers modifiers, + QState *sourceState) + : QAbstractTransition(*new QBasicKeyEventTransitionPrivate, sourceState) +{ + Q_D(QBasicKeyEventTransition); + d->eventType = type; + d->key = key; + d->modifiers = modifiers; +} + +/*! + Destroys this event transition. +*/ +QBasicKeyEventTransition::~QBasicKeyEventTransition() +{ +} + +/*! + Returns the event type that this key event transition is associated with. +*/ +QEvent::Type QBasicKeyEventTransition::eventType() const +{ + Q_D(const QBasicKeyEventTransition); + return d->eventType; +} + +/*! + Sets the event \a type that this key event transition is associated with. +*/ +void QBasicKeyEventTransition::setEventType(QEvent::Type type) +{ + Q_D(QBasicKeyEventTransition); + d->eventType = type; +} + +/*! + Returns the key that this key event transition checks for. +*/ +int QBasicKeyEventTransition::key() const +{ + Q_D(const QBasicKeyEventTransition); + return d->key; +} + +/*! + Sets the key that this key event transition will check for. +*/ +void QBasicKeyEventTransition::setKey(int key) +{ + Q_D(QBasicKeyEventTransition); + d->key = key; +} + +/*! + Returns the keyboard modifiers that this key event transition checks for. +*/ +Qt::KeyboardModifiers QBasicKeyEventTransition::modifiers() const +{ + Q_D(const QBasicKeyEventTransition); + return d->modifiers; +} + +/*! + Sets the keyboard modifiers that this key event transition will check for. +*/ +void QBasicKeyEventTransition::setModifiers(Qt::KeyboardModifiers modifiers) +{ + Q_D(QBasicKeyEventTransition); + d->modifiers = modifiers; +} + +/*! + \reimp +*/ +bool QBasicKeyEventTransition::eventTest(QEvent *event) const +{ + Q_D(const QBasicKeyEventTransition); + if (event->type() == d->eventType) { + QKeyEvent *ke = static_cast<QKeyEvent*>(event); + return (ke->key() == d->key) && (ke->modifiers() == d->modifiers); + } + return false; +} + +/*! + \reimp +*/ +void QBasicKeyEventTransition::onTransition() +{ +} + +QT_END_NAMESPACE diff --git a/src/gui/statemachine/qbasickeyeventtransition_p.h b/src/gui/statemachine/qbasickeyeventtransition_p.h new file mode 100644 index 0000000..ff52f4d --- /dev/null +++ b/src/gui/statemachine/qbasickeyeventtransition_p.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QBASICKEYEVENTTRANSITION_P_H +#define QBASICKEYEVENTTRANSITION_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 +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qabstracttransition.h" +#else +# include <QtCore/qabstracttransition.h> +#endif +#include <QtGui/qevent.h> + +QT_BEGIN_NAMESPACE + +class QBasicKeyEventTransitionPrivate; +class Q_AUTOTEST_EXPORT QBasicKeyEventTransition : public QAbstractTransition +{ + Q_OBJECT +public: + QBasicKeyEventTransition(QState *sourceState = 0); + QBasicKeyEventTransition(QEvent::Type type, int key, QState *sourceState = 0); + QBasicKeyEventTransition(QEvent::Type type, int key, + Qt::KeyboardModifiers modifiers, QState *sourceState = 0); + ~QBasicKeyEventTransition(); + + QEvent::Type eventType() const; + void setEventType(QEvent::Type type); + + int key() const; + void setKey(int key); + + Qt::KeyboardModifiers modifiers() const; + void setModifiers(Qt::KeyboardModifiers modifiers); + +protected: + bool eventTest(QEvent *event) const; + void onTransition(); + +private: + Q_DISABLE_COPY(QBasicKeyEventTransition) + Q_DECLARE_PRIVATE(QBasicKeyEventTransition) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/statemachine/qbasicmouseeventtransition.cpp b/src/gui/statemachine/qbasicmouseeventtransition.cpp new file mode 100644 index 0000000..b7583f8 --- /dev/null +++ b/src/gui/statemachine/qbasicmouseeventtransition.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "qbasicmouseeventtransition_p.h" +#include <QtGui/qevent.h> +#include <QtGui/qpainterpath.h> +#include <qdebug.h> + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qabstracttransition_p.h" +#else +# include <private/qabstracttransition_p.h> +#endif + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QBasicMouseEventTransition + + \brief The QBasicMouseEventTransition class provides a transition for Qt mouse events. +*/ + +class QBasicMouseEventTransitionPrivate : public QAbstractTransitionPrivate +{ + Q_DECLARE_PUBLIC(QBasicMouseEventTransition) +public: + QBasicMouseEventTransitionPrivate(); + + static QBasicMouseEventTransitionPrivate *get(QBasicMouseEventTransition *q); + + QEvent::Type eventType; + Qt::MouseButton button; + QPainterPath path; +}; + +QBasicMouseEventTransitionPrivate::QBasicMouseEventTransitionPrivate() +{ + eventType = QEvent::None; + button = Qt::NoButton; +} + +QBasicMouseEventTransitionPrivate *QBasicMouseEventTransitionPrivate::get(QBasicMouseEventTransition *q) +{ + return q->d_func(); +} + +/*! + Constructs a new mouse event transition with the given \a sourceState. +*/ +QBasicMouseEventTransition::QBasicMouseEventTransition(QState *sourceState) + : QAbstractTransition(*new QBasicMouseEventTransitionPrivate, sourceState) +{ +} + +/*! + Constructs a new mouse event transition for events of the given \a type. +*/ +QBasicMouseEventTransition::QBasicMouseEventTransition(QEvent::Type type, + Qt::MouseButton button, + QState *sourceState) + : QAbstractTransition(*new QBasicMouseEventTransitionPrivate, sourceState) +{ + Q_D(QBasicMouseEventTransition); + d->eventType = type; + d->button = button; +} + +/*! + Destroys this mouse event transition. +*/ +QBasicMouseEventTransition::~QBasicMouseEventTransition() +{ +} + +/*! + Returns the event type that this mouse event transition is associated with. +*/ +QEvent::Type QBasicMouseEventTransition::eventType() const +{ + Q_D(const QBasicMouseEventTransition); + return d->eventType; +} + +/*! + Sets the event \a type that this mouse event transition is associated with. +*/ +void QBasicMouseEventTransition::setEventType(QEvent::Type type) +{ + Q_D(QBasicMouseEventTransition); + d->eventType = type; +} + +/*! + Returns the button that this mouse event transition checks for. +*/ +Qt::MouseButton QBasicMouseEventTransition::button() const +{ + Q_D(const QBasicMouseEventTransition); + return d->button; +} + +/*! + Sets the button that this mouse event transition will check for. +*/ +void QBasicMouseEventTransition::setButton(Qt::MouseButton button) +{ + Q_D(QBasicMouseEventTransition); + d->button = button; +} + +/*! + Returns the path for this mouse event transition. +*/ +QPainterPath QBasicMouseEventTransition::path() const +{ + Q_D(const QBasicMouseEventTransition); + return d->path; +} + +/*! + Sets the path for this mouse event transition. +*/ +void QBasicMouseEventTransition::setPath(const QPainterPath &path) +{ + Q_D(QBasicMouseEventTransition); + d->path = path; +} + +/*! + \reimp +*/ +bool QBasicMouseEventTransition::eventTest(QEvent *event) const +{ + Q_D(const QBasicMouseEventTransition); + if (event->type() == d->eventType) { + QMouseEvent *me = static_cast<QMouseEvent*>(event); + return (me->button() == d->button) + && (d->path.isEmpty() || d->path.contains(me->pos())); + } + return false; +} + +/*! + \reimp +*/ +void QBasicMouseEventTransition::onTransition() +{ +} + +QT_END_NAMESPACE diff --git a/src/gui/statemachine/qbasicmouseeventtransition_p.h b/src/gui/statemachine/qbasicmouseeventtransition_p.h new file mode 100644 index 0000000..ee04fc7 --- /dev/null +++ b/src/gui/statemachine/qbasicmouseeventtransition_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QBASICMOUSEEVENTTRANSITION_P_H +#define QBASICMOUSEEVENTTRANSITION_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. +// + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qabstracttransition.h" +#else +# include <QtCore/qabstracttransition.h> +#endif + +#include <QtGui/qevent.h> + +QT_BEGIN_NAMESPACE + +class QPainterPath; + +class QBasicMouseEventTransitionPrivate; +class Q_AUTOTEST_EXPORT QBasicMouseEventTransition : public QAbstractTransition +{ + Q_OBJECT +public: + QBasicMouseEventTransition(QState *sourceState = 0); + QBasicMouseEventTransition(QEvent::Type type, Qt::MouseButton button, + QState *sourceState = 0); + ~QBasicMouseEventTransition(); + + QEvent::Type eventType() const; + void setEventType(QEvent::Type type); + + Qt::MouseButton button() const; + void setButton(Qt::MouseButton button); + + QPainterPath path() const; + void setPath(const QPainterPath &path); + +protected: + bool eventTest(QEvent *event) const; + void onTransition(); + +private: + Q_DISABLE_COPY(QBasicMouseEventTransition) + Q_DECLARE_PRIVATE(QBasicMouseEventTransition) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/statemachine/qguistatemachine.cpp b/src/gui/statemachine/qguistatemachine.cpp new file mode 100644 index 0000000..d30265a --- /dev/null +++ b/src/gui/statemachine/qguistatemachine.cpp @@ -0,0 +1,534 @@ + /**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifdef QT_STATEMACHINE_SOLUTION +#include "qstatemachine.h" +#include "qstatemachine_p.h" +#else +#include <QtCore/qstatemachine.h> +#include <private/qstatemachine_p.h> +#endif +#include <QtGui/qevent.h> +#include <QtGui/qgraphicssceneevent.h> + +QT_BEGIN_NAMESPACE + +Q_CORE_EXPORT const QStateMachinePrivate::Handler *qcoreStateMachineHandler(); + +static QEvent *cloneEvent(QEvent *e) +{ + switch (e->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + return new QMouseEvent(*static_cast<QMouseEvent*>(e)); + case QEvent::KeyPress: + case QEvent::KeyRelease: + return new QKeyEvent(*static_cast<QKeyEvent*>(e)); + case QEvent::FocusIn: + case QEvent::FocusOut: + return new QFocusEvent(*static_cast<QFocusEvent*>(e)); + case QEvent::Enter: + return new QEvent(*e); + case QEvent::Leave: + return new QEvent(*e); + break; + case QEvent::Paint: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::Move: + return new QMoveEvent(*static_cast<QMoveEvent*>(e)); + case QEvent::Resize: + return new QResizeEvent(*static_cast<QResizeEvent*>(e)); + case QEvent::Create: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::Destroy: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::Show: + return new QShowEvent(*static_cast<QShowEvent*>(e)); + case QEvent::Hide: + return new QHideEvent(*static_cast<QHideEvent*>(e)); + case QEvent::Close: + return new QCloseEvent(*static_cast<QCloseEvent*>(e)); + case QEvent::Quit: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ParentChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ParentAboutToChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ThreadChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::WindowActivate: + case QEvent::WindowDeactivate: + return new QEvent(*e); + + case QEvent::ShowToParent: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::HideToParent: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::Wheel: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::WindowTitleChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::WindowIconChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ApplicationWindowIconChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ApplicationFontChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ApplicationLayoutDirectionChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ApplicationPaletteChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::PaletteChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::Clipboard: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::Speech: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::MetaCall: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::SockAct: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::WinEventAct: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::DeferredDelete: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::DragEnter: + return new QDragEnterEvent(*static_cast<QDragEnterEvent*>(e)); + case QEvent::DragMove: + return new QDragMoveEvent(*static_cast<QDragMoveEvent*>(e)); + case QEvent::DragLeave: + return new QDragLeaveEvent(*static_cast<QDragLeaveEvent*>(e)); + case QEvent::Drop: + return new QDropEvent(*static_cast<QDragMoveEvent*>(e)); + case QEvent::DragResponse: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ChildAdded: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ChildPolished: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; +#ifdef QT3_SUPPORT + case QEvent::ChildInsertedRequest: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ChildInserted: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::LayoutHint: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; +#endif + case QEvent::ChildRemoved: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ShowWindowRequest: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::PolishRequest: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::Polish: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::LayoutRequest: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::UpdateRequest: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::UpdateLater: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::EmbeddingControl: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ActivateControl: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::DeactivateControl: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ContextMenu: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::InputMethod: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::AccessibilityPrepare: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::TabletMove: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::LocaleChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::LanguageChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::LayoutDirectionChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::Style: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::TabletPress: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::TabletRelease: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::OkRequest: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::HelpRequest: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::IconDrag: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::FontChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::EnabledChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ActivationChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::StyleChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::IconTextChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ModifiedChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::MouseTrackingChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::WindowBlocked: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::WindowUnblocked: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::WindowStateChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::ToolTip: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::WhatsThis: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::StatusTip: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::ActionChanged: + case QEvent::ActionAdded: + case QEvent::ActionRemoved: + return new QActionEvent(*static_cast<QActionEvent*>(e)); + + case QEvent::FileOpen: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::Shortcut: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ShortcutOverride: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + +#ifdef QT3_SUPPORT + case QEvent::Accel: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::AccelAvailable: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; +#endif + + case QEvent::WhatsThisClicked: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::ToolBarChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::ApplicationActivate: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ApplicationDeactivate: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::QueryWhatsThis: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::EnterWhatsThisMode: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::LeaveWhatsThisMode: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::ZOrderChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::HoverEnter: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::HoverLeave: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::HoverMove: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::AccessibilityHelp: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::AccessibilityDescription: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + +#ifdef QT_KEYPAD_NAVIGATION + case QEvent::EnterEditFocus: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::LeaveEditFocus: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; +#endif + case QEvent::AcceptDropsChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::MenubarUpdated: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::ZeroTimerEvent: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseRelease: + case QEvent::GraphicsSceneMouseDoubleClick: { + QGraphicsSceneMouseEvent *me = static_cast<QGraphicsSceneMouseEvent*>(e); + QGraphicsSceneMouseEvent *me2 = new QGraphicsSceneMouseEvent(me->type()); + me2->setWidget(me->widget()); + me2->setPos(me->pos()); + me2->setScenePos(me->scenePos()); + me2->setScreenPos(me->screenPos()); +// ### for all buttons + me2->setButtonDownPos(Qt::LeftButton, me->buttonDownPos(Qt::LeftButton)); + me2->setButtonDownPos(Qt::RightButton, me->buttonDownPos(Qt::RightButton)); + me2->setButtonDownScreenPos(Qt::LeftButton, me->buttonDownScreenPos(Qt::LeftButton)); + me2->setButtonDownScreenPos(Qt::RightButton, me->buttonDownScreenPos(Qt::RightButton)); + me2->setLastPos(me->lastPos()); + me2->setLastScenePos(me->lastScenePos()); + me2->setLastScreenPos(me->lastScreenPos()); + me2->setButtons(me->buttons()); + me2->setButton(me->button()); + me2->setModifiers(me->modifiers()); + return me2; + } + + case QEvent::GraphicsSceneContextMenu: { + QGraphicsSceneContextMenuEvent *me = static_cast<QGraphicsSceneContextMenuEvent*>(e); + QGraphicsSceneContextMenuEvent *me2 = new QGraphicsSceneContextMenuEvent(me->type()); + me2->setWidget(me->widget()); + me2->setPos(me->pos()); + me2->setScenePos(me->scenePos()); + me2->setScreenPos(me->screenPos()); + me2->setModifiers(me->modifiers()); + me2->setReason(me->reason()); + return me2; + } + + case QEvent::GraphicsSceneHoverEnter: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneHoverMove: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneHoverLeave: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneHelp: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneDragEnter: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneDragMove: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneDragLeave: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneDrop: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneWheel: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::KeyboardLayoutChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::DynamicPropertyChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::TabletEnterProximity: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::TabletLeaveProximity: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::NonClientAreaMouseMove: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::NonClientAreaMouseButtonPress: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::NonClientAreaMouseButtonRelease: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::NonClientAreaMouseButtonDblClick: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::MacSizeChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::ContentsRectChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::MacGLWindowChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::FutureCallOut: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::GraphicsSceneResize: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneMove: { + QGraphicsSceneMoveEvent *me = static_cast<QGraphicsSceneMoveEvent*>(e); + QGraphicsSceneMoveEvent *me2 = new QGraphicsSceneMoveEvent(); + me2->setWidget(me->widget()); + me2->setNewPos(me->newPos()); + me2->setOldPos(me->oldPos()); + return me2; + } + + case QEvent::CursorChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ToolTipChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::NetworkReplyUpdated: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::GrabMouse: + case QEvent::UngrabMouse: + case QEvent::GrabKeyboard: + case QEvent::UngrabKeyboard: + return new QEvent(*e); + +#ifdef QT_MAC_USE_COCOA + case QEvent::CocoaRequestModal: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; +#endif + case QEvent::User: + case QEvent::MaxUser: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + default: + ; + } + return qcoreStateMachineHandler()->cloneEvent(e); +} + +const QStateMachinePrivate::Handler qt_gui_statemachine_handler = { + cloneEvent +}; + +static const QStateMachinePrivate::Handler *qt_guistatemachine_last_handler = 0; +int qRegisterGuiStateMachine() +{ + qt_guistatemachine_last_handler = QStateMachinePrivate::handler; + QStateMachinePrivate::handler = &qt_gui_statemachine_handler; + return 1; +} +Q_CONSTRUCTOR_FUNCTION(qRegisterGuiStateMachine) + +int qUnregisterGuiStateMachine() +{ + QStateMachinePrivate::handler = qt_guistatemachine_last_handler; + return 1; +} +Q_DESTRUCTOR_FUNCTION(qUnregisterGuiStateMachine) + +QT_END_NAMESPACE diff --git a/src/gui/statemachine/qkeyeventtransition.cpp b/src/gui/statemachine/qkeyeventtransition.cpp new file mode 100644 index 0000000..a4e882d --- /dev/null +++ b/src/gui/statemachine/qkeyeventtransition.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "qkeyeventtransition.h" +#include "qbasickeyeventtransition_p.h" + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qeventtransition_p.h" +#else +# include <private/qeventtransition_p.h> +#endif + +QT_BEGIN_NAMESPACE + +/*! + \class QKeyEventTransition + + \brief The QKeyEventTransition class provides a transition for key events. + + \ingroup statemachine + + QKeyEventTransition is part of \l{The State Machine Framework}. + + \sa QState::addTransition() +*/ + +/*! + \property QKeyEventTransition::key + + \brief the key that this key event transition is associated with +*/ + +class QKeyEventTransitionPrivate : public QEventTransitionPrivate +{ + Q_DECLARE_PUBLIC(QKeyEventTransition) +public: + QKeyEventTransitionPrivate() {} + + QBasicKeyEventTransition *transition; +}; + +/*! + Constructs a new key event transition with the given \a sourceState. +*/ +QKeyEventTransition::QKeyEventTransition(QState *sourceState) + : QEventTransition(*new QKeyEventTransitionPrivate, sourceState) +{ + Q_D(QKeyEventTransition); + d->transition = new QBasicKeyEventTransition(); +} + +/*! + Constructs a new key event transition for events of the given \a type for + the given \a object, with the given \a key and \a sourceState. +*/ +QKeyEventTransition::QKeyEventTransition(QObject *object, QEvent::Type type, + int key, QState *sourceState) + : QEventTransition(*new QKeyEventTransitionPrivate, object, type, sourceState) +{ + Q_D(QKeyEventTransition); + d->transition = new QBasicKeyEventTransition(type, key); +} + +/*! + Constructs a new key event transition for events of the given \a type for + the given \a object, with the given \a key, \a targets and \a sourceState. +*/ +QKeyEventTransition::QKeyEventTransition(QObject *object, QEvent::Type type, + int key, const QList<QAbstractState*> &targets, + QState *sourceState) + : QEventTransition(*new QKeyEventTransitionPrivate, object, type, targets, sourceState) +{ + Q_D(QKeyEventTransition); + d->transition = new QBasicKeyEventTransition(type, key); +} + +/*! + Destroys this key event transition. +*/ +QKeyEventTransition::~QKeyEventTransition() +{ + Q_D(QKeyEventTransition); + delete d->transition; +} + +/*! + Returns the key that this key event transition checks for. +*/ +int QKeyEventTransition::key() const +{ + Q_D(const QKeyEventTransition); + return d->transition->key(); +} + +/*! + Sets the key that this key event transition will check for. +*/ +void QKeyEventTransition::setKey(int key) +{ + Q_D(QKeyEventTransition); + d->transition->setKey(key); +} + +/*! + Returns the keyboard modifiers that this key event transition checks for. +*/ +Qt::KeyboardModifiers QKeyEventTransition::modifiers() const +{ + Q_D(const QKeyEventTransition); + return d->transition->modifiers(); +} + +/*! + Sets the keyboard \a modifiers that this key event transition will check + for. +*/ +void QKeyEventTransition::setModifiers(Qt::KeyboardModifiers modifiers) +{ + Q_D(QKeyEventTransition); + d->transition->setModifiers(modifiers); +} + +/*! + \reimp +*/ +bool QKeyEventTransition::testEventCondition(QEvent *event) const +{ + Q_D(const QKeyEventTransition); + d->transition->setEventType(event->type()); + return QAbstractTransitionPrivate::get(d->transition)->callEventTest(event); +} + +/*! + \reimp +*/ +bool QKeyEventTransition::eventTest(QEvent *event) const +{ + return QEventTransition::eventTest(event); +} + +/*! + \reimp +*/ +void QKeyEventTransition::onTransition() +{ + QEventTransition::onTransition(); +} + +QT_END_NAMESPACE diff --git a/src/gui/statemachine/qkeyeventtransition.h b/src/gui/statemachine/qkeyeventtransition.h new file mode 100644 index 0000000..5bae8d7 --- /dev/null +++ b/src/gui/statemachine/qkeyeventtransition.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QKEYEVENTTRANSITION_H +#define QKEYEVENTTRANSITION_H + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qeventtransition.h" +#else +# include <QtCore/qeventtransition.h> +#endif +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QKeyEventTransitionPrivate; +class Q_GUI_EXPORT QKeyEventTransition : public QEventTransition +{ + Q_OBJECT + Q_PROPERTY(int key READ key WRITE setKey) +public: + QKeyEventTransition(QState *sourceState = 0); + QKeyEventTransition(QObject *object, QEvent::Type type, int key, + QState *sourceState = 0); + QKeyEventTransition(QObject *object, QEvent::Type type, int key, + const QList<QAbstractState*> &targets, + QState *sourceState = 0); + ~QKeyEventTransition(); + + int key() const; + void setKey(int key); + + Qt::KeyboardModifiers modifiers() const; + void setModifiers(Qt::KeyboardModifiers modifiers); + +protected: + void onTransition(); + bool eventTest(QEvent *event) const; + bool testEventCondition(QEvent *event) const; + +private: + Q_DISABLE_COPY(QKeyEventTransition) + Q_DECLARE_PRIVATE(QKeyEventTransition) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/statemachine/qmouseeventtransition.cpp b/src/gui/statemachine/qmouseeventtransition.cpp new file mode 100644 index 0000000..1621c78 --- /dev/null +++ b/src/gui/statemachine/qmouseeventtransition.cpp @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "qmouseeventtransition.h" +#include "qbasicmouseeventtransition_p.h" +#include <QtGui/qpainterpath.h> + +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qeventtransition_p.h" +#else +# include <private/qeventtransition_p.h> +#endif + +QT_BEGIN_NAMESPACE + +/*! + \class QMouseEventTransition + + \brief The QMouseEventTransition class provides a transition for mouse events. + + \ingroup statemachine + + QMouseEventTransition is part of \l{The State Machine Framework}. + + \sa QState::addTransition() +*/ + +class QMouseEventTransitionPrivate : public QEventTransitionPrivate +{ + Q_DECLARE_PUBLIC(QMouseEventTransition) +public: + QMouseEventTransitionPrivate(); + + QBasicMouseEventTransition *transition; +}; + +QMouseEventTransitionPrivate::QMouseEventTransitionPrivate() +{ +} + +/*! + Constructs a new mouse event transition with the given \a sourceState. +*/ +QMouseEventTransition::QMouseEventTransition(QState *sourceState) + : QEventTransition(*new QMouseEventTransitionPrivate, sourceState) +{ + Q_D(QMouseEventTransition); + d->transition = new QBasicMouseEventTransition(); +} + +/*! + Constructs a new mouse event transition for events of the given \a type for + the given \a object, with the given \a button and \a sourceState. +*/ +QMouseEventTransition::QMouseEventTransition(QObject *object, QEvent::Type type, + Qt::MouseButton button, + QState *sourceState) + : QEventTransition(*new QMouseEventTransitionPrivate, object, type, sourceState) +{ + Q_D(QMouseEventTransition); + d->transition = new QBasicMouseEventTransition(type, button); +} + +/*! + Constructs a new mouse event transition for events of the given \a type for + the given \a object, with the given \a button, \a targets and \a + sourceState. +*/ +QMouseEventTransition::QMouseEventTransition(QObject *object, QEvent::Type type, + Qt::MouseButton button, + const QList<QAbstractState*> &targets, + QState *sourceState) + : QEventTransition(*new QMouseEventTransitionPrivate, object, type, targets, sourceState) +{ + Q_D(QMouseEventTransition); + d->transition = new QBasicMouseEventTransition(type, button); +} + +/*! + Destroys this mouse event transition. +*/ +QMouseEventTransition::~QMouseEventTransition() +{ + Q_D(QMouseEventTransition); + delete d->transition; +} + +/*! + Returns the button that this mouse event transition checks for. +*/ +Qt::MouseButton QMouseEventTransition::button() const +{ + Q_D(const QMouseEventTransition); + return d->transition->button(); +} + +/*! + Sets the button that this mouse event transition will check for. +*/ +void QMouseEventTransition::setButton(Qt::MouseButton button) +{ + Q_D(QMouseEventTransition); + d->transition->setButton(button); +} + +/*! + Returns the path for this mouse event transition. +*/ +QPainterPath QMouseEventTransition::path() const +{ + Q_D(const QMouseEventTransition); + return d->transition->path(); +} + +/*! + Sets the \a path for this mouse event transition. + If a valid path has been set, the transition will only trigger if the mouse + event position (QMouseEvent::pos()) is inside the path. + + \sa QPainterPath::contains() +*/ +void QMouseEventTransition::setPath(const QPainterPath &path) +{ + Q_D(QMouseEventTransition); + d->transition->setPath(path); +} + +/*! + \reimp +*/ +bool QMouseEventTransition::testEventCondition(QEvent *event) const +{ + Q_D(const QMouseEventTransition); + d->transition->setEventType(event->type()); + return QAbstractTransitionPrivate::get(d->transition)->callEventTest(event); +} + +/*! + \reimp +*/ +bool QMouseEventTransition::eventTest(QEvent *event) const +{ + return QEventTransition::eventTest(event); +} + +/*! + \reimp +*/ +void QMouseEventTransition::onTransition() +{ + QEventTransition::onTransition(); +} + +QT_END_NAMESPACE diff --git a/src/gui/statemachine/qmouseeventtransition.h b/src/gui/statemachine/qmouseeventtransition.h new file mode 100644 index 0000000..162b1b5 --- /dev/null +++ b/src/gui/statemachine/qmouseeventtransition.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QMOUSEEVENTTRANSITION_H +#define QMOUSEEVENTTRANSITION_H + +//Qt +#if defined(QT_EXPERIMENTAL_SOLUTION) +# include "qeventtransition.h" +#else +# include <QtCore/qeventtransition.h> +#endif +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QMouseEventTransitionPrivate; +class QPainterPath; +class Q_GUI_EXPORT QMouseEventTransition : public QEventTransition +{ + Q_OBJECT +public: + QMouseEventTransition(QState *sourceState = 0); + QMouseEventTransition(QObject *object, QEvent::Type type, + Qt::MouseButton button, QState *sourceState = 0); + QMouseEventTransition(QObject *object, QEvent::Type type, + Qt::MouseButton button, + const QList<QAbstractState*> &targets, + QState *sourceState = 0); + ~QMouseEventTransition(); + + Qt::MouseButton button() const; + void setButton(Qt::MouseButton button); + + QPainterPath path() const; + void setPath(const QPainterPath &path); + +protected: + void onTransition(); + bool eventTest(QEvent *event) const; + bool testEventCondition(QEvent *event) const; + +private: + Q_DISABLE_COPY(QMouseEventTransition) + Q_DECLARE_PRIVATE(QMouseEventTransition) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/statemachine/statemachine.pri b/src/gui/statemachine/statemachine.pri new file mode 100644 index 0000000..24de458 --- /dev/null +++ b/src/gui/statemachine/statemachine.pri @@ -0,0 +1,16 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD + +SOURCES += $$PWD/qguistatemachine.cpp +!contains(DEFINES, QT_NO_STATEMACHINE_EVENTFILTER) { + HEADERS += \ + $$PWD/qkeyeventtransition.h \ + $$PWD/qmouseeventtransition.h \ + $$PWD/qbasickeyeventtransition_p.h \ + $$PWD/qbasicmouseeventtransition_p.h + SOURCES += \ + $$PWD/qkeyeventtransition.cpp \ + $$PWD/qmouseeventtransition.cpp \ + $$PWD/qbasickeyeventtransition.cpp \ + $$PWD/qbasicmouseeventtransition.cpp +} diff --git a/src/script/qscriptengine_p.cpp b/src/script/qscriptengine_p.cpp index a2e58de..84a420d 100644 --- a/src/script/qscriptengine_p.cpp +++ b/src/script/qscriptengine_p.cpp @@ -1662,6 +1662,11 @@ bool QScriptEnginePrivate::convert(const QScriptValueImpl &value, return false; } +QScriptEngine::DemarshalFunction QScriptEnginePrivate::demarshalFunction(int type) const +{ + return m_customTypes.value(type).demarshal; +} + QScriptValuePrivate *QScriptEnginePrivate::registerValue(const QScriptValueImpl &value) { if (value.isString()) { diff --git a/src/script/qscriptenginefwd_p.h b/src/script/qscriptenginefwd_p.h index 2ea66c5..855317c 100644 --- a/src/script/qscriptenginefwd_p.h +++ b/src/script/qscriptenginefwd_p.h @@ -350,6 +350,7 @@ public: QScriptValueImpl create(int type, const void *ptr); static bool convert(const QScriptValueImpl &value, int type, void *ptr, QScriptEnginePrivate *eng); + QScriptEngine::DemarshalFunction demarshalFunction(int type) const; QScriptValueImpl arrayFromStringList(const QStringList &lst); static QStringList stringListFromArray(const QScriptValueImpl &arr); diff --git a/src/script/qscriptextqobject.cpp b/src/script/qscriptextqobject.cpp index d18c3da..4522807 100644 --- a/src/script/qscriptextqobject.cpp +++ b/src/script/qscriptextqobject.cpp @@ -425,7 +425,7 @@ static void callQtMethod(QScriptContextPrivate *context, QMetaMethod::MethodType matchDistance += 10; } } - } else if (actual.isNumber()) { + } else if (actual.isNumber() || actual.isString()) { // see if it's an enum value QMetaEnum m; if (argType.isMetaEnum()) { @@ -436,11 +436,21 @@ static void callQtMethod(QScriptContextPrivate *context, QMetaMethod::MethodType m = meta->enumerator(mi); } if (m.isValid()) { - int ival = actual.toInt32(); - if (m.valueToKey(ival) != 0) { - qVariantSetValue(v, ival); - converted = true; - matchDistance += 10; + if (actual.isNumber()) { + int ival = actual.toInt32(); + if (m.valueToKey(ival) != 0) { + qVariantSetValue(v, ival); + converted = true; + matchDistance += 10; + } + } else { + QString sval = actual.toString(); + int ival = m.keyToValue(sval.toLatin1()); + if (ival != -1) { + qVariantSetValue(v, ival); + converted = true; + matchDistance += 10; + } } } } @@ -1809,7 +1819,16 @@ void QScript::QtPropertyFunction::execute(QScriptContextPrivate *context) } } else { // set - QVariant v = variantFromValue(eng_p, prop.userType(), context->argument(0)); + QScriptValueImpl arg = context->argument(0); + QVariant v; + if (prop.isEnumType() && arg.isString() + && !eng_p->demarshalFunction(prop.userType())) { + // give QMetaProperty::write() a chance to convert from + // string to enum value + v = arg.toString(); + } else { + v = variantFromValue(eng_p, prop.userType(), arg); + } QScriptable *scriptable = scriptableFromQObject(qobject); QScriptEngine *oldEngine = 0; diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 60e6657..f8f15a3 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -75,6 +75,7 @@ SUBDIRS += bic \ qaction \ qactiongroup \ qalgorithms \ + qanimationgroup \ qapplication \ qatomicint \ qatomicpointer \ @@ -213,6 +214,7 @@ SUBDIRS += bic \ qpainter \ qpainterpath \ qpalette \ + qparallelanimationgroup \ qpathclipper \ qpen \ qpicture \ @@ -228,6 +230,7 @@ SUBDIRS += bic \ qprocess \ qprogressbar \ qprogressdialog \ + qpropertyanimation \ qpushbutton \ qqueue \ qradiobutton \ @@ -254,6 +257,7 @@ SUBDIRS += bic \ qscrollarea \ qsemaphore \ qsharedpointer \ + qsequentialanimationgroup \ qset \ qsettings \ qshortcut \ @@ -287,6 +291,7 @@ SUBDIRS += bic \ qstackedwidget \ qstandarditem \ qstandarditemmodel \ + qstate \ qstatusbar \ qstl \ qstring \ @@ -340,6 +345,7 @@ SUBDIRS += bic \ qtranslator \ qtransform \ qtransformedscreen \ + qtransition \ qtreeview \ qtreewidget \ qtreewidgetitemiterator \ diff --git a/tests/auto/qanimationgroup/qanimationgroup.pro b/tests/auto/qanimationgroup/qanimationgroup.pro new file mode 100644 index 0000000..97d33dd --- /dev/null +++ b/tests/auto/qanimationgroup/qanimationgroup.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT = core gui +SOURCES += tst_qanimationgroup.cpp + + diff --git a/tests/auto/qanimationgroup/tst_qanimationgroup.cpp b/tests/auto/qanimationgroup/tst_qanimationgroup.cpp new file mode 100644 index 0000000..ff5c3b4 --- /dev/null +++ b/tests/auto/qanimationgroup/tst_qanimationgroup.cpp @@ -0,0 +1,347 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QtCore/qanimationgroup.h> +#include <QtCore/qsequentialanimationgroup.h> +#include <QtCore/qparallelanimationgroup.h> + +//TESTED_CLASS=QAnimationGroup +//TESTED_FILES= + +Q_DECLARE_METATYPE(QAbstractAnimation::State) + +class tst_QAnimationGroup : public QObject +{ + Q_OBJECT +public: + tst_QAnimationGroup(); + virtual ~tst_QAnimationGroup(); + +public Q_SLOTS: + void init(); + void cleanup(); + +private slots: + void construction(); + void emptyGroup(); + void setCurrentTime(); + void statesAndSignals(); + void setParentAutoAdd(); + void beginNestedGroup(); +}; + +tst_QAnimationGroup::tst_QAnimationGroup() +{ +} + +tst_QAnimationGroup::~tst_QAnimationGroup() +{ +} + +void tst_QAnimationGroup::init() +{ + qRegisterMetaType<QAbstractAnimation::State>("QAbstractAnimation::State"); +} + +void tst_QAnimationGroup::cleanup() +{ +} + +void tst_QAnimationGroup::construction() +{ + QSequentialAnimationGroup animationgroup; +} + +class AnimationObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(int value READ value WRITE setValue) +public: + AnimationObject(int startValue = 0) + : v(startValue) + { } + + int value() const { return v; } + void setValue(int value) { v = value; } + + int v; +}; + +class TestAnimation : public QVariantAnimation +{ + Q_OBJECT +public: + virtual void updateCurrentValue(const QVariant &value) { Q_UNUSED(value)}; + virtual void updateState(QAbstractAnimation::State oldState, + QAbstractAnimation::State newState) + { + Q_UNUSED(oldState) + Q_UNUSED(newState) + }; +}; + +class UncontrolledAnimation : public QPropertyAnimation +{ + Q_OBJECT +public: + UncontrolledAnimation(QObject *target, const QByteArray &propertyName, QObject *parent = 0) + : QPropertyAnimation(target, propertyName, parent), id(0) + { + setDuration(250); + } + + int duration() const { return -1; /* not time driven */ } + +protected: + void timerEvent(QTimerEvent *event) + { + if (event->timerId() == id) + stop(); + } + + void updateRunning(bool running) + { + if (running) { + id = startTimer(500); + } else { + killTimer(id); + id = 0; + } + } + +private: + int id; +}; + +void tst_QAnimationGroup::emptyGroup() +{ + QSequentialAnimationGroup group; + QSignalSpy groupStateChangedSpy(&group, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + group.start(); + + QCOMPARE(groupStateChangedSpy.count(), 2); + + QCOMPARE(qVariantValue<QAbstractAnimation::State>(groupStateChangedSpy.at(0).at(1)), + QAnimationGroup::Running); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(groupStateChangedSpy.at(1).at(1)), + QAnimationGroup::Stopped); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + + QTest::ignoreMessage(QtWarningMsg, "QAbstractAnimation::pause: Cannot pause a stopped animation"); + group.pause(); + + QCOMPARE(groupStateChangedSpy.count(), 2); + QCOMPARE(group.state(), QAnimationGroup::Stopped); + + group.start(); + + QCOMPARE(qVariantValue<QAbstractAnimation::State>(groupStateChangedSpy.at(2).at(1)), + QAnimationGroup::Running); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(groupStateChangedSpy.at(3).at(1)), + QAnimationGroup::Stopped); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + + group.stop(); + + QCOMPARE(groupStateChangedSpy.count(), 4); + QCOMPARE(group.state(), QAnimationGroup::Stopped); +} + +void tst_QAnimationGroup::setCurrentTime() +{ + AnimationObject s_o1; + AnimationObject s_o2; + AnimationObject s_o3; + AnimationObject p_o1; + AnimationObject p_o2; + AnimationObject p_o3; + AnimationObject t_o1; + AnimationObject t_o2; + + // sequence operating on same object/property + QSequentialAnimationGroup *sequence = new QSequentialAnimationGroup(); + QVariantAnimation *a1_s_o1 = new QPropertyAnimation(&s_o1, "value"); + QVariantAnimation *a2_s_o1 = new QPropertyAnimation(&s_o1, "value"); + QVariantAnimation *a3_s_o1 = new QPropertyAnimation(&s_o1, "value"); + a2_s_o1->setIterationCount(3); + sequence->addAnimation(a1_s_o1); + sequence->addAnimation(a2_s_o1); + sequence->addAnimation(a3_s_o1); + + // sequence operating on different object/properties + QAnimationGroup *sequence2 = new QSequentialAnimationGroup(); + QVariantAnimation *a1_s_o2 = new QPropertyAnimation(&s_o2, "value"); + QVariantAnimation *a1_s_o3 = new QPropertyAnimation(&s_o3, "value"); + sequence2->addAnimation(a1_s_o2); + sequence2->addAnimation(a1_s_o3); + + // parallel operating on different object/properties + QAnimationGroup *parallel = new QParallelAnimationGroup(); + QVariantAnimation *a1_p_o1 = new QPropertyAnimation(&p_o1, "value"); + QVariantAnimation *a1_p_o2 = new QPropertyAnimation(&p_o2, "value"); + QVariantAnimation *a1_p_o3 = new QPropertyAnimation(&p_o3, "value"); + a1_p_o2->setIterationCount(3); + parallel->addAnimation(a1_p_o1); + parallel->addAnimation(a1_p_o2); + parallel->addAnimation(a1_p_o3); + + UncontrolledAnimation *notTimeDriven = new UncontrolledAnimation(&t_o1, "value"); + QCOMPARE(notTimeDriven->totalDuration(), -1); + + QVariantAnimation *loopsForever = new QPropertyAnimation(&t_o2, "value"); + loopsForever->setIterationCount(-1); + QCOMPARE(loopsForever->totalDuration(), -1); + + QParallelAnimationGroup group; + group.addAnimation(sequence); + group.addAnimation(sequence2); + group.addAnimation(parallel); + group.addAnimation(notTimeDriven); + group.addAnimation(loopsForever); + + // Current time = 1 + group.setCurrentTime(1); + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(sequence->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(sequence2->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroup::Stopped); + QCOMPARE(parallel->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_p_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_p_o2->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_p_o3->state(), QAnimationGroup::Stopped); + QCOMPARE(notTimeDriven->state(), QAnimationGroup::Stopped); + QCOMPARE(loopsForever->state(), QAnimationGroup::Stopped); + + QCOMPARE(group.currentTime(), 1); + QCOMPARE(sequence->currentTime(), 1); + QCOMPARE(a1_s_o1->currentTime(), 1); + QCOMPARE(a2_s_o1->currentTime(), 0); + QCOMPARE(a3_s_o1->currentTime(), 0); + QCOMPARE(a1_s_o2->currentTime(), 1); + QCOMPARE(a1_s_o3->currentTime(), 0); + QCOMPARE(a1_p_o1->currentTime(), 1); + QCOMPARE(a1_p_o2->currentTime(), 1); + QCOMPARE(a1_p_o3->currentTime(), 1); + QCOMPARE(notTimeDriven->currentTime(), 1); + QCOMPARE(loopsForever->currentTime(), 1); + + // Current time = 250 + group.setCurrentTime(250); + QCOMPARE(group.currentTime(), 250); + QCOMPARE(sequence->currentTime(), 250); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 0); + QCOMPARE(a3_s_o1->currentTime(), 0); + QCOMPARE(a1_s_o2->currentTime(), 250); + QCOMPARE(a1_s_o3->currentTime(), 0); + QCOMPARE(a1_p_o1->currentTime(), 250); + QCOMPARE(a1_p_o2->currentTime(), 0); + QCOMPARE(a1_p_o2->currentIteration(), 1); + QCOMPARE(a1_p_o3->currentTime(), 250); + QCOMPARE(notTimeDriven->currentTime(), 250); + QCOMPARE(loopsForever->currentTime(), 0); + QCOMPARE(loopsForever->currentIteration(), 1); + QCOMPARE(sequence->currentAnimation(), a2_s_o1); + + // Current time = 251 + group.setCurrentTime(251); + QCOMPARE(group.currentTime(), 251); + QCOMPARE(sequence->currentTime(), 251); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 1); + QCOMPARE(a2_s_o1->currentIteration(), 0); + QCOMPARE(a3_s_o1->currentTime(), 0); + QCOMPARE(sequence2->currentTime(), 251); + QCOMPARE(a1_s_o2->currentTime(), 250); + QCOMPARE(a1_s_o3->currentTime(), 1); + QCOMPARE(a1_p_o1->currentTime(), 250); + QCOMPARE(a1_p_o2->currentTime(), 1); + QCOMPARE(a1_p_o2->currentIteration(), 1); + QCOMPARE(a1_p_o3->currentTime(), 250); + QCOMPARE(notTimeDriven->currentTime(), 251); + QCOMPARE(loopsForever->currentTime(), 1); + QCOMPARE(sequence->currentAnimation(), a2_s_o1); +} + +void tst_QAnimationGroup::statesAndSignals() +{ +} + +void tst_QAnimationGroup::setParentAutoAdd() +{ + QParallelAnimationGroup group; + QVariantAnimation *animation = new QPropertyAnimation(&group); + QCOMPARE(animation->group(), &group); +} + +void tst_QAnimationGroup::beginNestedGroup() +{ + QAnimationGroup *subGroup; + QAnimationGroup *parent = new QParallelAnimationGroup(); + + for (int i = 0; i < 10; ++i) { + if (i & 1) + subGroup = new QParallelAnimationGroup(parent); + else + subGroup = new QSequentialAnimationGroup(parent); + + QCOMPARE(parent->animationCount(), 1); + QAnimationGroup *child = static_cast<QAnimationGroup *>(parent->animationAt(0)); + + QCOMPARE(child->parent(), static_cast<QObject *>(parent)); + if (i & 1) + QVERIFY(qobject_cast<QParallelAnimationGroup *> (child)); + else + QVERIFY(qobject_cast<QSequentialAnimationGroup *> (child)); + + parent = child; + } +} + +QTEST_MAIN(tst_QAnimationGroup) +#include "tst_qanimationgroup.moc" diff --git a/tests/auto/qanimationstate/qanimationstate.pro b/tests/auto/qanimationstate/qanimationstate.pro new file mode 100644 index 0000000..8862c27 --- /dev/null +++ b/tests/auto/qanimationstate/qanimationstate.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT = core +SOURCES += tst_qanimationstate.cpp + + diff --git a/tests/auto/qanimationstate/tst_qanimationstate.cpp b/tests/auto/qanimationstate/tst_qanimationstate.cpp new file mode 100644 index 0000000..085db24 --- /dev/null +++ b/tests/auto/qanimationstate/tst_qanimationstate.cpp @@ -0,0 +1,625 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtCore/qstate.h> +#include <QtCore/qstatemachine.h> +#include <QtCore/qanimationstate.h> + +//TESTED_CLASS=QAnimationState +//TESTED_FILES= + +#define QTRY_COMPARE(__expr, __expected) \ + do { \ + const int __step = 50; \ + const int __timeout = 5000; \ + if ((__expr) != (__expected)) { \ + QTest::qWait(0); \ + } \ + for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \ + QTest::qWait(__step); \ + } \ + QCOMPARE(__expr, __expected); \ + } while(0) + + +class tst_QAnimationState : public QObject +{ + Q_OBJECT +public: + tst_QAnimationState(); + virtual ~tst_QAnimationState(); + +private slots: + void init(); + void cleanup(); + void construction(); + void noAnimation(); + void simpleAnimation(); + void twoAnimations(); + void reuseAnimation(); + void nestedTargetState(); + void parallelTargetState(); + void playTwice(); + void twoAnimatedTransitions(); + void globalRestoreProperty(); + void specificRestoreProperty(); + void someAnimationsNotSpecified(); + void someActionsNotAnimated(); + void specificTargetValueOfAnimation(); + void persistentTargetValueOfAnimation(); +}; + +tst_QAnimationState::tst_QAnimationState() +{ +} + +tst_QAnimationState::~tst_QAnimationState() +{ +} + +void tst_QAnimationState::init() +{ +} + +void tst_QAnimationState::cleanup() +{ +} + +void tst_QAnimationState::construction() +{ + QAnimationState as; +} + +class EventTransition : public QTransition +{ +public: + EventTransition(QEvent::Type type, QAbstractState *target) + : QTransition(), m_type(type) { + setTargetState(target); + } +protected: + virtual bool eventTest(QEvent *e) const { + return (e->type() == m_type); + } +private: + QEvent::Type m_type; +}; + +void tst_QAnimationState::noAnimation() +{ + QStateMachine machine; + + QState *s1 = new QState(machine.rootState()); + QState *s2 = new QState(machine.rootState()); + s2->setProperty("entered", false); + s2->setPropertyOnEntry(s2, "entered", true); + + s1->addAnimatedTransition(new EventTransition(QEvent::User, s2)); + s2->addTransition(new EventTransition(QEvent::User, s1)); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + QVERIFY(machine.configuration().contains(s1)); + + machine.postEvent(new QEvent(QEvent::User)); + + QTRY_COMPARE(s2->property("entered").toBool(), true); + QVERIFY(machine.configuration().contains(s2)); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QVERIFY(machine.configuration().contains(s1)); + + s2->setProperty("entered", false); + machine.postEvent(new QEvent(QEvent::User)); + + QTRY_COMPARE(s2->property("entered").toBool(), true); + QVERIFY(machine.configuration().contains(s2)); +} + +class ValueCheckerState: public QState +{ +public: + ValueCheckerState(QState *parent) + : QState(parent) + { + } + + void addPropertyToCheck(const QObject *object, const char *propertyName) + { + m_objects.append(object); + m_propertyNames.append(propertyName); + valueOnEntry.append(QVariant()); + } + + QVariantList valueOnEntry; + +protected: + virtual void onEntry() + { + for (int i=0; i<m_objects.size(); ++i) + valueOnEntry[i] = m_objects.at(i)->property(m_propertyNames.at(i)); + + QState::onEntry(); + } + + QList<const QObject *> m_objects; + QList<QByteArray> m_propertyNames; + +}; + +void tst_QAnimationState::simpleAnimation() +{ + QStateMachine machine; + + QObject *object = new QObject(); + object->setProperty("fooBar", 1.0); + + QState *s1 = new QState(machine.rootState()); + ValueCheckerState *s2 = new ValueCheckerState(machine.rootState()); + s2->addPropertyToCheck(object, "fooBar"); + s2->setPropertyOnEntry(object, "fooBar", 2.0); + + QPropertyAnimation *animation = new QPropertyAnimation(object, "fooBar", s2); + s1->addAnimatedTransition(new EventTransition(QEvent::User, s2), animation); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + + QTRY_COMPARE(s2->valueOnEntry.at(0).isValid(), true); + + QCOMPARE(s2->valueOnEntry.at(0).toDouble(), 2.0); +} + +void tst_QAnimationState::twoAnimations() +{ + QStateMachine machine; + + QObject *object = new QObject(); + object->setProperty("foo", 1.0); + object->setProperty("bar", 3.0); + + QState *s1 = new QState(machine.rootState()); + ValueCheckerState *s2 = new ValueCheckerState(machine.rootState()); + s2->addPropertyToCheck(object, "foo"); + s2->addPropertyToCheck(object, "bar"); + s2->setPropertyOnEntry(object, "foo", 2.0); + s2->setPropertyOnEntry(object, "bar", 10.0); + + QPropertyAnimation *animationFoo = new QPropertyAnimation(object, "foo", s2); + QPropertyAnimation *animationBar = new QPropertyAnimation(object, "bar", s2); + animationBar->setDuration(900); + QAnimationState *as = s1->addAnimatedTransition(new EventTransition(QEvent::User, s2)); + as->addAnimation(animationFoo); + as->addAnimation(animationBar); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + + QTRY_COMPARE(s2->valueOnEntry.at(0).isValid(), true); + + QCOMPARE(s2->valueOnEntry.at(0).toDouble(), 2.0); + QCOMPARE(s2->valueOnEntry.at(1).toDouble(), 10.0); +} + +void tst_QAnimationState::parallelTargetState() +{ + QStateMachine machine; + + QObject *object = new QObject(); + object->setProperty("foo", 1.0); + object->setProperty("bar", 3.0); + + QState *s1 = new QState(machine.rootState()); + QState *s2 = new QState(QState::ParallelGroup, machine.rootState()); + + ValueCheckerState *c1 = new ValueCheckerState(s2); + c1->setPropertyOnEntry(object, "foo", 2.0); + c1->addPropertyToCheck(object, "foo"); + c1->addPropertyToCheck(object, "bar"); + + QState *c2 = new QState(s2); + c2->setPropertyOnEntry(object, "bar", 10.0); + + QAnimationState *as = s1->addAnimatedTransition(new EventTransition(QEvent::User, s2)); + as->addAnimation(new QPropertyAnimation(object, "foo", as)); + as->addAnimation(new QPropertyAnimation(object, "bar", as)); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + + QTRY_COMPARE(c1->valueOnEntry.at(0).isValid(), true); + QCOMPARE(c1->valueOnEntry.at(0).toDouble(), 2.0); + QCOMPARE(c1->valueOnEntry.at(1).toDouble(), 10.0); +} + + +void tst_QAnimationState::twoAnimatedTransitions() +{ + QStateMachine machine; + + QObject *object = new QObject(); + object->setProperty("foo", 1.0); + + QState *s1 = new QState(machine.rootState()); + ValueCheckerState *s2 = new ValueCheckerState(machine.rootState()); + s2->addPropertyToCheck(object, "foo"); + s2->setPropertyOnEntry(object, "foo", 5.0); + ValueCheckerState *s3 = new ValueCheckerState(machine.rootState()); + s3->setPropertyOnEntry(object, "foo", 2.0); + s3->addPropertyToCheck(object, "foo"); + + s1->addAnimatedTransition(new EventTransition(QEvent::User, s2), + new QPropertyAnimation(object, "foo", s2)); + s2->addAnimatedTransition(new EventTransition(QEvent::User, s3), + new QPropertyAnimation(object, "foo", s2)); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + QTRY_COMPARE(s2->valueOnEntry.at(0).isValid(), true); + QCOMPARE(s2->valueOnEntry.at(0).toDouble(), 5.0); + + machine.postEvent(new QEvent(QEvent::User)); + QTRY_COMPARE(s3->valueOnEntry.at(0).isValid(), true); + QCOMPARE(s3->valueOnEntry.at(0).toDouble(), 2.0); +} + +void tst_QAnimationState::playTwice() +{ + QStateMachine machine; + + QObject *object = new QObject(); + object->setProperty("foo", 1.0); + + QState *s1 = new QState(machine.rootState()); + ValueCheckerState *s2 = new ValueCheckerState(machine.rootState()); + s2->addPropertyToCheck(object, "foo"); + s2->setPropertyOnEntry(object, "foo", 5.0); + QState *s3 = new QState(machine.rootState()); + s3->setPropertyOnEntry(object, "foo", 2.0); + + s1->addAnimatedTransition(new EventTransition(QEvent::User, s2), + new QPropertyAnimation(object, "foo", s2)); + s2->addTransition(new EventTransition(QEvent::User, s3)); + s3->addTransition(s1); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + QTRY_COMPARE(s2->valueOnEntry.at(0).isValid(), true); + QCOMPARE(s2->valueOnEntry.at(0).toDouble(), 5.0); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + QVERIFY(machine.configuration().contains(s1)); + QCOMPARE(object->property("foo").toDouble(), 2.0); + + s2->valueOnEntry[0] = QVariant(); + + machine.postEvent(new QEvent(QEvent::User)); + QTRY_COMPARE(s2->valueOnEntry.at(0).isValid(), true); + QCOMPARE(object->property("foo").toDouble(), 5.0); +} + +void tst_QAnimationState::nestedTargetState() +{ + QStateMachine machine; + + QObject *object = new QObject(); + object->setProperty("foo", 1.0); + object->setProperty("bar", 3.0); + + QState *s1 = new QState(machine.rootState()); + ValueCheckerState *s2 = new ValueCheckerState(machine.rootState()); + s2->addPropertyToCheck(object, "foo"); + s2->addPropertyToCheck(object, "bar"); + s2->setPropertyOnEntry(object, "foo", 2.0); + + QState *s2Child = new QState(s2); + s2Child->setPropertyOnEntry(object, "bar", 10.0); + s2->setInitialState(s2Child); + + QState *s2Child2 = new QState(s2); + s2Child2->setPropertyOnEntry(object, "bar", 11.0); + s2Child->addTransition(s2Child2); // should *not* be considered by QAnimationState as part of target + + QAnimationState *as = s1->addAnimatedTransition(new EventTransition(QEvent::User, s2)); + as->addAnimation(new QPropertyAnimation(object, "foo", as)); + as->addAnimation(new QPropertyAnimation(object, "bar", as)); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + + QTRY_COMPARE(s2->valueOnEntry.at(0).isValid(), true); + + QCOMPARE(s2->valueOnEntry.at(0).toDouble(), 2.0); + QCOMPARE(s2->valueOnEntry.at(1).toDouble(), 10.0); + QCOMPARE(object->property("bar").toDouble(), 11.0); +} + +void tst_QAnimationState::reuseAnimation() +{ + QStateMachine machine; + + QObject *object = new QObject(); + object->setProperty("foo", 1.0); + + QState *s1 = new QState(machine.rootState()); + ValueCheckerState *s2 = new ValueCheckerState(machine.rootState()); + s2->addPropertyToCheck(object, "foo"); + s2->setPropertyOnEntry(object, "foo", 5.0); + ValueCheckerState *s3 = new ValueCheckerState(machine.rootState()); + s3->setPropertyOnEntry(object, "foo", 2.0); + s3->addPropertyToCheck(object, "foo"); + + QPropertyAnimation *anim = new QPropertyAnimation(object, "foo", s2); + s1->addAnimatedTransition(new EventTransition(QEvent::User, s2), anim); + s2->addAnimatedTransition(new EventTransition(QEvent::User, s3), anim); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + QTRY_COMPARE(s2->valueOnEntry.at(0).isValid(), true); + QCOMPARE(s2->valueOnEntry.at(0).toDouble(), 5.0); + + machine.postEvent(new QEvent(QEvent::User)); + QTRY_COMPARE(s3->valueOnEntry.at(0).isValid(), true); + QCOMPARE(s3->valueOnEntry.at(0).toDouble(), 2.0); +} + +void tst_QAnimationState::globalRestoreProperty() +{ + QStateMachine machine; + machine.setGlobalRestorePolicy(QState::RestoreProperties); + + QObject *object = new QObject(); + object->setProperty("foo", 1.0); + object->setProperty("bar", 3.0); + + QState *s1 = new QState(machine.rootState()); + + ValueCheckerState *s2 = new ValueCheckerState(machine.rootState()); + s2->addPropertyToCheck(object, "foo"); + s2->addPropertyToCheck(object, "bar"); + s2->setPropertyOnEntry(object, "foo", 2.0); + + ValueCheckerState *s3 = new ValueCheckerState(machine.rootState()); + s3->addPropertyToCheck(object, "foo"); + s3->addPropertyToCheck(object, "bar"); + s3->setPropertyOnEntry(object, "bar", 5.0); + + ValueCheckerState *s4 = new ValueCheckerState(machine.rootState()); + s4->addPropertyToCheck(object, "foo"); + s4->addPropertyToCheck(object, "bar"); + + QAnimationState *as = s1->addAnimatedTransition(new EventTransition(QEvent::User, s2)); + as->addAnimation(new QPropertyAnimation(object, "foo", as)); + as->addAnimation(new QPropertyAnimation(object, "bar", as)); + + as = s2->addAnimatedTransition(new EventTransition(QEvent::User, s3)); + as->addAnimation(new QPropertyAnimation(object, "foo", as)); + as->addAnimation(new QPropertyAnimation(object, "bar", as)); + + as = s3->addAnimatedTransition(new EventTransition(QEvent::User, s4)); + as->addAnimation(new QPropertyAnimation(object, "foo", as)); + as->addAnimation(new QPropertyAnimation(object, "bar", as)); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + QTRY_COMPARE(s2->valueOnEntry.at(0).isValid(), true); + QCOMPARE(s2->valueOnEntry.at(0).toDouble(), 2.0); + QCOMPARE(s2->valueOnEntry.at(1).toDouble(), 3.0); + + machine.postEvent(new QEvent(QEvent::User)); + QTRY_COMPARE(s3->valueOnEntry.at(0).isValid(), true); + QCOMPARE(s3->valueOnEntry.at(0).toDouble(), 1.0); + QCOMPARE(s3->valueOnEntry.at(1).toDouble(), 5.0); + + machine.postEvent(new QEvent(QEvent::User)); + QTRY_COMPARE(s4->valueOnEntry.at(0).isValid(), true); + QCOMPARE(s4->valueOnEntry.at(0).toDouble(), 1.0); + QCOMPARE(s4->valueOnEntry.at(1).toDouble(), 3.0); + +} + +void tst_QAnimationState::specificRestoreProperty() +{ + QStateMachine machine; + + QObject *object = new QObject(); + object->setProperty("foo", 1.0); + object->setProperty("bar", 3.0); + + QState *s1 = new QState(machine.rootState()); + + ValueCheckerState *s2 = new ValueCheckerState(machine.rootState()); + s2->setRestorePolicy(QState::RestoreProperties); + s2->addPropertyToCheck(object, "foo"); + s2->addPropertyToCheck(object, "bar"); + s2->setPropertyOnEntry(object, "foo", 2.0); + + ValueCheckerState *s3 = new ValueCheckerState(machine.rootState()); + s3->setRestorePolicy(QState::RestoreProperties); + s3->addPropertyToCheck(object, "foo"); + s3->addPropertyToCheck(object, "bar"); + s3->setPropertyOnEntry(object, "bar", 5.0); + + ValueCheckerState *s4 = new ValueCheckerState(machine.rootState()); + s4->setRestorePolicy(QState::RestoreProperties); + s4->addPropertyToCheck(object, "foo"); + s4->addPropertyToCheck(object, "bar"); + + QAnimationState *as = s1->addAnimatedTransition(new EventTransition(QEvent::User, s2)); + as->addAnimation(new QPropertyAnimation(object, "foo", as)); + + as = s2->addAnimatedTransition(new EventTransition(QEvent::User, s3)); + as->addAnimation(new QPropertyAnimation(object, "bar", as)); + + as = s3->addAnimatedTransition(new EventTransition(QEvent::User, s4)); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + QTRY_COMPARE(s2->valueOnEntry.at(0).isValid(), true); + QCOMPARE(s2->valueOnEntry.at(0).toDouble(), 2.0); + QCOMPARE(s2->valueOnEntry.at(1).toDouble(), 3.0); + + machine.postEvent(new QEvent(QEvent::User)); + QTRY_COMPARE(s3->valueOnEntry.at(0).isValid(), true); + QCOMPARE(s3->valueOnEntry.at(0).toDouble(), 1.0); + QCOMPARE(s3->valueOnEntry.at(1).toDouble(), 5.0); + + machine.postEvent(new QEvent(QEvent::User)); + QTRY_COMPARE(s4->valueOnEntry.at(0).isValid(), true); + QCOMPARE(s4->valueOnEntry.at(0).toDouble(), 1.0); + QCOMPARE(s4->valueOnEntry.at(1).toDouble(), 3.0); +} + +void tst_QAnimationState::someAnimationsNotSpecified() +{ + QStateMachine machine; + + QObject *object = new QObject(); + object->setProperty("foo", 1.0); + object->setProperty("bar", 3.0); + + QState *s1 = new QState(machine.rootState()); + + ValueCheckerState *s2 = new ValueCheckerState(machine.rootState()); + s2->addPropertyToCheck(object, "foo"); + s2->addPropertyToCheck(object, "bar"); + s2->setPropertyOnEntry(object, "foo", 2.0); + + QAnimationState *as = s1->addAnimatedTransition(new EventTransition(QEvent::User, s2)); + as->addAnimation(new QPropertyAnimation(object, "foo", as)); + as->addAnimation(new QPropertyAnimation(object, "bar", as)); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + QTRY_COMPARE(s2->valueOnEntry.at(0).isValid(), true); + QCOMPARE(s2->valueOnEntry.at(0).toDouble(), 2.0); + QCOMPARE(s2->valueOnEntry.at(1).toDouble(), 3.0); +} + +void tst_QAnimationState::someActionsNotAnimated() +{ + QStateMachine machine; + + QObject *object = new QObject(); + object->setProperty("foo", 1.0); + object->setProperty("bar", 3.0); + + QState *s1 = new QState(machine.rootState()); + + ValueCheckerState *s2 = new ValueCheckerState(machine.rootState()); + s2->addPropertyToCheck(object, "foo"); + s2->addPropertyToCheck(object, "bar"); + s2->setPropertyOnEntry(object, "foo", 2.0); + s2->setPropertyOnEntry(object, "bar", 5.0); + + s1->addAnimatedTransition(new EventTransition(QEvent::User, s2), + new QPropertyAnimation(object, "foo", s1)); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + QTRY_COMPARE(s2->valueOnEntry.at(0).isValid(), true); + QCOMPARE(s2->valueOnEntry.at(0).toDouble(), 2.0); + QCOMPARE(s2->valueOnEntry.at(1).toDouble(), 3.0); + QCOMPARE(object->property("foo").toDouble(), 2.0); + QCOMPARE(object->property("bar").toDouble(), 5.0); +} + +void tst_QAnimationState::specificTargetValueOfAnimation() +{ + QStateMachine machine; + + QObject *object = new QObject(); + object->setProperty("foo", 1.0); + + QState *s1 = new QState(machine.rootState()); + + ValueCheckerState *s2 = new ValueCheckerState(machine.rootState()); + s2->addPropertyToCheck(object, "foo"); + s2->setPropertyOnEntry(object, "foo", 2.0); + + QPropertyAnimation *anim = new QPropertyAnimation(object, "foo"); + anim->setEndValue(10.0); + s1->addAnimatedTransition(new EventTransition(QEvent::User, s2), anim); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + QTRY_COMPARE(s2->valueOnEntry.at(0).isValid(), true); + QCOMPARE(s2->valueOnEntry.at(0).toDouble(), 10.0); + QCOMPARE(object->property("foo").toDouble(), 2.0); +} + +void tst_QAnimationState::persistentTargetValueOfAnimation() +{ + QStateMachine machine; + + QObject *object = new QObject(); + object->setProperty("foo", 1.0); + + QState *s1 = new QState(machine.rootState()); + + ValueCheckerState *s2 = new ValueCheckerState(machine.rootState()); + s2->addPropertyToCheck(object, "foo"); + s2->setPropertyOnEntry(object, "foo", 2.0); + + QPropertyAnimation *anim = new QPropertyAnimation(object, "foo"); + s1->addAnimatedTransition(new EventTransition(QEvent::User, s2), anim); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + QTRY_COMPARE(s2->valueOnEntry.at(0).isValid(), true); + QCOMPARE(s2->valueOnEntry.at(0).toDouble(), 2.0); + QCOMPARE(anim->endValue().isValid(), false); +} + + +QTEST_MAIN(tst_QAnimationState) +#include "tst_qanimationstate.moc" diff --git a/tests/auto/qeasingcurve/qeasingcurve.pro b/tests/auto/qeasingcurve/qeasingcurve.pro new file mode 100644 index 0000000..2b66081 --- /dev/null +++ b/tests/auto/qeasingcurve/qeasingcurve.pro @@ -0,0 +1,3 @@ +load(qttest_p4) +QT = core +SOURCES += tst_qeasingcurve.cpp diff --git a/tests/auto/qeasingcurve/tst_qeasingcurve.cpp b/tests/auto/qeasingcurve/tst_qeasingcurve.cpp new file mode 100644 index 0000000..9e3ffcc --- /dev/null +++ b/tests/auto/qeasingcurve/tst_qeasingcurve.cpp @@ -0,0 +1,488 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#if QT_VERSION < 0x040200 +QTEST_NOOP_MAIN +#else + +#include <qeasingcurve.h> + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QEasingCurve : public QObject { + Q_OBJECT + +public: + tst_QEasingCurve(); + virtual ~tst_QEasingCurve(); + +public Q_SLOTS: + void init(); + void cleanup(); + +private slots: + void type(); + void propertyDefaults(); + void valueForProgress_data(); + void valueForProgress(); + void setCustomType(); + void operators(); + +protected: +}; + +tst_QEasingCurve::tst_QEasingCurve() +{ +} + +tst_QEasingCurve::~tst_QEasingCurve() +{ +} + +void tst_QEasingCurve::init() +{ +} + +void tst_QEasingCurve::cleanup() +{ +} +#include <qdebug.h> + +void tst_QEasingCurve::type() +{ + { + QEasingCurve curve(QEasingCurve::Linear); + QCOMPARE(curve.period(), 0.3); + QCOMPARE(curve.amplitude(), 1.0); + + curve.setPeriod(5); + curve.setAmplitude(3); + QCOMPARE(curve.period(), 5.0); + QCOMPARE(curve.amplitude(), 3.0); + + curve.setType(QEasingCurve::InElastic); + QCOMPARE(curve.period(), 5.0); + QCOMPARE(curve.amplitude(), 3.0); + } + + { + QEasingCurve curve(QEasingCurve::InElastic); + QCOMPARE(curve.period(), 0.3); + QCOMPARE(curve.amplitude(), 1.0); + curve.setAmplitude(2); + QCOMPARE(curve.type(), QEasingCurve::InElastic); + curve.setType(QEasingCurve::Linear); + } + + { + // check bounaries + QEasingCurve curve(QEasingCurve::InCubic); + QTest::ignoreMessage(QtWarningMsg, "QEasingCurve: Invalid curve type 9999"); + curve.setType((QEasingCurve::Type)9999); + QCOMPARE(curve.type(), QEasingCurve::InCubic); + QTest::ignoreMessage(QtWarningMsg, "QEasingCurve: Invalid curve type -9999"); + curve.setType((QEasingCurve::Type)-9999); + QCOMPARE(curve.type(), QEasingCurve::InCubic); + QTest::ignoreMessage(QtWarningMsg, QString::fromAscii("QEasingCurve: Invalid curve type %1") + .arg(QEasingCurve::NCurveTypes).toLatin1().constData()); + curve.setType(QEasingCurve::NCurveTypes); + QCOMPARE(curve.type(), QEasingCurve::InCubic); + QTest::ignoreMessage(QtWarningMsg, QString::fromAscii("QEasingCurve: Invalid curve type %1") + .arg(QEasingCurve::Custom).toLatin1().constData()); + curve.setType(QEasingCurve::Custom); + QCOMPARE(curve.type(), QEasingCurve::InCubic); + QTest::ignoreMessage(QtWarningMsg, QString::fromAscii("QEasingCurve: Invalid curve type %1") + .arg(-1).toLatin1().constData()); + curve.setType((QEasingCurve::Type)-1); + QCOMPARE(curve.type(), QEasingCurve::InCubic); + curve.setType(QEasingCurve::Linear); + QCOMPARE(curve.type(), QEasingCurve::Linear); + curve.setType(QEasingCurve::CosineCurve); + QCOMPARE(curve.type(), QEasingCurve::CosineCurve); + } +} + +void tst_QEasingCurve::propertyDefaults() +{ + { + // checks if the defaults are correct, but also demonstrates a weakness with the API. + QEasingCurve curve(QEasingCurve::InElastic); + QCOMPARE(curve.period(), 0.3); + QCOMPARE(curve.amplitude(), 1.0); + QCOMPARE(curve.overshoot(), qreal(1.70158f)); + curve.setType(QEasingCurve::InBounce); + QCOMPARE(curve.period(), 0.3); + QCOMPARE(curve.amplitude(), 1.0); + QCOMPARE(curve.overshoot(), qreal(1.70158f)); + curve.setType(QEasingCurve::Linear); + QCOMPARE(curve.period(), 0.3); + QCOMPARE(curve.amplitude(), 1.0); + QCOMPARE(curve.overshoot(), qreal(1.70158f)); + curve.setType(QEasingCurve::InElastic); + QCOMPARE(curve.period(), 0.3); + QCOMPARE(curve.amplitude(), 1.0); + QCOMPARE(curve.overshoot(), qreal(1.70158f)); + curve.setPeriod(0.4); + curve.setAmplitude(0.6); + curve.setOvershoot(1.0); + curve.setType(QEasingCurve::Linear); + QCOMPARE(curve.period(), 0.4); + QCOMPARE(curve.amplitude(), 0.6); + QCOMPARE(curve.overshoot(), 1.0); + curve.setType(QEasingCurve::InElastic); + QCOMPARE(curve.period(), 0.4); + QCOMPARE(curve.amplitude(), 0.6); + QCOMPARE(curve.overshoot(), 1.0); + } +} + +typedef QList<int> IntList; +Q_DECLARE_METATYPE(IntList) + +void tst_QEasingCurve::valueForProgress_data() +{ + QTest::addColumn<int>("type"); + QTest::addColumn<IntList>("at"); + QTest::addColumn<IntList>("expected"); + // automatically generated. + // note that values are scaled from range [0,1] to range [0, 100] in order to store them as + // integer values and avoid fp inaccuracies + + QTest::newRow("Linear") << int(QEasingCurve::Linear) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100); + + QTest::newRow("InQuad") << int(QEasingCurve::InQuad) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 4 << 16 << 36 << 64 << 100); + + QTest::newRow("OutQuad") << int(QEasingCurve::OutQuad) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 36 << 64 << 84 << 96 << 100); + + QTest::newRow("InOutQuad") << int(QEasingCurve::InOutQuad) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 8 << 32 << 68 << 92 << 100); + + QTest::newRow("OutInQuad") << int(QEasingCurve::OutInQuad) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 32 << 48 << 52 << 68 << 100); + + QTest::newRow("InCubic") << int(QEasingCurve::InCubic) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 0 << 6 << 21 << 51 << 100); + + QTest::newRow("OutCubic") << int(QEasingCurve::OutCubic) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 48 << 78 << 93 << 99 << 100); + + QTest::newRow("InOutCubic") << int(QEasingCurve::InOutCubic) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 3 << 25 << 74 << 96 << 100); + + QTest::newRow("OutInCubic") << int(QEasingCurve::OutInCubic) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 39 << 49 << 50 << 60 << 100); + + QTest::newRow("InQuart") << int(QEasingCurve::InQuart) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 0 << 2 << 12 << 40 << 100); + + QTest::newRow("OutQuart") << int(QEasingCurve::OutQuart) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 59 << 87 << 97 << 99 << 100); + + QTest::newRow("InOutQuart") << int(QEasingCurve::InOutQuart) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 1 << 20 << 79 << 98 << 100); + + QTest::newRow("OutInQuart") << int(QEasingCurve::OutInQuart) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 43 << 49 << 50 << 56 << 100); + + QTest::newRow("InQuint") << int(QEasingCurve::InQuint) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 0 << 1 << 7 << 32 << 100); + + QTest::newRow("OutQuint") << int(QEasingCurve::OutQuint) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 67 << 92 << 98 << 99 << 100); + + QTest::newRow("InOutQuint") << int(QEasingCurve::InOutQuint) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 0 << 16 << 83 << 99 << 100); + + QTest::newRow("OutInQuint") << int(QEasingCurve::OutInQuint) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 46 << 49 << 50 << 53 << 100); + + QTest::newRow("InSine") << int(QEasingCurve::InSine) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 4 << 19 << 41 << 69 << 100); + + QTest::newRow("OutSine") << int(QEasingCurve::OutSine) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 30 << 58 << 80 << 95 << 100); + + QTest::newRow("InOutSine") << int(QEasingCurve::InOutSine) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 9 << 34 << 65 << 90 << 100); + + QTest::newRow("OutInSine") << int(QEasingCurve::OutInSine) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 29 << 47 << 52 << 70 << 100); + + QTest::newRow("InExpo") << int(QEasingCurve::InExpo) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 0 << 1 << 6 << 24 << 100); + + QTest::newRow("OutExpo") << int(QEasingCurve::OutExpo) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 75 << 93 << 98 << 99 << 100); + + QTest::newRow("InOutExpo") << int(QEasingCurve::InOutExpo) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 0 << 12 << 87 << 99 << 100); + + QTest::newRow("OutInExpo") << int(QEasingCurve::OutInExpo) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 46 << 49 << 50 << 53 << 100); + + QTest::newRow("InCirc") << int(QEasingCurve::InCirc) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 2 << 8 << 19 << 40 << 100); + + QTest::newRow("OutCirc") << int(QEasingCurve::OutCirc) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 59 << 80 << 91 << 97 << 100); + + QTest::newRow("InOutCirc") << int(QEasingCurve::InOutCirc) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 4 << 20 << 80 << 95 << 100); + + QTest::newRow("OutInCirc") << int(QEasingCurve::OutInCirc) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 40 << 48 << 51 << 60 << 100); + + QTest::newRow("InElastic") << int(QEasingCurve::InElastic) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 0 << 1 << -3 << -12 << 100); + + QTest::newRow("OutElastic") << int(QEasingCurve::OutElastic) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 112 << 103 << 98 << 100 << 100); + + QTest::newRow("InOutElastic") << int(QEasingCurve::InOutElastic) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 0 << -6 << 106 << 99 << 100); + + QTest::newRow("OutInElastic") << int(QEasingCurve::OutInElastic) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 56 << 49 << 49 << 53 << 100); + + QTest::newRow("InBack") << int(QEasingCurve::InBack) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << -4 << -9 << -2 << 29 << 100); + + QTest::newRow("OutBack") << int(QEasingCurve::OutBack) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 70 << 102 << 109 << 104 << 100); + + QTest::newRow("InOutBack") << int(QEasingCurve::InOutBack) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << -9 << 8 << 91 << 109 << 100); + + QTest::newRow("OutInBack") << int(QEasingCurve::OutInBack) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 51 << 52 << 47 << 48 << 100); + + QTest::newRow("InBounce") << int(QEasingCurve::InBounce) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 6 << 22 << 8 << 69 << 100); + + QTest::newRow("OutBounce") << int(QEasingCurve::OutBounce) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 30 << 91 << 77 << 93 << 100); + + QTest::newRow("InOutBounce") << int(QEasingCurve::InOutBounce) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 11 << 34 << 65 << 88 << 100); + + QTest::newRow("OutInBounce") << int(QEasingCurve::OutInBounce) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 41 << 43 << 56 << 58 << 100); + + QTest::newRow("InCurve") << int(QEasingCurve::InCurve) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 10 << 37 << 60 << 80 << 100); + + QTest::newRow("OutCurve") << int(QEasingCurve::OutCurve) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 20 << 39 << 62 << 89 << 100); + + QTest::newRow("SineCurve") << int(QEasingCurve::SineCurve) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 0 << 34 << 90 << 90 << 34 << 0); + + QTest::newRow("CosineCurve") << int(QEasingCurve::CosineCurve) + << (IntList() << 0 << 20 << 40 << 60 << 80 << 100) + << (IntList() << 50 << 97 << 79 << 20 << 2 << 49); + + +} + + +void tst_QEasingCurve::valueForProgress() +{ +#if 0 + // used to generate data tables... + QFile out; + out.open(stdout, QIODevice::WriteOnly); + for (int c = QEasingCurve::Linear; c < QEasingCurve::NCurveTypes - 1; ++c) { + QEasingCurve curve((QEasingCurve::Type)c); + QMetaObject mo = QEasingCurve::staticMetaObject; + QString strCurve = QLatin1String(mo.enumerator(mo.indexOfEnumerator("Type")).key(c)); + QString strInputs; + QString strOutputs; + + for (int t = 0; t <= 100; t+= 20) { + qreal ease = curve.valueForProgress(t/qreal(100)); + strInputs += QString::fromAscii(" << %1").arg(t); + strOutputs += QString::fromAscii(" << %1").arg(int(100*ease)); + + } + QString str = QString::fromAscii(" QTest::newRow(\"%1\") << int(QEasingCurve::%2)\n" + "\t\t << (IntList() %3)\n" + "\t\t << (IntList() %4);\n\n") + .arg(strCurve) + .arg(strCurve) + .arg(strInputs) + .arg(strOutputs); + out.write(str.toLatin1().constData()); + } + out.close(); + exit(1); +#else + QFETCH(int, type); + QFETCH(IntList, at); + QFETCH(IntList, expected); + + QEasingCurve curve((QEasingCurve::Type)type); + for (int i = 0; i < at.count(); ++i) { + qreal ease = curve.valueForProgress(at.at(i)/qreal(100)); + int ex = expected.at(i); + QCOMPARE(int(100*ease), ex); + } +#endif +} + +static qreal discreteEase(qreal progress) +{ + return qFloor(progress * 10) / qreal(10.0); +} + +void tst_QEasingCurve::setCustomType() +{ + QEasingCurve curve; + curve.setCustomType(&discreteEase); + QCOMPARE(curve.type(), QEasingCurve::Custom); + QCOMPARE(curve.valueForProgress(0.0), 0.0); + QCOMPARE(curve.valueForProgress(0.05), 0.0); + QCOMPARE(curve.valueForProgress(0.10), 0.1); + QCOMPARE(curve.valueForProgress(0.15), 0.1); + QCOMPARE(curve.valueForProgress(0.20), 0.2); + QCOMPARE(curve.valueForProgress(0.25), 0.2); + QCOMPARE(curve.valueForProgress(0.30), 0.3); + QCOMPARE(curve.valueForProgress(0.35), 0.3); + QCOMPARE(curve.valueForProgress(0.999999), 0.9); + + curve.setType(QEasingCurve::Linear); + QCOMPARE(curve.type(), QEasingCurve::Linear); + QCOMPARE(curve.valueForProgress(0.0), 0.0); + QCOMPARE(curve.valueForProgress(0.1), 0.1); + QCOMPARE(curve.valueForProgress(0.5), 0.5); + QCOMPARE(curve.valueForProgress(0.99), 0.99); +} + +void tst_QEasingCurve::operators() +{ + // operator= + QEasingCurve curve; + QEasingCurve curve2; + curve.setCustomType(&discreteEase); + curve2 = curve; + QCOMPARE(curve2.type(), QEasingCurve::Custom); + QCOMPARE(curve2.valueForProgress(0.0), 0.0); + QCOMPARE(curve2.valueForProgress(0.05), 0.0); + QCOMPARE(curve2.valueForProgress(0.15), 0.1); + QCOMPARE(curve2.valueForProgress(0.25), 0.2); + QCOMPARE(curve2.valueForProgress(0.35), 0.3); + QCOMPARE(curve2.valueForProgress(0.999999), 0.9); + + // operator== + curve.setType(QEasingCurve::InBack); + curve2 = curve; + curve2.setOvershoot(qreal(1.70158f)); + QCOMPARE(curve.overshoot(), curve2.overshoot()); + QVERIFY(curve2 == curve); + + curve.setOvershoot(3.0); + QVERIFY(curve2 != curve); + curve2.setOvershoot(3.0); + QVERIFY(curve2 == curve); + + curve2.setType(QEasingCurve::Linear); + QCOMPARE(curve.overshoot(), curve2.overshoot()); + QVERIFY(curve2 != curve); + curve2.setType(QEasingCurve::InBack); + QCOMPARE(curve.overshoot(), curve2.overshoot()); + QVERIFY(curve2 == curve); +} + + +QTEST_MAIN(tst_QEasingCurve) +#include "tst_qeasingcurve.moc" + +#endif //QT_VERSION diff --git a/tests/auto/qmake/testdata/bundle-spaces/some-file b/tests/auto/qmake/testdata/bundle-spaces/some-file index e69de29..9975dba 100644 --- a/tests/auto/qmake/testdata/bundle-spaces/some-file +++ b/tests/auto/qmake/testdata/bundle-spaces/some-file @@ -0,0 +1,6 @@ +all: + C:\git\qt-kinetic-animations\bin\qmake qdir.pro -o Makefile -spec win32-msvc2008 + nmake -f Makefile +first: all +qmake: + C:\git\qt-kinetic-animations\bin\qmake qdir.pro -o Makefile -spec win32-msvc2008 diff --git a/tests/auto/qparallelanimationgroup/qparallelanimationgroup.pro b/tests/auto/qparallelanimationgroup/qparallelanimationgroup.pro new file mode 100644 index 0000000..f2cacd3 --- /dev/null +++ b/tests/auto/qparallelanimationgroup/qparallelanimationgroup.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT = core gui +SOURCES += tst_qparallelanimationgroup.cpp + + diff --git a/tests/auto/qparallelanimationgroup/tst_qparallelanimationgroup.cpp b/tests/auto/qparallelanimationgroup/tst_qparallelanimationgroup.cpp new file mode 100644 index 0000000..c7f33b7 --- /dev/null +++ b/tests/auto/qparallelanimationgroup/tst_qparallelanimationgroup.cpp @@ -0,0 +1,834 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QtCore/qparallelanimationgroup.h> + +//TESTED_CLASS=QParallelAnimationGroup +//TESTED_FILES= + +Q_DECLARE_METATYPE(QAbstractAnimation::State) + +class tst_QParallelAnimationGroup : public QObject +{ + Q_OBJECT +public: + tst_QParallelAnimationGroup(); + virtual ~tst_QParallelAnimationGroup(); + +public Q_SLOTS: + void init(); + void cleanup(); + +private slots: + void construction(); + void setCurrentTime(); + void clearGroup(); + void propagateGroupUpdateToChildren(); + void updateChildrenWithRunningGroup(); + void deleteChildrenWithRunningGroup(); + void startChildrenWithStoppedGroup(); + void stopGroupWithRunningChild(); + void startGroupWithRunningChild(); + void zeroDurationAnimation(); + void stopUncontrolledAnimations(); + void iterationCount_data(); + void iterationCount(); + void autoAdd(); +}; + +tst_QParallelAnimationGroup::tst_QParallelAnimationGroup() +{ +} + +tst_QParallelAnimationGroup::~tst_QParallelAnimationGroup() +{ +} + +void tst_QParallelAnimationGroup::init() +{ + qRegisterMetaType<QAbstractAnimation::State>("QAbstractAnimation::State"); +} + +void tst_QParallelAnimationGroup::cleanup() +{ +} + +void tst_QParallelAnimationGroup::construction() +{ + QParallelAnimationGroup animationgroup; +} + +class AnimationObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(int value READ value WRITE setValue) +public: + AnimationObject(int startValue = 0) + : v(startValue) + { } + + int value() const { return v; } + void setValue(int value) { v = value; } + + int v; +}; + +class TestAnimation : public QVariantAnimation +{ + Q_OBJECT +public: + virtual void updateCurrentValue(const QVariant &value) { Q_UNUSED(value)}; + virtual void updateState(QAbstractAnimation::State oldState, + QAbstractAnimation::State newState) + { + Q_UNUSED(oldState) + Q_UNUSED(newState) + }; +}; + +class TestAnimation2 : public QVariantAnimation +{ + Q_OBJECT +public: + TestAnimation2(QAbstractAnimation *animation) : QVariantAnimation(animation) {} + TestAnimation2(int duration, QAbstractAnimation *animation) : QVariantAnimation(animation), m_duration(duration) {} + + virtual void updateCurrentValue(const QVariant &value) { Q_UNUSED(value)}; + virtual void updateState(QAbstractAnimation::State oldState, + QAbstractAnimation::State newState) + { + Q_UNUSED(oldState) + Q_UNUSED(newState) + }; + + virtual int duration() const { + return m_duration; + } +private: + int m_duration; +}; + +class UncontrolledAnimation : public QPropertyAnimation +{ + Q_OBJECT +public: + UncontrolledAnimation(QObject *target, const QByteArray &propertyName, QObject *parent = 0) + : QPropertyAnimation(target, propertyName, parent), id(0) + { + setDuration(250); + } + + int duration() const { return -1; /* not time driven */ } + +protected: + void timerEvent(QTimerEvent *event) + { + if (event->timerId() == id) + stop(); + } + + void updateRunning(bool running) + { + if (running) { + id = startTimer(500); + } else { + killTimer(id); + id = 0; + } + } + +private: + int id; +}; + +void tst_QParallelAnimationGroup::setCurrentTime() +{ + AnimationObject p_o1; + AnimationObject p_o2; + AnimationObject p_o3; + AnimationObject t_o1; + AnimationObject t_o2; + + // parallel operating on different object/properties + QAnimationGroup *parallel = new QParallelAnimationGroup(); + QVariantAnimation *a1_p_o1 = new QPropertyAnimation(&p_o1, "value"); + QVariantAnimation *a1_p_o2 = new QPropertyAnimation(&p_o2, "value"); + QVariantAnimation *a1_p_o3 = new QPropertyAnimation(&p_o3, "value"); + a1_p_o2->setIterationCount(3); + parallel->addAnimation(a1_p_o1); + parallel->addAnimation(a1_p_o2); + parallel->addAnimation(a1_p_o3); + + UncontrolledAnimation *notTimeDriven = new UncontrolledAnimation(&t_o1, "value"); + QCOMPARE(notTimeDriven->totalDuration(), -1); + + QVariantAnimation *loopsForever = new QPropertyAnimation(&t_o2, "value"); + loopsForever->setIterationCount(-1); + QCOMPARE(loopsForever->totalDuration(), -1); + + QParallelAnimationGroup group; + group.addAnimation(parallel); + group.addAnimation(notTimeDriven); + group.addAnimation(loopsForever); + + // Current time = 1 + group.setCurrentTime(1); + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(parallel->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_p_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_p_o2->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_p_o3->state(), QAnimationGroup::Stopped); + QCOMPARE(notTimeDriven->state(), QAnimationGroup::Stopped); + QCOMPARE(loopsForever->state(), QAnimationGroup::Stopped); + + QCOMPARE(group.currentTime(), 1); + QCOMPARE(a1_p_o1->currentTime(), 1); + QCOMPARE(a1_p_o2->currentTime(), 1); + QCOMPARE(a1_p_o3->currentTime(), 1); + QCOMPARE(notTimeDriven->currentTime(), 1); + QCOMPARE(loopsForever->currentTime(), 1); + + // Current time = 250 + group.setCurrentTime(250); + QCOMPARE(group.currentTime(), 250); + QCOMPARE(a1_p_o1->currentTime(), 250); + QCOMPARE(a1_p_o2->currentTime(), 0); + QCOMPARE(a1_p_o2->currentIteration(), 1); + QCOMPARE(a1_p_o3->currentTime(), 250); + QCOMPARE(notTimeDriven->currentTime(), 250); + QCOMPARE(loopsForever->currentTime(), 0); + QCOMPARE(loopsForever->currentIteration(), 1); + + // Current time = 251 + group.setCurrentTime(251); + QCOMPARE(group.currentTime(), 251); + QCOMPARE(a1_p_o1->currentTime(), 250); + QCOMPARE(a1_p_o2->currentTime(), 1); + QCOMPARE(a1_p_o2->currentIteration(), 1); + QCOMPARE(a1_p_o3->currentTime(), 250); + QCOMPARE(notTimeDriven->currentTime(), 251); + QCOMPARE(loopsForever->currentTime(), 1); +} + +void tst_QParallelAnimationGroup::clearGroup() +{ + QParallelAnimationGroup group; + + for (int i = 0; i < 10; ++i) { + new QParallelAnimationGroup(&group); + } + + QCOMPARE(group.animationCount(), 10); + + int count = group.animationCount(); + QPointer<QAbstractAnimation> *children = new QPointer<QAbstractAnimation>[count]; + for (int i = 0; i < count; ++i) { + QVERIFY(group.animationAt(i) != 0); + children[i] = group.animationAt(i); + } + + group.clearAnimations(); + QCOMPARE(group.animationCount(), 0); + QCOMPARE(group.currentTime(), 0); + for (int i = 0; i < count; ++i) + QCOMPARE(children[i], QPointer<QAbstractAnimation>()); + + delete[] children; +} + +void tst_QParallelAnimationGroup::propagateGroupUpdateToChildren() +{ + // this test verifies if group state changes are updating its children correctly + QParallelAnimationGroup group; + + QObject o; + o.setProperty("ole", 42); + QCOMPARE(o.property("ole").toInt(), 42); + + QPropertyAnimation anim1(&o, "ole"); + anim1.setEndValue(43); + anim1.setDuration(100); + QVERIFY(!anim1.currentValue().isValid()); + QCOMPARE(anim1.currentValue().toInt(), 0); + QCOMPARE(o.property("ole").toInt(), 42); + + TestAnimation anim2; + anim2.setStartValue(0); + anim2.setEndValue(100); + anim2.setDuration(200); + + QVERIFY(anim2.currentValue().isValid()); + QCOMPARE(anim2.currentValue().toInt(), 0); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Stopped); + QCOMPARE(anim2.state(), QAnimationGroup::Stopped); + + group.addAnimation(&anim1); + group.addAnimation(&anim2); + + group.start(); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(anim1.state(), QAnimationGroup::Running); + QCOMPARE(anim2.state(), QAnimationGroup::Running); + + group.pause(); + + QCOMPARE(group.state(), QAnimationGroup::Paused); + QCOMPARE(anim1.state(), QAnimationGroup::Paused); + QCOMPARE(anim2.state(), QAnimationGroup::Paused); + + group.stop(); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Stopped); + QCOMPARE(anim2.state(), QAnimationGroup::Stopped); +} + +void tst_QParallelAnimationGroup::updateChildrenWithRunningGroup() +{ + // assert that its possible to modify a child's state directly while their group is running + QParallelAnimationGroup group; + + TestAnimation anim; + anim.setStartValue(0); + anim.setEndValue(100); + anim.setDuration(200); + + QSignalSpy groupStateChangedSpy(&group, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy childStateChangedSpy(&anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + + QCOMPARE(groupStateChangedSpy.count(), 0); + QCOMPARE(childStateChangedSpy.count(), 0); + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim.state(), QAnimationGroup::Stopped); + + group.addAnimation(&anim); + + group.start(); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(anim.state(), QAnimationGroup::Running); + + QCOMPARE(groupStateChangedSpy.count(), 1); + QCOMPARE(childStateChangedSpy.count(), 1); + + QCOMPARE(qVariantValue<QAbstractAnimation::State>(groupStateChangedSpy.at(0).at(1)), + QAnimationGroup::Running); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(childStateChangedSpy.at(0).at(1)), + QAnimationGroup::Running); + + // starting directly a running child will not have any effect + anim.start(); + + QCOMPARE(groupStateChangedSpy.count(), 1); + QCOMPARE(childStateChangedSpy.count(), 1); + + anim.pause(); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(anim.state(), QAnimationGroup::Paused); + + // in the animation stops directly, the group will still be running + anim.stop(); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(anim.state(), QAnimationGroup::Stopped); +} + +void tst_QParallelAnimationGroup::deleteChildrenWithRunningGroup() +{ + // test if children can be activated when their group is stopped + QParallelAnimationGroup group; + + QVariantAnimation *anim1 = new TestAnimation; + anim1->setStartValue(0); + anim1->setEndValue(100); + anim1->setDuration(200); + group.addAnimation(anim1); + + QCOMPARE(group.duration(), anim1->duration()); + + group.start(); + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(anim1->state(), QAnimationGroup::Running); + + QTest::qWait(50); + QVERIFY(group.currentTime() > 0); + + delete anim1; + QVERIFY(group.animationCount() == 0); + QCOMPARE(group.duration(), 0); + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(group.currentTime(), 0); //that's the invariant +} + +void tst_QParallelAnimationGroup::startChildrenWithStoppedGroup() +{ + // test if children can be activated when their group is stopped + QParallelAnimationGroup group; + + TestAnimation anim1; + anim1.setStartValue(0); + anim1.setEndValue(100); + anim1.setDuration(200); + + TestAnimation anim2; + anim2.setStartValue(0); + anim2.setEndValue(100); + anim2.setDuration(200); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Stopped); + QCOMPARE(anim2.state(), QAnimationGroup::Stopped); + + group.addAnimation(&anim1); + group.addAnimation(&anim2); + + group.stop(); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Stopped); + QCOMPARE(anim2.state(), QAnimationGroup::Stopped); + + anim1.start(); + anim2.start(); + anim2.pause(); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Running); + QCOMPARE(anim2.state(), QAnimationGroup::Paused); +} + +void tst_QParallelAnimationGroup::stopGroupWithRunningChild() +{ + // children that started independently will not be affected by a group stop + QParallelAnimationGroup group; + + TestAnimation anim1; + anim1.setStartValue(0); + anim1.setEndValue(100); + anim1.setDuration(200); + + TestAnimation anim2; + anim2.setStartValue(0); + anim2.setEndValue(100); + anim2.setDuration(200); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Stopped); + QCOMPARE(anim2.state(), QAnimationGroup::Stopped); + + group.addAnimation(&anim1); + group.addAnimation(&anim2); + + anim1.start(); + anim2.start(); + anim2.pause(); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Running); + QCOMPARE(anim2.state(), QAnimationGroup::Paused); + + group.stop(); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Running); + QCOMPARE(anim2.state(), QAnimationGroup::Paused); + + anim1.stop(); + anim2.stop(); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Stopped); + QCOMPARE(anim2.state(), QAnimationGroup::Stopped); +} + +void tst_QParallelAnimationGroup::startGroupWithRunningChild() +{ + // as the group has precedence over its children, starting a group will restart all the children + QParallelAnimationGroup group; + + TestAnimation anim1; + anim1.setStartValue(0); + anim1.setEndValue(100); + anim1.setDuration(200); + + TestAnimation anim2; + anim2.setStartValue(0); + anim2.setEndValue(100); + anim2.setDuration(200); + + QSignalSpy stateChangedSpy1(&anim1, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy stateChangedSpy2(&anim2, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + + QCOMPARE(stateChangedSpy1.count(), 0); + QCOMPARE(stateChangedSpy2.count(), 0); + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Stopped); + QCOMPARE(anim2.state(), QAnimationGroup::Stopped); + + group.addAnimation(&anim1); + group.addAnimation(&anim2); + + anim1.start(); + anim2.start(); + anim2.pause(); + + QCOMPARE(qVariantValue<QAbstractAnimation::State>(stateChangedSpy1.at(0).at(1)), + QAnimationGroup::Running); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(stateChangedSpy2.at(0).at(1)), + QAnimationGroup::Running); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(stateChangedSpy2.at(1).at(1)), + QAnimationGroup::Paused); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Running); + QCOMPARE(anim2.state(), QAnimationGroup::Paused); + + group.start(); + + QCOMPARE(stateChangedSpy1.count(), 3); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(stateChangedSpy1.at(1).at(1)), + QAnimationGroup::Stopped); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(stateChangedSpy1.at(2).at(1)), + QAnimationGroup::Running); + + QCOMPARE(stateChangedSpy2.count(), 4); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(stateChangedSpy2.at(2).at(1)), + QAnimationGroup::Stopped); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(stateChangedSpy2.at(3).at(1)), + QAnimationGroup::Running); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(anim1.state(), QAnimationGroup::Running); + QCOMPARE(anim2.state(), QAnimationGroup::Running); +} + +void tst_QParallelAnimationGroup::zeroDurationAnimation() +{ + QParallelAnimationGroup group; + + TestAnimation anim1; + anim1.setStartValue(0); + anim1.setEndValue(100); + anim1.setDuration(0); + + TestAnimation anim2; + anim2.setStartValue(0); + anim2.setEndValue(100); + anim2.setDuration(100); + + QSignalSpy stateChangedSpy1(&anim1, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy finishedSpy1(&anim1, SIGNAL(finished())); + + QSignalSpy stateChangedSpy2(&anim2, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy finishedSpy2(&anim2, SIGNAL(finished())); + + group.addAnimation(&anim1); + group.addAnimation(&anim2); + QCOMPARE(stateChangedSpy1.count(), 0); + group.start(); + QCOMPARE(stateChangedSpy1.count(), 2); + QCOMPARE(finishedSpy1.count(), 1); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(stateChangedSpy1.at(0).at(1)), + QAnimationGroup::Running); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(stateChangedSpy1.at(1).at(1)), + QAnimationGroup::Stopped); + + QCOMPARE(stateChangedSpy2.count(), 1); + QCOMPARE(finishedSpy2.count(), 0); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(stateChangedSpy1.at(0).at(1)), + QAnimationGroup::Running); + + + QCOMPARE(anim1.state(), QAnimationGroup::Stopped); + QCOMPARE(anim2.state(), QAnimationGroup::Running); + QCOMPARE(group.state(), QAnimationGroup::Running); + + + group.stop(); + group.setIterationCount(4); + stateChangedSpy1.clear(); + stateChangedSpy2.clear(); + + group.start(); + QCOMPARE(stateChangedSpy1.count(), 2); + QCOMPARE(stateChangedSpy2.count(), 1); + group.setCurrentTime(50); + QCOMPARE(stateChangedSpy1.count(), 2); + QCOMPARE(stateChangedSpy2.count(), 1); + group.setCurrentTime(150); + QCOMPARE(stateChangedSpy1.count(), 4); + QCOMPARE(stateChangedSpy2.count(), 3); + group.setCurrentTime(50); + QCOMPARE(stateChangedSpy1.count(), 6); + QCOMPARE(stateChangedSpy2.count(), 5); + +} + +void tst_QParallelAnimationGroup::stopUncontrolledAnimations() +{ + QParallelAnimationGroup group; + + TestAnimation anim1; + anim1.setStartValue(0); + anim1.setEndValue(100); + anim1.setDuration(0); + + AnimationObject o1; + UncontrolledAnimation notTimeDriven(&o1, "value"); + QCOMPARE(notTimeDriven.totalDuration(), -1); + + TestAnimation loopsForever; + loopsForever.setStartValue(0); + loopsForever.setEndValue(100); + loopsForever.setDuration(100); + loopsForever.setIterationCount(-1); + + QSignalSpy stateChangedSpy(&anim1, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + + group.addAnimation(&anim1); + group.addAnimation(¬TimeDriven); + group.addAnimation(&loopsForever); + + group.start(); + + QCOMPARE(stateChangedSpy.count(), 2); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(stateChangedSpy.at(0).at(1)), + QAnimationGroup::Running); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(stateChangedSpy.at(1).at(1)), + QAnimationGroup::Stopped); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(notTimeDriven.state(), QAnimationGroup::Running); + QCOMPARE(loopsForever.state(), QAnimationGroup::Running); + QCOMPARE(anim1.state(), QAnimationGroup::Stopped); + + notTimeDriven.stop(); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(notTimeDriven.state(), QAnimationGroup::Stopped); + QCOMPARE(loopsForever.state(), QAnimationGroup::Running); + + loopsForever.stop(); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(notTimeDriven.state(), QAnimationGroup::Stopped); + QCOMPARE(loopsForever.state(), QAnimationGroup::Stopped); +} + +struct AnimState { + AnimState(int time = -1) : time(time), state(-1) {} + AnimState(int time, int state) : time(time), state(state) {} + int time; + int state; +}; + +#define Running QAbstractAnimation::Running +#define Stopped QAbstractAnimation::Stopped + +Q_DECLARE_METATYPE(AnimState) +void tst_QParallelAnimationGroup::iterationCount_data() +{ + QTest::addColumn<bool>("directionBackward"); + QTest::addColumn<int>("setIterationCount"); + QTest::addColumn<int>("initialGroupTime"); + QTest::addColumn<int>("currentGroupTime"); + QTest::addColumn<AnimState>("expected1"); + QTest::addColumn<AnimState>("expected2"); + QTest::addColumn<AnimState>("expected3"); + + // D U R A T I O N + // 100 60*2 0 + // direction = Forward + QTest::newRow("50") << false << 3 << 0 << 50 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped); + QTest::newRow("100") << false << 3 << 0 << 100 << AnimState(100 ) << AnimState( 40, Running) << AnimState( 0, Stopped); + QTest::newRow("110") << false << 3 << 0 << 110 << AnimState(100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped); + QTest::newRow("120") << false << 3 << 0 << 120 << AnimState( 0, Running) << AnimState( 0, Running) << AnimState( 0, Stopped); + + QTest::newRow("170") << false << 3 << 0 << 170 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped); + QTest::newRow("220") << false << 3 << 0 << 220 << AnimState(100 ) << AnimState( 40, Running) << AnimState( 0, Stopped); + QTest::newRow("230") << false << 3 << 0 << 230 << AnimState(100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped); + QTest::newRow("240") << false << 3 << 0 << 240 << AnimState( 0, Running) << AnimState( 0, Running) << AnimState( 0, Stopped); + + QTest::newRow("290") << false << 3 << 0 << 290 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped); + QTest::newRow("340") << false << 3 << 0 << 340 << AnimState(100 ) << AnimState( 40, Running) << AnimState( 0, Stopped); + QTest::newRow("350") << false << 3 << 0 << 350 << AnimState(100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped); + QTest::newRow("360") << false << 3 << 0 << 360 << AnimState(100, Stopped) << AnimState( 60 ) << AnimState( 0, Stopped); + + QTest::newRow("410") << false << 3 << 0 << 410 << AnimState(100, Stopped) << AnimState( 60, Stopped) << AnimState( 0, Stopped); + QTest::newRow("460") << false << 3 << 0 << 460 << AnimState(100, Stopped) << AnimState( 60, Stopped) << AnimState( 0, Stopped); + QTest::newRow("470") << false << 3 << 0 << 470 << AnimState(100, Stopped) << AnimState( 60, Stopped) << AnimState( 0, Stopped); + QTest::newRow("480") << false << 3 << 0 << 480 << AnimState(100, Stopped) << AnimState( 60, Stopped) << AnimState( 0, Stopped); + + // direction = Forward, rewind + QTest::newRow("120-110") << false << 3 << 120 << 110 << AnimState( 0, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped); + QTest::newRow("120-50") << false << 3 << 120 << 50 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped); + QTest::newRow("120-0") << false << 3 << 120 << 0 << AnimState( 0, Running) << AnimState( 0, Running) << AnimState( 0, Stopped); + QTest::newRow("300-110") << false << 3 << 300 << 110 << AnimState( 0, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped); + QTest::newRow("300-50") << false << 3 << 300 << 50 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped); + QTest::newRow("300-0") << false << 3 << 300 << 0 << AnimState( 0, Running) << AnimState( 0, Running) << AnimState( 0, Stopped); + QTest::newRow("115-105") << false << 3 << 115 << 105 << AnimState( 42, Stopped) << AnimState( 45, Running) << AnimState( 0, Stopped); + + // direction = Backward + QTest::newRow("b120-120") << true << 3 << 120 << 120 << AnimState( 42, Stopped) << AnimState( 60, Running) << AnimState( 0, Stopped); + QTest::newRow("b120-110") << true << 3 << 120 << 110 << AnimState( 42, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped); + QTest::newRow("b120-100") << true << 3 << 120 << 100 << AnimState(100, Running) << AnimState( 40, Running) << AnimState( 0, Stopped); + QTest::newRow("b120-50") << true << 3 << 120 << 50 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped); + QTest::newRow("b120-0") << true << 3 << 120 << 0 << AnimState( 0, Stopped) << AnimState( 0, Stopped) << AnimState( 0, Stopped); + QTest::newRow("b360-170") << true << 3 << 360 << 170 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped); + QTest::newRow("b360-220") << true << 3 << 360 << 220 << AnimState(100, Running) << AnimState( 40, Running) << AnimState( 0, Stopped); + QTest::newRow("b360-210") << true << 3 << 360 << 210 << AnimState( 90, Running) << AnimState( 30, Running) << AnimState( 0, Stopped); + QTest::newRow("b360-120") << true << 3 << 360 << 120 << AnimState( 0, Stopped) << AnimState( 60, Running) << AnimState( 0, Stopped); + + // rewind, direction = Backward + QTest::newRow("b50-110") << true << 3 << 50 << 110 << AnimState(100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped); + QTest::newRow("b50-120") << true << 3 << 50 << 120 << AnimState(100, Stopped) << AnimState( 60, Running) << AnimState( 0, Stopped); + QTest::newRow("b50-140") << true << 3 << 50 << 140 << AnimState( 20, Running) << AnimState( 20, Running) << AnimState( 0, Stopped); + QTest::newRow("b50-240") << true << 3 << 50 << 240 << AnimState(100, Stopped) << AnimState( 60, Running) << AnimState( 0, Stopped); + QTest::newRow("b50-260") << true << 3 << 50 << 260 << AnimState( 20, Running) << AnimState( 20, Running) << AnimState( 0, Stopped); + QTest::newRow("b50-350") << true << 3 << 50 << 350 << AnimState(100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped); + + // infinite looping + QTest::newRow("inf1220") << false << -1 << 0 << 1220 << AnimState( 20, Running) << AnimState( 20, Running) << AnimState( 0, Stopped); + QTest::newRow("inf1310") << false << -1 << 0 << 1310 << AnimState( 100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped); + // infinite looping, direction = Backward (will only loop once) + QTest::newRow("b.inf120-120") << true << -1 << 120 << 120 << AnimState( 42, Stopped) << AnimState( 60, Running) << AnimState( 0, Stopped); + QTest::newRow("b.inf120-20") << true << -1 << 120 << 20 << AnimState( 20, Running) << AnimState( 20, Running) << AnimState( 0, Stopped); + QTest::newRow("b.inf120-110") << true << -1 << 120 << 110 << AnimState( 42, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped); + + +} + +void tst_QParallelAnimationGroup::iterationCount() +{ + QFETCH(bool, directionBackward); + QFETCH(int, setIterationCount); + QFETCH(int, initialGroupTime); + QFETCH(int, currentGroupTime); + QFETCH(AnimState, expected1); + QFETCH(AnimState, expected2); + QFETCH(AnimState, expected3); + + QParallelAnimationGroup group; + + TestAnimation anim1; + anim1.setStartValue(0); + anim1.setEndValue(100); + anim1.setDuration(100); + + TestAnimation anim2; + anim2.setStartValue(0); + anim2.setEndValue(100); + anim2.setDuration(60); //total 120 + anim2.setIterationCount(2); + + TestAnimation anim3; + anim3.setStartValue(0); + anim3.setEndValue(100); + anim3.setDuration(0); + + group.addAnimation(&anim1); + group.addAnimation(&anim2); + group.addAnimation(&anim3); + + group.setIterationCount(setIterationCount); + if (initialGroupTime >= 0) + group.setCurrentTime(initialGroupTime); + if (directionBackward) + group.setDirection(QAbstractAnimation::Backward); + + group.start(); + if (initialGroupTime >= 0) + group.setCurrentTime(initialGroupTime); + + anim1.setCurrentTime(42); // 42 is "untouched" + anim2.setCurrentTime(42); + + group.setCurrentTime(currentGroupTime); + + QCOMPARE(anim1.currentTime(), expected1.time); + QCOMPARE(anim2.currentTime(), expected2.time); + QCOMPARE(anim3.currentTime(), expected3.time); + + if (expected1.state >=0) + QCOMPARE(int(anim1.state()), expected1.state); + if (expected2.state >=0) + QCOMPARE(int(anim2.state()), expected2.state); + if (expected3.state >=0) + QCOMPARE(int(anim3.state()), expected3.state); + +} + +void tst_QParallelAnimationGroup::autoAdd() +{ + QParallelAnimationGroup group; + QCOMPARE(group.duration(), 0); + TestAnimation2 *test = new TestAnimation2(250, &group); // 0, duration = 250; + QCOMPARE(test->group(), &group); + QCOMPARE(test->duration(), 250); + QCOMPARE(group.duration(), 250); + + test = new TestAnimation2(750, &group); // 1 + QCOMPARE(test->group(), &group); + QCOMPARE(group.duration(), 750); + test = new TestAnimation2(500, &group); // 2 + QCOMPARE(test->group(), &group); + QCOMPARE(group.duration(), 750); + + delete group.animationAt(1); // remove the one with duration = 750 + QCOMPARE(group.duration(), 500); + + delete group.animationAt(1); // remove the one with duration = 500 + QCOMPARE(group.duration(), 250); + + test = static_cast<TestAnimation2*>(group.animationAt(0)); + test->setParent(0); // remove the last one (with duration = 250) + QCOMPARE(test->group(), static_cast<QAnimationGroup*>(0)); + QCOMPARE(group.duration(), 0); +} + +QTEST_MAIN(tst_QParallelAnimationGroup) +#include "tst_qparallelanimationgroup.moc" diff --git a/tests/auto/qpropertyanimation/qpropertyanimation.pro b/tests/auto/qpropertyanimation/qpropertyanimation.pro new file mode 100644 index 0000000..6d6ddbf --- /dev/null +++ b/tests/auto/qpropertyanimation/qpropertyanimation.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT = core gui +SOURCES += tst_qpropertyanimation.cpp + + diff --git a/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp b/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp new file mode 100644 index 0000000..f61df49 --- /dev/null +++ b/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp @@ -0,0 +1,831 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QtCore/qpropertyanimation.h> +#include <QtGui/qitemanimation.h> +#include <QtGui/qwidget.h> + +//TESTED_CLASS=QPropertyAnimation +//TESTED_FILES= + +class UncontrolledAnimation : public QPropertyAnimation +{ + Q_OBJECT +public: + int duration() const { return -1; /* not time driven */ } + +protected: + void updateCurrentTime(int msecs) + { + QPropertyAnimation::updateCurrentTime(msecs); + if (msecs >= QPropertyAnimation::duration()) + stop(); + } +}; + +class tst_QPropertyAnimation : public QObject +{ + Q_OBJECT +public: + tst_QPropertyAnimation(); + virtual ~tst_QPropertyAnimation(); + +public Q_SLOTS: + void init(); + void cleanup(); + +private slots: + void construction(); + void setCurrentTime_data(); + void setCurrentTime(); + void statesAndSignals_data(); + void statesAndSignals(); + void deletion1(); + void deletion2(); + void deletion3(); + void duration0(); + void noStartValue(); + void startWhenAnotherIsRunning(); + void easingcurve_data(); + void easingcurve(); + void startWithoutStartValue(); + void playForwardBackward(); + void interpolated(); + void setStartEndValues(); + void zeroDurationStart(); + void operationsInStates_data(); + void operationsInStates(); +}; + +tst_QPropertyAnimation::tst_QPropertyAnimation() +{ +} + +tst_QPropertyAnimation::~tst_QPropertyAnimation() +{ +} + +void tst_QPropertyAnimation::init() +{ + qRegisterMetaType<QAbstractAnimation::State>("QAbstractAnimation::State"); + qRegisterMetaType<QAbstractAnimation::DeletionPolicy>("QAbstractAnimation::DeletionPolicy"); +} + +void tst_QPropertyAnimation::cleanup() +{ +} + +class AnimationObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(int value READ value WRITE setValue) + Q_PROPERTY(qreal realValue READ realValue WRITE setRealValue) +public: + AnimationObject(int startValue = 0) + : v(startValue) + { } + + int value() const { return v; } + void setValue(int value) { v = value; } + + qreal realValue() const { return rv; } + void setRealValue(qreal value) { rv = value; } + + int v; + qreal rv; +}; + + +void tst_QPropertyAnimation::construction() +{ + QPropertyAnimation panimation; +} + +void tst_QPropertyAnimation::setCurrentTime_data() +{ + QTest::addColumn<int>("duration"); + QTest::addColumn<int>("iterationCount"); + QTest::addColumn<int>("currentTime"); + QTest::addColumn<int>("testCurrentTime"); + QTest::addColumn<int>("testCurrentLoop"); + + QTest::newRow("-1") << -1 << 1 << 0 << 0 << 0; + QTest::newRow("0") << 0 << 1 << 0 << 0 << 0; + QTest::newRow("1") << 0 << 1 << 1 << 0 << 0; + QTest::newRow("2") << 0 << 2 << 1 << 0 << 0; + QTest::newRow("3") << 1 << 1 << 0 << 0 << 0; + QTest::newRow("4") << 1 << 1 << 1 << 1 << 0; + QTest::newRow("5") << 1 << 2 << 1 << 0 << 1; + QTest::newRow("6") << 1 << 2 << 2 << 1 << 1; + QTest::newRow("7") << 1 << 2 << 3 << 1 << 1; + QTest::newRow("8") << 1 << 3 << 2 << 0 << 2; + QTest::newRow("9") << 1 << 3 << 3 << 1 << 2; + QTest::newRow("a") << 10 << 1 << 0 << 0 << 0; + QTest::newRow("b") << 10 << 1 << 1 << 1 << 0; + QTest::newRow("c") << 10 << 1 << 10 << 10 << 0; + QTest::newRow("d") << 10 << 2 << 10 << 0 << 1; + QTest::newRow("e") << 10 << 2 << 11 << 1 << 1; + QTest::newRow("f") << 10 << 2 << 20 << 10 << 1; + QTest::newRow("g") << 10 << 2 << 21 << 10 << 1; + QTest::newRow("negloop 0") << 10 << -1 << 0 << 0 << 0; + QTest::newRow("negloop 1") << 10 << -1 << 10 << 0 << 1; + QTest::newRow("negloop 2") << 10 << -1 << 15 << 5 << 1; + QTest::newRow("negloop 3") << 10 << -1 << 20 << 0 << 2; + QTest::newRow("negloop 4") << 10 << -1 << 30 << 0 << 3; +} + +void tst_QPropertyAnimation::setCurrentTime() +{ + QFETCH(int, duration); + QFETCH(int, iterationCount); + QFETCH(int, currentTime); + QFETCH(int, testCurrentTime); + QFETCH(int, testCurrentLoop); + + QPropertyAnimation animation; + if (duration < 0) + QTest::ignoreMessage(QtWarningMsg, "QVariantAnimation::setDuration: cannot set a negative duration"); + animation.setDuration(duration); + animation.setIterationCount(iterationCount); + animation.setCurrentTime(currentTime); + + QCOMPARE(animation.currentTime(), testCurrentTime); + QCOMPARE(animation.currentIteration(), testCurrentLoop); +} + +void tst_QPropertyAnimation::statesAndSignals_data() +{ + QTest::addColumn<bool>("uncontrolled"); + QTest::newRow("normal animation") << false; + QTest::newRow("animation with undefined duration") << true; +} + +void tst_QPropertyAnimation::statesAndSignals() +{ + QFETCH(bool, uncontrolled); + QPropertyAnimation *anim = uncontrolled ? new UncontrolledAnimation : new QPropertyAnimation; + anim->setDuration(100); + + QSignalSpy finishedSpy(anim, SIGNAL(finished())); + QSignalSpy runningSpy(anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy currentLoopSpy(anim, SIGNAL(currentIterationChanged(int))); + + anim->setCurrentTime(1); + anim->setCurrentTime(100); + QCOMPARE(finishedSpy.count(), 0); + QCOMPARE(runningSpy.count(), 0); + QCOMPARE(currentLoopSpy.count(), 0); + QCOMPARE(anim->state(), QAnimationGroup::Stopped); + + anim->setIterationCount(3); + anim->setCurrentTime(101); + + if (uncontrolled) + QSKIP("Uncontrolled animations don't handle looping", SkipSingle); + + QCOMPARE(currentLoopSpy.count(), 1); + QCOMPARE(anim->currentIteration(), 1); + + anim->setCurrentTime(0); + QCOMPARE(currentLoopSpy.count(), 2); + QCOMPARE(anim->currentIteration(), 0); + + anim->start(); + QCOMPARE(anim->state(), QAnimationGroup::Running); + QCOMPARE(runningSpy.count(), 1); //anim must have started + QCOMPARE(anim->currentIteration(), 0); + runningSpy.clear(); + + anim->stop(); + QCOMPARE(anim->state(), QAnimationGroup::Stopped); + QCOMPARE(runningSpy.count(), 1); //anim must have stopped + QCOMPARE(finishedSpy.count(), 0); + QCOMPARE(anim->currentTime(), 0); + QCOMPARE(anim->currentIteration(), 0); + QCOMPARE(currentLoopSpy.count(), 2); + runningSpy.clear(); + + anim->start(); + QTest::qWait(1000); + QCOMPARE(anim->state(), QAnimationGroup::Stopped); + QCOMPARE(runningSpy.count(), 2); //started and stopped again + runningSpy.clear(); + QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(anim->currentTime(), 100); + QCOMPARE(anim->currentIteration(), 2); + QCOMPARE(currentLoopSpy.count(), 4); + + anim->start(); // auto-rewinds + QCOMPARE(anim->state(), QAnimationGroup::Running); + QCOMPARE(anim->currentTime(), 0); + QCOMPARE(anim->currentIteration(), 0); + QCOMPARE(currentLoopSpy.count(), 5); + QCOMPARE(runningSpy.count(), 1); // anim has started + QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(anim->currentIteration(), 0); + runningSpy.clear(); + + QTest::qWait(1000); + + QCOMPARE(currentLoopSpy.count(), 7); + QCOMPARE(anim->state(), QAnimationGroup::Stopped); + QCOMPARE(anim->currentIteration(), 2); + QCOMPARE(runningSpy.count(), 1); // anim has stopped + QCOMPARE(finishedSpy.count(), 2); + QCOMPARE(anim->currentTime(), 100); + + delete anim; +} + +void tst_QPropertyAnimation::deletion1() +{ + QObject *object = new QWidget; + QPointer<QPropertyAnimation> anim = new QPropertyAnimation(object,"minimumWidth"); + + //test that the animation is deleted correctly depending of the deletion flag passed in start() + QSignalSpy runningSpy(anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy finishedSpy(anim, SIGNAL(finished())); + anim->setStartValue(10); + anim->setEndValue(20); + anim->setDuration(200); + anim->start(); + QCOMPARE(runningSpy.count(), 1); + QCOMPARE(finishedSpy.count(), 0); + + QVERIFY(anim); + QCOMPARE(anim->state(), QAnimationGroup::Running); + QTest::qWait(100); + QVERIFY(anim); + QCOMPARE(anim->state(), QAnimationGroup::Running); + QTest::qWait(150); + QVERIFY(anim); //The animation should not have been deleted + QCOMPARE(anim->state(), QAnimationGroup::Stopped); + QCOMPARE(runningSpy.count(), 2); + QCOMPARE(finishedSpy.count(), 1); + + anim->start(QVariantAnimation::DeleteWhenStopped); + QVERIFY(anim); + QCOMPARE(anim->state(), QAnimationGroup::Running); + QTest::qWait(100); + QVERIFY(anim); + QCOMPARE(anim->state(), QAnimationGroup::Running); + QTest::qWait(150); + QVERIFY(!anim); //The animation must have been deleted + QCOMPARE(runningSpy.count(), 4); + QCOMPARE(finishedSpy.count(), 2); + delete object; +} + +void tst_QPropertyAnimation::deletion2() +{ + //test that the animation get deleted if the object is deleted + QObject *object = new QWidget; + QPointer<QPropertyAnimation> anim = new QPropertyAnimation(object,"minimumWidth"); + anim->setStartValue(10); + anim->setEndValue(20); + anim->setDuration(200); + + QSignalSpy runningSpy(anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy finishedSpy(anim, SIGNAL(finished())); + + anim->setStartValue(10); + anim->setEndValue(20); + anim->setDuration(200); + anim->start(); + + QTest::qWait(50); + QVERIFY(anim); + QCOMPARE(anim->state(), QAnimationGroup::Running); + + QCOMPARE(runningSpy.count(), 1); + QCOMPARE(finishedSpy.count(), 0); + + //we can't call deletaLater directly because the delete would only happen in the next iteration of _this_ event loop + QTimer::singleShot(0, object, SLOT(deleteLater())); + QTest::qWait(50); + + QVERIFY(anim->targetObject() == 0); +} + +void tst_QPropertyAnimation::deletion3() +{ + //test that the stopped signal is emit when the animation is destroyed + QObject *object = new QWidget; + QPropertyAnimation *anim = new QPropertyAnimation(object,"minimumWidth"); + anim->setStartValue(10); + anim->setEndValue(20); + anim->setDuration(200); + + QSignalSpy runningSpy(anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy finishedSpy(anim, SIGNAL(finished())); + anim->start(); + + QTest::qWait(50); + QCOMPARE(anim->state(), QAnimationGroup::Running); + QCOMPARE(runningSpy.count(), 1); + QCOMPARE(finishedSpy.count(), 0); + delete anim; + QCOMPARE(runningSpy.count(), 2); + QCOMPARE(finishedSpy.count(), 0); +} + +void tst_QPropertyAnimation::duration0() +{ + QObject o; + o.setProperty("ole", 42); + QCOMPARE(o.property("ole").toInt(), 42); + + QPropertyAnimation animation(&o, "ole"); + animation.setEndValue(43); + QVERIFY(!animation.currentValue().isValid()); + QCOMPARE(animation.currentValue().toInt(), 0); + QCOMPARE(o.property("ole").toInt(), 42); + animation.setDuration(0); + animation.start(); + QCOMPARE(animation.state(), QAnimationGroup::Stopped); + QCOMPARE(animation.currentTime(), 0); + QCOMPARE(o.property("ole").toInt(), 43); +} + +class StartValueTester : public QObject +{ + Q_OBJECT + Q_PROPERTY(int ole READ ole WRITE setOle) +public: + StartValueTester() : o(0) { } + int ole() const { return o; } + void setOle(int v) { o = v; values << v; } + + int o; + QList<int> values; +}; + +void tst_QPropertyAnimation::noStartValue() +{ + StartValueTester o; + o.setProperty("ole", 42); + o.values.clear(); + + QPropertyAnimation a(&o, "ole"); + a.setEndValue(420); + a.setDuration(250); + a.start(); + + QTest::qWait(300); + + QCOMPARE(o.values.first(), 42); + QCOMPARE(o.values.last(), 420); +} + +void tst_QPropertyAnimation::startWhenAnotherIsRunning() +{ + StartValueTester o; + o.setProperty("ole", 42); + o.values.clear(); + + { + //normal case: the animation finishes and is deleted + QPointer<QVariantAnimation> anim = new QPropertyAnimation(&o, "ole"); + QSignalSpy runningSpy(anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + anim->start(QVariantAnimation::DeleteWhenStopped); + QTest::qWait(anim->duration() + 50); + QCOMPARE(runningSpy.count(), 2); //started and then stopped + QVERIFY(!anim); + } + + { + QPointer<QVariantAnimation> anim = new QPropertyAnimation(&o, "ole"); + QSignalSpy runningSpy(anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + anim->start(QVariantAnimation::DeleteWhenStopped); + QTest::qWait(anim->duration()/2); + QPointer<QVariantAnimation> anim2 = new QPropertyAnimation(&o, "ole"); + QCOMPARE(runningSpy.count(), 1); + QCOMPARE(anim->state(), QVariantAnimation::Running); + + //anim2 will interrupt anim1 + QMetaObject::invokeMethod(anim2, "start", Qt::QueuedConnection, Q_ARG(QAbstractAnimation::DeletionPolicy, QVariantAnimation::DeleteWhenStopped)); + QTest::qWait(50); + QVERIFY(!anim); //anim should have been deleted + QVERIFY(anim2); + QTest::qWait(anim2->duration()); + QVERIFY(!anim2); //anim2 is finished: it should have been deleted by now + QVERIFY(!anim); + } + +} + +// copy from easing.cpp in case that function changes definition +static qreal easeInOutBack(qreal t) +{ + qreal s = 1.70158; + qreal t_adj = 2.0f * (qreal)t; + if (t_adj < 1) { + s *= 1.525f; + return 1.0/2*(t_adj*t_adj*((s+1)*t_adj - s)); + } else { + t_adj -= 2; + s *= 1.525f; + return 1.0/2*(t_adj*t_adj*((s+1)*t_adj + s) + 2); + } +} + +void tst_QPropertyAnimation::easingcurve_data() +{ + QTest::addColumn<int>("currentTime"); + QTest::addColumn<int>("expectedvalue"); + + QTest::newRow("interpolation1") << 0 << 0; + QTest::newRow("interpolation2") << 1000 << 1000; + QTest::newRow("extrapolationbelow") << 250 << -99; + QTest::newRow("extrapolationabove") << 750 << 1099; +} + +void tst_QPropertyAnimation::easingcurve() +{ + QFETCH(int, currentTime); + QFETCH(int, expectedvalue); + QObject o; + o.setProperty("ole", 42); + QCOMPARE(o.property("ole").toInt(), 42); + + QPropertyAnimation pAnimation(&o, "ole"); + pAnimation.setStartValue(0); + pAnimation.setEndValue(1000); + pAnimation.setDuration(1000); + + // this easingcurve assumes that we extrapolate before startValue and after endValue + QEasingCurve easingCurve; + easingCurve.setCustomType(easeInOutBack); + pAnimation.setEasingCurve(easingCurve); + pAnimation.start(); + pAnimation.pause(); + pAnimation.setCurrentTime(currentTime); + QCOMPARE(o.property("ole").toInt(), expectedvalue); +} + +void tst_QPropertyAnimation::startWithoutStartValue() +{ + QObject o; + o.setProperty("ole", 42); + QCOMPARE(o.property("ole").toInt(), 42); + + QPropertyAnimation anim(&o, "ole"); + anim.setEndValue(100); + + anim.start(); + + QTest::qWait(100); + int current = anim.currentValue().toInt(); + //it is somewhere in the animation + QVERIFY(current > 42); + QVERIFY(current < 100); + + QTest::qWait(200); + QCOMPARE(anim.state(), QVariantAnimation::Stopped); + + anim.setEndValue(110); + anim.start(); + current = anim.currentValue().toInt(); + QCOMPARE(current, 42); // the initial default start value + + QTest::qWait(100); + current = anim.currentValue().toInt(); + //it is somewhere in the animation + QVERIFY(current > 42); + QVERIFY(current < 110); +} + +void tst_QPropertyAnimation::playForwardBackward() +{ + QObject o; + o.setProperty("ole", 0); + QCOMPARE(o.property("ole").toInt(), 0); + + QPropertyAnimation anim(&o, "ole"); + anim.setEndValue(100); + anim.start(); + QTest::qWait(anim.duration() + 50); + QCOMPARE(anim.state(), QAbstractAnimation::Stopped); + QCOMPARE(anim.currentTime(), anim.duration()); + + //the animation is at the end + anim.setDirection(QVariantAnimation::Backward); + anim.start(); + QCOMPARE(anim.state(), QAbstractAnimation::Running); + QTest::qWait(anim.duration() + 50); + QCOMPARE(anim.state(), QAbstractAnimation::Stopped); + QCOMPARE(anim.currentTime(), 0); + + //the direction is backward + //restarting should jump to the end + anim.start(); + QCOMPARE(anim.state(), QAbstractAnimation::Running); + QCOMPARE(anim.currentTime(), anim.duration()); + QTest::qWait(anim.duration() + 50); + QCOMPARE(anim.state(), QAbstractAnimation::Stopped); + QCOMPARE(anim.currentTime(), 0); +} + +struct Number +{ + Number() {} + Number(int n) + : n(n) {} + + Number(const Number &other) + : n(other.n){} + + Number &operator=(const Number &other) { + n = other.n; + return *this; + } + bool operator==(const Number &other) const { + return n == other.n; + } + + int n; +}; + +Q_DECLARE_METATYPE(Number) +Q_DECLARE_METATYPE(QAbstractAnimation::State) + +QVariant numberInterpolator(const Number &f, const Number &t, qreal progress) +{ + return qVariantFromValue<Number>(Number(f.n + (t.n - f.n)*progress)); +} + +QVariant xaxisQPointInterpolator(const QPointF &f, const QPointF &t, qreal progress) +{ + return QPointF(f.x() + (t.x() - f.x())*progress, f.y()); +} + +void tst_QPropertyAnimation::interpolated() +{ + QObject o; + o.setProperty("number", qVariantFromValue<Number>(Number(42))); + QCOMPARE(qVariantValue<Number>(o.property("number")), Number(42)); + { + qRegisterAnimationInterpolator<Number>(numberInterpolator); + QPropertyAnimation anim(&o, "number"); + anim.setStartValue(qVariantFromValue<Number>(Number(0))); + anim.setEndValue(qVariantFromValue<Number>(Number(100))); + anim.setDuration(1000); + anim.start(); + anim.pause(); + anim.setCurrentTime(100); + Number t(qVariantValue<Number>(o.property("number"))); + QCOMPARE(t, Number(10)); + anim.setCurrentTime(500); + QCOMPARE(qVariantValue<Number>(o.property("number")), Number(50)); + } + { + qRegisterAnimationInterpolator<QPointF>(xaxisQPointInterpolator); + QPropertyAnimation anim(&o, "point"); + anim.setStartValue(QPointF(0,0)); + anim.setEndValue(QPointF(100, 100)); + anim.setDuration(1000); + anim.start(); + anim.pause(); + anim.setCurrentTime(100); + QCOMPARE(o.property("point").toPointF(), QPointF(10, 0)); + anim.setCurrentTime(500); + QCOMPARE(o.property("point").toPointF(), QPointF(50, 0)); + } + { + // unregister it and see if we get back the default behaviour + qRegisterAnimationInterpolator<QPointF>(0); + QPropertyAnimation anim(&o, "point"); + anim.setStartValue(QPointF(0,0)); + anim.setEndValue(QPointF(100, 100)); + anim.setDuration(1000); + anim.start(); + anim.pause(); + anim.setCurrentTime(100); + QCOMPARE(o.property("point").toPointF(), QPointF(10, 10)); + anim.setCurrentTime(500); + QCOMPARE(o.property("point").toPointF(), QPointF(50, 50)); + } + + { + // Interpolate a qreal property with a int interpolator + AnimationObject o1; + o1.setRealValue(42.42); + QPropertyAnimation anim(&o1, "realValue"); + anim.setStartValue(0); + anim.setEndValue(100); + anim.start(); + QCOMPARE(o1.realValue(), qreal(0)); + anim.setCurrentTime(250); + QCOMPARE(o1.realValue(), qreal(100)); + } +} + +void tst_QPropertyAnimation::setStartEndValues() +{ + //this tests the start value, end value and default start value + QObject o; + o.setProperty("ole", 42); + QPropertyAnimation anim(&o, "ole"); + QVariantAnimation::KeyValues values; + QCOMPARE(anim.keyValues(), values); + + //let's add a start value + anim.setStartValue(0); + values << QVariantAnimation::KeyValue(0, 0); + QCOMPARE(anim.keyValues(), values); + + anim.setEndValue(10); + values << QVariantAnimation::KeyValue(1, 10); + QCOMPARE(anim.keyValues(), values); + + //now we can play with objects + QCOMPARE(o.property("ole").toInt(), 42); + QCOMPARE(o.property("ole").toInt(), 42); + anim.start(); + QVERIFY(anim.startValue().isValid()); + QCOMPARE(o.property("ole"), anim.startValue()); + + //now we remove the explicit start value and test the implicit one + anim.stop(); + o.setProperty("ole", 42); + values.remove(0); + anim.setStartValue(QVariant()); //reset the start value + QCOMPARE(anim.keyValues(), values); + QVERIFY(!anim.startValue().isValid()); + anim.start(); + QCOMPARE(o.property("ole").toInt(), 42); + anim.setCurrentTime(anim.duration()/2); + QCOMPARE(o.property("ole").toInt(), 26); //just in the middle of the animation + anim.setCurrentTime(anim.duration()); + QCOMPARE(anim.state(), QAnimationGroup::Stopped); //it should have stopped + QVERIFY(anim.endValue().isValid()); + QCOMPARE(o.property("ole"), anim.endValue()); //end of the animations + + //now we set back the startValue + anim.setStartValue(5); + QVERIFY(anim.startValue().isValid()); + anim.start(); + QCOMPARE(o.property("ole").toInt(), 5); +} + +void tst_QPropertyAnimation::zeroDurationStart() +{ + QPropertyAnimation anim; + QSignalSpy spy(&anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + anim.setDuration(0); + QCOMPARE(anim.state(), QAbstractAnimation::Stopped); + anim.start(); + //the animation stops immediately + QCOMPARE(anim.state(), QAbstractAnimation::Stopped); + QCOMPARE(spy.count(), 2); + + //let's check the first state change + const QVariantList firstChange = spy.first(); + //old state + QCOMPARE(qVariantValue<QAbstractAnimation::State>(firstChange.first()), QAbstractAnimation::Stopped); + //new state + QCOMPARE(qVariantValue<QAbstractAnimation::State>(firstChange.last()), QAbstractAnimation::Running); + + //let's check the first state change + const QVariantList secondChange = spy.last(); + //old state + QCOMPARE(qVariantValue<QAbstractAnimation::State>(secondChange.first()), QAbstractAnimation::Running); + //new state + QCOMPARE(qVariantValue<QAbstractAnimation::State>(secondChange.last()), QAbstractAnimation::Stopped); +} + +#define Pause 1 +#define Start 2 +#define Resume 3 +#define Stop 4 + +void tst_QPropertyAnimation::operationsInStates_data() +{ + QTest::addColumn<QAbstractAnimation::State>("originState"); + QTest::addColumn<int>("operation"); + QTest::addColumn<QString>("expectedWarning"); + QTest::addColumn<QAbstractAnimation::State>("expectedState"); + + QString pauseWarn(QLatin1String("QAbstractAnimation::pause: Cannot pause a stopped animation")); + QString resumeWarn(QLatin1String("QAbstractAnimation::resume: Cannot resume an animation that is not paused")); + + QTest::newRow("S-pause") << QAbstractAnimation::Stopped << Pause << pauseWarn << QAbstractAnimation::Stopped; + QTest::newRow("S-start") << QAbstractAnimation::Stopped << Start << QString() << QAbstractAnimation::Running; + QTest::newRow("S-resume") << QAbstractAnimation::Stopped << Resume << resumeWarn << QAbstractAnimation::Stopped; + QTest::newRow("S-stop") << QAbstractAnimation::Stopped << Stop << QString() << QAbstractAnimation::Stopped; + + QTest::newRow("P-pause") << QAbstractAnimation::Paused << Pause << QString() << QAbstractAnimation::Paused; + QTest::newRow("P-start") << QAbstractAnimation::Paused << Start << QString() << QAbstractAnimation::Running; + QTest::newRow("P-resume") << QAbstractAnimation::Paused << Resume << QString() << QAbstractAnimation::Running; + QTest::newRow("P-stop") << QAbstractAnimation::Paused << Stop << QString() << QAbstractAnimation::Stopped; + + QTest::newRow("R-pause") << QAbstractAnimation::Running << Pause << QString() << QAbstractAnimation::Paused; + QTest::newRow("R-start") << QAbstractAnimation::Running << Start << QString() << QAbstractAnimation::Running; + QTest::newRow("R-resume") << QAbstractAnimation::Running << Resume << resumeWarn << QAbstractAnimation::Running; + QTest::newRow("R-stop") << QAbstractAnimation::Running << Stop << QString() << QAbstractAnimation::Stopped; +} + +void tst_QPropertyAnimation::operationsInStates() +{ +/** + * | pause() |start() |resume() |stop() + * ----------+------------+-----------+-----------+-------------------+ + * Stopped | Stopped |Running |Stopped |Stopped | + * _| qWarning | |qWarning |- | + * Paused | Paused |Running |Running |Stopped | + * _| - | | | | + * Running | Paused |Running |Running |Stopped | + * | |restart |qWarning | | + * ----------+------------+-----------+-----------+-------------------+ +**/ + + QFETCH(QAbstractAnimation::State, originState); + QFETCH(int, operation); + QFETCH(QString, expectedWarning); + QFETCH(QAbstractAnimation::State, expectedState); + + QObject o; + o.setProperty("ole", 42); + QPropertyAnimation anim(&o, "ole"); + QSignalSpy spy(&anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + + anim.stop(); + switch (originState) { + case QAbstractAnimation::Stopped: + break; + case QAbstractAnimation::Paused: + anim.start(); + anim.pause(); + break; + case QAbstractAnimation::Running: + anim.start(); + break; + } + if (!expectedWarning.isEmpty()) { + QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); + } + QCOMPARE(anim.state(), originState); + switch (operation) { + case Pause: + anim.pause(); + break; + case Start: + anim.start(); + break; + case Resume: + anim.resume(); + break; + case Stop: + anim.stop(); + break; + } + + QCOMPARE(anim.state(), expectedState); +} +#undef Pause +#undef Start +#undef Resume +#undef Stop + +QTEST_MAIN(tst_QPropertyAnimation) +#include "tst_qpropertyanimation.moc" diff --git a/tests/auto/qscriptqobject/tst_qscriptqobject.cpp b/tests/auto/qscriptqobject/tst_qscriptqobject.cpp index 9b9dd16..1025d2a 100644 --- a/tests/auto/qscriptqobject/tst_qscriptqobject.cpp +++ b/tests/auto/qscriptqobject/tst_qscriptqobject.cpp @@ -107,6 +107,7 @@ class MyQObject : public QObject Q_PROPERTY(int readOnlyProperty READ readOnlyProperty) Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut) Q_PROPERTY(CustomType propWithCustomType READ propWithCustomType WRITE setPropWithCustomType) + Q_PROPERTY(Policy enumProperty READ enumProperty WRITE setEnumProperty) Q_ENUMS(Policy Strategy) Q_FLAGS(Ability) @@ -144,6 +145,7 @@ public: m_hiddenValue(456.0), m_writeOnlyValue(789), m_readOnlyValue(987), + m_enumValue(BarPolicy), m_qtFunctionInvoked(-1) { } @@ -205,6 +207,11 @@ public: void setPropWithCustomType(const CustomType &c) { m_customType = c; } + Policy enumProperty() const + { return m_enumValue; } + void setEnumProperty(Policy policy) + { m_enumValue = policy; } + int qtFunctionInvoked() const { return m_qtFunctionInvoked; } @@ -255,8 +262,8 @@ public: { m_qtFunctionInvoked = 36; m_actuals << policy; } Q_INVOKABLE Policy myInvokableReturningEnum() { m_qtFunctionInvoked = 37; return BazPolicy; } - Q_INVOKABLE MyQObject::Policy myInvokableReturningQualifiedEnum() - { m_qtFunctionInvoked = 38; return BazPolicy; } + Q_INVOKABLE MyQObject::Strategy myInvokableReturningQualifiedEnum() + { m_qtFunctionInvoked = 38; return BazStrategy; } Q_INVOKABLE QVector<int> myInvokableReturningVectorOfInt() { m_qtFunctionInvoked = 11; return QVector<int>(); } Q_INVOKABLE void myInvokableWithVectorOfIntArg(const QVector<int> &) @@ -410,6 +417,7 @@ protected: int m_readOnlyValue; QKeySequence m_shortcut; CustomType m_customType; + Policy m_enumValue; int m_qtFunctionInvoked; QVariantList m_actuals; QByteArray m_connectedSignal; @@ -417,6 +425,7 @@ protected: }; Q_DECLARE_METATYPE(MyQObject*) +Q_DECLARE_METATYPE(MyQObject::Policy) class MyOtherQObject : public MyQObject { @@ -530,6 +539,24 @@ static QScriptValue getSetProperty(QScriptContext *ctx, QScriptEngine *) return ctx->callee().property("value"); } +static QScriptValue policyToScriptValue(QScriptEngine *engine, const MyQObject::Policy &policy) +{ + return qScriptValueFromValue(engine, policy); +} + +static void policyFromScriptValue(const QScriptValue &value, MyQObject::Policy &policy) +{ + QString str = value.toString(); + if (str == QLatin1String("red")) + policy = MyQObject::FooPolicy; + else if (str == QLatin1String("green")) + policy = MyQObject::BarPolicy; + else if (str == QLatin1String("blue")) + policy = MyQObject::BazPolicy; + else + policy = (MyQObject::Policy)-1; +} + void tst_QScriptExtQObject::getSetStaticProperty() { QCOMPARE(m_engine->evaluate("myObject.noSuchProperty").isUndefined(), true); @@ -751,6 +778,31 @@ void tst_QScriptExtQObject::getSetStaticProperty() QScriptValue::ReadOnly); } + // enum property + QCOMPARE(m_myObject->enumProperty(), MyQObject::BarPolicy); + { + QScriptValue val = m_engine->evaluate("myObject.enumProperty"); + QVERIFY(val.isNumber()); + QCOMPARE(val.toInt32(), (int)MyQObject::BarPolicy); + } + m_engine->evaluate("myObject.enumProperty = 2"); + QCOMPARE(m_myObject->enumProperty(), MyQObject::BazPolicy); + m_engine->evaluate("myObject.enumProperty = 'BarPolicy'"); + QCOMPARE(m_myObject->enumProperty(), MyQObject::BarPolicy); + m_engine->evaluate("myObject.enumProperty = 'ScoobyDoo'"); + // ### ouch! Shouldn't QMetaProperty::write() rather not change the value...? + QCOMPARE(m_myObject->enumProperty(), (MyQObject::Policy)-1); + // enum property with custom conversion + qScriptRegisterMetaType<MyQObject::Policy>(m_engine, policyToScriptValue, policyFromScriptValue); + m_engine->evaluate("myObject.enumProperty = 'red'"); + QCOMPARE(m_myObject->enumProperty(), MyQObject::FooPolicy); + m_engine->evaluate("myObject.enumProperty = 'green'"); + QCOMPARE(m_myObject->enumProperty(), MyQObject::BarPolicy); + m_engine->evaluate("myObject.enumProperty = 'blue'"); + QCOMPARE(m_myObject->enumProperty(), MyQObject::BazPolicy); + m_engine->evaluate("myObject.enumProperty = 'nada'"); + QCOMPARE(m_myObject->enumProperty(), (MyQObject::Policy)-1); + // auto-dereferencing of pointers { QBrush b = QColor(0xCA, 0xFE, 0xBA, 0xBE); @@ -1879,6 +1931,16 @@ void tst_QScriptExtQObject::classEnums() QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy)); m_myObject->resetQtFunctionInvoked(); + QCOMPARE(m_engine->evaluate("myObject.myInvokableWithEnumArg('BarPolicy')").isUndefined(), true); + QCOMPARE(m_myObject->qtFunctionInvoked(), 10); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BarPolicy)); + + m_myObject->resetQtFunctionInvoked(); + QVERIFY(m_engine->evaluate("myObject.myInvokableWithEnumArg('NoSuchPolicy')").isError()); + QCOMPARE(m_myObject->qtFunctionInvoked(), -1); + + m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableWithQualifiedEnumArg(MyQObject.BazPolicy)").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 36); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); diff --git a/tests/auto/qsequentialanimationgroup/qsequentialanimationgroup.pro b/tests/auto/qsequentialanimationgroup/qsequentialanimationgroup.pro new file mode 100644 index 0000000..ad861c3 --- /dev/null +++ b/tests/auto/qsequentialanimationgroup/qsequentialanimationgroup.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT = core gui +SOURCES += tst_qsequentialanimationgroup.cpp + + diff --git a/tests/auto/qsequentialanimationgroup/tst_qsequentialanimationgroup.cpp b/tests/auto/qsequentialanimationgroup/tst_qsequentialanimationgroup.cpp new file mode 100644 index 0000000..18b57b2 --- /dev/null +++ b/tests/auto/qsequentialanimationgroup/tst_qsequentialanimationgroup.cpp @@ -0,0 +1,1649 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include "../../shared/util.h" + +#include <QtCore/qanimationgroup.h> +#include <QtCore/qsequentialanimationgroup.h> + +//TESTED_CLASS=QSequentialAnimationGroup +//TESTED_FILES= + +Q_DECLARE_METATYPE(QAbstractAnimation::State) +Q_DECLARE_METATYPE(QAbstractAnimation*) + +class tst_QSequentialAnimationGroup : public QObject +{ + Q_OBJECT +public: + tst_QSequentialAnimationGroup(); + virtual ~tst_QSequentialAnimationGroup(); + +public Q_SLOTS: + void init(); + void cleanup(); + +private slots: + void construction(); + void setCurrentTime(); + void setCurrentTimeWithUncontrolledAnimation(); + void seekingForwards(); + void seekingBackwards(); + void pauseAndResume(); + void restart(); + void looping(); + void startDelay(); + void clearGroup(); + void groupWithZeroDurationAnimations(); + void propagateGroupUpdateToChildren(); + void updateChildrenWithRunningGroup(); + void deleteChildrenWithRunningGroup(); + void startChildrenWithStoppedGroup(); + void stopGroupWithRunningChild(); + void startGroupWithRunningChild(); + void zeroDurationAnimation(); + void stopUncontrolledAnimations(); + void finishWithUncontrolledAnimation(); + void addRemoveAnimation(); + void currentAnimation(); + void currentAnimationWithZeroDuration(); + void insertAnimation(); + void clearAnimations(); +}; + +tst_QSequentialAnimationGroup::tst_QSequentialAnimationGroup() +{ +} + +tst_QSequentialAnimationGroup::~tst_QSequentialAnimationGroup() +{ +} + +void tst_QSequentialAnimationGroup::init() +{ + qRegisterMetaType<QAbstractAnimation::State>("QAbstractAnimation::State"); + qRegisterMetaType<QAbstractAnimation*>("QAbstractAnimation*"); +} + +void tst_QSequentialAnimationGroup::cleanup() +{ +} + +void tst_QSequentialAnimationGroup::construction() +{ + QSequentialAnimationGroup animationgroup; +} + +class AnimationObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(int value READ value WRITE setValue) +public: + AnimationObject(int startValue = 0) + : v(startValue) + { } + + int value() const { return v; } + void setValue(int value) { v = value; } + + int v; +}; + +class TestAnimation : public QVariantAnimation +{ + Q_OBJECT +public: + virtual void updateCurrentValue(const QVariant &value) { Q_UNUSED(value)}; + virtual void updateState(QAbstractAnimation::State oldState, + QAbstractAnimation::State newState) + { + Q_UNUSED(oldState) + Q_UNUSED(newState) + }; +}; + +class UncontrolledAnimation : public QPropertyAnimation +{ + Q_OBJECT +public: + UncontrolledAnimation(QObject *target, const QByteArray &propertyName, QObject *parent = 0) + : QPropertyAnimation(target, propertyName, parent) + { + setDuration(250); + } + + int duration() const { return -1; /* not time driven */ } + +protected: + void updateCurrentTime(int msecs) + { + QPropertyAnimation::updateCurrentTime(msecs); + if (msecs >= QPropertyAnimation::duration()) + stop(); + } +}; + +void tst_QSequentialAnimationGroup::setCurrentTime() +{ + AnimationObject s_o1; + AnimationObject s_o2; + AnimationObject s_o3; + + // sequence operating on same object/property + QAnimationGroup *sequence = new QSequentialAnimationGroup(); + QVariantAnimation *a1_s_o1 = new QPropertyAnimation(&s_o1, "value"); + QVariantAnimation *a2_s_o1 = new QPropertyAnimation(&s_o1, "value"); + QVariantAnimation *a3_s_o1 = new QPropertyAnimation(&s_o1, "value"); + a2_s_o1->setIterationCount(3); + sequence->addAnimation(a1_s_o1); + sequence->addAnimation(a2_s_o1); + sequence->addAnimation(a3_s_o1); + + // sequence operating on different object/properties + QAnimationGroup *sequence2 = new QSequentialAnimationGroup(); + QVariantAnimation *a1_s_o2 = new QPropertyAnimation(&s_o2, "value"); + QVariantAnimation *a1_s_o3 = new QPropertyAnimation(&s_o3, "value"); + sequence2->addAnimation(a1_s_o2); + sequence2->addAnimation(a1_s_o3); + + QSequentialAnimationGroup group; + group.addAnimation(sequence); + group.addAnimation(sequence2); + + // Current time = 1 + group.setCurrentTime(1); + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(sequence->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(sequence2->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroup::Stopped); + + QCOMPARE(group.currentTime(), 1); + QCOMPARE(sequence->currentTime(), 1); + QCOMPARE(a1_s_o1->currentTime(), 1); + QCOMPARE(a2_s_o1->currentTime(), 0); + QCOMPARE(a3_s_o1->currentTime(), 0); + QCOMPARE(a1_s_o2->currentTime(), 0); + QCOMPARE(a1_s_o3->currentTime(), 0); + + // Current time = 250 + group.setCurrentTime(250); + QCOMPARE(group.currentTime(), 250); + QCOMPARE(sequence->currentTime(), 250); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 0); + QCOMPARE(a3_s_o1->currentTime(), 0); + QCOMPARE(a1_s_o2->currentTime(), 0); + QCOMPARE(a1_s_o3->currentTime(), 0); + + // Current time = 251 + group.setCurrentTime(251); + QCOMPARE(group.currentTime(), 251); + QCOMPARE(sequence->currentTime(), 251); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 1); + QCOMPARE(a2_s_o1->currentIteration(), 0); + QCOMPARE(a3_s_o1->currentTime(), 0); + QCOMPARE(sequence2->currentTime(), 0); + QCOMPARE(a1_s_o2->currentTime(), 0); + QCOMPARE(a1_s_o3->currentTime(), 0); + + // Current time = 750 + group.setCurrentTime(750); + QCOMPARE(group.currentTime(), 750); + QCOMPARE(sequence->currentTime(), 750); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 0); + QCOMPARE(a2_s_o1->currentIteration(), 2); + QCOMPARE(a3_s_o1->currentTime(), 0); + QCOMPARE(sequence2->currentTime(), 0); + QCOMPARE(a1_s_o2->currentTime(), 0); + QCOMPARE(a1_s_o3->currentTime(), 0); + + // Current time = 1000 + group.setCurrentTime(1000); + QCOMPARE(group.currentTime(), 1000); + QCOMPARE(sequence->currentTime(), 1000); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentIteration(), 2); + QCOMPARE(a3_s_o1->currentTime(), 0); + QCOMPARE(sequence2->currentTime(), 0); + QCOMPARE(a1_s_o2->currentTime(), 0); + QCOMPARE(a1_s_o3->currentTime(), 0); + + // Current time = 1010 + group.setCurrentTime(1010); + QCOMPARE(group.currentTime(), 1010); + QCOMPARE(sequence->currentTime(), 1010); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentIteration(), 2); + QCOMPARE(a3_s_o1->currentTime(), 10); + QCOMPARE(sequence2->currentTime(), 0); + QCOMPARE(a1_s_o2->currentTime(), 0); + QCOMPARE(a1_s_o3->currentTime(), 0); + + // Current time = 1250 + group.setCurrentTime(1250); + QCOMPARE(group.currentTime(), 1250); + QCOMPARE(sequence->currentTime(), 1250); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentIteration(), 2); + QCOMPARE(a3_s_o1->currentTime(), 250); + QCOMPARE(sequence2->currentTime(), 0); + QCOMPARE(a1_s_o2->currentTime(), 0); + QCOMPARE(a1_s_o3->currentTime(), 0); + + // Current time = 1500 + group.setCurrentTime(1500); + QCOMPARE(group.currentTime(), 1500); + QCOMPARE(sequence->currentTime(), 1250); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentIteration(), 2); + QCOMPARE(a3_s_o1->currentTime(), 250); + QCOMPARE(sequence2->currentTime(), 250); + QCOMPARE(a1_s_o2->currentTime(), 250); + QCOMPARE(a1_s_o3->currentTime(), 0); + + // Current time = 1750 + group.setCurrentTime(1750); + QCOMPARE(group.currentTime(), 1750); + QCOMPARE(sequence->currentTime(), 1250); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentIteration(), 2); + QCOMPARE(a3_s_o1->currentTime(), 250); + QCOMPARE(sequence2->currentTime(), 500); + QCOMPARE(a1_s_o2->currentTime(), 250); + QCOMPARE(a1_s_o3->currentTime(), 250); + + // Current time = 2000 + group.setCurrentTime(2000); + QCOMPARE(group.currentTime(), 1750); + QCOMPARE(sequence->currentTime(), 1250); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentIteration(), 2); + QCOMPARE(a3_s_o1->currentTime(), 250); + QCOMPARE(sequence2->currentTime(), 500); + QCOMPARE(a1_s_o2->currentTime(), 250); + QCOMPARE(a1_s_o3->currentTime(), 250); +} + +void tst_QSequentialAnimationGroup::setCurrentTimeWithUncontrolledAnimation() +{ + AnimationObject s_o1; + AnimationObject s_o2; + AnimationObject t_o1; + AnimationObject t_o2; + + // sequence operating on different object/properties + QAnimationGroup *sequence = new QSequentialAnimationGroup(); + QVariantAnimation *a1_s_o1 = new QPropertyAnimation(&s_o1, "value"); + QVariantAnimation *a1_s_o2 = new QPropertyAnimation(&s_o2, "value"); + sequence->addAnimation(a1_s_o1); + sequence->addAnimation(a1_s_o2); + + UncontrolledAnimation *notTimeDriven = new UncontrolledAnimation(&t_o1, "value"); + QCOMPARE(notTimeDriven->totalDuration(), -1); + + QVariantAnimation *loopsForever = new QPropertyAnimation(&t_o2, "value"); + loopsForever->setIterationCount(-1); + QCOMPARE(loopsForever->totalDuration(), -1); + + QSequentialAnimationGroup group; + group.addAnimation(sequence); + group.addAnimation(notTimeDriven); + group.addAnimation(loopsForever); + group.start(); + group.pause(); // this allows the group to listen for the finish signal of its children + + // Current time = 1 + group.setCurrentTime(1); + QCOMPARE(group.state(), QAnimationGroup::Paused); + QCOMPARE(sequence->state(), QAnimationGroup::Paused); + QCOMPARE(a1_s_o1->state(), QAnimationGroup::Paused); + QCOMPARE(a1_s_o2->state(), QAnimationGroup::Stopped); + QCOMPARE(notTimeDriven->state(), QAnimationGroup::Stopped); + QCOMPARE(loopsForever->state(), QAnimationGroup::Stopped); + + QCOMPARE(group.currentTime(), 1); + QCOMPARE(sequence->currentTime(), 1); + QCOMPARE(a1_s_o1->currentTime(), 1); + QCOMPARE(a1_s_o2->currentTime(), 0); + QCOMPARE(notTimeDriven->currentTime(), 0); + QCOMPARE(loopsForever->currentTime(), 0); + + // Current time = 250 + group.setCurrentTime(250); + QCOMPARE(group.currentTime(), 250); + QCOMPARE(sequence->currentTime(), 250); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a1_s_o2->currentTime(), 0); + QCOMPARE(notTimeDriven->currentTime(), 0); + QCOMPARE(loopsForever->currentTime(), 0); + + // Current time = 500 + group.setCurrentTime(500); + QCOMPARE(group.currentTime(), 500); + QCOMPARE(sequence->currentTime(), 500); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a1_s_o2->currentTime(), 250); + QCOMPARE(notTimeDriven->currentTime(), 0); + QCOMPARE(loopsForever->currentTime(), 0); + QCOMPARE(group.currentAnimation(), notTimeDriven); + + // Current time = 505 + group.setCurrentTime(505); + QCOMPARE(group.currentTime(), 505); + QCOMPARE(sequence->currentTime(), 500); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a1_s_o2->currentTime(), 250); + QCOMPARE(notTimeDriven->currentTime(), 5); + QCOMPARE(loopsForever->currentTime(), 0); + QCOMPARE(group.currentAnimation(), notTimeDriven); + QCOMPARE(sequence->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroup::Stopped); + QCOMPARE(notTimeDriven->state(), QAnimationGroup::Paused); + QCOMPARE(loopsForever->state(), QAnimationGroup::Stopped); + + // Current time = 750 (end of notTimeDriven animation) + group.setCurrentTime(750); + QCOMPARE(group.currentTime(), 750); + QCOMPARE(sequence->currentTime(), 500); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a1_s_o2->currentTime(), 250); + QCOMPARE(notTimeDriven->currentTime(), 250); + QCOMPARE(loopsForever->currentTime(), 0); + QCOMPARE(group.currentAnimation(), loopsForever); + QCOMPARE(sequence->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroup::Stopped); + QCOMPARE(notTimeDriven->state(), QAnimationGroup::Stopped); + QCOMPARE(loopsForever->state(), QAnimationGroup::Paused); + + // Current time = 800 (as notTimeDriven was finished at 750, loopsforever should still run) + group.setCurrentTime(800); + QCOMPARE(group.currentTime(), 800); + QCOMPARE(group.currentAnimation(), loopsForever); + QCOMPARE(sequence->currentTime(), 500); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a1_s_o2->currentTime(), 250); + QCOMPARE(notTimeDriven->currentTime(), 250); + QCOMPARE(loopsForever->currentTime(), 50); + + loopsForever->stop(); // this should stop the group + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(sequence->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroup::Stopped); + QCOMPARE(notTimeDriven->state(), QAnimationGroup::Stopped); + QCOMPARE(loopsForever->state(), QAnimationGroup::Stopped); +} + +void tst_QSequentialAnimationGroup::seekingForwards() +{ + AnimationObject s_o1; + AnimationObject s_o2; + AnimationObject s_o3; + + // sequence operating on same object/property + QAnimationGroup *sequence = new QSequentialAnimationGroup(); + QVariantAnimation *a1_s_o1 = new QPropertyAnimation(&s_o1, "value"); + QVariantAnimation *a2_s_o1 = new QPropertyAnimation(&s_o1, "value"); + QVariantAnimation *a3_s_o1 = new QPropertyAnimation(&s_o1, "value"); + a2_s_o1->setIterationCount(3); + sequence->addAnimation(a1_s_o1); + sequence->addAnimation(a2_s_o1); + sequence->addAnimation(a3_s_o1); + + // sequence operating on different object/properties + QAnimationGroup *sequence2 = new QSequentialAnimationGroup(); + QVariantAnimation *a1_s_o2 = new QPropertyAnimation(&s_o2, "value"); + QVariantAnimation *a1_s_o3 = new QPropertyAnimation(&s_o3, "value"); + sequence2->addAnimation(a1_s_o2); + sequence2->addAnimation(a1_s_o3); + + QSequentialAnimationGroup group; + group.addAnimation(sequence); + group.addAnimation(sequence2); + + // Current time = 1 + group.setCurrentTime(1); + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(sequence->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(sequence2->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o3->state(), QAnimationGroup::Stopped); + + QCOMPARE(group.currentTime(), 1); + QCOMPARE(sequence->currentTime(), 1); + QCOMPARE(a1_s_o1->currentTime(), 1); + QCOMPARE(a2_s_o1->currentTime(), 0); + QCOMPARE(a3_s_o1->currentTime(), 0); + QCOMPARE(sequence2->currentTime(), 0); + QCOMPARE(a1_s_o2->currentTime(), 0); + QCOMPARE(a1_s_o3->currentTime(), 0); + + // Current time = 1500 + group.setCurrentTime(1500); + QCOMPARE(group.currentTime(), 1500); + QCOMPARE(sequence->currentTime(), 1250); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentIteration(), 2); + QCOMPARE(a3_s_o1->currentTime(), 250); + QCOMPARE(sequence2->currentTime(), 250); + QCOMPARE(a1_s_o2->currentTime(), 250); + QCOMPARE(a1_s_o3->currentTime(), 0); + + // this will restart the group + group.start(); + group.pause(); + QCOMPARE(group.state(), QAnimationGroup::Paused); + QCOMPARE(sequence->state(), QAnimationGroup::Paused); + QCOMPARE(a1_s_o1->state(), QAnimationGroup::Paused); + QCOMPARE(sequence2->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o3->state(), QAnimationGroup::Stopped); + + // Current time = 1750 + group.setCurrentTime(1750); + QCOMPARE(group.currentTime(), 1750); + QCOMPARE(sequence->currentTime(), 1250); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentIteration(), 2); + QCOMPARE(a3_s_o1->currentTime(), 250); + QCOMPARE(sequence2->currentTime(), 500); + QCOMPARE(a1_s_o2->currentTime(), 250); + QCOMPARE(a1_s_o3->currentTime(), 250); +} + +void tst_QSequentialAnimationGroup::seekingBackwards() +{ + AnimationObject s_o1; + AnimationObject s_o2; + AnimationObject s_o3; + + // sequence operating on same object/property + QAnimationGroup *sequence = new QSequentialAnimationGroup(); + QVariantAnimation *a1_s_o1 = new QPropertyAnimation(&s_o1, "value"); + QVariantAnimation *a2_s_o1 = new QPropertyAnimation(&s_o1, "value"); + QVariantAnimation *a3_s_o1 = new QPropertyAnimation(&s_o1, "value"); + a2_s_o1->setIterationCount(3); + sequence->addAnimation(a1_s_o1); + sequence->addAnimation(a2_s_o1); + sequence->addAnimation(a3_s_o1); + + // sequence operating on different object/properties + QAnimationGroup *sequence2 = new QSequentialAnimationGroup(); + QVariantAnimation *a1_s_o2 = new QPropertyAnimation(&s_o2, "value"); + QVariantAnimation *a1_s_o3 = new QPropertyAnimation(&s_o3, "value"); + sequence2->addAnimation(a1_s_o2); + sequence2->addAnimation(a1_s_o3); + + QSequentialAnimationGroup group; + group.addAnimation(sequence); + group.addAnimation(sequence2); + + group.start(); + + // Current time = 1600 + group.setCurrentTime(1600); + QCOMPARE(group.currentTime(), 1600); + QCOMPARE(sequence->currentTime(), 1250); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentIteration(), 2); + QCOMPARE(a3_s_o1->currentTime(), 250); + QCOMPARE(sequence2->currentTime(), 350); + QCOMPARE(a1_s_o2->currentTime(), 250); + QCOMPARE(a1_s_o3->currentTime(), 100); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(sequence->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(sequence2->state(), QAnimationGroup::Running); + QCOMPARE(a1_s_o2->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o3->state(), QAnimationGroup::Running); + + // Seeking backwards, current time = 1 + group.setCurrentTime(1); + QCOMPARE(group.currentTime(), 1); + QCOMPARE(sequence->currentTime(), 1); + QCOMPARE(a1_s_o1->currentTime(), 1); + + QEXPECT_FAIL("", "rewinding in nested groups is considered as a restart from the children," + "hence they don't reset from their current animation", Continue); + QCOMPARE(a2_s_o1->currentTime(), 0); + QEXPECT_FAIL("", "rewinding in nested groups is considered as a restart from the children," + "hence they don't reset from their current animation", Continue); + QCOMPARE(a2_s_o1->currentIteration(), 0); + QEXPECT_FAIL("", "rewinding in nested groups is considered as a restart from the children," + "hence they don't reset from their current animation", Continue); + QCOMPARE(a3_s_o1->currentTime(), 0); + QCOMPARE(sequence2->currentTime(), 0); + QCOMPARE(a1_s_o2->currentTime(), 0); + QCOMPARE(a1_s_o3->currentTime(), 0); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(sequence->state(), QAnimationGroup::Running); + QCOMPARE(a1_s_o1->state(), QAnimationGroup::Running); + QCOMPARE(sequence2->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o3->state(), QAnimationGroup::Stopped); + + // Current time = 2000 + group.setCurrentTime(2000); + QCOMPARE(group.currentTime(), 1750); + QCOMPARE(sequence->currentTime(), 1250); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentIteration(), 2); + QCOMPARE(a3_s_o1->currentTime(), 250); + QCOMPARE(sequence2->currentTime(), 500); + QCOMPARE(a1_s_o2->currentTime(), 250); + QCOMPARE(a1_s_o3->currentTime(), 250); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(sequence->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(sequence2->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroup::Stopped); + QCOMPARE(a1_s_o3->state(), QAnimationGroup::Stopped); +} + +typedef QList<QAbstractAnimation::State> StateList; + +static bool compareStates(const QSignalSpy& spy, const StateList &expectedStates) +{ + bool equals = true; + for (int i = 0; i < qMax(expectedStates.count(), spy.count()); ++i) { + if (i >= spy.count() || i >= expectedStates.count()) { + equals = false; + break; + } + QList<QVariant> args = spy.at(i); + QAbstractAnimation::State st = expectedStates.at(i); + QAbstractAnimation::State actual = qVariantValue<QAbstractAnimation::State>(args.value(1)); + if (equals && actual != st) { + equals = false; + break; + } + } + if (!equals) { + const char *stateStrings[] = {"Stopped", "Paused", "Running"}; + QString e,a; + for (int i = 0; i < qMax(expectedStates.count(), spy.count()); ++i) { + if (i < expectedStates.count()) { + int exp = int(expectedStates.at(i)); + if (!e.isEmpty()) + e += QLatin1String(", "); + e += QLatin1String(stateStrings[exp]); + } + if (i < spy.count()) { + QList<QVariant> args = spy.at(i); + QAbstractAnimation::State actual = qVariantValue<QAbstractAnimation::State>(args.value(1)); + if (!a.isEmpty()) + a += QLatin1String(", "); + if (int(actual) >= 0 && int(actual) <= 2) { + a += QLatin1String(stateStrings[int(actual)]); + } else { + a += QLatin1String("NaN"); + } + } + + } + qDebug("\n" + "expected (count == %d): %s\n" + "actual (count == %d): %s\n", expectedStates.count(), qPrintable(e), spy.count(), qPrintable(a)); + } + return equals; +} + +void tst_QSequentialAnimationGroup::pauseAndResume() +{ + AnimationObject s_o1; + + // sequence operating on same object/property + QAnimationGroup *sequence = new QSequentialAnimationGroup(); + QVariantAnimation *a1_s_o1 = new QPropertyAnimation(&s_o1, "value"); + QVariantAnimation *a2_s_o1 = new QPropertyAnimation(&s_o1, "value"); + QVariantAnimation *a3_s_o1 = new QPropertyAnimation(&s_o1, "value"); + a2_s_o1->setIterationCount(2); + sequence->addAnimation(a1_s_o1); + sequence->addAnimation(a2_s_o1); + sequence->addAnimation(a3_s_o1); + sequence->setIterationCount(2); + + QSignalSpy a1StateChangedSpy(a1_s_o1, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy seqStateChangedSpy(sequence, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + + QSequentialAnimationGroup group; + group.addAnimation(sequence); + + group.start(); + group.pause(); + + // Current time = 1751 + group.setCurrentTime(1751); + QCOMPARE(group.currentTime(), 1751); + QCOMPARE(sequence->currentTime(), 751); + QCOMPARE(sequence->currentIteration(), 1); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentIteration(), 1); + QCOMPARE(a3_s_o1->currentIteration(), 0); + QCOMPARE(a3_s_o1->currentTime(), 1); + + QCOMPARE(group.state(), QAnimationGroup::Paused); + QCOMPARE(sequence->state(), QAnimationGroup::Paused); + QCOMPARE(a1_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(a2_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(a3_s_o1->state(), QAnimationGroup::Paused); + + QCOMPARE(a1StateChangedSpy.count(), 5); // Running,Paused,Stopped,Running,Stopped + QCOMPARE(seqStateChangedSpy.count(), 2); // Running,Paused + + QVERIFY(compareStates(a1StateChangedSpy, (StateList() << QAbstractAnimation::Running + << QAbstractAnimation::Paused + << QAbstractAnimation::Stopped + << QAbstractAnimation::Running + << QAbstractAnimation::Stopped))); + + QCOMPARE(qVariantValue<QAbstractAnimation::State>(a1StateChangedSpy.at(0).at(1)), + QAnimationGroup::Running); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(a1StateChangedSpy.at(1).at(1)), + QAnimationGroup::Paused); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(a1StateChangedSpy.at(2).at(1)), + QAnimationGroup::Stopped); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(a1StateChangedSpy.at(3).at(1)), + QAnimationGroup::Running); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(a1StateChangedSpy.at(4).at(1)), + QAnimationGroup::Stopped); + + QCOMPARE(qVariantValue<QAbstractAnimation::State>(seqStateChangedSpy.at(0).at(1)), + QAnimationGroup::Running); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(seqStateChangedSpy.at(1).at(1)), + QAnimationGroup::Paused); + + group.resume(); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(sequence->state(), QAnimationGroup::Running); + QCOMPARE(a1_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(a2_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(a3_s_o1->state(), QAnimationGroup::Running); + + QVERIFY(group.currentTime() >= 1751); + QVERIFY(sequence->currentTime() >= 751); + QCOMPARE(sequence->currentIteration(), 1); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentIteration(), 1); + QCOMPARE(a3_s_o1->currentIteration(), 0); + QVERIFY(a3_s_o1->currentTime() >= 1); + + QCOMPARE(seqStateChangedSpy.count(), 3); // Running,Paused,Running + QCOMPARE(qVariantValue<QAbstractAnimation::State>(seqStateChangedSpy.at(2).at(1)), + QAnimationGroup::Running); + + group.pause(); + + QCOMPARE(group.state(), QAnimationGroup::Paused); + QCOMPARE(sequence->state(), QAnimationGroup::Paused); + QCOMPARE(a1_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(a2_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(a3_s_o1->state(), QAnimationGroup::Paused); + + QVERIFY(group.currentTime() >= 1751); + QVERIFY(sequence->currentTime() >= 751); + QCOMPARE(sequence->currentIteration(), 1); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentIteration(), 1); + QCOMPARE(a3_s_o1->currentIteration(), 0); + QVERIFY(a3_s_o1->currentTime() >= 1); + + QCOMPARE(seqStateChangedSpy.count(), 4); // Running,Paused,Running,Paused + QCOMPARE(qVariantValue<QAbstractAnimation::State>(seqStateChangedSpy.at(3).at(1)), + QAnimationGroup::Paused); + + group.stop(); + + QCOMPARE(seqStateChangedSpy.count(), 5); // Running,Paused,Running,Paused,Stopped + QCOMPARE(qVariantValue<QAbstractAnimation::State>(seqStateChangedSpy.at(4).at(1)), + QAnimationGroup::Stopped); +} + +void tst_QSequentialAnimationGroup::restart() +{ + AnimationObject s_o1; + + // sequence operating on same object/property + QAnimationGroup *sequence = new QSequentialAnimationGroup(); + QSignalSpy seqCurrentAnimChangedSpy(sequence, SIGNAL(currentAnimationChanged(QAbstractAnimation*))); + QSignalSpy seqStateChangedSpy(sequence, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + + QVariantAnimation *anims[3]; + QSignalSpy *animsStateChanged[3]; + + for (int i = 0; i < 3; i++) { + anims[i] = new QPropertyAnimation(&s_o1, "value"); + anims[i]->setDuration(100); + animsStateChanged[i] = new QSignalSpy(anims[i], SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + } + + anims[1]->setIterationCount(2); + sequence->addAnimation(anims[0]); + sequence->addAnimation(anims[1]); + sequence->addAnimation(anims[2]); + sequence->setIterationCount(2); + + QSequentialAnimationGroup group; + group.addAnimation(sequence); + + group.start(); + + QTest::qWait(500); + + QCOMPARE(group.state(), QAnimationGroup::Running); + + QTest::qWait(300); + QTRY_COMPARE(group.state(), QAnimationGroup::Stopped); + + for (int i = 0; i < 3; i++) { + QCOMPARE(animsStateChanged[i]->count(), 4); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(animsStateChanged[i]->at(0).at(1)), + QAnimationGroup::Running); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(animsStateChanged[i]->at(1).at(1)), + QAnimationGroup::Stopped); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(animsStateChanged[i]->at(2).at(1)), + QAnimationGroup::Running); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(animsStateChanged[i]->at(3).at(1)), + QAnimationGroup::Stopped); + } + + QCOMPARE(seqStateChangedSpy.count(), 2); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(seqStateChangedSpy.at(0).at(1)), + QAnimationGroup::Running); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(seqStateChangedSpy.at(1).at(1)), + QAnimationGroup::Stopped); + + QCOMPARE(seqCurrentAnimChangedSpy.count(), 6); + for(int i=0; i<seqCurrentAnimChangedSpy.count(); i++) + QCOMPARE(anims[i%3], qVariantValue<QAbstractAnimation*>(seqCurrentAnimChangedSpy.at(i).at(0))); + + group.start(); + + QCOMPARE(animsStateChanged[0]->count(), 5); + QCOMPARE(animsStateChanged[1]->count(), 4); + QCOMPARE(animsStateChanged[2]->count(), 4); + QCOMPARE(seqStateChangedSpy.count(), 3); +} + +void tst_QSequentialAnimationGroup::looping() +{ + AnimationObject s_o1; + AnimationObject s_o2; + AnimationObject s_o3; + + // sequence operating on same object/property + QSequentialAnimationGroup *sequence = new QSequentialAnimationGroup(); + QVariantAnimation *a1_s_o1 = new QPropertyAnimation(&s_o1, "value"); + QVariantAnimation *a2_s_o1 = new QPropertyAnimation(&s_o1, "value"); + QVariantAnimation *a3_s_o1 = new QPropertyAnimation(&s_o1, "value"); + + QSignalSpy a1Spy(a1_s_o1, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy a2Spy(a2_s_o1, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy a3Spy(a3_s_o1, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy seqSpy(sequence, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + + a2_s_o1->setIterationCount(2); + sequence->addAnimation(a1_s_o1); + sequence->addAnimation(a2_s_o1); + sequence->addAnimation(a3_s_o1); + sequence->setIterationCount(2); + + QSequentialAnimationGroup group; + QSignalSpy groupSpy(&group, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + + group.addAnimation(sequence); + group.setIterationCount(2); + + group.start(); + group.pause(); + + // Current time = 1750 + group.setCurrentTime(1750); + QCOMPARE(group.currentTime(), 1750); + QCOMPARE(sequence->currentTime(), 750); + QCOMPARE(sequence->currentIteration(), 1); + QCOMPARE(a1_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentIteration(), 1); + // this animation is at the beginning because it is the current one inside sequence + QCOMPARE(a3_s_o1->currentIteration(), 0); + QCOMPARE(a3_s_o1->currentTime(), 0); + QCOMPARE(sequence->currentAnimation(), a3_s_o1); + + QCOMPARE(group.state(), QAnimationGroup::Paused); + QCOMPARE(sequence->state(), QAnimationGroup::Paused); + QCOMPARE(a1_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(a2_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(a3_s_o1->state(), QAnimationGroup::Paused); + + QCOMPARE(a1Spy.count(), 5); // Running,Paused,Stopped,Running,Stopped + QVERIFY(compareStates(a1Spy, (StateList() << QAbstractAnimation::Running + << QAbstractAnimation::Paused + << QAbstractAnimation::Stopped + << QAbstractAnimation::Running + << QAbstractAnimation::Stopped))); + + QCOMPARE(a2Spy.count(), 4); // Running,Stopped,Running,Stopped + QVERIFY(compareStates(a3Spy, (StateList() << QAbstractAnimation::Running + << QAbstractAnimation::Stopped + << QAbstractAnimation::Running + << QAbstractAnimation::Paused))); + + QCOMPARE(seqSpy.count(), 2); // Running,Paused + QCOMPARE(groupSpy.count(), 2); // Running,Paused + + // Looping, current time = duration + 1 + group.setCurrentTime(group.duration() + 1); + QCOMPARE(group.currentTime(), 1); + QCOMPARE(group.currentIteration(), 1); + QCOMPARE(sequence->currentTime(), 1); + QCOMPARE(sequence->currentIteration(), 0); + QCOMPARE(a1_s_o1->currentTime(), 1); + QCOMPARE(a2_s_o1->currentTime(), 250); + QCOMPARE(a2_s_o1->currentIteration(), 1); + // this animation is at the end because it was run on the previous loop + QCOMPARE(a3_s_o1->currentIteration(), 0); + QCOMPARE(a3_s_o1->currentTime(), 250); + + QCOMPARE(group.state(), QAnimationGroup::Paused); + QCOMPARE(sequence->state(), QAnimationGroup::Paused); + QCOMPARE(a1_s_o1->state(), QAnimationGroup::Paused); + QCOMPARE(a2_s_o1->state(), QAnimationGroup::Stopped); + QCOMPARE(a3_s_o1->state(), QAnimationGroup::Stopped); + + QCOMPARE(a1Spy.count(), 7); // Running,Paused,Stopped,Running,Stopped,Running,Stopped + QCOMPARE(a2Spy.count(), 4); // Running, Stopped, Running, Stopped + QVERIFY(compareStates(a3Spy, (StateList() << QAbstractAnimation::Running + << QAbstractAnimation::Stopped + << QAbstractAnimation::Running + << QAbstractAnimation::Paused + << QAbstractAnimation::Stopped))); + QVERIFY(compareStates(seqSpy, (StateList() << QAbstractAnimation::Running + << QAbstractAnimation::Paused + << QAbstractAnimation::Stopped + << QAbstractAnimation::Running + << QAbstractAnimation::Paused))); + QCOMPARE(groupSpy.count(), 2); +} + +void tst_QSequentialAnimationGroup::startDelay() +{ + QSequentialAnimationGroup group; + group.addPause(250); + group.addPause(125); + QCOMPARE(group.totalDuration(), 375); + + QEventLoop loop; + QObject::connect(&group, SIGNAL(finished()), &loop, SLOT(quit())); + + QTime time; + time.start(); + group.start(); + loop.exec(); + + QVERIFY(time.elapsed() >= 375); + QVERIFY(time.elapsed() < 1000); +} + +void tst_QSequentialAnimationGroup::clearGroup() +{ + QSequentialAnimationGroup group; + + for (int i = 0; i < 10; ++i) { + QSequentialAnimationGroup *subGroup = new QSequentialAnimationGroup(&group); + group.addPause(100); + subGroup->addPause(10); + } + + QCOMPARE(group.animationCount(), 20); + + int count = group.animationCount(); + QPointer<QAbstractAnimation> *children = new QPointer<QAbstractAnimation>[count]; + for (int i = 0; i < count; ++i) { + QVERIFY(group.animationAt(i) != 0); + children[i] = group.animationAt(i); + } + + group.clearAnimations(); + QCOMPARE(group.animationCount(), 0); + QCOMPARE(group.currentTime(), 0); + for (int i = 0; i < count; ++i) + QCOMPARE(children[i], QPointer<QAbstractAnimation>()); + + delete[] children; +} + +void tst_QSequentialAnimationGroup::groupWithZeroDurationAnimations() +{ + QObject o; + QObject o2; + + o.setProperty("myProperty", 42); + o.setProperty("myOtherProperty", 13); + o2.setProperty("myProperty", 42); + o2.setProperty("myOtherProperty", 13); + + QSequentialAnimationGroup group; + + QVariantAnimation *a1 = new QPropertyAnimation(&o, "myProperty"); + a1->setDuration(0); + a1->setEndValue(43); + group.addAnimation(a1); + + //this should just run fine and change nothing + group.setCurrentTime(0); + QCOMPARE(group.currentAnimation(), a1); + + QVariantAnimation *a2 = new QPropertyAnimation(&o2, "myOtherProperty"); + a2->setDuration(500); + a2->setEndValue(31); + group.addAnimation(a2); + + QVariantAnimation *a3 = new QPropertyAnimation(&o, "myProperty"); + a3->setDuration(0); + a3->setEndValue(44); + group.addAnimation(a3); + + QVariantAnimation *a4 = new QPropertyAnimation(&o, "myOtherProperty"); + a4->setDuration(250); + a4->setEndValue(75); + group.addAnimation(a4); + + QVariantAnimation *a5 = new QPropertyAnimation(&o2, "myProperty"); + a5->setDuration(0); + a5->setEndValue(12); + group.addAnimation(a5); + + QCOMPARE(o.property("myProperty").toInt(), 42); + QCOMPARE(o.property("myOtherProperty").toInt(), 13); + QCOMPARE(o2.property("myProperty").toInt(), 42); + QCOMPARE(o2.property("myOtherProperty").toInt(), 13); + + + group.start(); + + QCOMPARE(o.property("myProperty").toInt(), 43); + QCOMPARE(o.property("myOtherProperty").toInt(), 13); + QCOMPARE(o2.property("myProperty").toInt(), 42); + QCOMPARE(o2.property("myOtherProperty").toInt(), 13); + + QTest::qWait(50); + + int o2val = o2.property("myOtherProperty").toInt(); + QVERIFY(o2val > 13); + QVERIFY(o2val < 31); + QCOMPARE(o.property("myProperty").toInt(), 43); + QCOMPARE(o.property("myOtherProperty").toInt(), 13); + + QTest::qWait(500); + + QCOMPARE(o.property("myProperty").toInt(), 44); + QCOMPARE(o2.property("myProperty").toInt(), 42); + QCOMPARE(o2.property("myOtherProperty").toInt(), 31); + QCOMPARE(a1->state(), QAnimationGroup::Stopped); + QCOMPARE(a2->state(), QAnimationGroup::Stopped); + QCOMPARE(a3->state(), QAnimationGroup::Stopped); + QCOMPARE(a4->state(), QAnimationGroup::Running); + QCOMPARE(a5->state(), QAnimationGroup::Stopped); + QCOMPARE(group.state(), QAnimationGroup::Running); + QTest::qWait(500); + + QTRY_COMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(o.property("myProperty").toInt(), 44); + QCOMPARE(o.property("myOtherProperty").toInt(), 75); + QCOMPARE(o2.property("myProperty").toInt(), 12); + QCOMPARE(o2.property("myOtherProperty").toInt(), 31); + QCOMPARE(a1->state(), QAnimationGroup::Stopped); + QCOMPARE(a2->state(), QAnimationGroup::Stopped); + QCOMPARE(a3->state(), QAnimationGroup::Stopped); + QCOMPARE(a4->state(), QAnimationGroup::Stopped); + QCOMPARE(a5->state(), QAnimationGroup::Stopped); +} + +void tst_QSequentialAnimationGroup::propagateGroupUpdateToChildren() +{ + // this test verifies if group state changes are updating its children correctly + QSequentialAnimationGroup group; + + QObject o; + o.setProperty("ole", 42); + QCOMPARE(o.property("ole").toInt(), 42); + + QPropertyAnimation anim1(&o, "ole"); + anim1.setEndValue(43); + anim1.setDuration(100); + QVERIFY(!anim1.currentValue().isValid()); + QCOMPARE(anim1.currentValue().toInt(), 0); + QCOMPARE(o.property("ole").toInt(), 42); + + TestAnimation anim2; + anim2.setStartValue(0); + anim2.setEndValue(100); + anim2.setDuration(200); + + QVERIFY(anim2.currentValue().isValid()); + QCOMPARE(anim2.currentValue().toInt(), 0); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Stopped); + QCOMPARE(anim2.state(), QAnimationGroup::Stopped); + + group.addAnimation(&anim1); + group.addAnimation(&anim2); + + group.start(); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(anim1.state(), QAnimationGroup::Running); + QCOMPARE(anim2.state(), QAnimationGroup::Stopped); + + group.pause(); + + QCOMPARE(group.state(), QAnimationGroup::Paused); + QCOMPARE(anim1.state(), QAnimationGroup::Paused); + QCOMPARE(anim2.state(), QAnimationGroup::Stopped); + + group.stop(); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Stopped); + QCOMPARE(anim2.state(), QAnimationGroup::Stopped); +} + +void tst_QSequentialAnimationGroup::updateChildrenWithRunningGroup() +{ + // assert that its possible to modify a child's state directly while their group is running + QSequentialAnimationGroup group; + + TestAnimation anim; + anim.setStartValue(0); + anim.setEndValue(100); + anim.setDuration(200); + + QSignalSpy groupStateChangedSpy(&group, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy childStateChangedSpy(&anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + + QCOMPARE(groupStateChangedSpy.count(), 0); + QCOMPARE(childStateChangedSpy.count(), 0); + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim.state(), QAnimationGroup::Stopped); + + group.addAnimation(&anim); + + group.start(); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(anim.state(), QAnimationGroup::Running); + + QCOMPARE(groupStateChangedSpy.count(), 1); + QCOMPARE(childStateChangedSpy.count(), 1); + + QCOMPARE(qVariantValue<QAbstractAnimation::State>(groupStateChangedSpy.at(0).at(1)), + QAnimationGroup::Running); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(childStateChangedSpy.at(0).at(1)), + QAnimationGroup::Running); + + // starting directly a running child will not have any effect + anim.start(); + + QCOMPARE(groupStateChangedSpy.count(), 1); + QCOMPARE(childStateChangedSpy.count(), 1); + + anim.pause(); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(anim.state(), QAnimationGroup::Paused); + + // in the animation stops directly, the group will still be running + anim.stop(); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(anim.state(), QAnimationGroup::Stopped); +} + +void tst_QSequentialAnimationGroup::deleteChildrenWithRunningGroup() +{ + // test if children can be activated when their group is stopped + QSequentialAnimationGroup group; + + QVariantAnimation *anim1 = new TestAnimation; + anim1->setStartValue(0); + anim1->setEndValue(100); + anim1->setDuration(200); + group.addAnimation(anim1); + + QCOMPARE(group.duration(), anim1->duration()); + + group.start(); + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(anim1->state(), QAnimationGroup::Running); + + QTest::qWait(50); + QVERIFY(group.currentTime() > 0); + + delete anim1; + QCOMPARE(group.animationCount(), 0); + QCOMPARE(group.duration(), 0); + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(group.currentTime(), 0); //that's the invariant +} + +void tst_QSequentialAnimationGroup::startChildrenWithStoppedGroup() +{ + // test if children can be activated when their group is stopped + QSequentialAnimationGroup group; + + TestAnimation anim1; + anim1.setStartValue(0); + anim1.setEndValue(100); + anim1.setDuration(200); + + TestAnimation anim2; + anim2.setStartValue(0); + anim2.setEndValue(100); + anim2.setDuration(200); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Stopped); + QCOMPARE(anim2.state(), QAnimationGroup::Stopped); + + group.addAnimation(&anim1); + group.addAnimation(&anim2); + + group.stop(); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Stopped); + QCOMPARE(anim2.state(), QAnimationGroup::Stopped); + + anim1.start(); + anim2.start(); + anim2.pause(); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Running); + QCOMPARE(anim2.state(), QAnimationGroup::Paused); +} + +void tst_QSequentialAnimationGroup::stopGroupWithRunningChild() +{ + // children that started independently will not be affected by a group stop + QSequentialAnimationGroup group; + + TestAnimation anim1; + anim1.setStartValue(0); + anim1.setEndValue(100); + anim1.setDuration(200); + + TestAnimation anim2; + anim2.setStartValue(0); + anim2.setEndValue(100); + anim2.setDuration(200); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Stopped); + QCOMPARE(anim2.state(), QAnimationGroup::Stopped); + + group.addAnimation(&anim1); + group.addAnimation(&anim2); + + anim1.start(); + anim2.start(); + anim2.pause(); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Running); + QCOMPARE(anim2.state(), QAnimationGroup::Paused); + + group.stop(); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Running); + QCOMPARE(anim2.state(), QAnimationGroup::Paused); + + anim1.stop(); + anim2.stop(); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1.state(), QAnimationGroup::Stopped); + QCOMPARE(anim2.state(), QAnimationGroup::Stopped); +} + +void tst_QSequentialAnimationGroup::startGroupWithRunningChild() +{ + // as the group has precedence over its children, starting a group will restart all the children + QSequentialAnimationGroup group; + + TestAnimation *anim1 = new TestAnimation(); + anim1->setStartValue(0); + anim1->setEndValue(100); + anim1->setDuration(200); + + TestAnimation *anim2 = new TestAnimation(); + anim2->setStartValue(0); + anim2->setEndValue(100); + anim2->setDuration(200); + + QSignalSpy stateChangedSpy1(anim1, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy stateChangedSpy2(anim2, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + + QCOMPARE(stateChangedSpy1.count(), 0); + QCOMPARE(stateChangedSpy2.count(), 0); + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1->state(), QAnimationGroup::Stopped); + QCOMPARE(anim2->state(), QAnimationGroup::Stopped); + + group.addAnimation(anim1); + group.addAnimation(anim2); + + anim1->start(); + anim2->start(); + anim2->pause(); + + QVERIFY(compareStates(stateChangedSpy1, (StateList() << QAbstractAnimation::Running))); + + QVERIFY(compareStates(stateChangedSpy2, (StateList() << QAbstractAnimation::Running + << QAbstractAnimation::Paused))); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1->state(), QAnimationGroup::Running); + QCOMPARE(anim2->state(), QAnimationGroup::Paused); + + group.start(); + + QVERIFY(compareStates(stateChangedSpy1, (StateList() << QAbstractAnimation::Running + << QAbstractAnimation::Stopped + << QAbstractAnimation::Running))); + QVERIFY(compareStates(stateChangedSpy2, (StateList() << QAbstractAnimation::Running + << QAbstractAnimation::Paused))); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(anim1->state(), QAnimationGroup::Running); + QCOMPARE(anim2->state(), QAnimationGroup::Paused); + + QTest::qWait(300); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(anim1->state(), QAnimationGroup::Stopped); + QCOMPARE(anim2->state(), QAnimationGroup::Running); + + QCOMPARE(stateChangedSpy2.count(), 4); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(stateChangedSpy2.at(2).at(1)), + QAnimationGroup::Stopped); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(stateChangedSpy2.at(3).at(1)), + QAnimationGroup::Running); + + group.stop(); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(anim1->state(), QAnimationGroup::Stopped); + QCOMPARE(anim2->state(), QAnimationGroup::Stopped); +} + +void tst_QSequentialAnimationGroup::zeroDurationAnimation() +{ + QSequentialAnimationGroup group; + + TestAnimation *anim1 = new TestAnimation(); + anim1->setStartValue(0); + anim1->setEndValue(100); + anim1->setDuration(0); + + TestAnimation *anim2 = new TestAnimation(); + anim2->setStartValue(0); + anim2->setEndValue(100); + anim2->setDuration(100); + + AnimationObject o1; + QPropertyAnimation *anim3 = new QPropertyAnimation(&o1, "value"); + anim3->setEndValue(100); + anim3->setDuration(0); + + QSignalSpy stateChangedSpy(anim1, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + + group.addAnimation(anim1); + group.addAnimation(anim2); + group.addAnimation(anim3); + group.setIterationCount(2); + group.start(); + + QCOMPARE(stateChangedSpy.count(), 2); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(stateChangedSpy.at(0).at(1)), + QAnimationGroup::Running); + QCOMPARE(qVariantValue<QAbstractAnimation::State>(stateChangedSpy.at(1).at(1)), + QAnimationGroup::Stopped); + + QCOMPARE(anim1->state(), QAnimationGroup::Stopped); + QCOMPARE(anim2->state(), QAnimationGroup::Running); + QCOMPARE(group.state(), QAnimationGroup::Running); + + //now let's try to seek to the next loop + group.setCurrentTime(group.duration() + 1); + QCOMPARE(anim1->state(), QAnimationGroup::Stopped); + QCOMPARE(anim2->state(), QAnimationGroup::Running); + QCOMPARE(anim3->state(), QAnimationGroup::Stopped); + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(o1.value(), 100); //anim3 should have been run +} + +void tst_QSequentialAnimationGroup::stopUncontrolledAnimations() +{ + QSequentialAnimationGroup group; + + AnimationObject o1; + UncontrolledAnimation notTimeDriven(&o1, "value"); + QCOMPARE(notTimeDriven.totalDuration(), -1); + + TestAnimation loopsForever; + loopsForever.setStartValue(0); + loopsForever.setEndValue(100); + loopsForever.setDuration(100); + loopsForever.setIterationCount(-1); + + group.addAnimation(¬TimeDriven); + group.addAnimation(&loopsForever); + + group.start(); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(notTimeDriven.state(), QAnimationGroup::Running); + QCOMPARE(loopsForever.state(), QAnimationGroup::Stopped); + + notTimeDriven.stop(); + + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(notTimeDriven.state(), QAnimationGroup::Stopped); + QCOMPARE(loopsForever.state(), QAnimationGroup::Running); + + loopsForever.stop(); + + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(notTimeDriven.state(), QAnimationGroup::Stopped); + QCOMPARE(loopsForever.state(), QAnimationGroup::Stopped); +} + +void tst_QSequentialAnimationGroup::finishWithUncontrolledAnimation() +{ + AnimationObject o1; + + //1st case: + //first we test a group with one uncontrolled animation + QSequentialAnimationGroup group; + UncontrolledAnimation notTimeDriven(&o1, "value", &group); + QSignalSpy spy(&group, SIGNAL(finished())); + + group.start(); + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(notTimeDriven.state(), QAnimationGroup::Running); + QCOMPARE(group.currentTime(), 0); + QCOMPARE(notTimeDriven.currentTime(), 0); + + QTest::qWait(300); //wait for the end of notTimeDriven + QCOMPARE(notTimeDriven.state(), QAnimationGroup::Stopped); + const int actualDuration = notTimeDriven.currentTime(); + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(group.currentTime(), actualDuration); + QCOMPARE(spy.count(), 1); + + //2nd case: + // lets make sure the seeking will work again + spy.clear(); + QPropertyAnimation anim(&group); + QSignalSpy animStateChangedSpy(&anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + + group.setCurrentTime(300); + QCOMPARE(group.state(), QAnimationGroup::Stopped); + QCOMPARE(notTimeDriven.currentTime(), actualDuration); + QCOMPARE(group.currentAnimation(), &anim); + + //3rd case: + //now let's add a perfectly defined animation at the end + QCOMPARE(animStateChangedSpy.count(), 0); + group.start(); + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(notTimeDriven.state(), QAnimationGroup::Running); + QCOMPARE(group.currentTime(), 0); + QCOMPARE(notTimeDriven.currentTime(), 0); + + QCOMPARE(animStateChangedSpy.count(), 0); + + QTest::qWait(300); //wait for the end of notTimeDriven + QCOMPARE(notTimeDriven.state(), QAnimationGroup::Stopped); + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(anim.state(), QAnimationGroup::Running); + QCOMPARE(group.currentAnimation(), &anim); + QCOMPARE(animStateChangedSpy.count(), 1); + QTest::qWait(300); //wait for the end of anim + + QCOMPARE(anim.state(), QAnimationGroup::Stopped); + QCOMPARE(anim.currentTime(), anim.duration()); + + //we should simply be at the end + QCOMPARE(spy.count(), 1); + QCOMPARE(animStateChangedSpy.count(), 2); + QCOMPARE(group.currentTime(), notTimeDriven.currentTime() + anim.currentTime()); +} + +void tst_QSequentialAnimationGroup::addRemoveAnimation() +{ + //this test is specific to the sequential animation group + QSequentialAnimationGroup group; + + QCOMPARE(group.duration(), 0); + QCOMPARE(group.currentTime(), 0); + QVariantAnimation *anim1 = new QPropertyAnimation; + group.addAnimation(anim1); + QCOMPARE(group.duration(), 250); + QCOMPARE(group.currentTime(), 0); + QCOMPARE(group.currentAnimation(), anim1); + + //let's append an animation + QVariantAnimation *anim2 = new QPropertyAnimation; + group.addAnimation(anim2); + QCOMPARE(group.duration(), 500); + QCOMPARE(group.currentTime(), 0); + QCOMPARE(group.currentAnimation(), anim1); + + //let's prepend an animation + QVariantAnimation *anim0 = new QPropertyAnimation; + group.insertAnimationAt(0, anim0); + QCOMPARE(group.duration(), 750); + QCOMPARE(group.currentTime(), 0); + QCOMPARE(group.currentAnimation(), anim0); //anim0 has become the new currentAnimation + + group.setCurrentTime(300); //anim0 | anim1 | anim2 + QCOMPARE(group.currentTime(), 300); + QCOMPARE(group.currentAnimation(), anim1); + QCOMPARE(anim1->currentTime(), 50); + + group.removeAnimation(anim0); //anim1 | anim2 + QCOMPARE(group.currentTime(), 50); + QCOMPARE(group.currentAnimation(), anim1); + QCOMPARE(anim1->currentTime(), 50); + + group.setCurrentTime(0); + group.insertAnimationAt(0, anim0); //anim0 | anim1 | anim2 + group.setCurrentTime(300); + QCOMPARE(group.currentTime(), 300); + QCOMPARE(group.currentAnimation(), anim1); + QCOMPARE(anim1->currentTime(), 50); + + group.removeAnimation(anim1); //anim0 | anim2 + QCOMPARE(group.currentTime(), 250); + QCOMPARE(group.currentAnimation(), anim2); + QCOMPARE(anim0->currentTime(), 250); +} + +void tst_QSequentialAnimationGroup::currentAnimation() +{ + QSequentialAnimationGroup group; + QVERIFY(group.currentAnimation() == 0); + + QPropertyAnimation anim; + anim.setDuration(0); + group.addAnimation(&anim); + QCOMPARE(group.currentAnimation(), &anim); +} + +void tst_QSequentialAnimationGroup::currentAnimationWithZeroDuration() +{ + QSequentialAnimationGroup group; + QVERIFY(group.currentAnimation() == 0); + + QPropertyAnimation zero1; + zero1.setDuration(0); + QPropertyAnimation zero2; + zero2.setDuration(0); + + QPropertyAnimation anim; + + QPropertyAnimation zero3; + zero3.setDuration(0); + QPropertyAnimation zero4; + zero4.setDuration(0); + + + group.addAnimation(&zero1); + group.addAnimation(&zero2); + group.addAnimation(&anim); + group.addAnimation(&zero3); + group.addAnimation(&zero4); + + QCOMPARE(group.currentAnimation(), &zero1); + + group.setCurrentTime(0); + QCOMPARE(group.currentAnimation(), &anim); + + group.setCurrentTime(group.duration()); + QCOMPARE(group.currentAnimation(), &zero4); + + group.setDirection(QAbstractAnimation::Backward); + + group.setCurrentTime(0); + QCOMPARE(group.currentAnimation(), &zero1); + + group.setCurrentTime(group.duration()); + QCOMPARE(group.currentAnimation(), &anim); +} + +void tst_QSequentialAnimationGroup::insertAnimation() +{ + QSequentialAnimationGroup group; + group.setIterationCount(2); + QPropertyAnimation *anim = new QPropertyAnimation(&group); + QCOMPARE(group.duration(), anim->duration()); + group.setCurrentTime(300); + QCOMPARE(group.currentIteration(), 1); + + //this will crash if the sequential group calls duration on the created animation + new QPropertyAnimation(&group); +} + + +class SequentialAnimationGroup : public QSequentialAnimationGroup +{ + Q_OBJECT +public slots: + void clearAnimations() + { + QSequentialAnimationGroup::clearAnimations(); + } + + void refill() + { + stop(); + clearAnimations(); + new QPropertyAnimation(this); + start(); + } + +}; + + +void tst_QSequentialAnimationGroup::clearAnimations() +{ + SequentialAnimationGroup group; + QPointer<QAbstractAnimation> anim1 = new QPropertyAnimation(&group); + group.connect(anim1, SIGNAL(finished()), SLOT(clearAnimations())); + new QPropertyAnimation(&group); + QCOMPARE(group.animationCount(), 2); + + group.start(); + QTest::qWait(anim1->duration() + 100); + QCOMPARE(group.animationCount(), 0); + QCOMPARE(group.state(), QAbstractAnimation::Stopped); + QCOMPARE(group.currentTime(), 0); + + anim1 = new QPropertyAnimation(&group); + group.connect(anim1, SIGNAL(finished()), SLOT(refill())); + group.start(); + QTest::qWait(anim1->duration() + 100); + QVERIFY(anim1 == 0); //anim1 should have been deleted + QCOMPARE(group.state(), QAbstractAnimation::Running); +} + +QTEST_MAIN(tst_QSequentialAnimationGroup) +#include "tst_qsequentialanimationgroup.moc" diff --git a/tests/auto/qstate/qstate.pro b/tests/auto/qstate/qstate.pro new file mode 100644 index 0000000..9131fa8 --- /dev/null +++ b/tests/auto/qstate/qstate.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT = core +SOURCES += tst_qstate.cpp + + diff --git a/tests/auto/qstate/tst_qstate.cpp b/tests/auto/qstate/tst_qstate.cpp new file mode 100644 index 0000000..c4a0050 --- /dev/null +++ b/tests/auto/qstate/tst_qstate.cpp @@ -0,0 +1,372 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include "qstate.h" +#include "qstatemachine.h" +#include "qtransition.h" +#include "qsignaltransition.h" +#include "qstateaction.h" + +// Will try to wait for the condition while allowing event processing +#define QTRY_COMPARE(__expr, __expected) \ + do { \ + const int __step = 50; \ + const int __timeout = 5000; \ + if ((__expr) != (__expected)) { \ + QTest::qWait(0); \ + } \ + for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \ + QTest::qWait(__step); \ + } \ + QCOMPARE(__expr, __expected); \ + } while(0) + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QState : public QObject +{ + Q_OBJECT + +public: + tst_QState(); + virtual ~tst_QState(); + +private slots: +#if 0 + void test(); +#endif + void setPropertyOnEntry(); + void setPropertyOnEntryTwice(); + void setPropertyOnExit(); + void setPropertyOnExitTwice(); + void historyInitialState(); + void addEntryAction(); +}; + +tst_QState::tst_QState() +{ +} + +tst_QState::~tst_QState() +{ +} + +#if 0 +void tst_QState::test() +{ + QStateMachine machine; + QState *s1 = new QState(machine.rootState()); + + QCOMPARE(s1->machine(), &machine); + QCOMPARE(s1->parentState(), machine.rootState()); + QCOMPARE(s1->initialState(), (QState*)0); + QVERIFY(s1->childStates().isEmpty()); + QVERIFY(s1->transitions().isEmpty()); + + QCOMPARE(s1->isFinal(), false); + s1->setFinal(true); + QCOMPARE(s1->isFinal(), true); + s1->setFinal(false); + QCOMPARE(s1->isFinal(), false); + + QCOMPARE(s1->isParallel(), false); + s1->setParallel(true); + QCOMPARE(s1->isParallel(), true); + s1->setParallel(false); + QCOMPARE(s1->isParallel(), false); + + QCOMPARE(s1->isAtomic(), true); + QCOMPARE(s1->isCompound(), false); + QCOMPARE(s1->isComplex(), false); + + QState *s11 = new QState(s1); + QCOMPARE(s11->parentState(), s1); + QCOMPARE(s11->isAtomic(), true); + QCOMPARE(s11->isCompound(), false); + QCOMPARE(s11->isComplex(), false); + QCOMPARE(s11->machine(), s1->machine()); + QVERIFY(s11->isDescendantOf(s1)); + + QCOMPARE(s1->initialState(), (QState*)0); + QCOMPARE(s1->childStates().size(), 1); + QCOMPARE(s1->childStates().at(0), s11); + + QCOMPARE(s1->isAtomic(), false); + QCOMPARE(s1->isCompound(), true); + QCOMPARE(s1->isComplex(), true); + + s1->setParallel(true); + QCOMPARE(s1->isAtomic(), false); + QCOMPARE(s1->isCompound(), false); + QCOMPARE(s1->isComplex(), true); + + QState *s12 = new QState(s1); + QCOMPARE(s12->parentState(), s1); + QCOMPARE(s12->isAtomic(), true); + QCOMPARE(s12->isCompound(), false); + QCOMPARE(s12->isComplex(), false); + QCOMPARE(s12->machine(), s1->machine()); + QVERIFY(s12->isDescendantOf(s1)); + QVERIFY(!s12->isDescendantOf(s11)); + + QCOMPARE(s1->initialState(), (QState*)0); + QCOMPARE(s1->childStates().size(), 2); + QCOMPARE(s1->childStates().at(0), s11); + QCOMPARE(s1->childStates().at(1), s12); + + QCOMPARE(s1->isAtomic(), false); + QCOMPARE(s1->isCompound(), false); + QCOMPARE(s1->isComplex(), true); + + s1->setParallel(false); + QCOMPARE(s1->isAtomic(), false); + QCOMPARE(s1->isCompound(), true); + QCOMPARE(s1->isComplex(), true); + + s1->setInitialState(s11); + QCOMPARE(s1->initialState(), s11); + + s1->setInitialState(0); + QCOMPARE(s1->initialState(), (QState*)0); + + s1->setInitialState(s12); + QCOMPARE(s1->initialState(), s12); + + QState *s13 = new QState(); + s1->setInitialState(s13); + QCOMPARE(s13->parentState(), s1); + QCOMPARE(s1->childStates().size(), 3); + QCOMPARE(s1->childStates().at(0), s11); + QCOMPARE(s1->childStates().at(1), s12); + QCOMPARE(s1->childStates().at(2), s13); + QVERIFY(s13->isDescendantOf(s1)); + + QVERIFY(s12->childStates().isEmpty()); + + QState *s121 = new QState(s12); + QCOMPARE(s121->parentState(), s12); + QCOMPARE(s121->isAtomic(), true); + QCOMPARE(s121->isCompound(), false); + QCOMPARE(s121->isComplex(), false); + QCOMPARE(s121->machine(), s12->machine()); + QVERIFY(s121->isDescendantOf(s12)); + QVERIFY(s121->isDescendantOf(s1)); + QVERIFY(!s121->isDescendantOf(s11)); + + QCOMPARE(s12->childStates().size(), 1); + QCOMPARE(s12->childStates().at(0), (QState*)s121); + + QCOMPARE(s1->childStates().size(), 3); + QCOMPARE(s1->childStates().at(0), s11); + QCOMPARE(s1->childStates().at(1), s12); + QCOMPARE(s1->childStates().at(2), s13); + + s11->addTransition(s12); + QCOMPARE(s11->transitions().size(), 1); + QCOMPARE(s11->transitions().at(0)->sourceState(), s11); + QCOMPARE(s11->transitions().at(0)->targetStates().size(), 1); + QCOMPARE(s11->transitions().at(0)->targetStates().at(0), s12); + QCOMPARE(s11->transitions().at(0)->eventType(), QEvent::None); + + QState *s14 = new QState(); + s12->addTransition(QList<QState*>() << s13 << s14); + QCOMPARE(s12->transitions().size(), 1); + QCOMPARE(s12->transitions().at(0)->sourceState(), s12); + QCOMPARE(s12->transitions().at(0)->targetStates().size(), 2); + QCOMPARE(s12->transitions().at(0)->targetStates().at(0), s13); + QCOMPARE(s12->transitions().at(0)->targetStates().at(1), s14); + QCOMPARE(s12->transitions().at(0)->eventType(), QEvent::None); + + s13->addTransition(this, SIGNAL(destroyed()), s14); + QCOMPARE(s13->transitions().size(), 1); + QCOMPARE(s13->transitions().at(0)->sourceState(), s13); + QCOMPARE(s13->transitions().at(0)->targetStates().size(), 1); + QCOMPARE(s13->transitions().at(0)->targetStates().at(0), s14); + QCOMPARE(s13->transitions().at(0)->eventType(), QEvent::Signal); + QVERIFY(qobject_cast<QSignalTransition*>(s13->transitions().at(0)) != 0); + + delete s13->transitions().at(0); + QCOMPARE(s13->transitions().size(), 0); + + s12->addTransition(this, SIGNAL(destroyed()), s11); + QCOMPARE(s12->transitions().size(), 2); +} +#endif + +void tst_QState::addEntryAction() +{ + QStateMachine sm; + QState *s0 = new QState(sm.rootState()); + s0->addEntryAction(new QStateSetPropertyAction(this, "objectName", "commandTest")); + sm.setInitialState(s0); + + sm.start(); + QCoreApplication::processEvents(); + + QCOMPARE(this->objectName(), QString::fromLatin1("commandTest")); +} + +void tst_QState::setPropertyOnEntry() +{ + QStateMachine machine; + + QObject *object = new QObject(); + object->setProperty("fooBar", 10); + + QState *s1 = new QState(machine.rootState()); + s1->setPropertyOnEntry(object, "fooBar", 20); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(object->property("fooBar").toInt(), 20); +} + +void tst_QState::setPropertyOnEntryTwice() +{ + QStateMachine machine; + + QObject *object = new QObject(); + object->setProperty("fooBar", 10); + + QState *s1 = new QState(machine.rootState()); + s1->setPropertyOnEntry(object, "fooBar", 20); + s1->setPropertyOnEntry(object, "fooBar", 30); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(object->property("fooBar").toInt(), 30); +} + +void tst_QState::setPropertyOnExit() +{ + QStateMachine machine; + + QObject *object = new QObject(); + object->setProperty("fooBar", 10); + + QState *s1 = new QState(machine.rootState()); + s1->setPropertyOnExit(object, "fooBar", 20); + + QState *s2 = new QState(machine.rootState()); + s1->addTransition(new QTransition(QEvent::User), s2); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(object->property("fooBar").toInt(), 10); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(object->property("fooBar").toInt(), 20); +} + +void tst_QState::setPropertyOnExitTwice() +{ + QStateMachine machine; + + QObject *object = new QObject(); + object->setProperty("fooBar", 10); + + QState *s1 = new QState(machine.rootState()); + s1->setPropertyOnExit(object, "fooBar", 20); + s1->setPropertyOnExit(object, "fooBar", 30); + + QState *s2 = new QState(machine.rootState()); + s1->addTransition(new QTransition(QEvent::User), s2); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(object->property("fooBar").toInt(), 10); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(object->property("fooBar").toInt(), 30); +} + +void tst_QState::historyInitialState() +{ + QStateMachine machine; + + QState *s1 = new QState(machine.rootState()); + + QState *s2 = new QState(machine.rootState()); + QHistoryState *h1 = s2->addHistoryState(); + + s2->setInitialState(h1); + + QState *s3 = new QState(s2); + h1->setDefaultState(s3); + + QState *s4 = new QState(s2); + + s1->addTransition(new QTransition(QEvent::User), s2); + s2->addTransition(new QTransition(QEvent::User), s1); + s3->addTransition(new QTransition(QEvent::Type(QEvent::User+1)), s4); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s1)); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().size(), 2); + QVERIFY(machine.configuration().contains(s2)); + QVERIFY(machine.configuration().contains(s3)); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s1)); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().size(), 2); + QVERIFY(machine.configuration().contains(s2)); + QVERIFY(machine.configuration().contains(s3)); + + machine.postEvent(new QEvent(QEvent::Type(QEvent::User+1))); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().size(), 2); + QVERIFY(machine.configuration().contains(s2)); + QVERIFY(machine.configuration().contains(s4)); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s1)); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().size(), 2); + QVERIFY(machine.configuration().contains(s2)); + QVERIFY(machine.configuration().contains(s4)); +} + + +QTEST_MAIN(tst_QState) +#include "tst_qstate.moc" diff --git a/tests/auto/qstatemachine/qstatemachine.pro b/tests/auto/qstatemachine/qstatemachine.pro new file mode 100644 index 0000000..e5b32b5 --- /dev/null +++ b/tests/auto/qstatemachine/qstatemachine.pro @@ -0,0 +1,4 @@ +load(qttest_p4) +QT = core gui +SOURCES += tst_qstatemachine.cpp + diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp new file mode 100644 index 0000000..c906d53 --- /dev/null +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -0,0 +1,2115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtCore/QCoreApplication> +#include <QtGui/QPushButton> + +#include "qstatemachine.h" +#include "qstate.h" +#include "qtransition.h" +#include "qhistorystate.h" +#include "qkeyeventtransition.h" +#include "qmouseeventtransition.h" +#include "private/qstatemachine_p.h" + +// Will try to wait for the condition while allowing event processing +#define QTRY_COMPARE(__expr, __expected) \ + do { \ + const int __step = 50; \ + const int __timeout = 5000; \ + if ((__expr) != (__expected)) { \ + QTest::qWait(0); \ + } \ + for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \ + QTest::qWait(__step); \ + } \ + QCOMPARE(__expr, __expected); \ + } while(0) + +//TESTED_CLASS= +//TESTED_FILES= + +static int globalTick; + +class tst_QStateMachine : public QObject +{ + Q_OBJECT +public: + tst_QStateMachine(); + virtual ~tst_QStateMachine(); + +private slots: + void init(); + void cleanup(); + + void rootState(); + void addAndRemoveState(); + void stateEntryAndExit(); + void postEvent(); + void stateFinished(); + void parallelStates(); + void allSourceToTargetConfigurations(); + void signalTransitions(); + void eventTransitions(); + void historyStates(); + void stateActions(); + void transitionActions(); + void transitionToRootState(); + void transitionEntersParent(); + + void defaultErrorState(); + void customGlobalErrorState(); + void customLocalErrorStateInBrokenState(); + void customLocalErrorStateInOtherState(); + void customLocalErrorStateInParentOfBrokenState(); + void customLocalErrorStateOverridesParent(); + void errorStateHasChildren(); + void errorStateHasErrors(); + void errorStateIsRootState(); + void errorStateEntersParentFirst(); + void customErrorStateIsNull(); + void clearError(); + void historyStateHasNowhereToGo(); + void brokenStateIsNeverEntered(); + void customErrorStateNotInGraph(); + void transitionToStateNotInGraph(); + void restoreProperties(); + void defaultGlobalRestorePolicy(); + void globalRestorePolicySetToRestore(); + void globalRestorePolicySetToDoNotRestore(); + void restorePolicyNotInherited(); + void mixedRestoreProperties(); + void setRestorePolicyToDoNotRestore(); + void setGlobalRestorePolicyToGlobalRestore(); + void restorePolicyOnChildState(); + void transitionWithParent(); +}; + +tst_QStateMachine::tst_QStateMachine() +{ +} + +tst_QStateMachine::~tst_QStateMachine() +{ +} + +class TestState : public QState +{ +public: + enum Event { + Entry, + Exit + }; + TestState(QState *parent) + : QState(parent) {} + QList<QPair<int, Event> > events; +protected: + virtual void onEntry() { + events.append(qMakePair(globalTick++, Entry)); + } + virtual void onExit() { + events.append(qMakePair(globalTick++, Exit)); + } +}; + +class TestTransition : public QAbstractTransition +{ +public: + TestTransition(QAbstractState *target) + : QAbstractTransition(QList<QAbstractState*>() << target) {} + QList<int> triggers; +protected: + virtual bool eventTest(QEvent *) const { + return true; + } + virtual void onTransition() { + triggers.append(globalTick++); + } +}; + +static QtMsgType s_msgType; +static QByteArray s_msg; +static bool s_countWarnings; +static QtMsgHandler s_oldHandler; + +static void defaultErrorStateTestMessageHandler(QtMsgType type, const char *msg) +{ + s_msgType = type; + s_msg = msg; + + if (s_countWarnings) + s_oldHandler(type, msg); +} + +void tst_QStateMachine::init() +{ + s_msg = QByteArray(); + s_msgType = QtDebugMsg; + s_countWarnings = true; + + s_oldHandler = qInstallMsgHandler(defaultErrorStateTestMessageHandler); +} + +void tst_QStateMachine::cleanup() +{ + qInstallMsgHandler(s_oldHandler); +} + +class EventTransition : public QTransition +{ +public: + EventTransition(QEvent::Type type, QAbstractState *target, QState *parent = 0) + : QTransition(QList<QAbstractState*>() << target, parent), m_type(type) {} +protected: + virtual bool eventTest(QEvent *e) const { + return (e->type() == m_type); + } +private: + QEvent::Type m_type; +}; + +void tst_QStateMachine::transitionToRootState() +{ + QStateMachine machine; + + QState *initialState = new QState(); + machine.addState(initialState); + machine.setInitialState(initialState); + + initialState->addTransition(new EventTransition(QEvent::User, machine.rootState())); + + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().count(), 2); + QVERIFY(machine.configuration().contains(initialState)); + QVERIFY(machine.configuration().contains(machine.rootState())); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().count(), 2); + QVERIFY(machine.configuration().contains(initialState)); + QVERIFY(machine.configuration().contains(machine.rootState())); +} + +void tst_QStateMachine::transitionEntersParent() +{ + QStateMachine machine; + + QObject *entryController = new QObject(&machine); + entryController->setObjectName("entryController"); + entryController->setProperty("greatGrandParentEntered", false); + entryController->setProperty("grandParentEntered", false); + entryController->setProperty("parentEntered", false); + entryController->setProperty("stateEntered", false); + + QState *greatGrandParent = new QState(); + greatGrandParent->setObjectName("grandParent"); + greatGrandParent->setPropertyOnEntry(entryController, "greatGrandParentEntered", true); + machine.addState(greatGrandParent); + machine.setInitialState(greatGrandParent); + + QState *grandParent = new QState(greatGrandParent); + grandParent->setObjectName("grandParent"); + grandParent->setPropertyOnEntry(entryController, "grandParentEntered", true); + + QState *parent = new QState(grandParent); + parent->setObjectName("parent"); + parent->setPropertyOnEntry(entryController, "parentEntered", true); + + QState *state = new QState(parent); + state->setObjectName("state"); + state->setPropertyOnEntry(entryController, "stateEntered", true); + + QState *initialStateOfGreatGrandParent = new QState(greatGrandParent); + initialStateOfGreatGrandParent->setObjectName("initialStateOfGreatGrandParent"); + greatGrandParent->setInitialState(initialStateOfGreatGrandParent); + + initialStateOfGreatGrandParent->addTransition(new EventTransition(QEvent::User, state)); + + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(entryController->property("greatGrandParentEntered").toBool(), true); + QCOMPARE(entryController->property("grandParentEntered").toBool(), false); + QCOMPARE(entryController->property("parentEntered").toBool(), false); + QCOMPARE(entryController->property("stateEntered").toBool(), false); + QCOMPARE(machine.configuration().count(), 2); + QVERIFY(machine.configuration().contains(greatGrandParent)); + QVERIFY(machine.configuration().contains(initialStateOfGreatGrandParent)); + + entryController->setProperty("greatGrandParentEntered", false); + entryController->setProperty("grandParentEntered", false); + entryController->setProperty("parentEntered", false); + entryController->setProperty("stateEntered", false); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(entryController->property("greatGrandParentEntered").toBool(), false); + QCOMPARE(entryController->property("grandParentEntered").toBool(), true); + QCOMPARE(entryController->property("parentEntered").toBool(), true); + QCOMPARE(entryController->property("stateEntered").toBool(), true); + QCOMPARE(machine.configuration().count(), 4); + QVERIFY(machine.configuration().contains(greatGrandParent)); + QVERIFY(machine.configuration().contains(grandParent)); + QVERIFY(machine.configuration().contains(parent)); + QVERIFY(machine.configuration().contains(state)); +} + +void tst_QStateMachine::defaultErrorState() +{ + s_countWarnings = false; // we expect warnings here + + QStateMachine machine; + QVERIFY(machine.errorState() != 0); + + QState *brokenState = new QState(); + brokenState->setObjectName("MyInitialState"); + + machine.addState(brokenState); + machine.setInitialState(brokenState); + + QState *childState = new QState(brokenState); + childState->setObjectName("childState"); + + // initialState has no initial state + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(s_msgType, QtWarningMsg); + QCOMPARE(QString::fromLatin1(s_msg.data()), + QString::fromLatin1("Unrecoverable error detected in running state machine: Missing initial state in compound state 'MyInitialState'")); + + QCOMPARE(machine.error(), QStateMachine::NoInitialStateError); + QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'MyInitialState'")); + + QCOMPARE(machine.configuration().count(), 1); + QVERIFY(machine.configuration().contains(machine.errorState())); +} + +class CustomErrorState: public QState +{ +public: + CustomErrorState(QStateMachine *machine, QState *parent = 0) + : QState(parent), error(QStateMachine::NoError), m_machine(machine) + { + } + + void onEntry() + { + error = m_machine->error(); + errorString = m_machine->errorString(); + } + + QStateMachine::Error error; + QString errorString; + +private: + QStateMachine *m_machine; +}; + +void tst_QStateMachine::customGlobalErrorState() +{ + QStateMachine machine; + + CustomErrorState *customErrorState = new CustomErrorState(&machine); + customErrorState->setObjectName("customErrorState"); + machine.addState(customErrorState); + machine.setErrorState(customErrorState); + + QState *initialState = new QState(); + initialState->setObjectName("initialState"); + machine.addState(initialState); + machine.setInitialState(initialState); + + QState *brokenState = new QState(); + brokenState->setObjectName("brokenState"); + machine.addState(brokenState); + QState *childState = new QState(brokenState); + childState->setObjectName("childState"); + + initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState)); + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(machine.errorState(), customErrorState); + QCOMPARE(machine.configuration().count(), 1); + QVERIFY(machine.configuration().contains(initialState)); + + machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); + QCOMPARE(machine.configuration().count(), 1); + QVERIFY(machine.configuration().contains(initialState)); + + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().count(), 1); + QVERIFY(machine.configuration().contains(customErrorState)); + QCOMPARE(customErrorState->error, QStateMachine::NoInitialStateError); + QCOMPARE(customErrorState->errorString, QString::fromLatin1("Missing initial state in compound state 'brokenState'")); + QCOMPARE(machine.error(), QStateMachine::NoInitialStateError); + QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'brokenState'")); + QVERIFY(s_msg.isEmpty()); + QCOMPARE(s_msgType, QtDebugMsg); +} + +void tst_QStateMachine::customLocalErrorStateInBrokenState() +{ + QStateMachine machine; + CustomErrorState *customErrorState = new CustomErrorState(&machine); + machine.addState(customErrorState); + + QState *initialState = new QState(); + initialState->setObjectName("initialState"); + machine.addState(initialState); + machine.setInitialState(initialState); + + QState *brokenState = new QState(); + brokenState->setObjectName("brokenState"); + machine.addState(brokenState); + brokenState->setErrorState(customErrorState); + + QState *childState = new QState(brokenState); + childState->setObjectName("childState"); + + initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState)); + + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().count(), 1); + QVERIFY(machine.configuration().contains(customErrorState)); + QCOMPARE(customErrorState->error, QStateMachine::NoInitialStateError); + QVERIFY(s_msg.isEmpty()); +} + +void tst_QStateMachine::customLocalErrorStateInOtherState() +{ + s_countWarnings = false; + + QStateMachine machine; + CustomErrorState *customErrorState = new CustomErrorState(&machine); + machine.addState(customErrorState); + + QState *initialState = new QState(); + initialState->setObjectName("initialState"); + initialState->setErrorState(customErrorState); + machine.addState(initialState); + machine.setInitialState(initialState); + + QState *brokenState = new QState(); + brokenState->setObjectName("brokenState"); + + machine.addState(brokenState); + + QState *childState = new QState(brokenState); + childState->setObjectName("childState"); + + initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState)); + + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().count(), 1); + QVERIFY(machine.configuration().contains(machine.errorState())); + QCOMPARE(s_msgType, QtWarningMsg); +} + +void tst_QStateMachine::customLocalErrorStateInParentOfBrokenState() +{ + QStateMachine machine; + CustomErrorState *customErrorState = new CustomErrorState(&machine); + machine.addState(customErrorState); + + QState *initialState = new QState(); + initialState->setObjectName("initialState"); + machine.addState(initialState); + machine.setInitialState(initialState); + + QState *parentOfBrokenState = new QState(); + machine.addState(parentOfBrokenState); + parentOfBrokenState->setObjectName("parentOfBrokenState"); + parentOfBrokenState->setErrorState(customErrorState); + + + QState *brokenState = new QState(parentOfBrokenState); + brokenState->setObjectName("brokenState"); + parentOfBrokenState->setInitialState(brokenState); + + QState *childState = new QState(brokenState); + childState->setObjectName("childState"); + + initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState)); + + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().count(), 1); + QVERIFY(machine.configuration().contains(customErrorState)); + QVERIFY(s_msg.isEmpty()); +} + +void tst_QStateMachine::customLocalErrorStateOverridesParent() +{ + QStateMachine machine; + CustomErrorState *customErrorStateForParent = new CustomErrorState(&machine); + machine.addState(customErrorStateForParent); + + CustomErrorState *customErrorStateForBrokenState = new CustomErrorState(&machine); + machine.addState(customErrorStateForBrokenState); + + QState *initialState = new QState(); + initialState->setObjectName("initialState"); + machine.addState(initialState); + machine.setInitialState(initialState); + + QState *parentOfBrokenState = new QState(); + machine.addState(parentOfBrokenState); + parentOfBrokenState->setObjectName("parentOfBrokenState"); + parentOfBrokenState->setErrorState(customErrorStateForParent); + + QState *brokenState = new QState(parentOfBrokenState); + brokenState->setObjectName("brokenState"); + brokenState->setErrorState(customErrorStateForBrokenState); + parentOfBrokenState->setInitialState(brokenState); + + QState *childState = new QState(brokenState); + childState->setObjectName("childState"); + + initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState)); + + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().count(), 1); + QVERIFY(machine.configuration().contains(customErrorStateForBrokenState)); + QCOMPARE(customErrorStateForBrokenState->error, QStateMachine::NoInitialStateError); + QCOMPARE(customErrorStateForParent->error, QStateMachine::NoError); + QVERIFY(s_msg.isEmpty()); +} + +void tst_QStateMachine::errorStateHasChildren() +{ + QStateMachine machine; + CustomErrorState *customErrorState = new CustomErrorState(&machine); + customErrorState->setObjectName("customErrorState"); + machine.addState(customErrorState); + + machine.setErrorState(customErrorState); + + QState *childOfErrorState = new QState(customErrorState); + childOfErrorState->setObjectName("childOfErrorState"); + customErrorState->setInitialState(childOfErrorState); + + QState *initialState = new QState(); + initialState->setObjectName("initialState"); + machine.addState(initialState); + machine.setInitialState(initialState); + + QState *brokenState = new QState(); + brokenState->setObjectName("brokenState"); + machine.addState(brokenState); + + QState *childState = new QState(brokenState); + childState->setObjectName("childState"); + + initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState)); + + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().count(), 2); + QVERIFY(machine.configuration().contains(customErrorState)); + QVERIFY(machine.configuration().contains(childOfErrorState)); + QVERIFY(s_msg.isEmpty()); +} + + +void tst_QStateMachine::errorStateHasErrors() +{ + s_countWarnings = false; + + QStateMachine machine; + CustomErrorState *customErrorState = new CustomErrorState(&machine); + customErrorState->setObjectName("customErrorState"); + machine.addState(customErrorState); + + QAbstractState *oldErrorState = machine.errorState(); + machine.setErrorState(customErrorState); + + QState *childOfErrorState = new QState(customErrorState); + childOfErrorState->setObjectName("childOfErrorState"); + + QState *initialState = new QState(); + initialState->setObjectName("initialState"); + machine.addState(initialState); + machine.setInitialState(initialState); + + QState *brokenState = new QState(); + brokenState->setObjectName("brokenState"); + machine.addState(brokenState); + + QState *childState = new QState(brokenState); + childState->setObjectName("childState"); + + initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState)); + + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().count(), 1); + QVERIFY(machine.configuration().contains(oldErrorState)); // Fall back to default + QCOMPARE(machine.error(), QStateMachine::NoInitialStateError); + QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'customErrorState'")); + + QCOMPARE(s_msgType, QtWarningMsg); +} + +void tst_QStateMachine::errorStateIsRootState() +{ + QStateMachine machine; + machine.setErrorState(machine.rootState()); + + QState *initialState = new QState(); + initialState->setObjectName("initialState"); + machine.addState(initialState); + machine.setInitialState(initialState); + + QState *brokenState = new QState(); + brokenState->setObjectName("brokenState"); + machine.addState(brokenState); + + QState *childState = new QState(brokenState); + childState->setObjectName("childState"); + + initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState)); + + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); + QCoreApplication::processEvents(); + + QEXPECT_FAIL("", "Covered by transitionToRootState test. Remove this when that test passes", Continue); + QCOMPARE(machine.configuration().count(), 1); + QVERIFY(machine.configuration().contains(initialState)); +} + +void tst_QStateMachine::errorStateEntersParentFirst() +{ + QStateMachine machine; + + QObject *entryController = new QObject(&machine); + entryController->setObjectName("entryController"); + entryController->setProperty("greatGrandParentEntered", false); + entryController->setProperty("grandParentEntered", false); + entryController->setProperty("parentEntered", false); + entryController->setProperty("errorStateEntered", false); + + QState *greatGrandParent = new QState(); + greatGrandParent->setObjectName("greatGrandParent"); + greatGrandParent->setPropertyOnEntry(entryController, "greatGrandParentEntered", true); + machine.addState(greatGrandParent); + machine.setInitialState(greatGrandParent); + + QState *grandParent = new QState(greatGrandParent); + grandParent->setObjectName("grandParent"); + grandParent->setPropertyOnEntry(entryController, "grandParentEntered", true); + + QState *parent = new QState(grandParent); + parent->setObjectName("parent"); + parent->setPropertyOnEntry(entryController, "parentEntered", true); + + QState *errorState = new QState(parent); + errorState->setObjectName("errorState"); + errorState->setPropertyOnEntry(entryController, "errorStateEntered", true); + machine.setErrorState(errorState); + + QState *initialStateOfGreatGrandParent = new QState(greatGrandParent); + initialStateOfGreatGrandParent->setObjectName("initialStateOfGreatGrandParent"); + greatGrandParent->setInitialState(initialStateOfGreatGrandParent); + + QState *brokenState = new QState(greatGrandParent); + brokenState->setObjectName("brokenState"); + + QState *childState = new QState(brokenState); + childState->setObjectName("childState"); + + initialStateOfGreatGrandParent->addTransition(new EventTransition(QEvent::User, brokenState)); + + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(entryController->property("greatGrandParentEntered").toBool(), true); + QCOMPARE(entryController->property("grandParentEntered").toBool(), false); + QCOMPARE(entryController->property("parentEntered").toBool(), false); + QCOMPARE(entryController->property("errorStateEntered").toBool(), false); + QCOMPARE(machine.configuration().count(), 2); + QVERIFY(machine.configuration().contains(greatGrandParent)); + QVERIFY(machine.configuration().contains(initialStateOfGreatGrandParent)); + + entryController->setProperty("greatGrandParentEntered", false); + entryController->setProperty("grandParentEntered", false); + entryController->setProperty("parentEntered", false); + entryController->setProperty("errorStateEntered", false); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(entryController->property("greatGrandParentEntered").toBool(), false); + QCOMPARE(entryController->property("grandParentEntered").toBool(), true); + QCOMPARE(entryController->property("parentEntered").toBool(), true); + QCOMPARE(entryController->property("errorStateEntered").toBool(), true); + QCOMPARE(machine.configuration().count(), 4); + QVERIFY(machine.configuration().contains(greatGrandParent)); + QVERIFY(machine.configuration().contains(grandParent)); + QVERIFY(machine.configuration().contains(parent)); + QVERIFY(machine.configuration().contains(errorState)); +} + +void tst_QStateMachine::customErrorStateIsNull() +{ + s_countWarnings = false; + + QStateMachine machine; + QAbstractState *oldErrorState = machine.errorState(); + machine.rootState()->setErrorState(0); + + QState *initialState = new QState(); + machine.addState(initialState); + machine.setInitialState(initialState); + + QState *brokenState = new QState(); + machine.addState(brokenState); + + new QState(brokenState); + initialState->addTransition(new EventTransition(QEvent::User, brokenState)); + + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(machine.errorState(), reinterpret_cast<void *>(0)); + QCOMPARE(machine.configuration().count(), 1); + QVERIFY(machine.configuration().contains(oldErrorState)); + QCOMPARE(s_msgType, QtWarningMsg); +} + +void tst_QStateMachine::clearError() +{ + QStateMachine machine; + machine.setErrorState(new QState(machine.rootState())); // avoid warnings + + QState *brokenState = new QState(machine.rootState()); + brokenState->setObjectName("brokenState"); + machine.setInitialState(brokenState); + new QState(brokenState); + + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(machine.error(), QStateMachine::NoInitialStateError); + QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'brokenState'")); + + machine.clearError(); + + QCOMPARE(machine.error(), QStateMachine::NoError); + QVERIFY(machine.errorString().isEmpty()); +} + +void tst_QStateMachine::historyStateHasNowhereToGo() +{ + QStateMachine machine; + + QState *initialState = new QState(machine.rootState()); + machine.setInitialState(initialState); + machine.setErrorState(new QState(machine.rootState())); // avoid warnings + + QState *brokenState = new QState(machine.rootState()); + brokenState->setObjectName("brokenState"); + brokenState->setInitialState(new QState(brokenState)); + + QHistoryState *historyState = brokenState->addHistoryState(); + historyState->setObjectName("historyState"); + initialState->addTransition(new EventTransition(QEvent::User, historyState)); + + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().count(), 1); + QVERIFY(machine.configuration().contains(machine.errorState())); + QCOMPARE(machine.error(), QStateMachine::NoDefaultStateInHistoryState); + QCOMPARE(machine.errorString(), QString::fromLatin1("Missing transition from history state 'historyState'")); +} + +void tst_QStateMachine::brokenStateIsNeverEntered() +{ + QStateMachine machine; + + QObject *entryController = new QObject(); + entryController->setProperty("brokenStateEntered", false); + entryController->setProperty("childStateEntered", false); + entryController->setProperty("errorStateEntered", false); + + QState *initialState = new QState(machine.rootState()); + machine.setInitialState(initialState); + + QState *errorState = new QState(machine.rootState()); + errorState->setPropertyOnEntry(entryController, "errorStateEntered", true); + machine.setErrorState(errorState); + + QState *brokenState = new QState(machine.rootState()); + brokenState->setPropertyOnEntry(entryController, "brokenStateEntered", true); + brokenState->setObjectName("brokenState"); + + QState *childState = new QState(brokenState); + childState->setPropertyOnEntry(entryController, "childStateEntered", true); + + initialState->addTransition(new EventTransition(QEvent::User, brokenState)); + + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(entryController->property("errorStateEntered").toBool(), true); + QCOMPARE(entryController->property("brokenStateEntered").toBool(), false); + QCOMPARE(entryController->property("childStateEntered").toBool(), false); +} + +void tst_QStateMachine::transitionToStateNotInGraph() +{ + QSKIP("Hangs", SkipAll); + + s_countWarnings = false; + + QStateMachine machine; + + QState *initialState = new QState(machine.rootState()); + initialState->setObjectName("initialState"); + machine.setInitialState(initialState); + + QState *independentState = new QState(); + initialState->addTransition(independentState); + + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().count(), 1); + QVERIFY(machine.configuration().contains(initialState)); +} + +void tst_QStateMachine::customErrorStateNotInGraph() +{ + s_countWarnings = false; + + QStateMachine machine; + + QState *errorState = new QState(); + errorState->setObjectName("errorState"); + machine.setErrorState(errorState); + QVERIFY(errorState != machine.errorState()); + + QState *initialBrokenState = new QState(machine.rootState()); + initialBrokenState->setObjectName("initialBrokenState"); + machine.setInitialState(initialBrokenState); + new QState(initialBrokenState); + + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(machine.configuration().count(), 1); + QVERIFY(machine.configuration().contains(machine.errorState())); +} + +void tst_QStateMachine::restoreProperties() +{ + QObject *object = new QObject(); + object->setProperty("a", 1); + object->setProperty("b", 2); + + QStateMachine machine; + + QState *S1 = new QState(); + S1->setObjectName("S1"); + S1->setPropertyOnEntry(object, "a", 3); + S1->setRestorePolicy(QState::RestoreProperties); + machine.addState(S1); + + QState *S2 = new QState(); + S2->setObjectName("S2"); + S2->setPropertyOnEntry(object, "b", 5); + S2->setRestorePolicy(QState::RestoreProperties); + machine.addState(S2); + + QState *S3 = new QState(); + S3->setObjectName("S3"); + S3->setRestorePolicy(QState::RestoreProperties); + machine.addState(S3); + + QFinalState *S4 = new QFinalState(); + machine.addState(S4); + + S1->addTransition(new EventTransition(QEvent::User, S2)); + S2->addTransition(new EventTransition(QEvent::User, S3)); + S3->addTransition(S4); + + machine.setInitialState(S1); + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(object->property("a").toInt(), 3); + QCOMPARE(object->property("b").toInt(), 2); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(object->property("a").toInt(), 1); + QCOMPARE(object->property("b").toInt(), 5); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(object->property("a").toInt(), 1); + QCOMPARE(object->property("b").toInt(), 2); +} + +void tst_QStateMachine::rootState() +{ + QStateMachine machine; + QVERIFY(machine.rootState() != 0); + QVERIFY(qobject_cast<QState*>(machine.rootState()) != 0); + QCOMPARE(qobject_cast<QState*>(machine.rootState())->parentState(), (QState*)0); + QCOMPARE(machine.rootState()->parent(), (QObject*)&machine); + + QState *s1 = new QState(machine.rootState()); + QCOMPARE(s1->parentState(), machine.rootState()); + + QState *s2 = new QState(); + s2->setParent(&machine); + QCOMPARE(s2->parentState(), machine.rootState()); +} + +void tst_QStateMachine::addAndRemoveState() +{ + QStateMachine machine; + QCOMPARE(machine.states().size(), 1); // the error state + QCOMPARE(machine.states().at(0), (QAbstractState*)machine.errorState()); + + QTest::ignoreMessage(QtWarningMsg, "QStateMachine::addState: cannot add null state"); + machine.addState(0); + + QState *s1 = new QState(); + QCOMPARE(s1->parentState(), (QState*)0); + machine.addState(s1); + QCOMPARE(s1->parentState(), machine.rootState()); + QCOMPARE(machine.states().size(), 2); + QCOMPARE(machine.states().at(0), (QAbstractState*)machine.errorState()); + QCOMPARE(machine.states().at(1), (QAbstractState*)s1); + + QTest::ignoreMessage(QtWarningMsg, "QStateMachine::addState: state has already been added to this machine"); + machine.addState(s1); + + QState *s2 = new QState(); + QCOMPARE(s2->parentState(), (QState*)0); + machine.addState(s2); + QCOMPARE(s2->parentState(), machine.rootState()); + QCOMPARE(machine.states().size(), 3); + QCOMPARE(machine.states().at(0), (QAbstractState*)machine.errorState()); + QCOMPARE(machine.states().at(1), (QAbstractState*)s1); + QCOMPARE(machine.states().at(2), (QAbstractState*)s2); + + QTest::ignoreMessage(QtWarningMsg, "QStateMachine::addState: state has already been added to this machine"); + machine.addState(s2); + + machine.removeState(s1); + QCOMPARE(s1->parentState(), (QState*)0); + QCOMPARE(machine.states().size(), 2); + QCOMPARE(machine.states().at(0), (QAbstractState*)machine.errorState()); + QCOMPARE(machine.states().at(1), (QAbstractState*)s2); + + machine.removeState(s2); + QCOMPARE(s2->parentState(), (QState*)0); + QCOMPARE(machine.states().size(), 1); + QCOMPARE(machine.states().at(0), (QAbstractState*)machine.errorState()); + + QTest::ignoreMessage(QtWarningMsg, "QStateMachine::removeState: cannot remove null state"); + machine.removeState(0); + + delete s1; + delete s2; + // ### how to deal with this? + // machine.removeState(machine.errorState()); +} + +void tst_QStateMachine::stateEntryAndExit() +{ + // Two top-level states + { + QStateMachine machine; + + TestState *s1 = new TestState(machine.rootState()); + TestState *s2 = new TestState(machine.rootState()); + QFinalState *s3 = new QFinalState(machine.rootState()); + TestTransition *t = new TestTransition(s2); + s1->addTransition(t); + s2->addTransition(s3); + + QSignalSpy startedSpy(&machine, SIGNAL(started())); + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.setInitialState(s1); + QVERIFY(machine.configuration().isEmpty()); + globalTick = 0; + machine.start(); + + QTRY_COMPARE(startedSpy.count(), 1); + QTRY_COMPARE(finishedSpy.count(), 1); + QCOMPARE(machine.configuration().count(), 1); + QVERIFY(machine.configuration().contains(s3)); + + // s1 is entered + QCOMPARE(s1->events.count(), 2); + QCOMPARE(s1->events.at(0).first, 0); + QCOMPARE(s1->events.at(0).second, TestState::Entry); + // s1 is exited + QCOMPARE(s1->events.at(1).first, 1); + QCOMPARE(s1->events.at(1).second, TestState::Exit); + // t is triggered + QCOMPARE(t->triggers.count(), 1); + QCOMPARE(t->triggers.at(0), 2); + // s2 is entered + QCOMPARE(s2->events.count(), 2); + QCOMPARE(s2->events.at(0).first, 3); + QCOMPARE(s2->events.at(0).second, TestState::Entry); + // s2 is exited + QCOMPARE(s2->events.at(1).first, 4); + QCOMPARE(s2->events.at(1).second, TestState::Exit); + } + // Two top-level states, one has two child states + { + QStateMachine machine; + + TestState *s1 = new TestState(machine.rootState()); + TestState *s11 = new TestState(s1); + TestState *s12 = new TestState(s1); + TestState *s2 = new TestState(machine.rootState()); + QFinalState *s3 = new QFinalState(machine.rootState()); + s1->setInitialState(s11); + TestTransition *t1 = new TestTransition(s12); + s11->addTransition(t1); + TestTransition *t2 = new TestTransition(s2); + s12->addTransition(t2); + s2->addTransition(s3); + + QSignalSpy startedSpy(&machine, SIGNAL(started())); + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.setInitialState(s1); + globalTick = 0; + machine.start(); + + QTRY_COMPARE(startedSpy.count(), 1); + QTRY_COMPARE(finishedSpy.count(), 1); + QCOMPARE(machine.configuration().count(), 1); + QVERIFY(machine.configuration().contains(s3)); + + // s1 is entered + QCOMPARE(s1->events.count(), 2); + QCOMPARE(s1->events.at(0).first, 0); + QCOMPARE(s1->events.at(0).second, TestState::Entry); + // s11 is entered + QCOMPARE(s11->events.count(), 2); + QCOMPARE(s11->events.at(0).first, 1); + QCOMPARE(s11->events.at(0).second, TestState::Entry); + // s11 is exited + QCOMPARE(s11->events.at(1).first, 2); + QCOMPARE(s11->events.at(1).second, TestState::Exit); + // t1 is triggered + QCOMPARE(t1->triggers.count(), 1); + QCOMPARE(t1->triggers.at(0), 3); + // s12 is entered + QCOMPARE(s12->events.count(), 2); + QCOMPARE(s12->events.at(0).first, 4); + QCOMPARE(s12->events.at(0).second, TestState::Entry); + // s12 is exited + QCOMPARE(s12->events.at(1).first, 5); + QCOMPARE(s12->events.at(1).second, TestState::Exit); + // s1 is exited + QCOMPARE(s1->events.at(1).first, 6); + QCOMPARE(s1->events.at(1).second, TestState::Exit); + // t2 is triggered + QCOMPARE(t2->triggers.count(), 1); + QCOMPARE(t2->triggers.at(0), 7); + // s2 is entered + QCOMPARE(s2->events.count(), 2); + QCOMPARE(s2->events.at(0).first, 8); + QCOMPARE(s2->events.at(0).second, TestState::Entry); + // s2 is exited + QCOMPARE(s2->events.at(1).first, 9); + QCOMPARE(s2->events.at(1).second, TestState::Exit); + } +} + +struct StringEvent : public QEvent +{ +public: + StringEvent(const QString &val) + : QEvent(QEvent::Type(QEvent::User+2)), + value(val) {} + + QString value; +}; + +class StringTransition : public QAbstractTransition +{ +public: + StringTransition(const QString &value, QAbstractState *target) + : QAbstractTransition(QList<QAbstractState*>() << target), m_value(value) {} + +protected: + virtual bool eventTest(QEvent *e) const + { + if (e->type() != QEvent::Type(QEvent::User+2)) + return false; + StringEvent *se = static_cast<StringEvent*>(e); + return (m_value == se->value) && (!m_cond.isValid() || (m_cond.indexIn(m_value) != -1)); + } + virtual void onTransition() {} + +private: + QString m_value; + QRegExp m_cond; +}; + +class StringEventPoster : public QState +{ +public: + StringEventPoster(QStateMachine *machine, const QString &value, QState *parent = 0) + : QState(parent), m_machine(machine), m_value(value) {} + + void setString(const QString &value) + { m_value = value; } + +protected: + virtual void onEntry() + { + m_machine->postEvent(new StringEvent(m_value)); + } + virtual void onExit() {} + +private: + QStateMachine *m_machine; + QString m_value; +}; + +void tst_QStateMachine::postEvent() +{ + QStateMachine machine; + StringEventPoster *s1 = new StringEventPoster(&machine, "a"); + QFinalState *s2 = new QFinalState; + s1->addTransition(new StringTransition("a", s2)); + machine.addState(s1); + machine.addState(s2); + machine.setInitialState(s1); + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.start(); + QTRY_COMPARE(finishedSpy.count(), 1); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s2)); + + s1->setString("b"); + QFinalState *s3 = new QFinalState(); + machine.addState(s3); + s1->addTransition(new StringTransition("b", s3)); + finishedSpy.clear(); + machine.start(); + QTRY_COMPARE(finishedSpy.count(), 1); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s3)); +} + +void tst_QStateMachine::stateFinished() +{ + QStateMachine machine; + QState *s1 = new QState(machine.rootState()); + QState *s1_1 = new QState(s1); + QFinalState *s1_2 = new QFinalState(s1); + s1_1->addTransition(s1_2); + s1->setInitialState(s1_1); + QFinalState *s2 = new QFinalState(machine.rootState()); + s1->addTransition(new QStateFinishedTransition(s1, QList<QAbstractState*>() << s2)); + machine.setInitialState(s1); + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.start(); + QTRY_COMPARE(finishedSpy.count(), 1); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s2)); +} + +void tst_QStateMachine::parallelStates() +{ + QStateMachine machine; + + QState *s1 = new QState(QState::ParallelGroup); + QState *s1_1 = new QState(s1); + QState *s1_1_1 = new QState(s1_1); + QFinalState *s1_1_f = new QFinalState(s1_1); + s1_1_1->addTransition(s1_1_f); + s1_1->setInitialState(s1_1_1); + QState *s1_2 = new QState(s1); + QState *s1_2_1 = new QState(s1_2); + QFinalState *s1_2_f = new QFinalState(s1_2); + s1_2_1->addTransition(s1_2_f); + s1_2->setInitialState(s1_2_1); + machine.addState(s1); + + QFinalState *s2 = new QFinalState(); + machine.addState(s2); + + s1->addFinishedTransition(s2); + + machine.setInitialState(s1); + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.start(); + QTRY_COMPARE(finishedSpy.count(), 1); +} + +void tst_QStateMachine::allSourceToTargetConfigurations() +{ + QStateMachine machine; + QState *s0 = new QState(machine.rootState()); + s0->setObjectName("s0"); + QState *s1 = new QState(s0); + s1->setObjectName("s1"); + QState *s11 = new QState(s1); + s11->setObjectName("s11"); + QState *s2 = new QState(s0); + s2->setObjectName("s2"); + QState *s21 = new QState(s2); + s21->setObjectName("s21"); + QState *s211 = new QState(s21); + s211->setObjectName("s211"); + QFinalState *f = new QFinalState(machine.rootState()); + f->setObjectName("f"); + + s0->setInitialState(s1); + s1->setInitialState(s11); + s2->setInitialState(s21); + s21->setInitialState(s211); + + s11->addTransition(new StringTransition("g", s211)); + s1->addTransition(new StringTransition("a", s1)); + s1->addTransition(new StringTransition("b", s11)); + s1->addTransition(new StringTransition("c", s2)); + s1->addTransition(new StringTransition("d", s0)); + s1->addTransition(new StringTransition("f", s211)); + s211->addTransition(new StringTransition("d", s21)); + s211->addTransition(new StringTransition("g", s0)); + s211->addTransition(new StringTransition("h", f)); + s21->addTransition(new StringTransition("b", s211)); + s2->addTransition(new StringTransition("c", s1)); + s2->addTransition(new StringTransition("f", s11)); + s0->addTransition(new StringTransition("e", s211)); + + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.setInitialState(s0); + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new StringEvent("a")); + QCoreApplication::processEvents(); + machine.postEvent(new StringEvent("b")); + QCoreApplication::processEvents(); + machine.postEvent(new StringEvent("c")); + QCoreApplication::processEvents(); + machine.postEvent(new StringEvent("d")); + QCoreApplication::processEvents(); + machine.postEvent(new StringEvent("e")); + QCoreApplication::processEvents(); + machine.postEvent(new StringEvent("f")); + QCoreApplication::processEvents(); + machine.postEvent(new StringEvent("g")); + QCoreApplication::processEvents(); + machine.postEvent(new StringEvent("h")); + QCoreApplication::processEvents(); + + QTRY_COMPARE(finishedSpy.count(), 1); +} + +class SignalEmitter : public QObject +{ +Q_OBJECT + public: + SignalEmitter(QObject *parent = 0) + : QObject(parent) {} + void emitSignalWithNoArg() + { emit signalWithNoArg(); } + void emitSignalWithIntArg(int arg) + { emit signalWithIntArg(arg); } + void emitSignalWithStringArg(const QString &arg) + { emit signalWithStringArg(arg); } +Q_SIGNALS: + void signalWithNoArg(); + void signalWithIntArg(int); + void signalWithStringArg(const QString &); +}; + +class TestSignalTransition : public QSignalTransition +{ +public: + TestSignalTransition() + : QSignalTransition() {} + TestSignalTransition(QObject *sender, const char *signal, + QAbstractState *target) + : QSignalTransition(sender, signal, QList<QAbstractState*>() << target) {} + QVariantList argumentsReceived() const { + return m_args; + } +protected: + bool eventTest(QEvent *e) const { + if (!QSignalTransition::eventTest(e)) + return false; + QSignalEvent *se = static_cast<QSignalEvent*>(e); + const_cast<TestSignalTransition*>(this)->m_args = se->arguments(); + return true; + } +private: + QVariantList m_args; +}; + +void tst_QStateMachine::signalTransitions() +{ + { + QStateMachine machine; + QState *s0 = new QState(machine.rootState()); + QFinalState *s1 = new QFinalState(machine.rootState()); + SignalEmitter emitter; + s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), s1); + + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.setInitialState(s0); + machine.start(); + QCoreApplication::processEvents(); + + emitter.emitSignalWithNoArg(); + + QTRY_COMPARE(finishedSpy.count(), 1); + } + { + QStateMachine machine; + QState *s0 = new QState(machine.rootState()); + QFinalState *s1 = new QFinalState(machine.rootState()); + SignalEmitter emitter; + TestSignalTransition *trans = new TestSignalTransition(&emitter, SIGNAL(signalWithIntArg(int)), s1); + s0->addTransition(trans); + + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.setInitialState(s0); + machine.start(); + QCoreApplication::processEvents(); + + emitter.emitSignalWithIntArg(123); + + QTRY_COMPARE(finishedSpy.count(), 1); + QCOMPARE(trans->argumentsReceived().size(), 1); + QCOMPARE(trans->argumentsReceived().at(0).toInt(), 123); + } + { + QStateMachine machine; + QState *s0 = new QState(machine.rootState()); + QFinalState *s1 = new QFinalState(machine.rootState()); + SignalEmitter emitter; + TestSignalTransition *trans = new TestSignalTransition(&emitter, SIGNAL(signalWithStringArg(QString)), s1); + s0->addTransition(trans); + + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.setInitialState(s0); + machine.start(); + QCoreApplication::processEvents(); + + QString testString = QString::fromLatin1("hello"); + emitter.emitSignalWithStringArg(testString); + + QTRY_COMPARE(finishedSpy.count(), 1); + QCOMPARE(trans->argumentsReceived().size(), 1); + QCOMPARE(trans->argumentsReceived().at(0).toString(), testString); + } + { + QStateMachine machine; + QState *s0 = new QState(machine.rootState()); + QFinalState *s1 = new QFinalState(machine.rootState()); + + TestSignalTransition *trans = new TestSignalTransition(); + QCOMPARE(trans->senderObject(), (QObject*)0); + QCOMPARE(trans->signal(), QByteArray()); + + SignalEmitter emitter; + trans->setSenderObject(&emitter); + QCOMPARE(trans->senderObject(), (QObject*)&emitter); + trans->setSignal(SIGNAL(signalWithNoArg())); + QCOMPARE(trans->signal(), QByteArray(SIGNAL(signalWithNoArg()))); + trans->setTargetState(s1); + s0->addTransition(trans); + + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.setInitialState(s0); + machine.start(); + QCoreApplication::processEvents(); + + emitter.emitSignalWithNoArg(); + + QTRY_COMPARE(finishedSpy.count(), 1); + } +} + +void tst_QStateMachine::eventTransitions() +{ + QPushButton button; + { + QStateMachine machine; + QState *s0 = new QState(machine.rootState()); + QFinalState *s1 = new QFinalState(machine.rootState()); + + QMouseEventTransition *trans = new QMouseEventTransition(&button, QEvent::MouseButtonPress, Qt::LeftButton); + QCOMPARE(trans->eventType(), QEvent::MouseButtonPress); + QCOMPARE(trans->button(), Qt::LeftButton); + trans->setTargetState(s1); + s0->addTransition(trans); + + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.setInitialState(s0); + machine.start(); + QCoreApplication::processEvents(); + + QTest::mousePress(&button, Qt::LeftButton); + QCoreApplication::processEvents(); + + QTRY_COMPARE(finishedSpy.count(), 1); + } + { + QStateMachine machine; + QState *s0 = new QState(machine.rootState()); + QFinalState *s1 = new QFinalState(machine.rootState()); + + QEventTransition *trans = new QEventTransition(); + QCOMPARE(trans->eventSource(), (QObject*)0); + QCOMPARE(trans->eventType(), QEvent::None); + trans->setEventSource(&button); + QCOMPARE(trans->eventSource(), (QObject*)&button); + trans->setEventType(QEvent::MouseButtonPress); + QCOMPARE(trans->eventType(), QEvent::MouseButtonPress); + trans->setTargetState(s1); + s0->addTransition(trans); + + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.setInitialState(s0); + machine.start(); + QCoreApplication::processEvents(); + + QTest::mousePress(&button, Qt::LeftButton); + QCoreApplication::processEvents(); + + QTRY_COMPARE(finishedSpy.count(), 1); + } + { + QStateMachine machine; + QState *s0 = new QState(machine.rootState()); + QFinalState *s1 = new QFinalState(machine.rootState()); + + QMouseEventTransition *trans = new QMouseEventTransition(); + QCOMPARE(trans->eventSource(), (QObject*)0); + QCOMPARE(trans->eventType(), QEvent::None); + QCOMPARE(trans->button(), Qt::NoButton); + trans->setEventSource(&button); + trans->setEventType(QEvent::MouseButtonPress); + trans->setButton(Qt::LeftButton); + trans->setTargetState(s1); + s0->addTransition(trans); + + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.setInitialState(s0); + machine.start(); + QCoreApplication::processEvents(); + + QTest::mousePress(&button, Qt::LeftButton); + QCoreApplication::processEvents(); + + QTRY_COMPARE(finishedSpy.count(), 1); + } + + { + QStateMachine machine; + QState *s0 = new QState(machine.rootState()); + QFinalState *s1 = new QFinalState(machine.rootState()); + + QKeyEventTransition *trans = new QKeyEventTransition(&button, QEvent::KeyPress, Qt::Key_A); + QCOMPARE(trans->eventType(), QEvent::KeyPress); + QCOMPARE(trans->key(), (int)Qt::Key_A); + trans->setTargetState(s1); + s0->addTransition(trans); + + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.setInitialState(s0); + machine.start(); + QCoreApplication::processEvents(); + + QTest::keyPress(&button, Qt::Key_A); + QCoreApplication::processEvents(); + + QTRY_COMPARE(finishedSpy.count(), 1); + } + { + QStateMachine machine; + QState *s0 = new QState(machine.rootState()); + QFinalState *s1 = new QFinalState(machine.rootState()); + + QKeyEventTransition *trans = new QKeyEventTransition(); + QCOMPARE(trans->eventSource(), (QObject*)0); + QCOMPARE(trans->eventType(), QEvent::None); + QCOMPARE(trans->key(), 0); + trans->setEventSource(&button); + trans->setEventType(QEvent::KeyPress); + trans->setKey(Qt::Key_A); + trans->setTargetState(s1); + s0->addTransition(trans); + + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.setInitialState(s0); + machine.start(); + QCoreApplication::processEvents(); + + QTest::keyPress(&button, Qt::Key_A); + QCoreApplication::processEvents(); + + QTRY_COMPARE(finishedSpy.count(), 1); + } +} + +void tst_QStateMachine::historyStates() +{ + QStateMachine machine; + QState *root = machine.rootState(); + QState *s0 = new QState(root); + QState *s00 = new QState(s0); + QState *s01 = new QState(s0); + QHistoryState *s0h = s0->addHistoryState(); + QState *s1 = new QState(root); + QFinalState *s2 = new QFinalState(root); + + s00->addTransition(new StringTransition("a", s01)); + s0->addTransition(new StringTransition("b", s1)); + s1->addTransition(new StringTransition("c", s0h)); + s0->addTransition(new StringTransition("d", s2)); + + root->setInitialState(s0); + s0->setInitialState(s00); + + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.start(); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 2); + QVERIFY(machine.configuration().contains(s0)); + QVERIFY(machine.configuration().contains(s00)); + + machine.postEvent(new StringEvent("a")); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 2); + QVERIFY(machine.configuration().contains(s0)); + QVERIFY(machine.configuration().contains(s01)); + + machine.postEvent(new StringEvent("b")); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s1)); + + machine.postEvent(new StringEvent("c")); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 2); + QVERIFY(machine.configuration().contains(s0)); + QVERIFY(machine.configuration().contains(s01)); + + machine.postEvent(new StringEvent("d")); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s2)); + + QTRY_COMPARE(finishedSpy.count(), 1); +} + +class TestStateAction : public QStateAction +{ +public: + TestStateAction() : m_didExecute(false) + {} + bool didExecute() const { + return m_didExecute; + } +protected: + void execute() { + m_didExecute = true; + } +private: + bool m_didExecute; +}; + +void tst_QStateMachine::stateActions() +{ + QStateMachine machine; + QState *s1 = new QState(machine.rootState()); + + QVERIFY(s1->entryActions().isEmpty()); + QVERIFY(s1->exitActions().isEmpty()); + + QTest::ignoreMessage(QtWarningMsg, "QActionState::addEntryAction: cannot add null action"); + s1->addEntryAction(0); + QTest::ignoreMessage(QtWarningMsg, "QActionState::addExitAction: cannot add null action"); + s1->addExitAction(0); + QTest::ignoreMessage(QtWarningMsg, "QActionState::removeEntryAction: cannot remove null action"); + s1->removeEntryAction(0); + QTest::ignoreMessage(QtWarningMsg, "QActionState::removeExitAction: cannot remove null action"); + s1->removeExitAction(0); + + QFinalState *s2 = new QFinalState(machine.rootState()); + s1->addTransition(s2); + + machine.setInitialState(s1); + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + + QObject *obj = new QObject(); + QStateInvokeMethodAction *ima = new QStateInvokeMethodAction(obj, "deleteLater"); + QPointer<QObject> ptr(obj); + QVERIFY(ptr != 0); + s1->addEntryAction(ima); + finishedSpy.clear(); + machine.start(); + QTRY_COMPARE(finishedSpy.count(), 1); + QCoreApplication::processEvents(); + QVERIFY(ptr == 0); + + s1->removeEntryAction(ima); + + s1->invokeMethodOnEntry(ima, "deleteLater"); + QCOMPARE(s1->entryActions().size(), 1); + + ptr = ima; + QVERIFY(ptr != 0); + finishedSpy.clear(); + machine.start(); + QTRY_COMPARE(finishedSpy.count(), 1); + QCoreApplication::processEvents(); + QVERIFY(ptr == 0); + + while (!s1->entryActions().isEmpty()) { + QStateAction *act = s1->entryActions().first(); + s1->removeEntryAction(act); + delete act; + } + + TestStateAction *act1 = new TestStateAction(); + s1->addEntryAction(act1); + TestStateAction *act2 = new TestStateAction(); + s1->addExitAction(act2); + QVERIFY(!act1->didExecute()); + QVERIFY(!act2->didExecute()); + + finishedSpy.clear(); + machine.start(); + QTRY_COMPARE(finishedSpy.count(), 1); + + QVERIFY(act1->didExecute()); + QVERIFY(act2->didExecute()); + + QCOMPARE(s1->entryActions().size(), 1); + QCOMPARE(s2->entryActions().size(), 0); + s2->addEntryAction(act1); // should remove it from s1 + QCOMPARE(s1->entryActions().size(), 0); + QCOMPARE(s2->entryActions().size(), 1); + QCOMPARE(act1->parent(), (QObject*)s2); + + QCOMPARE(s2->exitActions().size(), 0); + s2->addExitAction(act1); // should remove entry action + QCOMPARE(s2->exitActions().size(), 1); + QCOMPARE(s2->entryActions().size(), 0); + QCOMPARE(act1->parent(), (QObject*)s2); +} + +void tst_QStateMachine::transitionActions() +{ + QStateMachine machine; + QState *s1 = new QState(machine.rootState()); + + QFinalState *s2 = new QFinalState(machine.rootState()); + QTransition *trans = new EventTransition(QEvent::User, s2); + s1->addTransition(trans); + QVERIFY(trans->actions().isEmpty()); + QTest::ignoreMessage(QtWarningMsg, "QTransition::addAction: cannot add null action"); + trans->addAction(0); + QVERIFY(trans->actions().isEmpty()); + + TestStateAction *act = new TestStateAction(); + trans->addAction(act); + QCOMPARE(trans->actions().size(), 1); + QCOMPARE(trans->actions().at(0), (QStateAction*)act); + QCOMPARE(act->parent(), (QObject*)trans); + QVERIFY(!act->didExecute()); + + trans->removeAction(act); + QVERIFY(trans->actions().isEmpty()); + QCOMPARE(act->parent(), (QObject*)0); + + trans->addAction(act); + + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + QTRY_COMPARE(finishedSpy.count(), 1); + QVERIFY(act->didExecute()); + + trans->invokeMethodOnTransition(act, "deleteLater"); + + QPointer<QStateAction> ptr(act); + QVERIFY(ptr != 0); + finishedSpy.clear(); + machine.start(); + QCoreApplication::processEvents(); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + QTRY_COMPARE(finishedSpy.count(), 1); + QCoreApplication::processEvents(); + QVERIFY(ptr == 0); +} + +void tst_QStateMachine::defaultGlobalRestorePolicy() +{ + QStateMachine machine; + + QObject *propertyHolder = new QObject(); + propertyHolder->setProperty("a", 1); + propertyHolder->setProperty("b", 2); + + QState *s1 = new QState(machine.rootState()); + s1->setPropertyOnEntry(propertyHolder, "a", 3); + + QState *s2 = new QState(machine.rootState()); + s2->setPropertyOnEntry(propertyHolder, "b", 4); + + QState *s3 = new QState(machine.rootState()); + + s1->addTransition(new EventTransition(QEvent::User, s2)); + s2->addTransition(new EventTransition(QEvent::User, s3)); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(propertyHolder->property("a").toInt(), 3); + QCOMPARE(propertyHolder->property("b").toInt(), 2); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(propertyHolder->property("a").toInt(), 3); + QCOMPARE(propertyHolder->property("b").toInt(), 4); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(propertyHolder->property("a").toInt(), 3); + QCOMPARE(propertyHolder->property("b").toInt(), 4); +} + +void tst_QStateMachine::restorePolicyNotInherited() +{ + QStateMachine machine; + + QObject *propertyHolder = new QObject(); + propertyHolder->setProperty("a", 1); + propertyHolder->setProperty("b", 2); + + QState *parentState = new QState(machine.rootState()); + parentState->setObjectName("parentState"); + parentState->setRestorePolicy(QState::RestoreProperties); + + QState *s1 = new QState(parentState); + s1->setObjectName("s1"); + s1->setPropertyOnEntry(propertyHolder, "a", 3); + parentState->setInitialState(s1); + + QState *s2 = new QState(parentState); + s2->setObjectName("s2"); + s2->setPropertyOnEntry(propertyHolder, "b", 4); + + QState *s3 = new QState(parentState); + s3->setObjectName("s3"); + + s1->addTransition(new EventTransition(QEvent::User, s2)); + s2->addTransition(new EventTransition(QEvent::User, s3)); + + machine.setInitialState(parentState); + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(propertyHolder->property("a").toInt(), 3); + QCOMPARE(propertyHolder->property("b").toInt(), 2); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(propertyHolder->property("a").toInt(), 3); + QCOMPARE(propertyHolder->property("b").toInt(), 4); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(propertyHolder->property("a").toInt(), 3); + QCOMPARE(propertyHolder->property("b").toInt(), 4); + +} + +void tst_QStateMachine::globalRestorePolicySetToDoNotRestore() +{ + QStateMachine machine; + machine.setGlobalRestorePolicy(QState::DoNotRestoreProperties); + + QObject *propertyHolder = new QObject(); + propertyHolder->setProperty("a", 1); + propertyHolder->setProperty("b", 2); + + QState *s1 = new QState(machine.rootState()); + s1->setPropertyOnEntry(propertyHolder, "a", 3); + + QState *s2 = new QState(machine.rootState()); + s2->setPropertyOnEntry(propertyHolder, "b", 4); + + QState *s3 = new QState(machine.rootState()); + + s1->addTransition(new EventTransition(QEvent::User, s2)); + s2->addTransition(new EventTransition(QEvent::User, s3)); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(propertyHolder->property("a").toInt(), 3); + QCOMPARE(propertyHolder->property("b").toInt(), 2); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(propertyHolder->property("a").toInt(), 3); + QCOMPARE(propertyHolder->property("b").toInt(), 4); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(propertyHolder->property("a").toInt(), 3); + QCOMPARE(propertyHolder->property("b").toInt(), 4); +} + +void tst_QStateMachine::setRestorePolicyToDoNotRestore() +{ + QObject *object = new QObject(); + object->setProperty("a", 1); + object->setProperty("b", 2); + + QStateMachine machine; + + QState *S1 = new QState(); + S1->setObjectName("S1"); + S1->setPropertyOnEntry(object, "a", 3); + S1->setRestorePolicy(QState::DoNotRestoreProperties); + machine.addState(S1); + + QState *S2 = new QState(); + S2->setObjectName("S2"); + S2->setPropertyOnEntry(object, "b", 5); + S2->setRestorePolicy(QState::DoNotRestoreProperties); + machine.addState(S2); + + QState *S3 = new QState(); + S3->setObjectName("S3"); + S3->setRestorePolicy(QState::DoNotRestoreProperties); + machine.addState(S3); + + QFinalState *S4 = new QFinalState(); + machine.addState(S4); + + S1->addTransition(new EventTransition(QEvent::User, S2)); + S2->addTransition(new EventTransition(QEvent::User, S3)); + S3->addTransition(S4); + + machine.setInitialState(S1); + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(object->property("a").toInt(), 3); + QCOMPARE(object->property("b").toInt(), 2); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(object->property("a").toInt(), 3); + QCOMPARE(object->property("b").toInt(), 5); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(object->property("a").toInt(), 3); + QCOMPARE(object->property("b").toInt(), 5); +} + +void tst_QStateMachine::setGlobalRestorePolicyToGlobalRestore() +{ + s_countWarnings = false; + QStateMachine machine; + machine.setGlobalRestorePolicy(QState::GlobalRestorePolicy); + + QCOMPARE(machine.globalRestorePolicy(), QState::DoNotRestoreProperties); + QCOMPARE(s_msgType, QtWarningMsg); + + s_msgType = QtDebugMsg; + machine.setGlobalRestorePolicy(QState::RestoreProperties); + machine.setGlobalRestorePolicy(QState::GlobalRestorePolicy); + + QCOMPARE(machine.globalRestorePolicy(), QState::RestoreProperties); + QCOMPARE(s_msgType, QtWarningMsg); +} + +void tst_QStateMachine::restorePolicyOnChildState() +{ + QStateMachine machine; + + QObject *propertyHolder = new QObject(); + propertyHolder->setProperty("a", 1); + propertyHolder->setProperty("b", 2); + + QState *parentState = new QState(machine.rootState()); + parentState->setObjectName("parentState"); + + QState *s1 = new QState(parentState); + s1->setRestorePolicy(QState::RestoreProperties); + s1->setObjectName("s1"); + s1->setPropertyOnEntry(propertyHolder, "a", 3); + parentState->setInitialState(s1); + + QState *s2 = new QState(parentState); + s2->setRestorePolicy(QState::RestoreProperties); + s2->setObjectName("s2"); + s2->setPropertyOnEntry(propertyHolder, "b", 4); + + QState *s3 = new QState(parentState); + s3->setRestorePolicy(QState::RestoreProperties); + s3->setObjectName("s3"); + + s1->addTransition(new EventTransition(QEvent::User, s2)); + s2->addTransition(new EventTransition(QEvent::User, s3)); + + machine.setInitialState(parentState); + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(propertyHolder->property("a").toInt(), 3); + QCOMPARE(propertyHolder->property("b").toInt(), 2); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(propertyHolder->property("a").toInt(), 1); + QCOMPARE(propertyHolder->property("b").toInt(), 4); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(propertyHolder->property("a").toInt(), 1); + QCOMPARE(propertyHolder->property("b").toInt(), 2); +} + +void tst_QStateMachine::globalRestorePolicySetToRestore() +{ + QStateMachine machine; + machine.setGlobalRestorePolicy(QState::RestoreProperties); + + QObject *propertyHolder = new QObject(); + propertyHolder->setProperty("a", 1); + propertyHolder->setProperty("b", 2); + + QState *s1 = new QState(machine.rootState()); + s1->setPropertyOnEntry(propertyHolder, "a", 3); + + QState *s2 = new QState(machine.rootState()); + s2->setPropertyOnEntry(propertyHolder, "b", 4); + + QState *s3 = new QState(machine.rootState()); + + s1->addTransition(new EventTransition(QEvent::User, s2)); + s2->addTransition(new EventTransition(QEvent::User, s3)); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + QCOMPARE(propertyHolder->property("a").toInt(), 3); + QCOMPARE(propertyHolder->property("b").toInt(), 2); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(propertyHolder->property("a").toInt(), 1); + QCOMPARE(propertyHolder->property("b").toInt(), 4); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + QCOMPARE(propertyHolder->property("a").toInt(), 1); + QCOMPARE(propertyHolder->property("b").toInt(), 2); +} + +void tst_QStateMachine::mixedRestoreProperties() +{ + QStateMachine machine; + + QObject *propertyHolder = new QObject(); + propertyHolder->setProperty("a", 1); + + QState *s1 = new QState(machine.rootState()); + s1->setRestorePolicy(QState::RestoreProperties); + s1->setPropertyOnEntry(propertyHolder, "a", 3); + + QState *s2 = new QState(machine.rootState()); + s2->setPropertyOnEntry(propertyHolder, "a", 4); + + QState *s3 = new QState(machine.rootState()); + + QState *s4 = new QState(machine.rootState()); + s4->setPropertyOnEntry(propertyHolder, "a", 5); + + QState *s5 = new QState(machine.rootState()); + s5->setRestorePolicy(QState::RestoreProperties); + s5->setPropertyOnEntry(propertyHolder, "a", 6); + + s1->addTransition(new EventTransition(QEvent::User, s2)); + s2->addTransition(new EventTransition(QEvent::User, s3)); + s3->addTransition(new EventTransition(QEvent::User, s4)); + s4->addTransition(new EventTransition(QEvent::User, s5)); + s5->addTransition(new EventTransition(QEvent::User, s3)); + + machine.setInitialState(s1); + machine.start(); + QCoreApplication::processEvents(); + + // Enter s1, save current + QCOMPARE(propertyHolder->property("a").toInt(), 3); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + // Enter s2, restorePolicy == DoNotRestore, so restore all properties + QCOMPARE(propertyHolder->property("a").toInt(), 4); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + // Enter s3 + QCOMPARE(propertyHolder->property("a").toInt(), 4); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + // Enter s4 + QCOMPARE(propertyHolder->property("a").toInt(), 5); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + // Enter s5, save current + QCOMPARE(propertyHolder->property("a").toInt(), 6); + + machine.postEvent(new QEvent(QEvent::User)); + QCoreApplication::processEvents(); + + // Enter s3, restore + QCOMPARE(propertyHolder->property("a").toInt(), 5); +} + +void tst_QStateMachine::transitionWithParent() +{ + QStateMachine machine; + QState *s1 = new QState(machine.rootState()); + QState *s2 = new QState(machine.rootState()); + EventTransition *trans = new EventTransition(QEvent::User, s2, s1); + QCOMPARE(trans->sourceState(), s1); + QCOMPARE(trans->targetState(), (QAbstractState*)s2); + QCOMPARE(trans->targetStates().size(), 1); + QCOMPARE(trans->targetStates().at(0), (QAbstractState*)s2); +} + +QTEST_MAIN(tst_QStateMachine) +#include "tst_qstatemachine.moc" diff --git a/tests/benchmarks/benchmarks.pro b/tests/benchmarks/benchmarks.pro index 8e2c243..4c39373 100644 --- a/tests/benchmarks/benchmarks.pro +++ b/tests/benchmarks/benchmarks.pro @@ -1,6 +1,7 @@ TEMPLATE = subdirs SUBDIRS = containers-associative \ containers-sequential \ + qanimation \ qbytearray \ qpainter \ qtestlib-simple events \ diff --git a/tests/benchmarks/qanimation/dummyanimation.cpp b/tests/benchmarks/qanimation/dummyanimation.cpp new file mode 100644 index 0000000..6fb1d0a --- /dev/null +++ b/tests/benchmarks/qanimation/dummyanimation.cpp @@ -0,0 +1,20 @@ +#include "dummyanimation.h" +#include "dummyobject.h" + + +DummyAnimation::DummyAnimation(DummyObject *d) : m_dummy(d) +{ +} + +void DummyAnimation::updateCurrentValue(const QVariant &value) +{ + if (state() == Stopped) + return; + if (m_dummy) + m_dummy->setRect(value.toRect()); +} + +void DummyAnimation::updateState(State state) +{ + Q_UNUSED(state); +} diff --git a/tests/benchmarks/qanimation/dummyanimation.h b/tests/benchmarks/qanimation/dummyanimation.h new file mode 100644 index 0000000..fe6592b --- /dev/null +++ b/tests/benchmarks/qanimation/dummyanimation.h @@ -0,0 +1,19 @@ +#include <QtGui> + +#ifndef _DUMMYANIMATION_H__ + +class DummyObject; + +class DummyAnimation : public QVariantAnimation +{ +public: + DummyAnimation(DummyObject *d); + + void updateCurrentValue(const QVariant &value); + void updateState(State state); + +private: + DummyObject *m_dummy; +}; + +#endif
\ No newline at end of file diff --git a/tests/benchmarks/qanimation/dummyobject.cpp b/tests/benchmarks/qanimation/dummyobject.cpp new file mode 100644 index 0000000..bd76388 --- /dev/null +++ b/tests/benchmarks/qanimation/dummyobject.cpp @@ -0,0 +1,25 @@ +#include "dummyobject.h" + +DummyObject::DummyObject() +{ +} + +QRect DummyObject::rect() const +{ + return m_rect; +} + +void DummyObject::setRect(const QRect &r) +{ + m_rect = r; +} + +float DummyObject::opacity() const +{ + return m_opacity; +} + +void DummyObject::setOpacity(float o) +{ + m_opacity = o; +} diff --git a/tests/benchmarks/qanimation/dummyobject.h b/tests/benchmarks/qanimation/dummyobject.h new file mode 100644 index 0000000..c989662 --- /dev/null +++ b/tests/benchmarks/qanimation/dummyobject.h @@ -0,0 +1,23 @@ +#include <QtGui> + +#ifndef _DUMMYOBJECT_H__ + +class DummyObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(QRect rect READ rect WRITE setRect) + Q_PROPERTY(float opacity READ opacity WRITE setOpacity) +public: + DummyObject(); + QRect rect() const; + void setRect(const QRect &r); + float opacity() const; + void setOpacity(float); + +private: + QRect m_rect; + float m_opacity; +}; + + +#endif
\ No newline at end of file diff --git a/tests/benchmarks/qanimation/main.cpp b/tests/benchmarks/qanimation/main.cpp new file mode 100644 index 0000000..7bbcffb --- /dev/null +++ b/tests/benchmarks/qanimation/main.cpp @@ -0,0 +1,173 @@ +#include <QtGui> +#include <qtest.h> + +#include "dummyobject.h" +#include "dummyanimation.h" +#include "rectanimation.h" + +#define ITERATION_COUNT 10e3 + +class tst_qanimation : public QObject +{ + Q_OBJECT +private slots: + void itemAnimation(); + void itemAnimation_data() { data();} + void itemPropertyAnimation(); + void itemPropertyAnimation_data() { data();} + void dummyAnimation(); + void dummyAnimation_data() { data();} + void dummyPropertyAnimation(); + void dummyPropertyAnimation_data() { data();} + void rectAnimation(); + void rectAnimation_data() { data();} + + void floatAnimation_data() { data(); } + void floatAnimation(); + +private: + void data(); +}; + + +void tst_qanimation::data() +{ + QTest::addColumn<bool>("paused"); + QTest::newRow("NotRunning") << false; + QTest::newRow("Paused") << true; +} + +void tst_qanimation::itemAnimation() +{ + QFETCH(bool, paused); + QGraphicsWidget item; + + //first the item animation + { + QItemAnimation anim(&item, QItemAnimation::Position); + anim.setDuration(ITERATION_COUNT); + anim.setStartValue(QPointF(0,0)); + anim.setEndValue(QPointF(ITERATION_COUNT,ITERATION_COUNT)); + if (paused) + anim.pause(); + QBENCHMARK { + for(int i = 0; i < anim.duration(); ++i) { + anim.setCurrentTime(i); + } + } + } +} + +void tst_qanimation::itemPropertyAnimation() +{ + QFETCH(bool, paused); + QGraphicsWidget item; + + //then the property animation + { + QPropertyAnimation anim(&item, "pos"); + anim.setDuration(ITERATION_COUNT); + anim.setStartValue(QPointF(0,0)); + anim.setEndValue(QPointF(ITERATION_COUNT,ITERATION_COUNT)); + if (paused) + anim.pause(); + QBENCHMARK { + for(int i = 0; i < ITERATION_COUNT; ++i) { + anim.setCurrentTime(i); + } + } + } + +} + +void tst_qanimation::dummyAnimation() +{ + QFETCH(bool, paused); + DummyObject dummy; + + //first the dummy animation + { + DummyAnimation anim(&dummy); + anim.setDuration(ITERATION_COUNT); + anim.setStartValue(QRect(0, 0, 0, 0)); + anim.setEndValue(QRect(0, 0, ITERATION_COUNT,ITERATION_COUNT)); + if (paused) + anim.pause(); + QBENCHMARK { + for(int i = 0; i < anim.duration(); ++i) { + anim.setCurrentTime(i); + } + } + } +} + +void tst_qanimation::dummyPropertyAnimation() +{ + QFETCH(bool, paused); + DummyObject dummy; + + //then the property animation + { + QPropertyAnimation anim(&dummy, "rect"); + anim.setDuration(ITERATION_COUNT); + anim.setStartValue(QRect(0, 0, 0, 0)); + anim.setEndValue(QRect(0, 0, ITERATION_COUNT,ITERATION_COUNT)); + if (paused) + anim.pause(); + QBENCHMARK { + for(int i = 0; i < ITERATION_COUNT; ++i) { + anim.setCurrentTime(i); + } + } + } +} + +void tst_qanimation::rectAnimation() +{ + //this is the simplest animation you can do + QFETCH(bool, paused); + DummyObject dummy; + + //then the property animation + { + RectAnimation anim(&dummy); + anim.setDuration(ITERATION_COUNT); + anim.setStartValue(QRect(0, 0, 0, 0)); + anim.setEndValue(QRect(0, 0, ITERATION_COUNT,ITERATION_COUNT)); + if (paused) + anim.pause(); + QBENCHMARK { + for(int i = 0; i < ITERATION_COUNT; ++i) { + anim.setCurrentTime(i); + } + } + } +} + +void tst_qanimation::floatAnimation() +{ + //this is the simplest animation you can do + QFETCH(bool, paused); + DummyObject dummy; + + //then the property animation + { + QPropertyAnimation anim(&dummy, "opacity"); + anim.setDuration(ITERATION_COUNT); + anim.setStartValue(0.f); + anim.setEndValue(1.f); + if (paused) + anim.pause(); + QBENCHMARK { + for(int i = 0; i < ITERATION_COUNT; ++i) { + anim.setCurrentTime(i); + } + } + } +} + + + +QTEST_MAIN(tst_qanimation) + +#include "main.moc" diff --git a/tests/benchmarks/qanimation/qanimation.pro b/tests/benchmarks/qanimation/qanimation.pro new file mode 100644 index 0000000..55cd75e --- /dev/null +++ b/tests/benchmarks/qanimation/qanimation.pro @@ -0,0 +1,18 @@ +load(qttest_p4) +TEMPLATE = app +TARGET = tst_qanimation +DEPENDPATH += . +INCLUDEPATH += . + +CONFIG += release +#CONFIG += debug + + +SOURCES += main.cpp \ + dummyobject.cpp \ + dummyanimation.cpp \ + rectanimation.cpp + +HEADERS += dummyobject.h \ + dummyanimation.h \ + rectanimation.h diff --git a/tests/benchmarks/qanimation/rectanimation.cpp b/tests/benchmarks/qanimation/rectanimation.cpp new file mode 100644 index 0000000..66c7a33 --- /dev/null +++ b/tests/benchmarks/qanimation/rectanimation.cpp @@ -0,0 +1,58 @@ +#include "rectanimation.h" +#include "dummyobject.h" + +static inline int interpolateInteger(int from, int to, qreal progress) +{ + return from + (to - from) * progress; +} + + +RectAnimation::RectAnimation(DummyObject *obj) : object(obj), dura(250) +{ +} + +void RectAnimation::setEndValue(const QRect &rect) +{ + end = rect; +} + +void RectAnimation::setStartValue(const QRect &rect) +{ + start = rect; +} + +void RectAnimation::setDuration(int d) +{ + dura = d; +} + +int RectAnimation::duration() const +{ + return dura; +} + + +void RectAnimation::updateCurrentTime(int msecs) +{ + qreal progress = easing.valueForProgress( qreal(msecs) / qreal(dura) ); + QRect now; + now.setCoords(interpolateInteger(start.left(), end.left(), progress), + interpolateInteger(start.top(), end.top(), progress), + interpolateInteger(start.right(), end.right(), progress), + interpolateInteger(start.bottom(), end.bottom(), progress)); + + bool changed = (now != current); + if (changed) + current = now; + + if (state() == Stopped) + return; + + if (object) + object->setRect(current); +} + +void RectAnimation::updateState(QAbstractAnimation::State state) +{ + Q_UNUSED(state); +} diff --git a/tests/benchmarks/qanimation/rectanimation.h b/tests/benchmarks/qanimation/rectanimation.h new file mode 100644 index 0000000..c9770c4 --- /dev/null +++ b/tests/benchmarks/qanimation/rectanimation.h @@ -0,0 +1,30 @@ +#include <QtGui> + +#ifndef _RECTANIMATION_H__ + +class DummyObject; + +//this class is even simpler than the dummy +//and uses no QVariant at all +class RectAnimation : public QAbstractAnimation +{ +public: + RectAnimation(DummyObject *obj); + + void setEndValue(const QRect &rect); + void setStartValue(const QRect &rect); + + void setDuration(int d); + int duration() const; + + virtual void updateCurrentTime(int msecs); + virtual void updateState(QAbstractAnimation::State state); + +private: + DummyObject *object; + QEasingCurve easing; + QRect start, end, current; + int dura; +}; + +#endif
\ No newline at end of file diff --git a/tests/benchmarks/qvariant/qvariant.pro b/tests/benchmarks/qvariant/qvariant.pro index 68b4a97..63b5442 100644 --- a/tests/benchmarks/qvariant/qvariant.pro +++ b/tests/benchmarks/qvariant/qvariant.pro @@ -3,7 +3,6 @@ TEMPLATE = app TARGET = tst_qvariant DEPENDPATH += . INCLUDEPATH += . -QT -= gui CONFIG += release #CONFIG += debug diff --git a/tests/benchmarks/qvariant/tst_qvariant.cpp b/tests/benchmarks/qvariant/tst_qvariant.cpp index 4a7ad02..0eb1ae2 100644 --- a/tests/benchmarks/qvariant/tst_qvariant.cpp +++ b/tests/benchmarks/qvariant/tst_qvariant.cpp @@ -38,7 +38,9 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ + #include <QtCore> +#include <QtGui/QPixmap> #include <qtest.h> #define ITERATION_COUNT 1e5 @@ -47,64 +49,73 @@ class tst_qvariant : public QObject { Q_OBJECT private slots: + void testBound(); + void doubleVariantCreation(); void floatVariantCreation(); void rectVariantCreation(); void stringVariantCreation(); + void pixmapVariantCreation(); + void doubleVariantSetValue(); void floatVariantSetValue(); void rectVariantSetValue(); void stringVariantSetValue(); + void doubleVariantAssignment(); void floatVariantAssignment(); void rectVariantAssignment(); void stringVariantAssignment(); }; - -void tst_qvariant::doubleVariantCreation() +void tst_qvariant::testBound() { - double d = 0; + qreal d = qreal(.5); QBENCHMARK { for(int i = 0; i < ITERATION_COUNT; ++i) { - QVariant v(d); + d = qBound<qreal>(0, d, 1); } } } -void tst_qvariant::floatVariantCreation() +template <typename T> +static void variantCreation(T val) { - float f = 0; QBENCHMARK { for(int i = 0; i < ITERATION_COUNT; ++i) { - QVariant v(f); + QVariant v(val); } } } +void tst_qvariant::doubleVariantCreation() +{ + variantCreation<double>(0.0); +} + +void tst_qvariant::floatVariantCreation() +{ + variantCreation<float>(0.0f); +} + void tst_qvariant::rectVariantCreation() { - QRect r(1,2,3,4); - QBENCHMARK { - for(int i = 0; i < ITERATION_COUNT; ++i) { - QVariant v(r); - } - } + variantCreation<QRect>(QRect(1, 2, 3, 4)); } void tst_qvariant::stringVariantCreation() { - QString s; - QBENCHMARK { - for(int i = 0; i < ITERATION_COUNT; ++i) { - QVariant v(s); - } - } + variantCreation<QString>(QString()); } -void tst_qvariant::doubleVariantSetValue() +void tst_qvariant::pixmapVariantCreation() +{ + variantCreation<QPixmap>(QPixmap()); +} + +template <typename T> +static void variantSetValue(T d) { - double d = 0; QVariant v; QBENCHMARK { for(int i = 0; i < ITERATION_COUNT; ++i) { @@ -113,42 +124,29 @@ void tst_qvariant::doubleVariantSetValue() } } +void tst_qvariant::doubleVariantSetValue() +{ + variantSetValue<double>(0.0); +} + void tst_qvariant::floatVariantSetValue() { - float f = 0; - QVariant v; - QBENCHMARK { - for(int i = 0; i < ITERATION_COUNT; ++i) { - qVariantSetValue(v, f); - } - } + variantSetValue<float>(0.0f); } void tst_qvariant::rectVariantSetValue() { - QRect r; - QVariant v; - QBENCHMARK { - for(int i = 0; i < ITERATION_COUNT; ++i) { - qVariantSetValue(v, r); - } - } + variantSetValue<QRect>(QRect()); } void tst_qvariant::stringVariantSetValue() { - QString s; - QVariant v; - QBENCHMARK { - for(int i = 0; i < ITERATION_COUNT; ++i) { - qVariantSetValue(v, s); - } - } + variantSetValue<QString>(QString()); } -void tst_qvariant::doubleVariantAssignment() +template <typename T> +static void variantAssignment(T d) { - double d = 0; QVariant v; QBENCHMARK { for(int i = 0; i < ITERATION_COUNT; ++i) { @@ -157,37 +155,24 @@ void tst_qvariant::doubleVariantAssignment() } } +void tst_qvariant::doubleVariantAssignment() +{ + variantAssignment<double>(0.0); +} + void tst_qvariant::floatVariantAssignment() { - float f = 0; - QVariant v; - QBENCHMARK { - for(int i = 0; i < ITERATION_COUNT; ++i) { - v = f; - } - } + variantAssignment<float>(0.0f); } void tst_qvariant::rectVariantAssignment() { - QRect r; - QVariant v; - QBENCHMARK { - for(int i = 0; i < ITERATION_COUNT; ++i) { - v = r; - } - } + variantAssignment<QRect>(QRect()); } void tst_qvariant::stringVariantAssignment() { - QString s; - QVariant v; - QBENCHMARK { - for(int i = 0; i < ITERATION_COUNT; ++i) { - v = s; - } - } + variantAssignment<QString>(QString()); } QTEST_MAIN(tst_qvariant) |