diff options
Diffstat (limited to 'src/corelib')
154 files changed, 15053 insertions, 2189 deletions
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..fcc63a4 --- /dev/null +++ b/src/corelib/animation/qabstractanimation.cpp @@ -0,0 +1,733 @@ +/**************************************************************************** +** +** 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 is the base of all animations. + \since 4.6 + + The class defines the functions for the functionality shared by + all animations. By inheriting this class, you can create custom + animations that plug into the rest of the animation framework. + + The progress of an animation is given by its current time + (currentTime()), which is measured in milliseconds from the start + of the animation (0) to its end (duration()). The value is updated + automatically while the animation is running. It can also be set + directly with setCurrentTime(). + + At any point an animation is in one of three states: + \l{QAbstractAnimation::}{Running}, + \l{QAbstractAnimation::}{Stopped}, or + \l{QAbstractAnimation::}{Paused}--as defined by the + \l{QAbstractAnimation::}{State} enum. The current state can be + changed by calling start(), stop(), pause(), or resume(). An + animation will always reset its \l{currentTime()}{current time} + when it is started. If paused, it will continue with the same + current time when resumed. When an animation is stopped, it cannot + be resumed, but will keep its current time (until started again). + QAbstractAnimation will emit stateChanged() whenever its state + changes. + + An animation can loop any number of times by setting the loopCount + property. When an animation's current time reaches its duration(), + it will reset the current time and keep running. A loop count of 1 + (the default value) means that the animation will run one time. + Note that a duration of -1 means that the animation will run until + stopped; the current time will increase indefinitely. When the + current time equals duration() and the animation is in its + final loop, the \l{QAbstractAnimation::}{Stopped} state is + entered, and the finished() signal is emitted. + + QAbstractAnimation provides pure virtual functions used by + subclasses to track the progress of the animation: duration() and + updateCurrentTime(). The duration() function lets you report a + duration for the animation (as discussed above). The current time + is delivered by the animation framework through calls to + updateCurrentTime(). By reimplementing this function, you can + track the animation progress. Note that neither the interval + between calls nor the number of calls to this function are + defined; though, it will normally be 60 updates per second. + + By reimplementing updateState(), you can track the animation's + state changes, which is particularly useful for animations that + are not driven by time. + + \sa QVariantAnimation, QPropertyAnimation, 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::currentLoopChanged(int currentLoop) + + QAbstractAnimation emits this signal whenever the current loop + changes. \a currentLoop is the current loop. + + \sa currentLoop(), loopCount() +*/ + +/*! + \fn QAbstractAnimation::directionChanged(QAbstractAnimation::Direction newDirection); + + QAbstractAnimation emits this signal whenever the direction has been + changed. \a newDirection is the new direction. + + \sa direction +*/ + +#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> + +#ifndef QT_NO_ANIMATION + +#define DEFAULT_TIMER_INTERVAL 16 + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(QThreadStorage<QUnifiedTimer *>, unifiedTimer) + +QUnifiedTimer::QUnifiedTimer() : QObject(), lastTick(0), timingInterval(DEFAULT_TIMER_INTERVAL), consistentTiming(false) +{ +} + +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 + const int oldLastTick = lastTick; + if (time.isValid()) + lastTick = consistentTiming ? oldLastTick + timingInterval : 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(timingInterval, 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 oldCurrentLoop = currentLoop; + 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(loopCount == -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 || loopCount < 0 + || (oldDirection == QAbstractAnimation::Forward && (oldCurrentTime * (oldCurrentLoop + 1)) == (dura * loopCount)) + || (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 +*/ +QAbstractAnimation::QAbstractAnimation(QObject *parent) + : QObject(*new QAbstractAnimationPrivate, 0) +{ + // Allow auto-add on reparent + setParent(parent); +} + +/*! + \internal +*/ +QAbstractAnimation::QAbstractAnimation(QAbstractAnimationPrivate &dd, QObject *parent) + : QObject(dd, 0) +{ + // Allow auto-add on reparent + setParent(parent); +} + +/*! + 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->currentLoop = d->loopCount - 1; + } else { + d->currentTime = 0; + d->currentLoop = 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, loopCount is ignored. +*/ + +/*! + \property QAbstractAnimation::loopCount + \brief the loop count of the animation + + This property describes the loop 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::loopCount() const +{ + Q_D(const QAbstractAnimation); + return d->loopCount; +} +void QAbstractAnimation::setLoopCount(int loopCount) +{ + Q_D(QAbstractAnimation); + d->loopCount = loopCount; +} + +/*! + \property QAbstractAnimation::currentLoop + \brief the current loop of the animation + + This property describes the current loop of the animation. By default, + the animation's loop count is 1, and so the current loop will + always be 0. If the loop count is 2 and the animation runs past its + duration, it will automatically rewind and restart at current time 0, and + current loop 1, and so on. + + When the current loop changes, QAbstractAnimation emits the + currentLoopChanged() signal. +*/ +int QAbstractAnimation::currentLoop() const +{ + Q_D(const QAbstractAnimation); + return d->currentLoop; +} + +/*! + \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 loop 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 loopCount +*/ + +/*! + Returns the total and effective duration of the animation, including the + loop count. + + \sa duration(), currentTime +*/ +int QAbstractAnimation::totalDuration() const +{ + Q_D(const QAbstractAnimation); + if (d->loopCount < 0) + return -1; + int dura = duration(); + if (dura == -1) + return -1; + return dura * d->loopCount; +} + +/*! + \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 loopCount 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 loopCount + */ +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->loopCount < 0 || dura == -1) ? -1 : dura * d->loopCount; + if (totalDura != -1) + msecs = qMin(totalDura, msecs); + d->totalCurrentTime = msecs; + + // Update new values. + int oldLoop = d->currentLoop; + d->currentLoop = ((dura <= 0) ? 0 : (msecs / dura)); + if (d->currentLoop == d->loopCount) { + //we're at the end + d->currentTime = qMax(0, dura); + d->currentLoop = qMax(0, d->loopCount - 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->currentLoop; + } + } + + updateCurrentTime(msecs); + if (d->currentLoop != oldLoop) + emit currentLoopChanged(d->currentLoop); + + // 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 currentLoop() > loopCount() - 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..d6d50dc --- /dev/null +++ b/src/corelib/animation/qabstractanimation.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_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 loopCount READ loopCount WRITE setLoopCount) + Q_PROPERTY(int currentTime READ currentTime WRITE setCurrentTime) + Q_PROPERTY(int currentLoop READ currentLoop NOTIFY currentLoopChanged) + 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 loopCount() const; + void setLoopCount(int loopCount); + int currentLoop() 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 currentLoopChanged(int currentLoop); + 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); + +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..7a4bfcf --- /dev/null +++ b/src/corelib/animation/qabstractanimation_p.h @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** 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> +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QAnimationGroup; +class QAbstractAnimation; +class QAbstractAnimationPrivate : public QObjectPrivate +{ +public: + QAbstractAnimationPrivate() + : state(QAbstractAnimation::Stopped), + direction(QAbstractAnimation::Forward), + deleteWhenStopped(false), + totalCurrentTime(0), + currentTime(0), + loopCount(1), + currentLoop(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 loopCount; + int currentLoop; + + QAnimationGroup *group; + +private: + Q_DECLARE_PUBLIC(QAbstractAnimation) +}; + + +class QUnifiedTimer : public QObject +{ +private: + QUnifiedTimer(); + +public: + //XXX this is needed by dui + static Q_CORE_EXPORT QUnifiedTimer *instance(); + + void registerAnimation(QAbstractAnimation *animation); + void unregisterAnimation(QAbstractAnimation *animation); + + //defines the timing interval. Default is DEFAULT_TIMER_INTERVAL + void setTimingInterval(int interval) + { + timingInterval = interval; + if (animationTimer.isActive()) { + //we changed the timing interval + animationTimer.start(timingInterval, this); + } + } + + /* + this allows to have a consistent timer interval at each tick from the timer + not taking the real time that passed into account. + */ + void setConsistentTiming(bool consistent) { consistentTiming = consistent; } + + int elapsedTime() const { return lastTick; } + +protected: + void timerEvent(QTimerEvent *); + void updateTimer(); + +private: + void updateRecentlyStartedAnimations(); + + QBasicTimer animationTimer, startStopAnimationTimer; + QTime time; + int lastTick; + int timingInterval; + bool consistentTiming; + 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..e192a6c --- /dev/null +++ b/src/corelib/animation/qanimationgroup.cpp @@ -0,0 +1,309 @@ +/**************************************************************************** +** +** 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 class is an abstract base class for groups of animations. + \since 4.6 + \ingroup animation + + An animation group is a container for animations (subclasses of + QAbstractAnimation). A group is usually responsible for managing + the \l{QAbstractAnimation::State}{state} of its animations, i.e., + it decides when to start, stop, resume, and pause them. Currently, + Qt provides two such groups: QParallelAnimationGroup and + QSequentialAnimationGroup. Look up their class descriptions for + details. + + Since QAnimationGroup inherits from QAbstractAnimation, you can + combine groups, and easily construct complex animation graphs. + You can query QAbstractAnimation for the group it belongs to + (using the \l{QAbstractAnimation::}{group()} function). + + To start a top-level animation group, you simply use the + \l{QAbstractAnimation::}{start()} function from + QAbstractAnimation. By a top-level animation group, we think of a + group that itself is not contained within another group. Starting + sub groups directly is not supported, and may lead to unexpected + behavior. + + \omit OK, we'll put in a snippet on this here \endomit + + QAnimationGroup 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. + + \omit OK, let's find a snippet here as well. \endomit + + QAnimationGroup takes ownership of the animations it manages, and + ensures that they are deleted when the animation group is deleted. + + You can also use a \l{The State Machine Framework}{state machine} + to create complex animations. The framework provides a special + state, QAnimationState, that plays an animation upon entry and + transitions to a new state when the animation has finished + playing. This technique can also be combined with using animation + groups. + + \sa QAbstractAnimation, QVariantAnimation, {The Animation Framework} +*/ + +#include "qanimationgroup.h" +#include <QtCore/qdebug.h> +#include <QtCore/qcoreevent.h> +#include "qanimationgroup_p.h" + +#ifndef QT_NO_ANIMATION + +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(). + + \note The group takes ownership of the animation. + + \sa removeAnimation() +*/ +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. + + \note The group takes ownership of the animation. + + \sa takeAnimationAt(), addAnimation(), indexOfAnimation(), removeAnimation() +*/ +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; + } + + if (QAnimationGroup *oldGroup = animation->group()) + oldGroup->removeAnimation(animation); + + d->animations.insert(index, 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); +} + +/*! + Returns the animation at \a index and removes it from the animation group. + + \note The ownership of the animation is transferred to the caller. + + \sa removeAnimation(), addAnimation(), insertAnimationAt(), indexOfAnimation() +*/ +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..263bc38 --- /dev/null +++ b/src/corelib/animation/qanimationgroup.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 QANIMATIONGROUP_H +#define QANIMATIONGROUP_H + +#include <QtCore/qabstractanimation.h> + +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..0f07138 --- /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 "private/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..6ec4679 --- /dev/null +++ b/src/corelib/animation/qparallelanimationgroup.cpp @@ -0,0 +1,323 @@ +/**************************************************************************** +** +** 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.6 + \ingroup animation + + QParallelAnimationGroup--a \l{QAnimationGroup}{container for + animations}--starts all its animations when it is + \l{QAbstractAnimation::start()}{started} itself, i.e., runs all + animations in parallel. The animation group finishes when the + longest lasting animation has finished. + + You can treat QParallelAnimation as any other QAbstractAnimation, + e.g., pause, resume, or add it to other animation groups. + + \code + QParallelAnimationGroup *group = new QParallelAnimationGroup; + group->addAnimation(anim1); + group->addAnimation(anim2); + + group->start(); + \endcode + + In this example, \c anim1 and \c anim2 are two + \l{QPropertyAnimation}s that have already been set up. + + \sa QAnimationGroup, QPropertyAnimation, {The Animation Framework} +*/ + + +#include "qparallelanimationgroup.h" +#include "qparallelanimationgroup_p.h" +//#define QANIMATION_DEBUG + +#ifndef QT_NO_ANIMATION + +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->currentLoop > d->lastLoop) { + // simulate completion of the loop + int dura = duration(); + if (dura > 0) { + for (int i = 0; i < d->animations.size(); ++i) { + d->animations.at(i)->setCurrentTime(dura); // will stop + } + } + } else if (d->currentLoop < d->lastLoop) { + // simulate completion of the loop seeking backwards + for (int i = 0; i < d->animations.size(); ++i) { + QAbstractAnimation *animation = d->animations.at(i); + animation->setCurrentTime(0); + animation->stop(); + } + } + + bool timeFwd = ((d->currentLoop == d->lastLoop && d->currentTime >= d->lastCurrentTime) + || d->currentLoop > d->lastLoop); +#ifdef QANIMATION_DEBUG + qDebug("QParallellAnimationGroup %5d: setCurrentTime(%d), loop:%d, last:%d, timeFwd:%d, lastcurrent:%d, %d", + __LINE__, d->currentTime, d->currentLoop, d->lastLoop, timeFwd, d->lastCurrentTime, state()); +#endif + // finally move into the actual time of the current loop + for (int i = 0; i < d->animations.size(); ++i) { + QAbstractAnimation *animation = d->animations.at(i); + const int dura = animation->totalDuration(); + if (dura == -1 && d->isUncontrolledAnimationFinished(animation)) + continue; + if (dura == -1 || (d->currentTime <= dura && dura != 0) + || (dura == 0 && d->currentLoop != d->lastLoop)) { + 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->lastLoop = d->currentLoop; + 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: + for (int i = 0; i < d->animations.size(); ++i) + d->animations.at(i)->stop(); + d->disconnectUncontrolledAnimations(); + break; + case Paused: + for (int i = 0; i < d->animations.size(); ++i) + d->animations.at(i)->pause(); + break; + case Running: + d->connectUncontrolledAnimations(); + for (int i = 0; i < d->animations.size(); ++i) { + QAbstractAnimation *animation = d->animations.at(i); + 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->loopCount() < 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; + for (int i = 0; i < animations.size(); ++i) + maxDuration = qMax(maxDuration, animations.at(i)->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); + + for (int i = 0; i < animations.size(); ++i) { + QAbstractAnimation *animation = animations.at(i); + if (animation->duration() == -1 || animation->loopCount() < 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) { + for (int i = 0; i < d->animations.size(); ++i) { + QAbstractAnimation *animation = d->animations.at(i); + animation->setDirection(direction); + } + } else { + if (direction == Forward) { + d->lastLoop = 0; + d->lastCurrentTime = 0; + } else { + // Looping backwards with loopCount == -1 does not really work well... + d->lastLoop = (d->loopCount == -1 ? 0 : d->loopCount - 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..57a8146 --- /dev/null +++ b/src/corelib/animation/qparallelanimationgroup.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 QPARALLELANIMATIONGROUP_H +#define QPARALLELANIMATIONGROUP_H + +#include <QtCore/qanimationgroup.h> + +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..ecd6791 --- /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 "private/qanimationgroup_p.h" +#include <QtCore/QHash> + +QT_BEGIN_NAMESPACE + +class QParallelAnimationGroupPrivate : public QAnimationGroupPrivate +{ + Q_DECLARE_PUBLIC(QParallelAnimationGroup) +public: + QParallelAnimationGroupPrivate() + : lastLoop(0), lastCurrentTime(0) + { + } + + QHash<QAbstractAnimation*, int> uncontrolledFinishTime; + int lastLoop; + 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..93043c2 --- /dev/null +++ b/src/corelib/animation/qpauseanimation.cpp @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** 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.6 + \ingroup animation + + If you wish to introduce a delay between animations in a + QSequentialAnimationGroup, you can insert a QPauseAnimation. This + class does not animate anything, but does not + \l{QAbstractAnimation::finished()}{finish} before a specified + number of milliseconds have elapsed from when it was started. You + specify the duration of the pause in the constructor. It can also + be set directly with setDuration(). + + It is not necessary to construct a QPauseAnimation yourself. + QSequentialAnimationGroup provides the convenience functions + \l{QSequentialAnimationGroup::}{addPause()} and + \l{QSequentialAnimationGroup::}{insertPauseAt()}. These functions + simply take the number of milliseconds the pause should last. + + \sa QSequentialAnimationGroup +*/ + +#include "qpauseanimation.h" +#include "qabstractanimation_p.h" + + +#ifndef QT_NO_ANIMATION + +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..cb6e041 --- /dev/null +++ b/src/corelib/animation/qpauseanimation.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 QPAUSEANIMATION_P_H +#define QPAUSEANIMATION_P_H + +#include <QtCore/qanimationgroup.h> + +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..65f1361 --- /dev/null +++ b/src/corelib/animation/qpropertyanimation.cpp @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** 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 Qt properties + \since 4.6 + \ingroup animation + + QPropertyAnimation interpolates over \l{Qt's Property System}{Qt + properties}. As property values are stored in \l{QVariant}s, the + class inherits QVariantAnimation, and supports animation of the + same \l{QVariant::Type}{variant types} as its super class. + + A class declaring properties must be a QObject. To make it + possible to animate a property, it must provide a setter (so that + QPropertyAnimation can set the property's value). Note that this + makes it possible to animate many of Qt's widgets. Let's look at + an example: + + \code + QPropertyAnimation animation(myWidget, "geometry"); + animation.setDuration(10000); + animation.setStartValue(QRect(0, 0, 100, 30)); + animation.setEndValue(QRect(250, 250, 100, 30)); + + animation.start(); + \endcode + + The property name and the QObject instance of which property + should be animated are passed to the constructor. You can then + specify the start and end value of the property. The procedure is + equal for properties in classes you have implemented + yourself--just check with QVariantAnimation that your QVariant + type is supported. + + The QVariantAnimation class description explains how to set up the + animation in detail. Note, however, that if a start value is not + set, the property will start at the value it had when the + QPropertyAnimation instance was created. + + QPropertyAnimation works like a charm on its own. For complex + animations that, for instance, contain several objects, + QAnimationGroup is provided. An animation group is an animation + that can contain other animations, and that can manage when its + animations are played. Look at QParallelAnimationGroup for an + example. + + \sa QVariantAnimation, QAnimationGroup, {The Animation Framework} +*/ + +#include "qpropertyanimation.h" +#include "qanimationgroup.h" +#include "qpropertyanimation_p.h" + +#include <QtCore/qmath.h> +#include <QtCore/qmutex.h> +#include <private/qmutexpool_p.h> + +#ifndef QT_NO_ANIMATION + +QT_BEGIN_NAMESPACE + +typedef QPair<QObject *, QByteArray> QPropertyAnimationPair; +typedef QHash<QPropertyAnimationPair, QPropertyAnimation*> QPropertyAnimationHash; +Q_GLOBAL_STATIC(QPropertyAnimationHash, _q_runningAnimations) + +void QPropertyAnimationPrivate::updateMetaProperty() +{ + if (!target || propertyName.isEmpty()) + return; + + if (!hasMetaProperty && !property.isValid()) { + const QMetaObject *mo = target->metaObject(); + propertyIndex = mo->indexOfProperty(propertyName); + if (propertyIndex != -1) { + hasMetaProperty = true; + property = mo->property(propertyIndex); + propertyType = property.userType(); + } else { + if (!target->dynamicPropertyNames().contains(propertyName)) + qWarning("QPropertyAnimation: you're trying to animate a non-existing property %s of your QObject", propertyName.constData()); + } + } + + if (property.isValid()) + convertValues(propertyType); +} + +void QPropertyAnimationPrivate::updateProperty(const QVariant &newValue) +{ + if (!target || state == QAbstractAnimation::Stopped) + return; + + if (hasMetaProperty) { + 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); + } +} + +void QPropertyAnimationPrivate::_q_targetDestroyed() +{ + Q_Q(QPropertyAnimation); + //we stop here so that this animation is removed from the global hash + q->stop(); + target = 0; +} + +/*! + 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; + + if (d->state != QAbstractAnimation::Stopped) { + qWarning("QPropertyAnimation::setTargetObject: you can't change the target of a running animation"); + return; + } + + //we need to get notified when the target is destroyed + if (d->target) + disconnect(d->target, SIGNAL(destroyed()), this, SLOT(_q_targetDestroyed())); + + if (target) + connect(target, SIGNAL(destroyed()), SLOT(_q_targetDestroyed())); + + d->target = target; + d->hasMetaProperty = false; + 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); + if (d->state != QAbstractAnimation::Stopped) { + qWarning("QPropertyAnimation::setPropertyName: you can't change the property name of a running animation"); + return; + } + + d->propertyName = propertyName; + d->hasMetaProperty = false; + 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 + + If the startValue is not defined when the state of the animation changes from Stopped to Running, + the current property value is used as the initial value for the animation. +*/ +void QPropertyAnimation::updateState(QAbstractAnimation::State oldState, + QAbstractAnimation::State newState) +{ + Q_D(QPropertyAnimation); + + if (!d->target) { + qWarning("QPropertyAnimation::updateState: Changing state of an animation without target"); + return; + } + + QVariantAnimation::updateState(oldState, newState); + + QPropertyAnimation *animToStop = 0; + { + QPropertyAnimationHash * hash = _q_runningAnimations(); + QMutexLocker locker(QMutexPool::globalInstanceGet(hash)); + QPropertyAnimationPair key(d->target, d->propertyName); + if (newState == Running) { + d->updateMetaProperty(); + animToStop = hash->value(key, 0); + hash->insert(key, this); + // update the default start value + if (oldState == Stopped) { + d->setDefaultStartValue(d->target->property(d->propertyName.constData())); + } + } else if (hash->value(key) == this) { + hash->remove(key); + } + } + + //we need to do that after the mutex was unlocked + if (animToStop) { + // try to stop the top level group + QAbstractAnimation *current = animToStop; + while (current->group() && current->state() != Stopped) + current = current->group(); + current->stop(); + } +} + +#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..2fdd50c --- /dev/null +++ b/src/corelib/animation/qpropertyanimation.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 QPROPERTYANIMATION_H +#define QPROPERTYANIMATION_H + +#include <QtCore/qvariantanimation.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_PRIVATE_SLOT(d_func(), void _q_targetDestroyed()) + 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..16c63ab --- /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 "private/qvariantanimation_p.h" + +QT_BEGIN_NAMESPACE + +class QPropertyAnimationPrivate : public QVariantAnimationPrivate +{ + Q_DECLARE_PUBLIC(QPropertyAnimation) +public: + QPropertyAnimationPrivate() + : target(0), propertyType(0), propertyIndex(0), hasMetaProperty(false) + { + } + + void _q_targetDestroyed(); + + QObject *target; + + //for the QProperty + QMetaProperty property; + int propertyType; + int propertyIndex; + + bool 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..25db52f --- /dev/null +++ b/src/corelib/animation/qsequentialanimationgroup.cpp @@ -0,0 +1,592 @@ +/**************************************************************************** +** +** 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.6 + \ingroup animation + + QSequentialAnimationGroup is a QAnimationGroup that runs its + animations in sequence, i.e., it starts one animation after + another has finished playing. The animations are played in the + order they are added to the group (using + \l{QAnimationGroup::}{addAnimation()} or + \l{QAnimationGroup::}{insertAnimationAt()}). The animation group + finishes when its last animation has finished. + + At each moment there is at most one animation that is active in + the group; it is returned by currentAnimation(). An empty group + has no current animation. + + A sequential animation group can be treated as any other + animation, i.e., it can be started, stopped, and added to other + groups. You can also call addPause() or insertPauseAt() to add a + pause to a sequential animation group. + + \code + QSequentialAnimationGroup group; + + group.addAnimation(anim1); + group.addAnimation(anim2); + + group.start(); + \endcode + + In this example, \c anim1 and \c anim2 are two already set up + \l{QPropertyAnimation}s. + + \sa QAnimationGroup, QAbstractAnimation, {The Animation Framework} +*/ + +#include "qsequentialanimationgroup.h" +#include "qsequentialanimationgroup_p.h" + +#include "qpauseanimation.h" + +#include <QtCore/qdebug.h> + +#ifndef QT_NO_ANIMATION + +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 (currentLoop == loopCount - 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 = currentLoop * 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) { + lastLoop = 0; + if (currentAnimationIndex == 0) + activateCurrentAnimation(); + else + setCurrentAnimation(0); + } else { // direction == QAbstractAnimation::Backward + lastLoop = loopCount - 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 (lastLoop < currentLoop) { + // 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 (lastLoop > currentLoop) { + // 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->lastLoop < d->currentLoop + || (d->lastLoop == d->currentLoop && d->currentAnimationIndex < newAnimationIndex.index)) { + // advancing with forward direction is the same as rewinding with backwards direction + d->advanceForwards(newAnimationIndex); + } else if (d->lastLoop > d->currentLoop + || (d->lastLoop == d->currentLoop && 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->lastLoop = d->currentLoop; +} + +/*! + \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->currentLoop() == 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 || currentLoop != 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 + loopCount * 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..4701a76 --- /dev/null +++ b/src/corelib/animation/qsequentialanimationgroup.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 QSEQUENTIALANIMATIONGROUP_H +#define QSEQUENTIALANIMATIONGROUP_H + +#include <QtCore/qanimationgroup.h> + +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..c01aaf0 --- /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 "private/qanimationgroup_p.h" + + +QT_BEGIN_NAMESPACE + +class QSequentialAnimationGroupPrivate : public QAnimationGroupPrivate +{ + Q_DECLARE_PUBLIC(QSequentialAnimationGroup) +public: + QSequentialAnimationGroupPrivate() + : currentAnimation(0), currentAnimationIndex(-1), lastLoop(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 lastLoop; + + // 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..ab73373 --- /dev/null +++ b/src/corelib/animation/qvariantanimation.cpp @@ -0,0 +1,673 @@ +/**************************************************************************** +** +** 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 "qvariantanimation.h" +#include "qvariantanimation_p.h" + +#include <QtCore/qrect.h> +#include <QtCore/qline.h> +#include <QtCore/qmutex.h> +#include <private/qmutexpool_p.h> + +#ifndef QT_NO_ANIMATION + +QT_BEGIN_NAMESPACE + +/*! + \class QVariantAnimation + \ingroup animation + \brief The QVariantAnimation class provides an abstract base class for animations. + \since 4.6 + + This class is part of \l{The Animation Framework}. It serves as a + base class for property and item animations, with functions for + shared functionality. + + QVariantAnimation cannot be used directly as it is an abstract + class; it has a pure virtual method called updateCurrentValue(). + The class performs interpolation over + \l{QVariant}s, but leaves using the interpolated values to its + subclasses. Currently, Qt provides QPropertyAnimation, which + animates Qt \l{Qt's Property System}{properties}. See the + QPropertyAnimation class description if you wish to animate such + properties. + + You can then set start and end values for the property by calling + setStartValue() and setEndValue(), and finally call start() to + start the animation. QVariantAnimation will interpolate the + property of the target object and emit valueChanged(). To react to + a change in the current value you have to reimplement the + updateCurrentValue() virtual function. + + It is also possible to set values at specified steps situated + between the start and end value. The interpolation will then + touch these points at the specified steps. Note that the start and + end values are defined as the key values at 0.0 and 1.0. + + 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. + + Subclassing QVariantAnimation can be an alternative if you have + \l{QVariant}s that you do not wish to declare as Qt properties. + Note, however, that you in most cases will be better off declaring + your QVariant as a property. + + Not all QVariant types are supported. Below is a list of currently + supported QVariant types: + + \list + \o \l{QMetaType::}{Int} + \o \l{QMetaType::}{Double} + \o \l{QMetaType::}{Float} + \o \l{QMetaType::}{QLine} + \o \l{QMetaType::}{QLineF} + \o \l{QMetaType::}{QPoint} + \o \l{QMetaType::}{QSize} + \o \l{QMetaType::}{QSizeF} + \o \l{QMetaType::}{QRect} + \o \l{QMetaType::}{QRectF} + \endlist + + If you need to interpolate other variant types, including custom + types, you have to implement interpolation for these yourself. + You do this by reimplementing interpolated(), which returns + interpolation values for the value being interpolated. + + \omit We need some snippets around here. \endomit + + \sa QPropertyAnimation, QAbstractAnimation, {The Animation Framework} +*/ + +/*! + \fn void QVariantAnimation::valueChanged(const QVariant &value) + + QVariantAnimation emits this signal whenever the current \a value changes. + + \sa currentValue, startValue, endValue +*/ + +/*! + \fn void QVariantAnimation::updateCurrentValue(const QVariant &value) = 0; + + This pure virtual function is called every time the animation's current + value changes. The \a value argument is the new current value. + + \sa currentValue +*/ + + +static bool animationValueLessThan(const QVariantAnimation::KeyValue &p1, const QVariantAnimation::KeyValue &p2) +{ + return p1.first < p2.first; +} + +static QVariant defaultInterpolator(const void *, const void *, qreal) +{ + return QVariant(); +} + +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)); +} + +QVariantAnimationPrivate::QVariantAnimationPrivate() : duration(250), hasStartValue(false), + interpolator(&defaultInterpolator), + changedSignalMask(1 << QVariantAnimation::staticMetaObject.indexOfSignal("valueChanged(QVariant)")) +{ + //we keep the mask so that we emit valueChanged only when needed (for performance reasons) +} + +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]; + pair.second.convert(static_cast<QVariant::Type>(t)); + } + //we also need update to the current interval if needed + currentInterval.start.second.convert(static_cast<QVariant::Type>(t)); + currentInterval.end.second.convert(static_cast<QVariant::Type>(t)); + + //... and the interpolator + updateInterpolator(); +} + +void QVariantAnimationPrivate::updateInterpolator() +{ + int type = currentInterval.start.second.userType(); + if (type == currentInterval.end.second.userType()) + interpolator = getInterpolator(type); + else + interpolator = 0; + + //we make sure that the interpolator is always set to something + if (!interpolator) + interpolator = &defaultInterpolator; +} + +/*! + \internal + The goal of this function is to update the currentInterval member. As a consequence, we also + need to update the currentValue. + Set \a force to true to always recalculate the interval. +*/ +void QVariantAnimationPrivate::recalculateCurrentInterval(bool force/*=false*/) +{ + // can't interpolate if we have only 1 key value + if (keyValues.count() <= 1) + return; + + const qreal progress = easing.valueForProgress(((duration == 0) ? qreal(1) : qreal(currentTime) / qreal(duration))); + + if (force || 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.constBegin()) { + ++itEnd; + } else { + --itStart; + } + + // update all the values of the currentInterval + currentInterval.start = *itStart; + currentInterval.end = *itEnd; + updateInterpolator(); + } + } + setCurrentValueForProgress(progress); +} + +void QVariantAnimationPrivate::setCurrentValueForProgress(const qreal progress) +{ + Q_Q(QVariantAnimation); + + const qreal startProgress = currentInterval.start.first; + const qreal endProgress = currentInterval.end.first; + const qreal localProgress = (progress - startProgress) / (endProgress - startProgress); + + QVariant ret = q->interpolated(currentInterval.start.second, + currentInterval.end.second, + localProgress); + qSwap(currentValue, ret); + q->updateCurrentValue(currentValue); + if ((connectedSignals & changedSignalMask) && currentValue != ret) { + //the value has changed + 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; // replaces the previous value + else if (step == 0 && !hasStartValue && defaultStartValue.isValid()) + result->second = defaultStartValue; // resets to the default start value + else + keyValues.erase(result); // removes the previous value + } + + recalculateCurrentInterval(/*force=*/true); +} + +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) +{ +} + +/*! + \internal +*/ +QVariantAnimation::QVariantAnimation(QVariantAnimationPrivate &dd, QObject *parent) : QAbstractAnimation(dd, parent) +{ +} + +/*! + 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. Other curves are provided, for instance, + QEasingCurve::InCirc, which provides a circular entry curve. + Another example is QEasingCurve::InOutElastic, which provides an + elastic effect on the values of the interpolated variant. + + 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->recalculateCurrentInterval(); +} + +typedef QVector<QVariantAnimation::Interpolator> QInterpolatorVector; +Q_GLOBAL_STATIC(QInterpolatorVector, registeredInterpolators) + +/*! + \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 + QInterpolatorVector *interpolators = registeredInterpolators(); + QMutexLocker locker(QMutexPool::globalInstanceGet(interpolators)); + if (int(interpolationType) >= interpolators->count()) + interpolators->resize(int(interpolationType) + 1); + interpolators->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); +} + +QVariantAnimation::Interpolator QVariantAnimationPrivate::getInterpolator(int interpolationType) +{ + QInterpolatorVector *interpolators = registeredInterpolators(); + QMutexLocker locker(QMutexPool::globalInstanceGet(interpolators)); + QVariantAnimation::Interpolator ret = 0; + if (interpolationType < interpolators->count()) { + ret = interpolators->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 in milliseconds 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->recalculateCurrentInterval(); +} + +/*! + \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->hasStartValue = !d->keyValues.isEmpty() && d->keyValues.at(0).first == 0; + d->recalculateCurrentInterval(/*force=*/true); +} + +/*! + \property QVariantAnimation::currentValue + \brief the current value of the animation. + + This property describes the current value; an interpolated value + between the \l{startValue}{start value} and the \l{endValue}{end + value}, using the current time for progress. The value itself is + obtained from interpolated(), which is called repeatedly as the + animation is running. + + QVariantAnimation calls the virtual updateCurrentValue() function + when the current value changes. This is particularly useful for + subclasses that need to track updates. For example, + QPropertyAnimation uses this function to animate Qt \l{Qt's + Property System}{properties}. + + \sa startValue, endValue +*/ +QVariant QVariantAnimation::currentValue() const +{ + Q_D(const QVariantAnimation); + if (!d->currentValue.isValid()) + const_cast<QVariantAnimationPrivate*>(d)->recalculateCurrentInterval(); + 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); +} + +/*! + + 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. + + You should call the QVariantAnimation implementation of this + function if you want your class to handle the types already + supported by Qt (see class QVariantAnimation description for a + list of supported types). + + \sa QEasingCurve + */ +QVariant QVariantAnimation::interpolated(const QVariant &from, const QVariant &to, qreal progress) const +{ + return d_func()->interpolator(from.constData(), to.constData(), progress); +} + +/*! + \reimp + */ +void QVariantAnimation::updateCurrentTime(int msecs) +{ + Q_UNUSED(msecs); + d_func()->recalculateCurrentInterval(); +} + +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..3ae172f --- /dev/null +++ b/src/corelib/animation/qvariantanimation.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** 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 + +#include <QtCore/qeasingcurve.h> +#include <QtCore/qabstractanimation.h> +#include <QtCore/qvector.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> +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..3ae0e39 --- /dev/null +++ b/src/corelib/animation/qvariantanimation_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** 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" +#include <QtCore/qeasingcurve.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qvector.h> + +#include "private/qabstractanimation_p.h" + +QT_BEGIN_NAMESPACE + +class QVariantAnimationPrivate : public QAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QVariantAnimation) +public: + + QVariantAnimationPrivate(); + + static QVariantAnimationPrivate *get(QVariantAnimation *q) + { + return q->d_func(); + } + + 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; + + QVariantAnimation::Interpolator interpolator; + + const quint32 changedSignalMask; + + void setCurrentValueForProgress(const qreal progress); + void recalculateCurrentInterval(bool force=false); + void setValueAt(qreal, const QVariant &); + QVariant valueAt(qreal step) const; + void convertValues(int t); + + void updateInterpolator(); + + //XXX this is needed by dui + static Q_CORE_EXPORT QVariantAnimation::Interpolator getInterpolator(int interpolationType); +}; + +//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/codecs/qisciicodec.cpp b/src/corelib/codecs/qisciicodec.cpp index 5619580..f9e3292 100644 --- a/src/corelib/codecs/qisciicodec.cpp +++ b/src/corelib/codecs/qisciicodec.cpp @@ -54,7 +54,7 @@ QT_BEGIN_NAMESPACE struct Codecs { - const char *name; + const char name[10]; ushort base; }; diff --git a/src/corelib/codecs/qtextcodec.cpp b/src/corelib/codecs/qtextcodec.cpp index cfb62c7..fcf0be1 100644 --- a/src/corelib/codecs/qtextcodec.cpp +++ b/src/corelib/codecs/qtextcodec.cpp @@ -461,10 +461,10 @@ static const char * const tis_620locales[] = { // static const char * const tcvnlocales[] = { // "vi", "vi_VN", 0 }; -static bool try_locale_list(const char * const locale[], const char * lang) +static bool try_locale_list(const char * const locale[], const QByteArray &lang) { int i; - for(i=0; locale[i] && *locale[i] && strcmp(locale[i], lang); i++) + for(i=0; locale[i] && lang != locale[i]; i++) ; return locale[i] != 0; } @@ -516,13 +516,12 @@ static QTextCodec * ru_RU_hack(const char * i) { #endif #if !defined(Q_OS_WIN32) && !defined(Q_OS_WINCE) -static QTextCodec *checkForCodec(const char *name) { +static QTextCodec *checkForCodec(const QByteArray &name) { QTextCodec *c = QTextCodec::codecForName(name); if (!c) { - const char *at = strchr(name, '@'); - if (at) { - QByteArray n(name, at - name); - c = QTextCodec::codecForName(n.data()); + const int index = name.indexOf('@'); + if (index != -1) { + c = QTextCodec::codecForName(name.left(index)); } } return c; @@ -563,21 +562,19 @@ static void setupLocaleMapper() // definitely knows it, but since we cannot fully trust it, get ready // to fall back to environment variables. #if !defined(QT_NO_SETLOCALE) - char * ctype = qstrdup(setlocale(LC_CTYPE, 0)); + const QByteArray ctype = setlocale(LC_CTYPE, 0); #else - char * ctype = qstrdup(""); + const QByteArray ctype; #endif // Get the first nonempty value from $LC_ALL, $LC_CTYPE, and $LANG // environment variables. - char * lang = qstrdup(qgetenv("LC_ALL").constData()); - if (!lang || lang[0] == 0 || strcmp(lang, "C") == 0) { - if (lang) delete [] lang; - lang = qstrdup(qgetenv("LC_CTYPE").constData()); + QByteArray lang = qgetenv("LC_ALL"); + if (lang.isEmpty() || lang == "C") { + lang = qgetenv("LC_CTYPE"); } - if (!lang || lang[0] == 0 || strcmp(lang, "C") == 0) { - if (lang) delete [] lang; - lang = qstrdup(qgetenv("LANG").constData()); + if (lang.isEmpty() || lang == "C") { + lang = qgetenv("LANG"); } // Now try these in order: @@ -590,35 +587,35 @@ static void setupLocaleMapper() // 7. guess locale from lang // 1. CODESET from ctype if it contains a .CODESET part (e.g. en_US.ISO8859-15) - char * codeset = ctype ? strchr(ctype, '.') : 0; - if (codeset && *codeset == '.') - localeMapper = checkForCodec(codeset + 1); + int indexOfDot = ctype.indexOf('.'); + if (indexOfDot != -1) + localeMapper = checkForCodec( ctype.mid(indexOfDot + 1) ); // 2. CODESET from lang if it contains a .CODESET part - codeset = lang ? strchr(lang, '.') : 0; - if (!localeMapper && codeset && *codeset == '.') - localeMapper = checkForCodec(codeset + 1); + if (!localeMapper) { + indexOfDot = lang.indexOf('.'); + if (indexOfDot != -1) + localeMapper = checkForCodec( lang.mid(indexOfDot + 1) ); + } // 3. ctype (maybe the locale is named "ISO-8859-1" or something) - if (!localeMapper && ctype && *ctype != 0 && strcmp (ctype, "C") != 0) + if (!localeMapper && !ctype.isEmpty() && ctype != "C") localeMapper = checkForCodec(ctype); // 4. locale (ditto) - if (!localeMapper && lang && *lang != 0) + if (!localeMapper && !lang.isEmpty()) localeMapper = checkForCodec(lang); // 5. "@euro" - if ((!localeMapper && ctype && strstr(ctype, "@euro")) || (lang && strstr(lang, "@euro"))) + if ((!localeMapper && ctype.contains("@euro")) || lang.contains("@euro")) localeMapper = checkForCodec("ISO 8859-15"); // 6. guess locale from ctype unless ctype is "C" // 7. guess locale from lang - char * try_by_name = ctype; - if (ctype && *ctype != 0 && strcmp (ctype, "C") != 0) - try_by_name = lang; + const QByteArray &try_by_name = (!ctype.isEmpty() && ctype != "C") ? lang : ctype; // Now do the guessing. - if (lang && *lang && !localeMapper && try_by_name && *try_by_name) { + if (!lang.isEmpty() && !localeMapper && !try_by_name.isEmpty()) { if (try_locale_list(iso8859_15locales, lang)) localeMapper = QTextCodec::codecForName("ISO 8859-15"); else if (try_locale_list(iso8859_2locales, lang)) @@ -651,8 +648,6 @@ static void setupLocaleMapper() localeMapper = ru_RU_hack(lang); } - delete [] ctype; - delete [] lang; } // If everything failed, we default to 8859-1 @@ -1525,9 +1520,14 @@ QString QTextDecoder::toUnicode(const QByteArray &ba) /*! \since 4.4 - Tries to detect the encoding of the provided snippet of HTML in the given byte array, \a ba, - and returns a QTextCodec instance that is capable of decoding the html to unicode. - If the codec cannot be detected from the content provided, \a defaultCodec is returned. + Tries to detect the encoding of the provided snippet of HTML in + the given byte array, \a ba, by checking the BOM (Byte Order Mark) + and the content-type meta header and returns a QTextCodec instance + that is capable of decoding the html to unicode. If the codec + cannot be detected from the content provided, \a defaultCodec is + returned. + + \sa codecForUtfText() */ QTextCodec *QTextCodec::codecForHtml(const QByteArray &ba, QTextCodec *defaultCodec) { @@ -1535,15 +1535,8 @@ QTextCodec *QTextCodec::codecForHtml(const QByteArray &ba, QTextCodec *defaultCo int pos; QTextCodec *c = 0; - if (ba.size() > 1 && (((uchar)ba[0] == 0xfe && (uchar)ba[1] == 0xff) - || ((uchar)ba[0] == 0xff && (uchar)ba[1] == 0xfe))) { - c = QTextCodec::codecForMib(1015); // utf16 - } else if (ba.size() > 2 - && (uchar)ba[0] == 0xef - && (uchar)ba[1] == 0xbb - && (uchar)ba[2] == 0xbf) { - c = QTextCodec::codecForMib(106); // utf-8 - } else { + c = QTextCodec::codecForUtfText(ba, c); + if (!c) { QByteArray header = ba.left(512).toLower(); if ((pos = header.indexOf("http-equiv=")) != -1) { pos = header.indexOf("charset=", pos) + int(strlen("charset=")); @@ -1571,6 +1564,61 @@ QTextCodec *QTextCodec::codecForHtml(const QByteArray &ba) return codecForHtml(ba, QTextCodec::codecForMib(/*Latin 1*/ 4)); } +/*! + \since 4.6 + + Tries to detect the encoding of the provided snippet \a ba by + using the BOM (Byte Order Mark) and returns a QTextCodec instance + that is capable of decoding the text to unicode. If the codec + cannot be detected from the content provided, \a defaultCodec is + returned. + + \sa codecForHtml() +*/ +QTextCodec *QTextCodec::codecForUtfText(const QByteArray &ba, QTextCodec *defaultCodec) +{ + const uint arraySize = ba.size(); + + if (arraySize > 3) { + if ((uchar)ba[0] == 0x00 + && (uchar)ba[1] == 0x00 + && (uchar)ba[2] == 0xFE + && (uchar)ba[3] == 0xFF) + return QTextCodec::codecForMib(1018); // utf-32 be + else if ((uchar)ba[0] == 0xFF + && (uchar)ba[1] == 0xFE + && (uchar)ba[2] == 0x00 + && (uchar)ba[3] == 0x00) + return QTextCodec::codecForMib(1019); // utf-32 le + } + + if (arraySize < 2) + return defaultCodec; + if ((uchar)ba[0] == 0xfe && (uchar)ba[1] == 0xff) + return QTextCodec::codecForMib(1013); // utf16 be + else if ((uchar)ba[0] == 0xff && (uchar)ba[1] == 0xfe) + return QTextCodec::codecForMib(1014); // utf16 le + + if (arraySize < 3) + return defaultCodec; + if ((uchar)ba[0] == 0xef + && (uchar)ba[1] == 0xbb + && (uchar)ba[2] == 0xbf) + return QTextCodec::codecForMib(106); // utf-8 + + return defaultCodec; +} + +/*! + \overload + + If the codec cannot be detected, this overload returns a Latin-1 QTextCodec. +*/ +QTextCodec *QTextCodec::codecForUtfText(const QByteArray &ba) +{ + return codecForUtfText(ba, QTextCodec::codecForMib(/*Latin 1*/ 4)); +} + /*! \internal \since 4.3 diff --git a/src/corelib/codecs/qtextcodec.h b/src/corelib/codecs/qtextcodec.h index e32650f..83097a5 100644 --- a/src/corelib/codecs/qtextcodec.h +++ b/src/corelib/codecs/qtextcodec.h @@ -82,6 +82,9 @@ public: static QTextCodec *codecForHtml(const QByteArray &ba); static QTextCodec *codecForHtml(const QByteArray &ba, QTextCodec *defaultCodec); + static QTextCodec *codecForUtfText(const QByteArray &ba); + static QTextCodec *codecForUtfText(const QByteArray &ba, QTextCodec *defaultCodec); + QTextDecoder* makeDecoder() const; QTextEncoder* makeEncoder() const; diff --git a/src/corelib/codecs/qtsciicodec_p.h b/src/corelib/codecs/qtsciicodec_p.h index 8f11e48..425e7fd 100644 --- a/src/corelib/codecs/qtsciicodec_p.h +++ b/src/corelib/codecs/qtsciicodec_p.h @@ -88,7 +88,7 @@ QT_BEGIN_NAMESPACE #ifndef QT_NO_CODECS -class Q_CORE_EXPORT QTsciiCodec : public QTextCodec { +class QTsciiCodec : public QTextCodec { public: ~QTsciiCodec(); diff --git a/src/corelib/codecs/qutfcodec.cpp b/src/corelib/codecs/qutfcodec.cpp index d9defe1..27c0572 100644 --- a/src/corelib/codecs/qutfcodec.cpp +++ b/src/corelib/codecs/qutfcodec.cpp @@ -184,7 +184,10 @@ void QUtf8Codec::convertToUnicode(QString *target, const char *chars, int len, C uc = (uc << 6) | (ch & 0x3f); need--; if (!need) { - if (uc > 0xffff && uc < 0x110000) { + // utf-8 bom composes into 0xfeff code point + if (!headerdone && uc == 0xfeff) { + // dont do anything, just skip the BOM + } else if (uc > 0xffff && uc < 0x110000) { // surrogate pair uc -= 0x10000; unsigned short high = uc/0x400 + 0xd800; @@ -206,6 +209,7 @@ void QUtf8Codec::convertToUnicode(QString *target, const char *chars, int len, C } else { *qch++ = uc; } + headerdone = true; } } else { // error @@ -213,15 +217,18 @@ void QUtf8Codec::convertToUnicode(QString *target, const char *chars, int len, C *qch++ = replacement; ++invalid; need = 0; + headerdone = true; } } else { if (ch < 128) { *qch++ = QLatin1Char(ch); + headerdone = true; } else if ((ch & 0xe0) == 0xc0) { uc = ch & 0x1f; need = 1; error = i; min_uc = 0x80; + headerdone = true; } else if ((ch & 0xf0) == 0xe0) { uc = ch & 0x0f; need = 2; @@ -232,10 +239,12 @@ void QUtf8Codec::convertToUnicode(QString *target, const char *chars, int len, C need = 3; error = i; min_uc = 0x10000; + headerdone = true; } else { // error *qch++ = replacement; ++invalid; + headerdone = true; } } } @@ -387,7 +396,7 @@ QString QUtf16Codec::convertToUnicode(const char *chars, int len, ConverterState result.truncate(qch - result.unicode()); if (state) { - if (endian != Detect) + if (headerdone) state->flags |= IgnoreHeader; state->state_data[Endian] = endian; if (half) { @@ -569,7 +578,7 @@ QString QUtf32Codec::convertToUnicode(const char *chars, int len, ConverterState result.truncate(qch - result.unicode()); if (state) { - if (endian != Detect) + if (headerdone) state->flags |= IgnoreHeader; state->state_data[Endian] = endian; state->remainingChars = num; diff --git a/src/corelib/concurrent/qfuturewatcher_p.h b/src/corelib/concurrent/qfuturewatcher_p.h index 324839d..d53a1bd 100644 --- a/src/corelib/concurrent/qfuturewatcher_p.h +++ b/src/corelib/concurrent/qfuturewatcher_p.h @@ -63,8 +63,8 @@ QT_BEGIN_NAMESPACE class QFutureWatcherBase; -class Q_CORE_EXPORT QFutureWatcherBasePrivate : public QObjectPrivate, - public QFutureCallOutInterface +class QFutureWatcherBasePrivate : public QObjectPrivate, + public QFutureCallOutInterface { Q_DECLARE_PUBLIC(QFutureWatcherBase) diff --git a/src/corelib/corelib.pro b/src/corelib/corelib.pro index c55c2da..96e2b5c 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/qfeatures.h b/src/corelib/global/qfeatures.h index 2189723..9f7c7ba 100644 --- a/src/corelib/global/qfeatures.h +++ b/src/corelib/global/qfeatures.h @@ -311,6 +311,11 @@ #define QT_NO_ACCESSIBILITY #endif +// Animation +#if !defined(QT_NO_ANIMATION) && (defined(QT_NO_PROPERTIES)) +#define QT_NO_ANIMATION +#endif + // QButtonGroup #if !defined(QT_NO_BUTTONGROUP) && (defined(QT_NO_GROUPBOX)) #define QT_NO_BUTTONGROUP @@ -361,6 +366,11 @@ #define QT_NO_MENU #endif +// QNetworkDiskCache +#if !defined(QT_NO_NETWORKDISKCACHE) && (defined(QT_NO_TEMPORARYFILE)) +#define QT_NO_NETWORKDISKCACHE +#endif + // Phonon::SeekSlider #if !defined(QT_NO_PHONON_SEEKSLIDER) && (defined(QT_NO_SLIDER)) #define QT_NO_PHONON_SEEKSLIDER diff --git a/src/corelib/global/qfeatures.txt b/src/corelib/global/qfeatures.txt index 40ccb15..9408a5b 100644 --- a/src/corelib/global/qfeatures.txt +++ b/src/corelib/global/qfeatures.txt @@ -1085,6 +1085,13 @@ Requires: Name: QNetworkInterface SeeAlso: ??? +Feature: NETWORKDISKCACHE +Description: Supports a disk cache for network resources +Section: Networking +Requires: TEMPORARYFILE +Name: QNetworkDiskCache +SeeAlso: ??? + # Utilities Feature: COMPLETER @@ -1150,6 +1157,21 @@ Requires: PROPERTIES Name: Accessibility SeeAlso: ??? +Feature: ANIMATION +Description: Provides a framework for animations. +Section: Utilities +Requires: PROPERTIES +Name: Animation +SeeAlso: ??? + +Feature: STATEMACHINE +Description: Provides hierarchical finite state machines. +Section: Utilities +Requires: PROPERTIES +Name: State machine +SeeAlso: ??? + + # SVG Feature: SVG diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index 88f845a..6976c00 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -2147,7 +2147,7 @@ void qt_message_output(QtMsgType msgType, const char *buf) mac_default_handler(buf); #elif defined(Q_OS_WINCE) QString fstr = QString::fromLatin1(buf); - fstr += QLatin1String("\n"); + fstr += QLatin1Char('\n'); OutputDebugString(reinterpret_cast<const wchar_t *> (fstr.utf16())); #elif defined(Q_OS_SYMBIAN) // RDebug::Print has a cap of 256 characters so break it up @@ -2454,7 +2454,7 @@ bool qputenv(const char *varName, const QByteArray& value) return _putenv_s(varName, value.constData()) == 0; #else QByteArray buffer(varName); - buffer += "="; + buffer += '='; buffer += value; return putenv(qstrdup(buffer.constData())) == 0; #endif @@ -3170,7 +3170,12 @@ bool QInternal::callFunction(InternalFunction func, void **args) \relates <QtGlobal> \since 4.4 \threadsafe - \overload + + Compares the floating point value \a p1 and \a p2 and + returns \c true if they are considered equal, otherwise \c false. + + The two numbers are compared in a relative way, where the + exactness is stronger the smaller the numbers are. */ /*! diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 14b6895..c95338d 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -44,11 +44,11 @@ #include <stddef.h> -#define QT_VERSION_STR "4.5.2" +#define QT_VERSION_STR "4.6.0" /* QT_VERSION is (major << 16) + (minor << 8) + patch. */ -#define QT_VERSION 0x040502 +#define QT_VERSION 0x040600 /* can be used like #if (QT_VERSION >= QT_VERSION_CHECK(4, 4, 0)) */ @@ -772,6 +772,10 @@ namespace QT_NAMESPACE {} # endif #elif defined(Q_OS_WINCE) # define Q_WS_WIN32 +# define Q_WS_WINCE +# if defined(Q_OS_WINCE_WM) +# define Q_WS_WINCE_WM +# endif #elif defined(Q_OS_OS2) # define Q_WS_PM # error "Qt does not work with OS/2 Presentation Manager or Workplace Shell" @@ -791,10 +795,11 @@ namespace QT_NAMESPACE {} # endif #endif -#if defined(Q_WS_WIN16) || defined(Q_WS_WIN32) +#if defined(Q_WS_WIN16) || defined(Q_WS_WIN32) || defined(Q_WS_WINCE) # define Q_WS_WIN #endif + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -848,17 +853,12 @@ typedef quint64 qulonglong; sizeof(void *) == sizeof(quintptr) && sizeof(void *) == sizeof(qptrdiff) */ -template <int> class QUintForSize { private: typedef void Type; }; -template <> class QUintForSize<4> { public: typedef quint32 Type; }; -template <> class QUintForSize<8> { public: typedef quint64 Type; }; -template <typename T> class QUintForType : public QUintForSize<sizeof(T)> { }; -typedef QUintForType<void *>::Type quintptr; - -template <int> class QIntForSize { private: typedef void Type; }; -template <> class QIntForSize<4> { public: typedef qint32 Type; }; -template <> class QIntForSize<8> { public: typedef qint64 Type; }; -template <typename T> class QIntForType : public QIntForSize<sizeof(T)> { }; -typedef QIntForType<void *>::Type qptrdiff; +template <int> struct QIntegerForSize; +template <> struct QIntegerForSize<4> { typedef quint32 Unsigned; typedef qint32 Signed; }; +template <> struct QIntegerForSize<8> { typedef quint64 Unsigned; typedef qint64 Signed; }; +template <class T> struct QIntegerForSizeof: QIntegerForSize<sizeof(T)> { }; +typedef QIntegerForSizeof<void*>::Unsigned quintptr; +typedef QIntegerForSizeof<void*>::Signed qptrdiff; /* Useful type definitions for Qt @@ -1037,6 +1037,7 @@ typedef int QNoImplicitBoolCast; #define QT_NO_FPU #endif +// This logic must match the one in qmetatype.h #if defined(QT_COORD_TYPE) typedef QT_COORD_TYPE qreal; #elif defined(QT_NO_FPU) || defined(QT_ARCH_ARM) || defined(QT_ARCH_WINDOWSCE) || defined(QT_ARCH_SYMBIAN) @@ -1805,6 +1806,22 @@ static inline bool qFuzzyCompare(float p1, float p2) return (qAbs(p1 - p2) <= 0.00001f * qMin(qAbs(p1), qAbs(p2))); } +/*! + \internal +*/ +static inline bool qFuzzyIsNull(double d) +{ + return qAbs(d) <= 0.000000000001; +} + +/*! + \internal +*/ +static inline bool qFuzzyIsNull(float f) +{ + return qAbs(f) <= 0.00001f; +} + /* This function tests a double for a null value. It doesn't check whether the actual value is 0 or close to 0, but whether @@ -1936,7 +1953,7 @@ enum { /* TYPEINFO flags */ #define Q_DECLARE_TYPEINFO(TYPE, FLAGS) \ template <> \ -class QTypeInfo<TYPE> \ +class QTypeInfo<TYPE > \ { \ public: \ enum { \ diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp index 5e4b9bd..6eb6c3a 100644 --- a/src/corelib/global/qlibraryinfo.cpp +++ b/src/corelib/global/qlibraryinfo.cpp @@ -420,14 +420,14 @@ QLibraryInfo::location(LibraryLocation loc) #else if (QCoreApplication::instance()) { #ifdef Q_OS_MAC - CFBundleRef bundleRef = CFBundleGetMainBundle(); - if (bundleRef) { - QCFType<CFURLRef> urlRef = CFBundleCopyBundleURL(bundleRef); - if (urlRef) { - QCFString path = CFURLCopyFileSystemPath(urlRef, kCFURLPOSIXPathStyle); - return QDir::cleanPath(path + QLatin1String("/Contents")); - } - } + CFBundleRef bundleRef = CFBundleGetMainBundle(); + if (bundleRef) { + QCFType<CFURLRef> urlRef = CFBundleCopyBundleURL(bundleRef); + if (urlRef) { + QCFString path = CFURLCopyFileSystemPath(urlRef, kCFURLPOSIXPathStyle); + return QDir::cleanPath(path + QLatin1String("/Contents/") + ret); + } + } #endif return QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(ret); } else { diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 43740ed..a94d6d0 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -192,6 +192,12 @@ public: #endif }; + enum TileRule { + Stretch, + Repeat, + Round + }; + // Text formatting flags for QPainter::drawText and QLabel. // The following two enums can be combined to one integer which // is passed as 'flags' to drawText and qt_format_text. @@ -501,6 +507,8 @@ public: AA_NativeWindows = 3, AA_DontCreateNativeWidgetSiblings = 4, AA_MacPluginApplication = 5, + AA_DontUseNativeMenuBar = 6, + AA_MacDontSwapCtrlAndMeta = 7, // Add new attributes before this line AA_AttributeCount @@ -1323,7 +1331,8 @@ public: DirectConnection, QueuedConnection, AutoCompatConnection, - BlockingQueuedConnection + BlockingQueuedConnection, + UniqueConnection = 0x80 }; enum ShortcutContext { diff --git a/src/corelib/global/qt_windows.h b/src/corelib/global/qt_windows.h index 2ce4059..7fc7f47 100644 --- a/src/corelib/global/qt_windows.h +++ b/src/corelib/global/qt_windows.h @@ -106,6 +106,9 @@ #ifndef WM_MOUSEWHEEL #define WM_MOUSEWHEEL 0x020A #endif +#ifndef WM_MOUSEHWHEEL +#define WM_MOUSEHWHEEL 0x020E +#endif #ifndef ETO_PDY #define ETO_PDY 0x2000 #endif diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri index 845dc2e..edbab9c 100644 --- a/src/corelib/io/io.pri +++ b/src/corelib/io/io.pri @@ -13,6 +13,7 @@ HEADERS += \ io/qfileinfo_p.h \ io/qiodevice.h \ io/qiodevice_p.h \ + io/qnoncontiguousbytedevice_p.h \ io/qprocess.h \ io/qprocess_p.h \ io/qtextstream.h \ @@ -38,6 +39,7 @@ SOURCES += \ io/qfile.cpp \ io/qfileinfo.cpp \ io/qiodevice.cpp \ + io/qnoncontiguousbytedevice.cpp \ io/qprocess.cpp \ io/qtextstream.cpp \ io/qtemporaryfile.cpp \ diff --git a/src/corelib/io/qdatastream.h b/src/corelib/io/qdatastream.h index 4c8295e..077e720 100644 --- a/src/corelib/io/qdatastream.h +++ b/src/corelib/io/qdatastream.h @@ -65,9 +65,8 @@ template <typename T> class QSet; template <class Key, class T> class QHash; template <class Key, class T> class QMap; -class QDataStreamPrivate; - #ifndef QT_NO_DATASTREAM +class QDataStreamPrivate; class Q_CORE_EXPORT QDataStream { public: @@ -83,10 +82,11 @@ public: Qt_4_2 = 8, Qt_4_3 = 9, Qt_4_4 = 10, - Qt_4_5 = 11 -#if QT_VERSION >= 0x040600 + Qt_4_5 = 11, + Qt_4_6 = Qt_4_5 +#if QT_VERSION >= 0x040700 #error Add the datastream version for this Qt version - , Qt_4_6 = Qt_4_5 + Qt_4_7 = Qt_4_6 #endif }; diff --git a/src/corelib/io/qdebug.h b/src/corelib/io/qdebug.h index 60dc688..332058e 100644 --- a/src/corelib/io/qdebug.h +++ b/src/corelib/io/qdebug.h @@ -51,6 +51,7 @@ #include <QtCore/qstring.h> #include <QtCore/qvector.h> #include <QtCore/qset.h> +#include <QtCore/qcontiguouscache.h> QT_BEGIN_HEADER @@ -87,11 +88,11 @@ public: delete stream; } } - inline QDebug &space() { stream->space = true; stream->ts << " "; return *this; } + inline QDebug &space() { stream->space = true; stream->ts << ' '; return *this; } inline QDebug &nospace() { stream->space = false; return *this; } - inline QDebug &maybeSpace() { if (stream->space) stream->ts << " "; return *this; } + inline QDebug &maybeSpace() { if (stream->space) stream->ts << ' '; return *this; } - inline QDebug &operator<<(QChar t) { stream->ts << "\'" << t << "\'"; return maybeSpace(); } + inline QDebug &operator<<(QChar t) { stream->ts << '\'' << t << '\''; return maybeSpace(); } inline QDebug &operator<<(QBool t) { stream->ts << (bool(t) ? "true" : "false"); return maybeSpace(); } inline QDebug &operator<<(bool t) { stream->ts << (t ? "true" : "false"); return maybeSpace(); } inline QDebug &operator<<(char t) { stream->ts << t; return maybeSpace(); } @@ -108,10 +109,10 @@ public: inline QDebug &operator<<(float t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(double t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(const char* t) { stream->ts << QString::fromAscii(t); return maybeSpace(); } - inline QDebug &operator<<(const QString & t) { stream->ts << "\"" << t << "\""; return maybeSpace(); } + inline QDebug &operator<<(const QString & t) { stream->ts << '\"' << t << '\"'; return maybeSpace(); } inline QDebug &operator<<(const QStringRef & t) { return operator<<(t.toString()); } - inline QDebug &operator<<(const QLatin1String &t) { stream->ts << "\"" << t.latin1() << "\""; return maybeSpace(); } - inline QDebug &operator<<(const QByteArray & t) { stream->ts << "\"" << t << "\""; return maybeSpace(); } + inline QDebug &operator<<(const QLatin1String &t) { stream->ts << '\"' << t.latin1() << '\"'; return maybeSpace(); } + inline QDebug &operator<<(const QByteArray & t) { stream->ts << '\"' << t << '\"'; return maybeSpace(); } inline QDebug &operator<<(const void * t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(QTextStreamFunction f) { stream->ts << f; @@ -161,13 +162,13 @@ template <class T> inline QDebug operator<<(QDebug debug, const QList<T> &list) #endif { - debug.nospace() << "("; + debug.nospace() << '('; for (Q_TYPENAME QList<T>::size_type i = 0; i < list.count(); ++i) { if (i) debug << ", "; debug << list.at(i); } - debug << ")"; + debug << ')'; return debug.space(); } @@ -194,9 +195,9 @@ inline QDebug operator<<(QDebug debug, const QMap<aKey, aT> &map) debug.nospace() << "QMap("; for (typename QMap<aKey, aT>::const_iterator it = map.constBegin(); it != map.constEnd(); ++it) { - debug << "(" << it.key() << ", " << it.value() << ")"; + debug << '(' << it.key() << ", " << it.value() << ')'; } - debug << ")"; + debug << ')'; return debug.space(); } @@ -211,8 +212,8 @@ inline QDebug operator<<(QDebug debug, const QHash<aKey, aT> &hash) debug.nospace() << "QHash("; for (typename QHash<aKey, aT>::const_iterator it = hash.constBegin(); it != hash.constEnd(); ++it) - debug << "(" << it.key() << ", " << it.value() << ")"; - debug << ")"; + debug << '(' << it.key() << ", " << it.value() << ')'; + debug << ')'; return debug.space(); } @@ -224,7 +225,7 @@ template <class T1, class T2> inline QDebug operator<<(QDebug debug, const QPair<T1, T2> &pair) #endif { - debug.nospace() << "QPair(" << pair.first << "," << pair.second << ")"; + debug.nospace() << "QPair(" << pair.first << ',' << pair.second << ')'; return debug.space(); } @@ -235,6 +236,24 @@ inline QDebug operator<<(QDebug debug, const QSet<T> &set) return operator<<(debug, set.toList()); } +#if defined(FORCE_UREF) +template <class T> +inline QDebug &operator<<(QDebug debug, const QContiguousCache<T> &cache) +#else +template <class T> +inline QDebug operator<<(QDebug debug, const QContiguousCache<T> &cache) +#endif +{ + debug.nospace() << "QContiguousCache("; + for (int i = cache.firstIndex(); i <= cache.lastIndex(); ++i) { + debug << cache[i]; + if (i != cache.lastIndex()) + debug << ", "; + } + debug << ')'; + return debug.space(); +} + #if !defined(QT_NO_DEBUG_STREAM) Q_CORE_EXPORT_INLINE QDebug qDebug() { return QDebug(QtDebugMsg); } diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index e1cc7ac..46c29d4 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -45,6 +45,7 @@ #ifndef QT_NO_DEBUG_STREAM #include "qdebug.h" #endif +#include "qdiriterator.h" #include "qfsfileengine.h" #include "qdatetime.h" #include "qstring.h" @@ -55,6 +56,8 @@ # include "qresource.h" #endif +#include "qvarlengtharray.h" + #include "../kernel/qcoreglobaldata_p.h" #include <stdlib.h> @@ -91,7 +94,7 @@ protected: QString initFileEngine(const QString &file); void updateFileLists() const; - void sortFileList(QDir::SortFlags, QStringList &, QStringList *, QFileInfoList *) const; + void sortFileList(QDir::SortFlags, QFileInfoList &, QStringList *, QFileInfoList *) const; private: #ifdef QT3_SUPPORT @@ -261,45 +264,57 @@ bool QDirSortItemComparator::operator()(const QDirSortItem &n1, const QDirSortIt ? f1->filename_cache.localeAwareCompare(f2->filename_cache) : f1->filename_cache.compare(f2->filename_cache); } - + if (r == 0) // Enforce an order - the order the items appear in the array + r = (&n1) - (&n2); if (qt_cmp_si_sort_flags & QDir::Reversed) return r > 0; return r < 0; } -inline void QDirPrivate::sortFileList(QDir::SortFlags sort, QStringList &l, +inline void QDirPrivate::sortFileList(QDir::SortFlags sort, QFileInfoList &l, QStringList *names, QFileInfoList *infos) const { if(names) names->clear(); if(infos) infos->clear(); - if(!l.isEmpty()) { - QDirSortItem *si= new QDirSortItem[l.count()]; - int i; - for (i = 0; i < l.size(); ++i) { - QString path = data->path; - if (!path.isEmpty() && !path.endsWith(QLatin1Char('/'))) - path += QLatin1Char('/'); - si[i].item = QFileInfo(path + l.at(i)); - } - if ((sort & QDir::SortByMask) != QDir::Unsorted) - qStableSort(si, si+i, QDirSortItemComparator(sort)); - // put them back in the list(s) - for (int j = 0; j<i; j++) { + int n = l.size(); + if(n > 0) { + if (n == 1 || (sort & QDir::SortByMask) == QDir::Unsorted) { if(infos) - infos->append(si[j].item); - if(names) - names->append(si[j].item.fileName()); + *infos = l; + if(names) { + for (int i = 0; i < n; ++i) + names->append(l.at(i).fileName()); + } + } else { + QDirSortItem *si = new QDirSortItem[n]; + for (int i = 0; i < n; ++i) + si[i].item = l.at(i); + qSort(si, si+n, QDirSortItemComparator(sort)); + // put them back in the list(s) + if(infos) { + for (int i = 0; i < n; ++i) + infos->append(si[i].item); + } + if(names) { + for (int i = 0; i < n; ++i) + names->append(si[i].item.fileName()); + } + delete [] si; } - delete [] si; } } inline void QDirPrivate::updateFileLists() const { if(data->listsDirty) { - QStringList l = data->fileEngine->entryList(data->filters, data->nameFilters); + QFileInfoList l; + QDirIterator it(data->path, data->nameFilters, data->filters); + while (it.hasNext()) { + it.next(); + l.append(it.fileInfo()); + } sortFileList(data->sort, l, &data->files, &data->fileInfos); data->listsDirty = 0; } @@ -1303,7 +1318,6 @@ QStringList QDir::entryList(Filters filters, SortFlags sort) const \sa entryList(), setNameFilters(), setSorting(), setFilter(), isReadable(), exists() */ - QFileInfoList QDir::entryInfoList(Filters filters, SortFlags sort) const { Q_D(const QDir); @@ -1345,10 +1359,12 @@ QStringList QDir::entryList(const QStringList &nameFilters, Filters filters, d->updateFileLists(); return d->data->files; } - QStringList l = d->data->fileEngine->entryList(filters, nameFilters); - if ((sort & QDir::SortByMask) == QDir::Unsorted) - return l; - + QFileInfoList l; + QDirIterator it(d->data->path, nameFilters, filters); + while (it.hasNext()) { + it.next(); + l.append(it.fileInfo()); + } QStringList ret; d->sortFileList(sort, l, &ret, 0); return ret; @@ -1388,8 +1404,13 @@ QFileInfoList QDir::entryInfoList(const QStringList &nameFilters, Filters filter d->updateFileLists(); return d->data->fileInfos; } + QFileInfoList l; + QDirIterator it(d->data->path, nameFilters, filters); + while (it.hasNext()) { + it.next(); + l.append(it.fileInfo()); + } QFileInfoList ret; - QStringList l = d->data->fileEngine->entryList(filters, nameFilters); d->sortFileList(sort, l, 0, &ret); return ret; } @@ -2056,11 +2077,13 @@ QString QDir::cleanPath(const QString &path) QString name = path; QChar dir_separator = separator(); if(dir_separator != QLatin1Char('/')) - name.replace(dir_separator, QLatin1Char('/')); + name.replace(dir_separator, QLatin1Char('/')); int used = 0, levels = 0; const int len = name.length(); - QVector<QChar> out(len); + QVarLengthArray<QChar> outVector(len); + QChar *out = outVector.data(); + const QChar *p = name.unicode(); for(int i = 0, last = -1, iwrite = 0; i < len; i++) { if(p[i] == QLatin1Char('/')) { @@ -2160,7 +2183,7 @@ QString QDir::cleanPath(const QString &path) if(used == len) ret = name; else - ret = QString(out.data(), used); + ret = QString(out, used); // Strip away last slash except for root directories if (ret.endsWith(QLatin1Char('/')) @@ -2223,7 +2246,7 @@ QStringList QDir::nameFiltersFromString(const QString &nameFilter) \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 13 - If the file name contains characters that cannot be part of a valid C++ function name + If the file name contains characters that cannot be part of a valid C++ function name (such as '-'), they have to be replaced by the underscore character ('_'). Note: This macro cannot be used in a namespace. It should be called from @@ -2415,7 +2438,7 @@ QDebug operator<<(QDebug debug, QDir::Filters filters) if (filters & QDir::System) flags << QLatin1String("System"); if (filters & QDir::CaseSensitive) flags << QLatin1String("CaseSensitive"); } - debug << "QDir::Filters(" << qPrintable(flags.join(QLatin1String("|"))) << ")"; + debug << "QDir::Filters(" << qPrintable(flags.join(QLatin1String("|"))) << ')'; return debug; } @@ -2437,8 +2460,8 @@ QDebug operator<<(QDebug debug, QDir::SortFlags sorting) if (sorting & QDir::LocaleAware) flags << QLatin1String("LocaleAware"); if (sorting & QDir::Type) flags << QLatin1String("Type"); debug << "QDir::SortFlags(" << qPrintable(type) - << "|" - << qPrintable(flags.join(QLatin1String("|"))) << ")"; + << '|' + << qPrintable(flags.join(QLatin1String("|"))) << ')'; } return debug; } @@ -2450,9 +2473,9 @@ QDebug operator<<(QDebug debug, const QDir &dir) << qPrintable(dir.nameFilters().join(QLatin1String(","))) << "}, " << dir.sorting() - << "," + << ',' << dir.filter() - << ")"; + << ')'; return debug.space(); } diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp index 1e3d5a3..9cbd3dc 100644 --- a/src/corelib/io/qfile.cpp +++ b/src/corelib/io/qfile.cpp @@ -156,17 +156,15 @@ QFilePrivate::setError(QFile::FileError err) void QFilePrivate::setError(QFile::FileError err, const QString &errStr) { - Q_Q(QFile); error = err; - q->setErrorString(errStr); + errorString = errStr; } void QFilePrivate::setError(QFile::FileError err, int errNum) { - Q_Q(QFile); error = err; - q->setErrorString(qt_error_string(errNum)); + errorString = qt_error_string(errNum); } //************* QFile @@ -707,6 +705,7 @@ QFile::rename(const QString &newName) d->setError(QFile::RenameError, tr("Destination file exists")); return false; } + unsetError(); close(); if(error() == QFile::NoError) { if (fileEngine()->rename(newName)) { @@ -861,6 +860,7 @@ QFile::copy(const QString &newName) d->setError(QFile::CopyError, tr("Destination file exists")); return false; } + unsetError(); close(); if(error() == QFile::NoError) { if(fileEngine()->copy(newName)) { diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp index b36fb69..82b21e9 100644 --- a/src/corelib/io/qfileinfo.cpp +++ b/src/corelib/io/qfileinfo.cpp @@ -48,73 +48,10 @@ #include "qatomic.h" #include "qhash.h" #include "qdir.h" +#include "qfileinfo_p.h" QT_BEGIN_NAMESPACE -class QFileInfoPrivate -{ -public: - QFileInfoPrivate(const QFileInfo *copy=0); - ~QFileInfoPrivate(); - - void initFileEngine(const QString &); - - enum Access { - ReadAccess, - WriteAccess, - ExecuteAccess - }; - bool hasAccess(Access access) const; - - uint getFileFlags(QAbstractFileEngine::FileFlags) const; - QDateTime &getFileTime(QAbstractFileEngine::FileTime) const; - QString getFileName(QAbstractFileEngine::FileName) const; - - enum { CachedFileFlags=0x01, CachedLinkTypeFlag=0x02, CachedBundleTypeFlag=0x04, - CachedMTime=0x10, CachedCTime=0x20, CachedATime=0x40, - CachedSize =0x08 }; - struct Data { - inline Data() - : ref(1), fileEngine(0), cache_enabled(1) - { clear(); } - inline Data(const Data ©) - : ref(1), fileEngine(QAbstractFileEngine::create(copy.fileName)), - fileName(copy.fileName), cache_enabled(copy.cache_enabled) - { clear(); } - inline ~Data() { delete fileEngine; } - inline void clearFlags() { - fileFlags = 0; - cachedFlags = 0; - if (fileEngine) - (void)fileEngine->fileFlags(QFSFileEngine::Refresh); - } - inline void clear() { - fileNames.clear(); - clearFlags(); - } - mutable QAtomicInt ref; - - QAbstractFileEngine *fileEngine; - mutable QString fileName; - mutable QHash<int, QString> fileNames; - - mutable uint cachedFlags : 31; - mutable uint cache_enabled : 1; - mutable uint fileFlags; - mutable qint64 fileSize; - mutable QDateTime fileTimes[3]; - inline bool getCachedFlag(uint c) const - { return cache_enabled ? (cachedFlags & c) : 0; } - inline void setCachedFlag(uint c) - { if (cache_enabled) cachedFlags |= c; } - } *data; - inline void reset() { - detach(); - data->clear(); - } - void detach(); -}; - QFileInfoPrivate::QFileInfoPrivate(const QFileInfo *copy) { if(copy) { @@ -535,8 +472,7 @@ QFileInfo &QFileInfo::operator=(const QFileInfo &fileinfo) void QFileInfo::setFile(const QString &file) { - Q_D(QFileInfo); - d->initFileEngine(file); + *this = QFileInfo(file); } /*! @@ -553,8 +489,7 @@ void QFileInfo::setFile(const QString &file) void QFileInfo::setFile(const QFile &file) { - Q_D(QFileInfo); - d->initFileEngine(file.fileName()); + *this = QFileInfo(file.fileName()); } /*! @@ -571,8 +506,7 @@ void QFileInfo::setFile(const QFile &file) void QFileInfo::setFile(const QDir &dir, const QString &file) { - Q_D(QFileInfo); - d->initFileEngine(dir.filePath(file)); + *this = QFileInfo(dir.filePath(file)); } /*! @@ -919,7 +853,7 @@ QString QFileInfo::suffix() const \bold{Note:} The QDir returned always corresponds to the object's parent directory, even if the QFileInfo represents a directory. - + For each of the follwing, dir() returns a QDir for \c{"~/examples/191697"}. diff --git a/src/corelib/io/qfileinfo_p.h b/src/corelib/io/qfileinfo_p.h index 7d66581..8155bcb 100644 --- a/src/corelib/io/qfileinfo_p.h +++ b/src/corelib/io/qfileinfo_p.h @@ -46,8 +46,8 @@ // 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 +// 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. @@ -76,70 +76,51 @@ public: QDateTime &getFileTime(QAbstractFileEngine::FileTime) const; QString getFileName(QAbstractFileEngine::FileName) const; - enum { - CachedFileFlags = 0x01, - CachedLinkTypeFlag = 0x02, - CachedBundleTypeFlag= 0x04, - CachedMTime = 0x10, - CachedCTime = 0x20, - CachedATime = 0x40, - CachedSize = 0x08 - }; - - struct Data - { + enum { CachedFileFlags=0x01, CachedLinkTypeFlag=0x02, CachedBundleTypeFlag=0x04, + CachedMTime=0x10, CachedCTime=0x20, CachedATime=0x40, + CachedSize =0x08 }; + struct Data { inline Data() : ref(1), fileEngine(0), cache_enabled(1) - { - clear(); - } - + { clear(); } inline Data(const Data ©) : ref(1), fileEngine(QAbstractFileEngine::create(copy.fileName)), fileName(copy.fileName), cache_enabled(copy.cache_enabled) - { - clear(); - } - - inline ~Data() - { - delete fileEngine; - } - - inline void clear() - { - fileNames.clear(); + { clear(); } + inline ~Data() { delete fileEngine; } + inline void clearFlags() { fileFlags = 0; cachedFlags = 0; + if (fileEngine) + (void)fileEngine->fileFlags(QFSFileEngine::Refresh); + } + inline void clear() { + fileNames.clear(); + clearFlags(); } - mutable QAtomicInt ref; QAbstractFileEngine *fileEngine; mutable QString fileName; mutable QHash<int, QString> fileNames; + mutable uint cachedFlags : 31; mutable uint cache_enabled : 1; mutable uint fileFlags; mutable qint64 fileSize; mutable QDateTime fileTimes[3]; - inline bool getCachedFlag(uint c) const { return cache_enabled ? (cachedFlags & c) : 0; } - inline void setCachedFlag(uint c) { if (cache_enabled) cachedFlags |= c; } } *data; - inline void reset() { detach(); data->clear(); } - void detach(); }; - QT_END_NAMESPACE -#endif +#endif // QFILEINFO_P_H diff --git a/src/corelib/io/qfilesystemwatcher.cpp b/src/corelib/io/qfilesystemwatcher.cpp index b83ab85..c5a58e2 100644 --- a/src/corelib/io/qfilesystemwatcher.cpp +++ b/src/corelib/io/qfilesystemwatcher.cpp @@ -120,7 +120,7 @@ public: void stop(); -private slots: +private Q_SLOTS: void timeout(); }; diff --git a/src/corelib/io/qfilesystemwatcher_dnotify.cpp b/src/corelib/io/qfilesystemwatcher_dnotify.cpp index e87375a..a8397f8 100644 --- a/src/corelib/io/qfilesystemwatcher_dnotify.cpp +++ b/src/corelib/io/qfilesystemwatcher_dnotify.cpp @@ -114,7 +114,7 @@ private: bool isExecing; }; -Q_GLOBAL_STATIC(QDnotifySignalThread, dnotifySignal); +Q_GLOBAL_STATIC(QDnotifySignalThread, dnotifySignal) QDnotifySignalThread::QDnotifySignalThread() : isExecing(false) diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp index 45e547f..c29e4ec 100644 --- a/src/corelib/io/qfsfileengine_unix.cpp +++ b/src/corelib/io/qfsfileengine_unix.cpp @@ -103,12 +103,12 @@ static QByteArray openModeToFopenMode(QIODevice::OpenMode flags, const QString & } else if (flags & QIODevice::WriteOnly) { mode = "wb"; if (flags & QIODevice::ReadOnly) - mode += "+"; + mode += '+'; } if (flags & QIODevice::Append) { mode = "ab"; if (flags & QIODevice::ReadOnly) - mode += "+"; + mode += '+'; } return mode; } @@ -986,7 +986,7 @@ QString QFSFileEngine::fileName(FileName file) const bool isDir = ret.endsWith(QLatin1Char('/')); ret = QDir::cleanPath(ret); if (isDir) - ret += QLatin1String("/"); + ret += QLatin1Char('/'); if (file == AbsolutePathName) { int slash = ret.lastIndexOf(QLatin1Char('/')); if (slash == -1) @@ -1202,6 +1202,8 @@ bool QFSFileEngine::setSize(qint64 size) Q_D(QFSFileEngine); if (d->fd != -1) return !QT_FTRUNCATE(d->fd, size); + if (d->fh) + return !QT_FTRUNCATE(QT_FILENO(d->fh), size); return !QT_TRUNCATE(d->nativeFilePath.constData(), size); } diff --git a/src/corelib/io/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp index 6ae13e9..896bcc1 100644 --- a/src/corelib/io/qfsfileengine_win.cpp +++ b/src/corelib/io/qfsfileengine_win.cpp @@ -433,7 +433,7 @@ static QString nativeAbsoluteFilePathW(const QString &path) if (retLen != 0) ret = QString::fromUtf16((unsigned short *)buf.data(), retLen); #else - if (path.startsWith(QLatin1String("/")) || path.startsWith(QLatin1String("\\"))) + if (path.startsWith(QLatin1Char('/')) || path.startsWith(QLatin1Char('\\'))) ret = QDir::toNativeSeparators(path); else ret = QDir::toNativeSeparators(QDir::cleanPath(qfsPrivateCurrentDir + QLatin1Char('/') + path)); @@ -1240,7 +1240,7 @@ QString QFSFileEngine::rootPath() QString ret = QString::fromLatin1(qgetenv("SystemDrive").constData()); if(ret.isEmpty()) ret = QLatin1String("c:"); - ret += QLatin1String("/"); + ret += QLatin1Char('/'); #elif defined(Q_OS_OS2EMX) char dir[4]; _abspath(dir, QLatin1String("/"), _MAX_PATH); @@ -1288,19 +1288,17 @@ QFileInfoList QFSFileEngine::drives() exit(1); driveBits &= 0x3ffffff; #endif - char driveName[4]; - - qstrcpy(driveName, "A:/"); + char driveName[] = "A:/"; while(driveBits) { if(driveBits & 1) - ret.append(QString::fromLatin1(driveName).toUpper()); + ret.append(QString::fromLatin1(driveName)); driveName[0]++; driveBits = driveBits >> 1; } return ret; #else - ret.append(QString::fromLatin1("/").toUpper()); + ret.append(QString::fromLatin1("/")); return ret; #endif } @@ -1333,8 +1331,6 @@ bool QFSFileEnginePrivate::doStat() const if (tmpAttributes != -1) { fileAttrib = tmpAttributes; could_stat = true; - } else { - return false; } #endif } else { @@ -1774,10 +1770,10 @@ QString QFSFileEngine::fileName(FileName file) const if(slash == -1) { if(d->filePath.length() >= 2 && d->filePath.at(1) == QLatin1Char(':')) return d->filePath.left(2); - return QString::fromLatin1("."); + return QString(QLatin1Char('.')); } else { if(!slash) - return QString::fromLatin1("/"); + return QString(QLatin1Char('/')); if(slash == 2 && d->filePath.length() >= 2 && d->filePath.at(1) == QLatin1Char(':')) slash++; return d->filePath.left(slash); @@ -1835,7 +1831,7 @@ QString QFSFileEngine::fileName(FileName file) const if (slash == -1) ret = QDir::currentPath(); else if (slash == 0) - ret = QLatin1String("/"); + ret = QString(QLatin1Char('/')); ret = ret.left(slash); } return ret; @@ -1897,7 +1893,7 @@ QString QFSFileEngine::owner(FileOwner own) const #else Q_UNUSED(own); #endif - return QString(QLatin1String("")); + return QString(); } bool QFSFileEngine::setPermissions(uint perms) diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp index 765bf40..82e54fa 100644 --- a/src/corelib/io/qiodevice.cpp +++ b/src/corelib/io/qiodevice.cpp @@ -48,6 +48,10 @@ #include "qstringlist.h" #include <limits.h> +#ifdef QIODEVICE_DEBUG +# include <ctype.h> +#endif + QT_BEGIN_NAMESPACE #ifdef QIODEVICE_DEBUG @@ -1756,7 +1760,7 @@ QDebug operator<<(QDebug debug, QIODevice::OpenMode modes) } qSort(modeList); debug << modeList.join(QLatin1String("|")); - debug << ")"; + debug << ')'; return debug; } #endif diff --git a/src/corelib/io/qnoncontiguousbytedevice.cpp b/src/corelib/io/qnoncontiguousbytedevice.cpp new file mode 100644 index 0000000..1ff3a4c --- /dev/null +++ b/src/corelib/io/qnoncontiguousbytedevice.cpp @@ -0,0 +1,544 @@ +/**************************************************************************** +** +** 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 "qnoncontiguousbytedevice_p.h" +#include <QObject> +#include <QBuffer> +#include <QDebug> +#include <QFile> + +QT_BEGIN_NAMESPACE + +/*! + \class QNonContiguousByteDevice + \brief A QNonContiguousByteDevice is a representation of a + file, array or buffer that allows access with a read pointer. + \since 4.6 + + \inmodule QtCore + + The goal of this class is to have a data representation that + allows us to avoid doing a memcpy as we have to do with QIODevice. + + \sa QNonContiguousByteDeviceFactory + + \internal +*/ +/*! + \fn virtual const char* QNonContiguousByteDevice::readPointer(qint64 maximumLength, qint64 &len) + + Return a byte pointer for at most \a maximumLength bytes of that device. + if \a maximumLength is -1, the caller does not care about the length and + the device may return what it desires to. + The actual number of bytes the pointer is valid for is returned in + the \a len variable. + \a len will be -1 if EOF or an error occurs. + If it was really EOF can then afterwards be checked with atEnd() + Returns 0 if it is not possible to read at that position. + + \sa atEnd() + + \internal +*/ +/*! + \fn virtual bool QNonContiguousByteDevice::advanceReadPointer(qint64 amount) + + will advance the internal read pointer by \a amount bytes. + The old readPointer is invalid after this call. + + \sa readPointer() + + \internal +*/ +/*! + \fn virtual bool QNonContiguousByteDevice::atEnd() + + Returns true if everything has been read and the read + pointer cannot be advanced anymore. + + \sa readPointer(), advanceReadPointer(), reset() + + \internal +*/ +/*! + \fn virtual bool QNonContiguousByteDevice::reset() + + Moves the internal read pointer back to the beginning. + Returns false if this was not possible. + + \sa atEnd(), disableReset() + + \internal +*/ +/*! + \fn void QNonContiguousByteDevice::disableReset() + + Disable the reset() call, e.g. it will always + do nothing and return false. + + \sa reset() + + \internal +*/ +/*! + \fn virtual qint64 QNonContiguousByteDevice::size() + + Returns the size of the complete device or -1 if unknown. + May also return less/more than what can be actually read with readPointer() + + \internal +*/ +/*! + \fn void QNonContiguousByteDevice::readyRead() + + Emitted when there is data available + + \internal +*/ +/*! + \fn void QNonContiguousByteDevice::readProgress(qint64 current, qint64 total) + + Emitted when data has been "read" by advancing the read pointer + + \internal +*/ + +QNonContiguousByteDevice::QNonContiguousByteDevice() : QObject((QObject*)0), resetDisabled(false) +{ +} + +QNonContiguousByteDevice::~QNonContiguousByteDevice() +{ +} + +void QNonContiguousByteDevice::disableReset() +{ + resetDisabled = true; +} + +QNonContiguousByteDeviceBufferImpl::QNonContiguousByteDeviceBufferImpl(QBuffer *b) : QNonContiguousByteDevice() +{ + buffer = b; + byteArray = QByteArray::fromRawData(buffer->buffer().constData() + buffer->pos(), buffer->size() - buffer->pos()); + arrayImpl = new QNonContiguousByteDeviceByteArrayImpl(&byteArray); + arrayImpl->setParent(this); + connect(arrayImpl, SIGNAL(readyRead()), SIGNAL(readyRead())); + connect(arrayImpl, SIGNAL(readProgress(qint64,qint64)), SIGNAL(readProgress(qint64,qint64))); +} + +QNonContiguousByteDeviceBufferImpl::~QNonContiguousByteDeviceBufferImpl() +{ +} + +const char* QNonContiguousByteDeviceBufferImpl::readPointer(qint64 maximumLength, qint64 &len) +{ + return arrayImpl->readPointer(maximumLength, len); +} + +bool QNonContiguousByteDeviceBufferImpl::advanceReadPointer(qint64 amount) +{ + return arrayImpl->advanceReadPointer(amount); +} + +bool QNonContiguousByteDeviceBufferImpl::atEnd() +{ + return arrayImpl->atEnd(); +} + +bool QNonContiguousByteDeviceBufferImpl::reset() +{ + if (resetDisabled) + return false; + return arrayImpl->reset(); +} + +qint64 QNonContiguousByteDeviceBufferImpl::size() +{ + return arrayImpl->size(); +} + +QNonContiguousByteDeviceByteArrayImpl::QNonContiguousByteDeviceByteArrayImpl(QByteArray *ba) : QNonContiguousByteDevice(), currentPosition(0) +{ + byteArray = ba; +} + +QNonContiguousByteDeviceByteArrayImpl::~QNonContiguousByteDeviceByteArrayImpl() +{ +} + +const char* QNonContiguousByteDeviceByteArrayImpl::readPointer(qint64 maximumLength, qint64 &len) +{ + if (atEnd()) { + len = -1; + return 0; + } + + if (maximumLength != -1) + len = qMin(maximumLength, size() - currentPosition); + else + len = size() - currentPosition; + + return byteArray->constData() + currentPosition; +} + +bool QNonContiguousByteDeviceByteArrayImpl::advanceReadPointer(qint64 amount) +{ + currentPosition += amount; + emit readProgress(currentPosition, size()); + return true; +} + +bool QNonContiguousByteDeviceByteArrayImpl::atEnd() +{ + return currentPosition >= size(); +} + +bool QNonContiguousByteDeviceByteArrayImpl::reset() +{ + if (resetDisabled) + return false; + + currentPosition = 0; + return true; +} + +qint64 QNonContiguousByteDeviceByteArrayImpl::size() +{ + return byteArray->size(); +} + +QNonContiguousByteDeviceRingBufferImpl::QNonContiguousByteDeviceRingBufferImpl(QRingBuffer *rb) + : QNonContiguousByteDevice(), currentPosition(0) +{ + ringBuffer = rb; +} + +QNonContiguousByteDeviceRingBufferImpl::~QNonContiguousByteDeviceRingBufferImpl() +{ +} + +const char* QNonContiguousByteDeviceRingBufferImpl::readPointer(qint64 maximumLength, qint64 &len) +{ + if (atEnd()) { + len = -1; + return 0; + } + + const char *returnValue = ringBuffer->readPointerAtPosition(currentPosition, len); + + if (maximumLength != -1) + len = qMin(len, maximumLength); + + return returnValue; +} + +bool QNonContiguousByteDeviceRingBufferImpl::advanceReadPointer(qint64 amount) +{ + currentPosition += amount; + emit readProgress(currentPosition, size()); + return true; +} + +bool QNonContiguousByteDeviceRingBufferImpl::atEnd() +{ + return currentPosition >= size(); +} + +bool QNonContiguousByteDeviceRingBufferImpl::reset() +{ + if (resetDisabled) + return false; + + currentPosition = 0; + return true; +} + +qint64 QNonContiguousByteDeviceRingBufferImpl::size() +{ + return ringBuffer->size(); +} + +QNonContiguousByteDeviceIoDeviceImpl::QNonContiguousByteDeviceIoDeviceImpl(QIODevice *d) + : QNonContiguousByteDevice(), + currentReadBuffer(0), currentReadBufferSize(16*1024), + currentReadBufferAmount(0), currentReadBufferPosition(0), totalAdvancements(0), + eof(false) +{ + device = d; + initialPosition = d->pos(); + connect(device, SIGNAL(readyRead()), this, SIGNAL(readyRead()), Qt::QueuedConnection); + connect(device, SIGNAL(readChannelFinished()), this, SIGNAL(readyRead()), Qt::QueuedConnection); +} + +QNonContiguousByteDeviceIoDeviceImpl::~QNonContiguousByteDeviceIoDeviceImpl() +{ + delete currentReadBuffer; +} + +const char* QNonContiguousByteDeviceIoDeviceImpl::readPointer(qint64 maximumLength, qint64 &len) +{ + if (eof == true) { + len = -1; + return 0; + } + + if (currentReadBuffer == 0) + currentReadBuffer = new QByteArray(currentReadBufferSize, '\0'); // lazy alloc + + if (maximumLength == -1) + maximumLength = currentReadBufferSize; + + if (currentReadBufferAmount - currentReadBufferPosition > 0) { + len = currentReadBufferAmount - currentReadBufferPosition; + return currentReadBuffer->data() + currentReadBufferPosition; + } + + qint64 haveRead = device->read(currentReadBuffer->data(), qMin(maximumLength, currentReadBufferSize)); + + if ((haveRead == -1) || (haveRead == 0 && device->atEnd() && !device->isSequential())) { + eof = true; + len = -1; + // size was unknown before, emit a readProgress with the final size + if (size() == -1) + emit readProgress(totalAdvancements, totalAdvancements); + return 0; + } + + currentReadBufferAmount = haveRead; + currentReadBufferPosition = 0; + + len = haveRead; + return currentReadBuffer->data(); +} + +bool QNonContiguousByteDeviceIoDeviceImpl::advanceReadPointer(qint64 amount) +{ + totalAdvancements += amount; + + // normal advancement + currentReadBufferPosition += amount; + + // advancing over that what has actually been read before + if (currentReadBufferPosition > currentReadBufferAmount) { + qint64 i = currentReadBufferPosition - currentReadBufferAmount; + while (i > 0) { + if (device->getChar(0) == false) { + emit readProgress(totalAdvancements - i, size()); + return false; // ### FIXME handle eof + } + i--; + } + + currentReadBufferPosition = 0; + currentReadBufferAmount = 0; + } + + if (size() == -1) + emit readProgress(totalAdvancements, totalAdvancements); + else + emit readProgress(totalAdvancements, size()); + + return true; +} + +bool QNonContiguousByteDeviceIoDeviceImpl::atEnd() +{ + return eof == true; +} + +bool QNonContiguousByteDeviceIoDeviceImpl::reset() +{ + if (resetDisabled) + return false; + + if (device->seek(initialPosition)) { + eof = false; // assume eof is false, it will be true after a read has been attempted + return true; + } + + return false; +} + +qint64 QNonContiguousByteDeviceIoDeviceImpl::size() +{ + // note that this is different from the size() implementation of QIODevice! + + if (device->isSequential()) + return -1; + + return device->size() - initialPosition; +} + +QByteDeviceWrappingIoDevice::QByteDeviceWrappingIoDevice(QNonContiguousByteDevice *bd) : QIODevice((QObject*)0) +{ + byteDevice = bd; + connect(bd, SIGNAL(readyRead()), SIGNAL(readyRead())); + + open(ReadOnly); +} + +QByteDeviceWrappingIoDevice::~QByteDeviceWrappingIoDevice() +{ + +} + +bool QByteDeviceWrappingIoDevice::isSequential() const +{ + return (byteDevice->size() == -1); +} + +bool QByteDeviceWrappingIoDevice::atEnd() const +{ + return byteDevice->atEnd(); +} + +bool QByteDeviceWrappingIoDevice::reset() +{ + return byteDevice->reset(); +} + +qint64 QByteDeviceWrappingIoDevice::size() const +{ + if (isSequential()) + return 0; + + return byteDevice->size(); +} + + +qint64 QByteDeviceWrappingIoDevice::readData( char * data, qint64 maxSize) +{ + qint64 len; + const char *readPointer = byteDevice->readPointer(maxSize, len); + if (len == -1) + return -1; + + memcpy(data, readPointer, len); + byteDevice->advanceReadPointer(len); + return len; +} + +qint64 QByteDeviceWrappingIoDevice::writeData( const char* data, qint64 maxSize) +{ + Q_UNUSED(data); + Q_UNUSED(maxSize); + return -1; +} + +/*! + \class QNonContiguousByteDeviceFactory + \since 4.6 + + \inmodule QtCore + + Creates a QNonContiguousByteDevice out of a QIODevice, + QByteArray etc. + + \sa QNonContiguousByteDevice + + \internal +*/ + +/*! + \fn static QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QIODevice *device); + + Create a QNonContiguousByteDevice out of a QIODevice. + For QFile, QBuffer and all other QIoDevice, sequential or not. + + \internal +*/ +QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QIODevice *device) +{ + // shortcut if it is a QBuffer + if (QBuffer* buffer = qobject_cast<QBuffer*>(device)) { + return new QNonContiguousByteDeviceBufferImpl(buffer); + } + + // ### FIXME special case if device is a QFile that supports map() + // then we can actually deal with the file without using read/peek + + // generic QIODevice + return new QNonContiguousByteDeviceIoDeviceImpl(device); // FIXME +} + +/*! + \fn static QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QRingBuffer *ringBuffer); + + Create a QNonContiguousByteDevice out of a QRingBuffer. + + \internal +*/ +QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QRingBuffer *ringBuffer) +{ + return new QNonContiguousByteDeviceRingBufferImpl(ringBuffer); +} + +/*! + \fn static QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QByteArray *byteArray); + + Create a QNonContiguousByteDevice out of a QByteArray. + + \internal +*/ +QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QByteArray *byteArray) +{ + return new QNonContiguousByteDeviceByteArrayImpl(byteArray); +} + +/*! + \fn static QIODevice* QNonContiguousByteDeviceFactory::wrap(QNonContiguousByteDevice* byteDevice); + + Wrap the \a byteDevice (possibly again) into a QIODevice. + + \internal +*/ +QIODevice* QNonContiguousByteDeviceFactory::wrap(QNonContiguousByteDevice* byteDevice) +{ + // ### FIXME if it already has been based on QIoDevice, we could that one out again + // and save some calling + + // needed for FTP backend + + return new QByteDeviceWrappingIoDevice(byteDevice); +} + +QT_END_NAMESPACE + diff --git a/src/corelib/io/qnoncontiguousbytedevice_p.h b/src/corelib/io/qnoncontiguousbytedevice_p.h new file mode 100644 index 0000000..562b759 --- /dev/null +++ b/src/corelib/io/qnoncontiguousbytedevice_p.h @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** 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 QNONCONTIGUOUSBYTEDEVICE_H +#define QNONCONTIGUOUSBYTEDEVICE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QObject> +#include <QtCore/qbytearray.h> +#include <QtCore/qbuffer.h> +#include <QtCore/qiodevice.h> +#include "private/qringbuffer_p.h" + +QT_BEGIN_NAMESPACE + +class Q_CORE_EXPORT QNonContiguousByteDevice : public QObject +{ + Q_OBJECT +public: + virtual const char* readPointer(qint64 maximumLength, qint64 &len) = 0; + virtual bool advanceReadPointer(qint64 amount) = 0; + virtual bool atEnd() = 0; + virtual bool reset() = 0; + void disableReset(); + virtual qint64 size() = 0; + +protected: + QNonContiguousByteDevice(); + virtual ~QNonContiguousByteDevice(); + + bool resetDisabled; +Q_SIGNALS: + void readyRead(); + void readProgress(qint64 current, qint64 total); +}; + +class Q_CORE_EXPORT QNonContiguousByteDeviceFactory +{ +public: + static QNonContiguousByteDevice* create(QIODevice *device); + static QNonContiguousByteDevice* create(QByteArray *byteArray); + static QNonContiguousByteDevice* create(QRingBuffer *ringBuffer); + static QIODevice* wrap(QNonContiguousByteDevice* byteDevice); +}; + +// the actual implementations +// + +class QNonContiguousByteDeviceByteArrayImpl : public QNonContiguousByteDevice +{ +public: + QNonContiguousByteDeviceByteArrayImpl(QByteArray *ba); + ~QNonContiguousByteDeviceByteArrayImpl(); + const char* readPointer(qint64 maximumLength, qint64 &len); + bool advanceReadPointer(qint64 amount); + bool atEnd(); + bool reset(); + qint64 size(); +protected: + QByteArray* byteArray; + qint64 currentPosition; +}; + +class QNonContiguousByteDeviceRingBufferImpl : public QNonContiguousByteDevice +{ +public: + QNonContiguousByteDeviceRingBufferImpl(QRingBuffer *rb); + ~QNonContiguousByteDeviceRingBufferImpl(); + const char* readPointer(qint64 maximumLength, qint64 &len); + bool advanceReadPointer(qint64 amount); + bool atEnd(); + bool reset(); + qint64 size(); +protected: + QRingBuffer* ringBuffer; + qint64 currentPosition; +}; + + +class QNonContiguousByteDeviceIoDeviceImpl : public QNonContiguousByteDevice +{ +public: + QNonContiguousByteDeviceIoDeviceImpl(QIODevice *d); + ~QNonContiguousByteDeviceIoDeviceImpl(); + const char* readPointer(qint64 maximumLength, qint64 &len); + bool advanceReadPointer(qint64 amount); + bool atEnd(); + bool reset(); + qint64 size(); +protected: + QIODevice* device; + QByteArray* currentReadBuffer; + qint64 currentReadBufferSize; + qint64 currentReadBufferAmount; + qint64 currentReadBufferPosition; + qint64 totalAdvancements; + bool eof; + qint64 initialPosition; +}; + +class QNonContiguousByteDeviceBufferImpl : public QNonContiguousByteDevice +{ +public: + QNonContiguousByteDeviceBufferImpl(QBuffer *b); + ~QNonContiguousByteDeviceBufferImpl(); + const char* readPointer(qint64 maximumLength, qint64 &len); + bool advanceReadPointer(qint64 amount); + bool atEnd(); + bool reset(); + qint64 size(); +protected: + QBuffer* buffer; + QByteArray byteArray; + QNonContiguousByteDeviceByteArrayImpl* arrayImpl; +}; + +// ... and the reverse thing +class QByteDeviceWrappingIoDevice : public QIODevice +{ +public: + QByteDeviceWrappingIoDevice (QNonContiguousByteDevice *bd); + ~QByteDeviceWrappingIoDevice (); + virtual bool isSequential () const; + virtual bool atEnd () const; + virtual bool reset (); + virtual qint64 size () const; +protected: + virtual qint64 readData ( char * data, qint64 maxSize ); + virtual qint64 writeData ( const char * data, qint64 maxSize ); + + QNonContiguousByteDevice *byteDevice; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp index 8183527..0becbb6 100644 --- a/src/corelib/io/qprocess.cpp +++ b/src/corelib/io/qprocess.cpp @@ -101,6 +101,29 @@ QT_END_NAMESPACE QT_BEGIN_NAMESPACE +static QHash<QString, QString> environmentHashFromList(const QStringList &environment) +{ + QHash<QString, QString> result; + QStringList::ConstIterator it = environment.constBegin(), + end = environment.constEnd(); + for ( ; it != end; ++it) { + int equals = it->indexOf(QLatin1Char('=')); + + QString name = *it; + QString value; + if (equals != -1) { + name.truncate(equals); +#ifdef Q_OS_WIN + name = name.toUpper(); +#endif + value = it->mid(equals + 1); + } + result.insert(name, value); + } + + return result; +} + void QProcessPrivate::Channel::clear() { switch (type) { @@ -421,6 +444,7 @@ QProcessPrivate::QProcessPrivate() sequenceNumber = 0; exitCode = 0; exitStatus = QProcess::NormalExit; + environment = 0; startupSocketNotifier = 0; deathNotifier = 0; notifier = 0; @@ -451,6 +475,7 @@ QProcessPrivate::QProcessPrivate() */ QProcessPrivate::~QProcessPrivate() { + delete environment; if (stdinChannel.process) stdinChannel.process->stdoutChannel.clear(); if (stdoutChannel.process) @@ -1211,29 +1236,83 @@ QProcess::ProcessState QProcess::state() const \snippet doc/src/snippets/qprocess-environment/main.cpp 0 - \sa environment(), systemEnvironment() + \sa environment(), systemEnvironment(), setEnvironmentHash() */ void QProcess::setEnvironment(const QStringList &environment) { - Q_D(QProcess); - d->environment = environment; + setEnvironmentHash(environmentHashFromList(environment)); } /*! Returns the environment that QProcess will use when starting a process, or an empty QStringList if no environment has been set - using setEnvironment(). If no environment has been set, the - environment of the calling process will be used. + using setEnvironment() or setEnvironmentHash(). If no environment + has been set, the environment of the calling process will be used. \note The environment settings are ignored on Windows CE and Symbian, as there is no concept of an environment. - \sa setEnvironment(), systemEnvironment() + \sa environmentHash(), setEnvironment(), systemEnvironment() */ QStringList QProcess::environment() const { Q_D(const QProcess); - return d->environment; + + QStringList result; + if (!d->environment) + return result; + + QHash<QString, QString>::ConstIterator it = d->environment->constBegin(), + end = d->environment->constEnd(); + for ( ; it != end; ++it) { + QString data = it.key(); + data.reserve(data.length() + it.value().length() + 1); + data.append(QLatin1Char('=')); + data.append(it.value()); + result << data; + } + return result; +} + +/*! + \since 4.5 + Sets the environment that QProcess will use when starting a process to the + \a environment hash map. + + For example, the following code adds the \c{C:\\BIN} directory to the list of + executable paths (\c{PATHS}) on Windows and sets \c{TMPDIR}: + + \snippet doc/src/snippets/qprocess-environment/main.cpp 1 + + \sa environment(), systemEnvironmentHash(), setEnvironment() +*/ +void QProcess::setEnvironmentHash(const QHash<QString, QString> &environment) +{ + Q_D(QProcess); + if (!d->environment) + d->environment = new QHash<QString, QString>(environment); + else + *d->environment = environment; +} + +/*! + \since 4.5 + Returns the environment that QProcess will use when starting a + process, or an empty QHash if no environment has been set using + setEnvironment() or setEnvironmentHash(). If no environment has + been set, the environment of the calling process will be used. + + \note The environment settings are ignored on Windows CE, + as there is no concept of an environment. + + \sa setEnvironmentHash(), setEnvironment(), systemEnvironmentHash() +*/ +QHash<QString, QString> QProcess::environmentHash() const +{ + Q_D(const QProcess); + if (d->environment) + return *d->environment; + return QHash<QString, QString>(); } /*! @@ -1527,7 +1606,7 @@ void QProcess::start(const QString &program, const QStringList &arguments, OpenM } #if defined QPROCESS_DEBUG - qDebug() << "QProcess::start(" << program << "," << arguments << "," << mode << ")"; + qDebug() << "QProcess::start(" << program << ',' << arguments << ',' << mode << ')'; #endif d->outputReadBuffer.clear(); @@ -1838,7 +1917,7 @@ QT_END_INCLUDE_NAMESPACE \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 8 - \sa environment(), setEnvironment() + \sa systemEnvironmentHash(), environment(), setEnvironment() */ QStringList QProcess::systemEnvironment() { @@ -1851,6 +1930,18 @@ QStringList QProcess::systemEnvironment() } /*! + \since 4.5 + + Returns the environment of the calling process as a QHash. + + \sa systemEnvironment(), environmentHash(), setEnvironmentHash() +*/ +QHash<QString, QString> QProcess::systemEnvironmentHash() +{ + return environmentHashFromList(systemEnvironment()); +} + +/*! \typedef Q_PID \relates QProcess diff --git a/src/corelib/io/qprocess.h b/src/corelib/io/qprocess.h index ae40de3..65b5b3e 100644 --- a/src/corelib/io/qprocess.h +++ b/src/corelib/io/qprocess.h @@ -53,6 +53,8 @@ QT_MODULE(Core) #ifndef QT_NO_PROCESS +template <class Key, class T> class QHash; + #if (!defined(Q_OS_WIN32) && !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)) || defined(qdoc) typedef qint64 Q_PID; #elif defined(Q_OS_SYMBIAN) @@ -126,6 +128,8 @@ public: void setEnvironment(const QStringList &environment); QStringList environment() const; + void setEnvironmentHash(const QHash<QString, QString> &environment); + QHash<QString, QString> environmentHash() const; QProcess::ProcessError error() const; QProcess::ProcessState state() const; @@ -161,6 +165,7 @@ public: static bool startDetached(const QString &program); static QStringList systemEnvironment(); + static QHash<QString, QString> systemEnvironmentHash(); public Q_SLOTS: void terminate(); diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h index 554c477..6a3342a 100644 --- a/src/corelib/io/qprocess_p.h +++ b/src/corelib/io/qprocess_p.h @@ -161,7 +161,7 @@ public: QString program; QStringList arguments; - QStringList environment; + QHash<QString, QString> *environment; QRingBuffer outputReadBuffer; QRingBuffer errorReadBuffer; diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index 1fedd55..e87c314 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -385,17 +385,10 @@ static void qt_create_pipe(int *pipe) qt_native_close(pipe[0]); if (pipe[1] != -1) qt_native_close(pipe[1]); -#ifdef Q_OS_IRIX - if (::socketpair(AF_UNIX, SOCK_STREAM, 0, pipe) == -1) { - qWarning("QProcessPrivate::createPipe: Cannot create pipe %p: %s", - pipe, qPrintable(qt_error_string(errno))); - } -#else if (::pipe(pipe) != 0) { qWarning("QProcessPrivate::createPipe: Cannot create pipe %p: %s", pipe, qPrintable(qt_error_string(errno))); } -#endif ::fcntl(pipe[0], F_SETFD, FD_CLOEXEC); ::fcntl(pipe[1], F_SETFD, FD_CLOEXEC); } @@ -522,8 +515,12 @@ bool QProcessPrivate::createChannel(Channel &channel) } } -static char **_q_dupEnvironment(const QStringList &environment, int *envc) +static char **_q_dupEnvironment(const QHash<QString, QString> *environment, int *envc) { + *envc = 0; + if (!environment) + return 0; // use the default environment + // if LD_LIBRARY_PATH exists in the current environment, but // not in the environment list passed by the programmer, then // copy it over. @@ -532,27 +529,29 @@ static char **_q_dupEnvironment(const QStringList &environment, int *envc) #else static const char libraryPath[] = "LD_LIBRARY_PATH"; #endif - const QString libraryPathString = QLatin1String(libraryPath); - QStringList env = environment; - QStringList matches = env.filter( - QRegExp(QLatin1Char('^') + libraryPathString + QLatin1Char('='))); - const QString envLibraryPath = QString::fromLocal8Bit(::getenv(libraryPath)); - if (matches.isEmpty() && !envLibraryPath.isEmpty()) { - QString entry = libraryPathString; - entry += QLatin1Char('='); - entry += envLibraryPath; - env << libraryPathString + QLatin1Char('=') + envLibraryPath; - } - - char **envp = new char *[env.count() + 1]; - envp[env.count()] = 0; - - for (int j = 0; j < env.count(); ++j) { - QString item = env.at(j); - envp[j] = ::strdup(item.toLocal8Bit().constData()); + const QByteArray envLibraryPath = qgetenv(libraryPath); + bool needToAddLibraryPath = !envLibraryPath.isEmpty() && + !environment->contains(QLatin1String(libraryPath)); + + char **envp = new char *[environment->count() + 2]; + envp[environment->count()] = 0; + envp[environment->count() + 1] = 0; + + QHash<QString, QString>::ConstIterator it = environment->constBegin(); + const QHash<QString, QString>::ConstIterator end = environment->constEnd(); + for ( ; it != end; ++it) { + QByteArray key = it.key().toLocal8Bit(); + QByteArray value = it.value().toLocal8Bit(); + key.reserve(key.length() + 1 + value.length()); + key.append('='); + key.append(value); + + envp[(*envc)++] = ::strdup(key.constData()); } - *envc = env.count(); + if (needToAddLibraryPath) + envp[(*envc)++] = ::strdup(QByteArray(libraryPath) + '=' + + envLibraryPath); return envp; } @@ -581,29 +580,27 @@ void QProcessPrivate::startProcess() processManager()->start(); // Initialize pipes + if (!createChannel(stdinChannel) || + !createChannel(stdoutChannel) || + !createChannel(stderrChannel)) + return; qt_create_pipe(childStartedPipe); + qt_create_pipe(deathPipe); + ::fcntl(deathPipe[0], F_SETFD, FD_CLOEXEC); + ::fcntl(deathPipe[1], F_SETFD, FD_CLOEXEC); + if (threadData->eventDispatcher) { startupSocketNotifier = new QSocketNotifier(childStartedPipe[0], QSocketNotifier::Read, q); QObject::connect(startupSocketNotifier, SIGNAL(activated(int)), q, SLOT(_q_startupNotification())); - } - qt_create_pipe(deathPipe); - ::fcntl(deathPipe[0], F_SETFD, FD_CLOEXEC); - ::fcntl(deathPipe[1], F_SETFD, FD_CLOEXEC); - if (threadData->eventDispatcher) { deathNotifier = new QSocketNotifier(deathPipe[0], QSocketNotifier::Read, q); QObject::connect(deathNotifier, SIGNAL(activated(int)), q, SLOT(_q_processDied())); } - if (!createChannel(stdinChannel) || - !createChannel(stdoutChannel) || - !createChannel(stderrChannel)) - return; - // Start the process (platform dependent) q->setProcessState(QProcess::Starting); @@ -791,7 +788,7 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv q->setupChildProcess(); // execute the process - if (environment.isEmpty()) { + if (!envp) { qt_native_execvp(argv[0], argv); } else { if (path) { diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp index 0e36760..179c3d0 100644 --- a/src/corelib/io/qprocess_win.cpp +++ b/src/corelib/io/qprocess_win.cpp @@ -272,12 +272,12 @@ static QString qt_create_commandline(const QString &program, const QStringList & QString args; if (!program.isEmpty()) { QString programName = program; - if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1String(" "))) - programName = QLatin1String("\"") + programName + QLatin1String("\""); - programName.replace(QLatin1String("/"), QLatin1String("\\")); + if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1Char(' '))) + programName = QLatin1Char('\"') + programName + QLatin1Char('\"'); + programName.replace(QLatin1Char('/'), QLatin1Char('\\')); // add the prgram as the first arg ... it works better - args = programName + QLatin1String(" "); + args = programName + QLatin1Char(' '); } for (int i=0; i<arguments.size(); ++i) { @@ -285,16 +285,16 @@ static QString qt_create_commandline(const QString &program, const QStringList & // in the case of \" already being in the string the \ must also be escaped tmp.replace( QLatin1String("\\\""), QLatin1String("\\\\\"") ); // escape a single " because the arguments will be parsed - tmp.replace( QLatin1String("\""), QLatin1String("\\\"") ); + tmp.replace( QLatin1Char('\"'), QLatin1String("\\\"") ); if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) { // The argument must not end with a \ since this would be interpreted // as escaping the quote -- rather put the \ behind the quote: e.g. // rather use "foo"\ than "foo\" - QString endQuote(QLatin1String("\"")); + QString endQuote(QLatin1Char('\"')); int i = tmp.length(); while (i>0 && tmp.at(i-1) == QLatin1Char('\\')) { --i; - endQuote += QLatin1String("\\"); + endQuote += QLatin1Char('\\'); } args += QLatin1String(" \"") + tmp.left(i) + endQuote; } else { @@ -304,44 +304,69 @@ static QString qt_create_commandline(const QString &program, const QStringList & return args; } -static QByteArray qt_create_environment(const QStringList &environment) +static QByteArray qt_create_environment(const QHash<QString, QString> *environment) { QByteArray envlist; - if (!environment.isEmpty()) { - QStringList envStrings = environment; - int pos = 0; - // add PATH if necessary (for DLL loading) - if (envStrings.filter(QRegExp(QLatin1String("^PATH="),Qt::CaseInsensitive)).isEmpty()) { + if (environment) { + QHash<QString, QString> copy = *environment; + + // add PATH if necessary (for DLL loading) + if (!copy.contains(QLatin1String("PATH"))) { QByteArray path = qgetenv("PATH"); if (!path.isEmpty()) - envStrings.prepend(QString(QLatin1String("PATH=%1")).arg(QString::fromLocal8Bit(path))); + copy.insert(QLatin1String("PATH"), QString::fromLocal8Bit(path)); } + // add systemroot if needed - if (envStrings.filter(QRegExp(QLatin1String("^SystemRoot="),Qt::CaseInsensitive)).isEmpty()) { - QByteArray systemRoot = qgetenv("SystemRoot"); + if (!copy.contains(QLatin1String("SYSTEMROOT"))) { + QByteArray systemRoot = qgetenv("SYSTEMROOT"); if (!systemRoot.isEmpty()) - envStrings.prepend(QString(QLatin1String("SystemRoot=%1")).arg(QString::fromLocal8Bit(systemRoot))); + copy.insert(QLatin1String("SYSTEMROOT"), QString::fromLocal8Bit(systemRoot)); } + + int pos = 0; + QHash<QString, QString>::ConstIterator it = copy.constBegin(), + end = copy.constEnd(); #ifdef UNICODE if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) { - for (QStringList::ConstIterator it = envStrings.constBegin(); it != envStrings.constEnd(); ++it) { - QString tmp = *it; - uint tmpSize = sizeof(TCHAR) * (tmp.length()+1); + static const TCHAR equal = L'='; + static const TCHAR nul = L'\0'; + + for ( ; it != end; ++it) { + uint tmpSize = sizeof(TCHAR) * (it.key().length() + it.value().length() + 2); + // ignore empty strings + if (tmpSize == sizeof(TCHAR)*2) + continue; envlist.resize(envlist.size() + tmpSize); - memcpy(envlist.data()+pos, tmp.utf16(), tmpSize); + + tmpSize = it.key().length() * sizeof(TCHAR); + memcpy(envlist.data()+pos, it.key().utf16(), tmpSize); + pos += tmpSize; + + memcpy(envlist.data()+pos, &equal, sizeof(TCHAR)); + pos += sizeof(TCHAR); + + tmpSize = it.value().length() * sizeof(TCHAR); + memcpy(envlist.data()+pos, it.value().utf16(), tmpSize); pos += tmpSize; - } - // add the 2 terminating 0 (actually 4, just to be on the safe side) - envlist.resize( envlist.size()+4 ); - envlist[pos++] = 0; - envlist[pos++] = 0; - envlist[pos++] = 0; - envlist[pos++] = 0; + + memcpy(envlist.data()+pos, &nul, sizeof(TCHAR)); + pos += sizeof(TCHAR); + } + // add the 2 terminating 0 (actually 4, just to be on the safe side) + envlist.resize( envlist.size()+4 ); + envlist[pos++] = 0; + envlist[pos++] = 0; + envlist[pos++] = 0; + envlist[pos++] = 0; } else #endif // UNICODE { - for (QStringList::ConstIterator it = envStrings.constBegin(); it != envStrings.constEnd(); it++) { - QByteArray tmp = (*it).toLocal8Bit(); + for ( ; it != end; it++) { + QByteArray tmp = it.key().toLocal8Bit(); + tmp.append('='); + tmp.append(it.value().toLocal8Bit()); + uint tmpSize = tmp.length() + 1; envlist.resize(envlist.size() + tmpSize); memcpy(envlist.data()+pos, tmp.data(), tmpSize); @@ -402,7 +427,7 @@ void QProcessPrivate::startProcess() QString fullPathProgram = program; if (!QDir::isAbsolutePath(fullPathProgram)) fullPathProgram = QFileInfo(fullPathProgram).absoluteFilePath(); - fullPathProgram.replace(QLatin1String("/"), QLatin1String("\\")); + fullPathProgram.replace(QLatin1Char('/'), QLatin1Char('\\')); success = CreateProcessW((WCHAR*)fullPathProgram.utf16(), (WCHAR*)args.utf16(), 0, 0, false, 0, 0, 0, 0, pid); @@ -418,7 +443,7 @@ void QProcessPrivate::startProcess() }; success = CreateProcessW(0, (WCHAR*)args.utf16(), 0, 0, TRUE, dwCreationFlags, - environment.isEmpty() ? 0 : envlist.data(), + environment ? envlist.data() : 0, workingDirectory.isEmpty() ? 0 : (WCHAR*)QDir::toNativeSeparators(workingDirectory).utf16(), &startupInfo, pid); @@ -437,7 +462,7 @@ void QProcessPrivate::startProcess() }; success = CreateProcessA(0, args.toLocal8Bit().data(), - 0, 0, TRUE, dwCreationFlags, environment.isEmpty() ? 0 : envlist.data(), + 0, 0, TRUE, dwCreationFlags, environment ? envlist.data() : 0, workingDirectory.isEmpty() ? 0 : QDir::toNativeSeparators(workingDirectory).toLocal8Bit().data(), &startupInfo, pid); @@ -862,8 +887,8 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a #if defined(Q_OS_WINCE) QString fullPathProgram = program; if (!QDir::isAbsolutePath(fullPathProgram)) - fullPathProgram.prepend(QDir::currentPath().append(QLatin1String("/"))); - fullPathProgram.replace(QLatin1String("/"), QLatin1String("\\")); + fullPathProgram.prepend(QDir::currentPath().append(QLatin1Char('/'))); + fullPathProgram.replace(QLatin1Char('/'), QLatin1Char('\\')); success = CreateProcessW((WCHAR*)fullPathProgram.utf16(), (WCHAR*)args.utf16(), 0, 0, false, CREATE_NEW_CONSOLE, 0, 0, 0, &pinfo); diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp index 0cbcc41..c3c7b76 100644 --- a/src/corelib/io/qresource.cpp +++ b/src/corelib/io/qresource.cpp @@ -60,6 +60,37 @@ QT_BEGIN_NAMESPACE + +class QStringSplitter +{ +public: + QStringSplitter(const QString &s) + : m_string(s), m_data(m_string.constData()), m_len(s.length()), m_pos(0) + { + m_splitChar = QLatin1Char('/'); + } + + inline bool hasNext() { + while (m_pos < m_len && m_data[m_pos] == m_splitChar) + ++m_pos; + return m_pos < m_len; + } + + inline QStringRef next() { + int start = m_pos; + while (m_pos < m_len && m_data[m_pos] != m_splitChar) + ++m_pos; + return QStringRef(&m_string, start, m_pos - start); + } + + QString m_string; + const QChar *m_data; + QChar m_splitChar; + int m_len; + int m_pos; +}; + + //resource glue class QResourceRoot { @@ -101,6 +132,16 @@ protected: } }; +static QString cleanPath(const QString &_path) +{ + QString path = QDir::cleanPath(_path); + // QDir::cleanPath does not remove two trailing slashes under _Windows_ + // due to support for UNC paths. Remove those manually. + if (path.startsWith(QLatin1String("//"))) + path.remove(0, 1); + return path; +} + Q_DECLARE_TYPEINFO(QResourceRoot, Q_MOVABLE_TYPE); Q_GLOBAL_STATIC_WITH_ARGS(QMutex, resourceMutex, (QMutex::Recursive)) @@ -216,9 +257,10 @@ QResourcePrivate::load(const QString &file) related.clear(); QMutexLocker lock(resourceMutex()); const ResourceList *list = resourceList(); + QString cleaned = cleanPath(file); for(int i = 0; i < list->size(); ++i) { QResourceRoot *res = list->at(i); - const int node = res->findNode(file); + const int node = res->findNode(cleaned); if(node != -1) { if(related.isEmpty()) { container = res->isContainer(node); @@ -292,6 +334,7 @@ QResourcePrivate::ensureChildren() const if(path.startsWith(QLatin1Char(':'))) path = path.mid(1); QSet<QString> kids; + QString cleaned = cleanPath(path); for(int i = 0; i < related.size(); ++i) { QResourceRoot *res = related.at(i); if(res->mappingRootSubdir(path, &k) && !k.isEmpty()) { @@ -300,7 +343,7 @@ QResourcePrivate::ensureChildren() const kids.insert(k); } } else { - const int node = res->findNode(path); + const int node = res->findNode(cleaned); if(node != -1) { QStringList related_children = res->children(node); for(int kid = 0; kid < related_children.size(); ++kid) { @@ -561,30 +604,32 @@ inline QString QResourceRoot::name(int node) const (names[name_offset+1] << 0); name_offset += 2; name_offset += 4; //jump past hash - for(int i = 0; i < name_length*2; i+=2) - ret += QChar(names[name_offset+i+1], names[name_offset+i]); + + ret.resize(name_length); + QChar *strData = ret.data(); + for(int i = 0; i < name_length*2; i+=2) { + QChar c(names[name_offset+i+1], names[name_offset+i]); + *strData = c; + ++strData; + } return ret; } + int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const { - QString path = QDir::cleanPath(_path); - // QDir::cleanPath does not remove two trailing slashes under _Windows_ - // due to support for UNC paths. Remove those manually. - if (path.startsWith(QLatin1String("//"))) - path.remove(0, 1); - + QString path = _path; { QString root = mappingRoot(); if(!root.isEmpty()) { if(root == path) { - path = QLatin1String("/"); + path = QLatin1Char('/'); } else { - if(!root.endsWith(QLatin1String("/"))) - root += QLatin1String("/"); + if(!root.endsWith(QLatin1Char('/'))) + root += QLatin1Char('/'); if(path.size() >= root.size() && path.startsWith(root)) path = path.mid(root.length()-1); if(path.isEmpty()) - path = QLatin1String("/"); + path = QLatin1Char('/'); } } } @@ -603,12 +648,11 @@ int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const //now iterate up the tree int node = -1; - QStringList segments = path.split(QLatin1Char('/'), QString::SkipEmptyParts); -#ifdef DEBUG_RESOURCE_MATCH - qDebug() << "****" << segments; -#endif - for(int i = 0; child_count && i < segments.size(); ++i) { - const QString &segment = segments[i]; + + QStringSplitter splitter(path); + while (child_count && splitter.hasNext()) { + QStringRef segment = splitter.next(); + #ifdef DEBUG_RESOURCE_MATCH qDebug() << " CHILDREN" << segment; for(int j = 0; j < child_count; ++j) { @@ -650,7 +694,7 @@ int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const (tree[offset+1] << 0); offset += 2; - if(i == segments.size()-1) { + if(!splitter.hasNext()) { if(!(flags & Directory)) { const short country = (tree[offset+0] << 8) + (tree[offset+1] << 0); diff --git a/src/corelib/io/qsettings_p.h b/src/corelib/io/qsettings_p.h index 1bdbf2c..a0f19a4 100644 --- a/src/corelib/io/qsettings_p.h +++ b/src/corelib/io/qsettings_p.h @@ -146,7 +146,7 @@ inline QString QSettingsGroup::toString() const return result; } -class Q_CORE_EXPORT QConfFile +class Q_AUTOTEST_EXPORT QConfFile { public: ParsedSettingsMap mergedKeyMap() const; diff --git a/src/corelib/io/qsettings_win.cpp b/src/corelib/io/qsettings_win.cpp index a08c969..57f65d6 100644 --- a/src/corelib/io/qsettings_win.cpp +++ b/src/corelib/io/qsettings_win.cpp @@ -148,7 +148,7 @@ static QString errorCodeToString(DWORD errorCode) if (data != 0) LocalFree(data); }) - if (result.endsWith(QLatin1String("\n"))) + if (result.endsWith(QLatin1Char('\n'))) result.truncate(result.length() - 1); return result; diff --git a/src/corelib/io/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp index 45a03e3..2d0d9b8 100644 --- a/src/corelib/io/qtemporaryfile.cpp +++ b/src/corelib/io/qtemporaryfile.cpp @@ -203,7 +203,7 @@ static int _gettemp(char *path, int *doopen, int domkdir, int slen) if (QDir::isAbsolutePath(QString::fromLatin1(path))) targetPath = QLatin1String(path); else - targetPath = QDir::currentPath().append(QLatin1String("/")) + QLatin1String(path); + targetPath = QDir::currentPath().append(QLatin1Char('/')) + QLatin1String(path); if ((*doopen = QT_OPEN(targetPath.toLocal8Bit(), O_CREAT|O_EXCL|O_RDWR diff --git a/src/corelib/io/qtextstream.cpp b/src/corelib/io/qtextstream.cpp index 9382fae..0261c19 100644 --- a/src/corelib/io/qtextstream.cpp +++ b/src/corelib/io/qtextstream.cpp @@ -333,7 +333,7 @@ public: this->stream = stream; } -public slots: +public Q_SLOTS: inline void flushStream() { stream->flush(); } private: @@ -411,6 +411,7 @@ public: QString writeBuffer; QString readBuffer; int readBufferOffset; + int readConverterSavedStateOffset; //the offset between readBufferStartDevicePos and that start of the buffer qint64 readBufferStartDevicePos; // streaming parameters @@ -437,6 +438,7 @@ QTextStreamPrivate::QTextStreamPrivate(QTextStream *q_ptr) #ifndef QT_NO_TEXTCODEC readConverterSavedState(0), #endif + readConverterSavedStateOffset(0), locale(QLocale::C) { this->q_ptr = q_ptr; @@ -559,13 +561,8 @@ bool QTextStreamPrivate::fillReadBuffer(qint64 maxBytes) if (!codec || autoDetectUnicode) { autoDetectUnicode = false; - if (bytesRead >= 4 && ((uchar(buf[0]) == 0xff && uchar(buf[1]) == 0xfe && uchar(buf[2]) == 0 && uchar(buf[3]) == 0) - || (uchar(buf[0]) == 0 && uchar(buf[1]) == 0 && uchar(buf[2]) == 0xfe && uchar(buf[3]) == 0xff))) { - codec = QTextCodec::codecForName("UTF-32"); - } else if (bytesRead >= 2 && ((uchar(buf[0]) == 0xff && uchar(buf[1]) == 0xfe) - || (uchar(buf[0]) == 0xfe && uchar(buf[1]) == 0xff))) { - codec = QTextCodec::codecForName("UTF-16"); - } else if (!codec) { + codec = QTextCodec::codecForUtfText(QByteArray::fromRawData(buf, bytesRead), codec); + if (!codec) { codec = QTextCodec::codecForLocale(); writeConverterState.flags |= QTextCodec::IgnoreHeader; } @@ -835,6 +832,10 @@ inline void QTextStreamPrivate::consume(int size) readBufferOffset = 0; readBuffer.clear(); saveConverterState(device->pos()); + } else if (readBufferOffset > QTEXTSTREAM_BUFFERSIZE) { + readBuffer = readBuffer.remove(0,readBufferOffset); + readConverterSavedStateOffset += readBufferOffset; + readBufferOffset = 0; } } } @@ -856,6 +857,7 @@ inline void QTextStreamPrivate::saveConverterState(qint64 newPos) #endif readBufferStartDevicePos = newPos; + readConverterSavedStateOffset = 0; } /*! \internal @@ -1217,7 +1219,7 @@ qint64 QTextStream::pos() const // Rewind the device to get to the current position Ensure that // readBufferOffset is unaffected by fillReadBuffer() - int oldReadBufferOffset = d->readBufferOffset; + int oldReadBufferOffset = d->readBufferOffset + d->readConverterSavedStateOffset; while (d->readBuffer.size() < oldReadBufferOffset) { if (!thatd->fillReadBuffer(1)) return qint64(-1); @@ -2287,7 +2289,7 @@ bool QTextStreamPrivate::putNumber(qulonglong number, bool negative) // ShowBase flag set zero should be written as '00' if (number == 0 && base == 8 && numberFlags & QTextStream::ShowBase && result == QLatin1String("0")) { - result.prepend(QLatin1String("0")); + result.prepend(QLatin1Char('0')); } } return putString(result, true); diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp index d1a5cdd..5846e23 100644 --- a/src/corelib/io/qurl.cpp +++ b/src/corelib/io/qurl.cpp @@ -5330,7 +5330,7 @@ QString QUrl::toString(FormattingOptions options) const url += QLatin1Char('/'); url += ourPath; // check if we need to remove trailing slashes - while ((options & StripTrailingSlash) && url.right(1) == QLatin1String("/")) + while ((options & StripTrailingSlash) && url.endsWith(QLatin1Char('/'))) url.chop(1); } @@ -5698,7 +5698,7 @@ QUrl QUrl::fromLocalFile(const QString &localFile) // magic for drives on windows if (deslashified.length() > 1 && deslashified.at(1) == QLatin1Char(':') && deslashified.at(0) != QLatin1Char('/')) { - url.setPath(QLatin1String("/") + deslashified); + url.setPath(QLatin1Char('/') + deslashified); // magic for shared drive on windows } else if (deslashified.startsWith(QLatin1String("//"))) { int indexOfPath = deslashified.indexOf(QLatin1Char('/'), 2); @@ -5728,7 +5728,7 @@ QString QUrl::toLocalFile() const // magic for shared drive on windows if (!d->host.isEmpty()) { tmp = QLatin1String("//") + d->host + (ourPath.length() > 0 && ourPath.at(0) != QLatin1Char('/') - ? QLatin1String("/") + ourPath : ourPath); + ? QLatin1Char('/') + ourPath : ourPath); } else { tmp = ourPath; // magic for drives on windows @@ -5976,7 +5976,7 @@ QDataStream &operator>>(QDataStream &in, QUrl &url) #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const QUrl &url) { - d.maybeSpace() << "QUrl(" << url.toString() << ")"; + d.maybeSpace() << "QUrl(" << url.toString() << ')'; return d.space(); } #endif diff --git a/src/corelib/io/qurl.h b/src/corelib/io/qurl.h index 9242092..e9c4a8d 100644 --- a/src/corelib/io/qurl.h +++ b/src/corelib/io/qurl.h @@ -223,7 +223,7 @@ public: inline QT3_SUPPORT QString ref() const { return fragment(); } inline QT3_SUPPORT void setRef(const QString &txt) { setFragment(txt); } inline QT3_SUPPORT bool hasRef() const { return !fragment().isEmpty(); } - inline QT3_SUPPORT void addPath(const QString &p) { setPath(path() + QLatin1String("/") + p); } + inline QT3_SUPPORT void addPath(const QString &p) { setPath(path() + QLatin1Char('/') + p); } QT3_SUPPORT void setFileName(const QString &txt); QT3_SUPPORT QString fileName() const; QT3_SUPPORT QString dirPath() const; diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri index 9458946..f7d4208 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 \ @@ -53,7 +53,8 @@ SOURCES += \ kernel/qvariant.cpp \ kernel/qcoreglobaldata.cpp \ kernel/qsharedmemory.cpp \ - kernel/qsystemsemaphore.cpp + kernel/qsystemsemaphore.cpp \ + kernel/qpointer.cpp win32 { SOURCES += \ diff --git a/src/corelib/kernel/qabstractitemmodel.cpp b/src/corelib/kernel/qabstractitemmodel.cpp index fd0e105..6d6caf5 100644 --- a/src/corelib/kernel/qabstractitemmodel.cpp +++ b/src/corelib/kernel/qabstractitemmodel.cpp @@ -428,8 +428,8 @@ bool QPersistentModelIndex::isValid() const QDebug operator<<(QDebug dbg, const QModelIndex &idx) { #ifndef Q_BROKEN_DEBUG_STREAM - dbg.nospace() << "QModelIndex(" << idx.row() << "," << idx.column() - << "," << idx.internalPointer() << "," << idx.model() << ")"; + dbg.nospace() << "QModelIndex(" << idx.row() << ',' << idx.column() + << ',' << idx.internalPointer() << ',' << idx.model() << ')'; return dbg.space(); #else qWarning("This compiler doesn't support streaming QModelIndex to QDebug"); @@ -467,6 +467,37 @@ QAbstractItemModel *QAbstractItemModelPrivate::staticEmptyModel() return qEmptyModel(); } +/*! + \internal + return true if \a value contains a numerical type + + This function is used by our Q{Tree,Widget,Table}WidgetModel classes to sort. + We cannot rely on QVariant::canConvert because this would take strings as double + and then not sort strings correctly +*/ +bool QAbstractItemModelPrivate::canConvertToDouble(const QVariant &value) +{ + switch (value.userType()) { + case QVariant::Bool: + case QVariant::Int: + case QVariant::UInt: + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::Double: + case QVariant::Char: + case QMetaType::Float: + case QMetaType::Short: + case QMetaType::UShort: + case QMetaType::UChar: + case QMetaType::ULong: + case QMetaType::Long: + return true; + default: + return false; + } + return false; +} + void QAbstractItemModelPrivate::removePersistentIndexData(QPersistentModelIndexData *data) { if (data->index.isValid()) { @@ -525,7 +556,7 @@ void QAbstractItemModelPrivate::rowsInserted(const QModelIndex &parent, if (data->index.isValid()) { persistent.insertMultiAtEnd(data->index, data); } else { - qWarning() << "QAbstractItemModel::endInsertRows: Invalid index (" << old.row() + count << "," << old.column() << ") in model" << q_func(); + qWarning() << "QAbstractItemModel::endInsertRows: Invalid index (" << old.row() + count << ',' << old.column() << ") in model" << q_func(); } } } @@ -574,7 +605,7 @@ void QAbstractItemModelPrivate::rowsRemoved(const QModelIndex &parent, if (data->index.isValid()) { persistent.insertMultiAtEnd(data->index, data); } else { - qWarning() << "QAbstractItemModel::endRemoveRows: Invalid index (" << old.row() - count << "," << old.column() << ") in model" << q_func(); + qWarning() << "QAbstractItemModel::endRemoveRows: Invalid index (" << old.row() - count << ',' << old.column() << ") in model" << q_func(); } } QVector<QPersistentModelIndexData *> persistent_invalidated = persistent.invalidated.pop(); @@ -619,7 +650,7 @@ void QAbstractItemModelPrivate::columnsInserted(const QModelIndex &parent, if (data->index.isValid()) { persistent.insertMultiAtEnd(data->index, data); } else { - qWarning() << "QAbstractItemModel::endInsertColumns: Invalid index (" << old.row() << "," << old.column() + count << ") in model" << q_func(); + qWarning() << "QAbstractItemModel::endInsertColumns: Invalid index (" << old.row() << ',' << old.column() + count << ") in model" << q_func(); } } } @@ -669,7 +700,7 @@ void QAbstractItemModelPrivate::columnsRemoved(const QModelIndex &parent, if (data->index.isValid()) { persistent.insertMultiAtEnd(data->index, data); } else { - qWarning() << "QAbstractItemModel::endRemoveColumns: Invalid index (" << old.row() << "," << old.column() - count << ") in model" << q_func(); + qWarning() << "QAbstractItemModel::endRemoveColumns: Invalid index (" << old.row() << ',' << old.column() - count << ") in model" << q_func(); } } QVector<QPersistentModelIndexData *> persistent_invalidated = persistent.invalidated.pop(); diff --git a/src/corelib/kernel/qabstractitemmodel_p.h b/src/corelib/kernel/qabstractitemmodel_p.h index df1a6ce..70c35cb 100644 --- a/src/corelib/kernel/qabstractitemmodel_p.h +++ b/src/corelib/kernel/qabstractitemmodel_p.h @@ -61,7 +61,7 @@ QT_BEGIN_NAMESPACE -class Q_CORE_EXPORT QPersistentModelIndexData +class QPersistentModelIndexData { public: QPersistentModelIndexData() : model(0) {} @@ -89,6 +89,7 @@ public: void columnsAboutToBeRemoved(const QModelIndex &parent, int first, int last); void columnsRemoved(const QModelIndex &parent, int first, int last); static QAbstractItemModel *staticEmptyModel(); + static bool canConvertToDouble(const QVariant &value); inline QModelIndex createIndex(int row, int column, void *data = 0) const { return q_func()->createIndex(row, column, data); diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 5c3fd76..4fbd63c 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -420,12 +420,12 @@ QString qAppName() QLibrary) can be retrieved with libraryPaths() and manipulated by setLibraryPaths(), addLibraryPath(), and removeLibraryPath(). - On Unix/Linux Qt is configured to use the system local settings by - default. This can cause a conflict when using POSIX functions, for - instance, when converting between data types such as floats and - strings, since the notation may differ between locales. To get - around this problem call the POSIX function setlocale(LC_NUMERIC,"C") - right after initializing QApplication or QCoreApplication to reset + On Unix/Linux Qt is configured to use the system local settings by + default. This can cause a conflict when using POSIX functions, for + instance, when converting between data types such as floats and + strings, since the notation may differ between locales. To get + around this problem call the POSIX function setlocale(LC_NUMERIC,"C") + right after initializing QApplication or QCoreApplication to reset the locale that is used for number formatting to "C"-locale. \sa QApplication, QAbstractEventDispatcher, QEventLoop, @@ -613,6 +613,20 @@ void QCoreApplication::setAttribute(Qt::ApplicationAttribute attribute, bool on) QCoreApplicationPrivate::attribs |= 1 << attribute; else QCoreApplicationPrivate::attribs &= ~(1 << attribute); +#ifdef Q_OS_MAC + // Turn on the no native menubar here, since we used to + // do this implicitly. We DO NOT flip it off if someone sets + // it to false. + // Ideally, we'd have magic that would be something along the lines of + // "follow MacPluginApplication" unless explicitly set. + // Considering this attribute isn't only at the beginning + // it's unlikely it will ever be a problem, but I want + // to have the behavior documented here. + if (attribute == Qt::AA_MacPluginApplication && on + && !testAttribute(Qt::AA_DontUseNativeMenuBar)) { + setAttribute(Qt::AA_DontUseNativeMenuBar, true); + } +#endif } /*! @@ -1880,7 +1894,7 @@ QString QCoreApplication::applicationFilePath() */ QByteArray pEnv = qgetenv("PATH"); QDir currentDir = QDir::current(); - QStringList paths = QString::fromLocal8Bit(pEnv.constData()).split(QLatin1String(":")); + QStringList paths = QString::fromLocal8Bit(pEnv.constData()).split(QLatin1Char(':')); for (QStringList::const_iterator p = paths.constBegin(); p != paths.constEnd(); ++p) { if ((*p).isEmpty()) continue; @@ -1990,7 +2004,7 @@ QStringList QCoreApplication::arguments() wchar_t tempFilename[MAX_PATH+1]; if (GetModuleFileNameW(0, tempFilename, MAX_PATH)) { tempFilename[MAX_PATH] = 0; - cmdline.prepend(QString(QLatin1String("\"")) + QString::fromUtf16((unsigned short *)tempFilename) + QString(QLatin1String("\" "))); + cmdline.prepend(QLatin1Char('\"') + QString::fromUtf16((unsigned short *)tempFilename) + QLatin1String("\" ")); } #endif // Q_OS_WINCE @@ -2004,8 +2018,7 @@ QStringList QCoreApplication::arguments() l1arg == "-qdebug" || l1arg == "-reverse" || l1arg == "-stylesheet" || - l1arg == "-widgetcount" || - l1arg == "-direct3d") + l1arg == "-widgetcount") ; else if (l1arg.startsWith("-style=")) ; diff --git a/src/corelib/kernel/qcoreapplication_win.cpp b/src/corelib/kernel/qcoreapplication_win.cpp index 225821f..9c1c235 100644 --- a/src/corelib/kernel/qcoreapplication_win.cpp +++ b/src/corelib/kernel/qcoreapplication_win.cpp @@ -51,12 +51,11 @@ QT_BEGIN_NAMESPACE -// ############### DON'T EXPORT HERE!!! -Q_CORE_EXPORT char appFileName[MAX_PATH+1]; // application file name -Q_CORE_EXPORT char theAppName[MAX_PATH+1]; // application name -Q_CORE_EXPORT HINSTANCE appInst = 0; // handle to app instance -Q_CORE_EXPORT HINSTANCE appPrevInst = 0; // handle to prev app instance -Q_CORE_EXPORT int appCmdShow = 0; +char appFileName[MAX_PATH+1]; // application file name +char theAppName[MAX_PATH+1]; // application name +HINSTANCE appInst = 0; // handle to app instance +HINSTANCE appPrevInst = 0; // handle to prev app instance +int appCmdShow = 0; bool usingWinMain = false; // whether the qWinMain() is used or not Q_CORE_EXPORT HINSTANCE qWinAppInst() // get Windows app handle @@ -69,6 +68,12 @@ Q_CORE_EXPORT HINSTANCE qWinAppPrevInst() // get Windows prev app return appPrevInst; } +Q_CORE_EXPORT int qWinAppCmdShow() // get main window show command +{ + return appCmdShow; +} + + void set_winapp_name() { static bool already_set = false; @@ -89,6 +94,14 @@ void set_winapp_name() int l = qstrlen(theAppName); if ((l > 4) && !qstricmp(theAppName + l - 4, ".exe")) theAppName[l-4] = '\0'; // drop .exe extension + + if (appInst == 0) { + QT_WA({ + appInst = GetModuleHandle(0); + }, { + appInst = GetModuleHandleA(0); + }); + } } } @@ -134,11 +147,11 @@ Q_CORE_EXPORT void qWinMsgHandler(QtMsgType t, const char* str) staticCriticalSection.lock(); QT_WA({ QString s(QString::fromLocal8Bit(str)); - s += QLatin1String("\n"); + s += QLatin1Char('\n'); OutputDebugStringW((TCHAR*)s.utf16()); }, { QByteArray s(str); - s += "\n"; + s += '\n'; OutputDebugStringA(s.data()); }) staticCriticalSection.unlock(); @@ -173,14 +186,14 @@ void qWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdParam, // Create command line - set_winapp_name(); - argv = qWinCmdLine<char>(cmdParam, int(strlen(cmdParam)), argc); // Get Windows parameters appInst = instance; appPrevInst = prevInstance; appCmdShow = cmdShow; + + set_winapp_name(); } /*! @@ -396,6 +409,7 @@ struct { { 0x020B, "WM_XBUTTONDOWN" }, { 0x020C, "WM_XBUTTONUP" }, { 0x020D, "WM_XBUTTONDBLCLK" }, + { 0x020E, "WM_MOUSEHWHEEL" }, { 0x0210, "WM_PARENTNOTIFY" }, { 0x0211, "WM_ENTERMENULOOP" }, { 0x0212, "WM_EXITMENULOOP" }, @@ -618,7 +632,7 @@ QString valueCheck(uint actual, ...) #ifdef Q_CC_BOR -Q_CORE_EXPORT QString decodeMSG(const MSG& msg) +QString decodeMSG(const MSG& msg) { return QString::fromLatin1("THis is not supported on Borland"); } @@ -827,7 +841,7 @@ QString decodeMSG(const MSG& msg) FLGSTR(ISC_SHOWUICANDIDATEWINDOW << 2), FLGSTR(ISC_SHOWUICANDIDATEWINDOW << 3), FLAG_STRING()); - parameters.sprintf("Input context(%s) Show flags(%s)", (fSet?"Active":"Inactive"), showFlgs.toLatin1().data()); + parameters.sprintf("Input context(%s) Show flags(%s)", (fSet? "Active" : "Inactive"), showFlgs.toLatin1().data()); } break; #endif @@ -885,6 +899,9 @@ QString decodeMSG(const MSG& msg) #ifdef WM_MOUSEWHEEL case WM_MOUSEWHEEL: #endif +#ifdef WM_MOUSEHWHEEL + case WM_MOUSEHWHEEL: +#endif #ifdef WM_LBUTTONDBLCLK case WM_LBUTTONDBLCLK: #endif diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp index e0be174..6d26db6 100644 --- a/src/corelib/kernel/qcoreevent.cpp +++ b/src/corelib/kernel/qcoreevent.cpp @@ -266,6 +266,8 @@ QT_BEGIN_NAMESPACE \omitvalue NetworkReplyUpdated \omitvalue FutureCallOut \omitvalue CocoaRequestModal + \omitvalue Wrapped + \omitvalue Signal */ /*! diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index d229ac9..1dd2ee4 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,10 +269,12 @@ public: CocoaRequestModal = 190, // Internal for requesting an application modal Cocoa Window MacGLClearDrawable = 191, // Internal Cocoa, the window has changed, so we must clear - RequestSoftwareInputPanel = 192, - CloseSoftwareInputPanel = 193, + Signal = 192, + Wrapped = 193, + RequestSoftwareInputPanel = 194, + CloseSoftwareInputPanel = 195, - // 512 reserved for Qt Jambi's MetaCall event + // 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/qeventdispatcher_unix.cpp b/src/corelib/kernel/qeventdispatcher_unix.cpp index ea8299c..fe0db73 100644 --- a/src/corelib/kernel/qeventdispatcher_unix.cpp +++ b/src/corelib/kernel/qeventdispatcher_unix.cpp @@ -263,7 +263,7 @@ QTimerInfoList::QTimerInfoList() # if (_POSIX_MONOTONIC_CLOCK == 0) // detect if the system support monotonic timers long x = sysconf(_SC_MONOTONIC_CLOCK); - useMonotonicTimers = x >= 200112L; + useMonotonicTimers = x != -1; # endif getTime(currentTime); diff --git a/src/corelib/kernel/qeventdispatcher_unix_p.h b/src/corelib/kernel/qeventdispatcher_unix_p.h index 816add5..ac2de50 100644 --- a/src/corelib/kernel/qeventdispatcher_unix_p.h +++ b/src/corelib/kernel/qeventdispatcher_unix_p.h @@ -154,14 +154,14 @@ public: int activateTimers(); }; -struct Q_CORE_EXPORT QSockNot +struct QSockNot { QSocketNotifier *obj; int fd; fd_set *queue; }; -class Q_CORE_EXPORT QSockNotType +class QSockNotType { public: QSockNotType(); diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp index c4061f4..7d3a13a 100644 --- a/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -396,13 +396,6 @@ Q_CORE_EXPORT bool winPostMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPa { return PostMessageA(hWnd, msg, wParam, lParam); }); } -Q_CORE_EXPORT bool winGetMessage(MSG* msg, HWND hWnd, UINT wMsgFilterMin, - UINT wMsgFilterMax) -{ - QT_WA({ return GetMessage(msg, hWnd, wMsgFilterMin, wMsgFilterMax); } , - { return GetMessageA(msg, hWnd, wMsgFilterMin, wMsgFilterMax); }); -} - // This function is called by a workerthread void WINAPI CALLBACK qt_fast_timer_proc(uint timerId, uint /*reserved*/, DWORD_PTR user, DWORD_PTR /*reserved*/, DWORD_PTR /*reserved*/) { @@ -703,7 +696,8 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) && msg.message <= WM_KEYLAST) || (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST) - || msg.message == WM_MOUSEWHEEL)) { + || msg.message == WM_MOUSEWHEEL + || msg.message == WM_MOUSEHWHEEL)) { // queue user input events for later processing haveMessage = false; d->queuedUserInputEvents.append(msg); diff --git a/src/corelib/kernel/qfunctions_wince.cpp b/src/corelib/kernel/qfunctions_wince.cpp index 1c929c7..e0f7687 100644 --- a/src/corelib/kernel/qfunctions_wince.cpp +++ b/src/corelib/kernel/qfunctions_wince.cpp @@ -285,11 +285,6 @@ int qt_wince_SetErrorMode(int newValue) return result; } -HRESULT qt_wince_CoInitialize(void* reserved) -{ - return CoInitializeEx(reserved, 0); -} - bool qt_wince__chmod(const char *file, int mode) { return _wchmod( reinterpret_cast<const wchar_t *> (QString::fromLatin1(file).utf16()), mode); diff --git a/src/corelib/kernel/qfunctions_wince.h b/src/corelib/kernel/qfunctions_wince.h index 123bd23..5f08bb3 100644 --- a/src/corelib/kernel/qfunctions_wince.h +++ b/src/corelib/kernel/qfunctions_wince.h @@ -199,7 +199,9 @@ int qt_wince__fstat( int handle, struct stat *buffer); #define SEM_FAILCRITICALERRORS 0x0001 #define SEM_NOOPENFILEERRORBOX 0x0002 int qt_wince_SetErrorMode(int); -HRESULT qt_wince_CoInitialize(void* reserved); +#ifndef CoInitialize +#define CoInitialize(x) CoInitializeEx(x, COINIT_MULTITHREADED) +#endif bool qt_wince__chmod(const char *file, int mode); bool qt_wince__wchmod(const WCHAR *file, int mode); @@ -376,7 +378,6 @@ typedef DWORD OLE_COLOR; #define _rename(a,b) qt_wince__rename(a,b) #define _remove(a) qt_wince__remove(a) #define SetErrorMode(a) qt_wince_SetErrorMode(a) -#define CoInitialize(a) qt_wince_CoInitialize(a) #define _chmod(a,b) qt_wince__chmod(a,b) #define _wchmod(a,b) qt_wince__wchmod(a,b) #define CreateFileA(a,b,c,d,e,f,g) qt_wince_CreateFileA(a,b,c,d,e,f,g) diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index b65f956..6f3316c 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -2003,6 +2003,11 @@ QVariant::Type QMetaProperty::type() const if (enumMetaTypeId == 0) return QVariant::Int; } +#ifdef QT_COORD_TYPE + // qreal metatype must be resolved at runtime. + if (strcmp(typeName(), "qreal") == 0) + return QVariant::Type(qMetaTypeId<qreal>()); +#endif return QVariant::UserType; } diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index ce10bae..6b40f46 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -179,6 +179,7 @@ QT_BEGIN_NAMESPACE \omitvalue LastCoreExtType \omitvalue LastCoreType \omitvalue LastGuiType + \omitvalue QReal Additional types can be registered using Q_DECLARE_METATYPE(). @@ -359,7 +360,7 @@ void QMetaType::registerStreamOperators(const char *typeName, SaveOperator saveO inf.saveOp = saveOp; inf.loadOp = loadOp; } -#endif +#endif // QT_NO_DATASTREAM /*! Returns the type name associated with the given \a type, or 0 if no @@ -884,7 +885,7 @@ bool QMetaType::load(QDataStream &stream, int type, void *data) } return true; } -#endif +#endif // QT_NO_DATASTREAM /*! Returns a copy of \a copy, assuming it is of type \a type. If \a diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index 648f933..687a070 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -86,6 +86,15 @@ public: UShort = 133, UChar = 134, Float = 135, QObjectStar = 136, QWidgetStar = 137, LastCoreExtType = 137 /* QWidgetStar */, +// This logic must match the one in qglobal.h +#if defined(QT_COORD_TYPE) + QReal = 0, +#elif defined(QT_NO_FPU) || defined(QT_ARCH_ARM) || defined(QT_ARCH_WINDOWSCE) || defined(QT_ARCH_SYMBIAN) + QReal = Float, +#else + QReal = Double, +#endif + User = 256 }; @@ -174,7 +183,7 @@ void qRegisterMetaTypeStreamOperators(const char *typeName QMetaType::registerStreamOperators(typeName, reinterpret_cast<QMetaType::SaveOperator>(sptr), reinterpret_cast<QMetaType::LoadOperator>(lptr)); } -#endif +#endif // QT_NO_DATASTREAM template <typename T> struct QMetaTypeId diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index ac505e1..eeb3550 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -58,6 +58,7 @@ #include <qsemaphore.h> #include <private/qorderedmutexlocker_p.h> +#include <private/qmutexpool_p.h> #include <new> @@ -70,7 +71,7 @@ static int DIRECT_CONNECTION_ONLY = 0; static int *queuedConnectionTypes(const QList<QByteArray> &typeNames) { - int *types = static_cast<int *>(qMalloc((typeNames.count() + 1) * sizeof(int))); + int *types = new int [typeNames.count() + 1]; for (int i = 0; i < typeNames.count(); ++i) { const QByteArray typeName = typeNames.at(i); if (typeName.endsWith('*')) @@ -82,7 +83,7 @@ static int *queuedConnectionTypes(const QList<QByteArray> &typeNames) qWarning("QObject::connect: Cannot queue arguments of type '%s'\n" "(Make sure '%s' is registered using qRegisterMetaType().)", typeName.constData(), typeName.constData()); - qFree(types); + delete [] types; return 0; } } @@ -91,16 +92,38 @@ static int *queuedConnectionTypes(const QList<QByteArray> &typeNames) return types; } +static QBasicAtomicPointer<QMutexPool> signalSlotMutexes = Q_BASIC_ATOMIC_INITIALIZER(0); +static QBasicAtomicInt objectCount = Q_BASIC_ATOMIC_INITIALIZER(0); + +/** \internal + * mutex to be locked when accessing the connectionlists or the senders list + */ +static QMutex *signalSlotLock(const QObject *o) +{ + if (!signalSlotMutexes) { + QMutexPool *mp = new QMutexPool; + if (!signalSlotMutexes.testAndSetOrdered(0, mp)) { + delete mp; + } + } + return signalSlotMutexes->get(o); +} + extern "C" Q_CORE_EXPORT void qt_addObject(QObject *) { + objectCount.ref(); } extern "C" Q_CORE_EXPORT void qt_removeObject(QObject *) { + if(!objectCount.deref()) { + QMutexPool *old = signalSlotMutexes.fetchAndStoreAcquire(0); + delete old; + } } QObjectPrivate::QObjectPrivate(int version) - : threadData(0), currentSender(0), currentChildBeingDeleted(0), connectionLists(0) + : threadData(0), currentSender(0), currentChildBeingDeleted(0), connectionLists(0), senders(0) { if (version != QObjectPrivateVersion) qFatal("Cannot mix incompatible Qt libraries"); @@ -120,6 +143,7 @@ QObjectPrivate::QObjectPrivate(int version) inEventHandler = false; inThreadChangeEvent = false; deleteWatch = 0; + hasGuards = false; } QObjectPrivate::~QObjectPrivate() @@ -216,19 +240,20 @@ public: } }; +// Used by QAccessibleWidget bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const { Q_Q(const QObject); int signal_index = q->metaObject()->indexOfSignal(signal); if (signal_index < 0) return false; - QMutexLocker locker(&threadData->mutex); + QMutexLocker locker(signalSlotLock(q)); if (connectionLists) { if (signal_index < connectionLists->count()) { const ConnectionList &connectionList = connectionLists->at(signal_index); for (int i = 0; i < connectionList.count(); ++i) { - const QObjectPrivate::Connection &c = connectionList.at(i); - if (c.receiver && c.receiver == receiver) + const QObjectPrivate::Connection *c = connectionList.at(i); + if (c->receiver == receiver) return true; } } @@ -236,6 +261,7 @@ bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const return false; } +// Used by QAccessibleWidget QObjectList QObjectPrivate::receiverList(const char *signal) const { Q_Q(const QObject); @@ -243,26 +269,27 @@ QObjectList QObjectPrivate::receiverList(const char *signal) const int signal_index = q->metaObject()->indexOfSignal(signal); if (signal_index < 0) return returnValue; - QMutexLocker locker(&threadData->mutex); + QMutexLocker locker(signalSlotLock(q)); if (connectionLists) { if (signal_index < connectionLists->count()) { const ConnectionList &connectionList = connectionLists->at(signal_index); for (int i = 0; i < connectionList.count(); ++i) { - const QObjectPrivate::Connection &c = connectionList.at(i); - if (c.receiver) - returnValue << c.receiver; + const QObjectPrivate::Connection *c = connectionList.at(i); + if (c->receiver) + returnValue << c->receiver; } } } return returnValue; } +// Used by QAccessibleWidget QObjectList QObjectPrivate::senderList() const { QObjectList returnValue; - QMutexLocker locker(&threadData->mutex); - for (int i = 0; i < senders.count(); ++i) - returnValue << senders.at(i).sender; + QMutexLocker locker(signalSlotLock(q_func())); + for (Connection *c = senders; c; c = c->next) + returnValue << c->sender; return returnValue; } @@ -274,33 +301,10 @@ void QObjectPrivate::addConnection(int signal, Connection *c) connectionLists->resize(signal + 1); ConnectionList &connectionList = (*connectionLists)[signal]; - connectionList.append(*c); - + connectionList.append(c); cleanConnectionLists(); } -void QObjectPrivate::removeReceiver(int signal, QObject *receiver) -{ - if (!connectionLists) - return; - - if (signal >= connectionLists->count()) - return; - - ConnectionList &connectionList = (*connectionLists)[signal]; - for (int i = 0; i < connectionList.count(); ++i) { - Connection &c = connectionList[i]; - if (c.receiver == receiver) { - c.receiver = 0; - if (c.argumentTypes && c.argumentTypes != &DIRECT_CONNECTION_ONLY) { - qFree(c.argumentTypes); - c.argumentTypes = 0; - } - connectionLists->dirty = true; - } - } -} - void QObjectPrivate::cleanConnectionLists() { if (connectionLists->dirty && !connectionLists->inUse) { @@ -308,43 +312,17 @@ void QObjectPrivate::cleanConnectionLists() for (int signal = -1; signal < connectionLists->count(); ++signal) { QObjectPrivate::ConnectionList &connectionList = (*connectionLists)[signal]; for (int i = 0; i < connectionList.count(); ++i) { - const QObjectPrivate::Connection &c = connectionList.at(i); - if (!c.receiver) + QObjectPrivate::Connection *c = connectionList.at(i); + if (!c->receiver) { + delete c; connectionList.removeAt(i--); + } } } connectionLists->dirty = false; } } -void QObjectPrivate::refSender(QObject *sender, int signal) -{ - for (int i = 0; i < senders.count(); ++i) { - Sender &s = senders[i]; - if (s.sender == sender && s.signal == signal) { - ++s.ref; - return; - } - } - - Sender s = { sender, signal, 1 }; - senders.append(s); -} - -void QObjectPrivate::derefSender(QObject *sender, int signal) -{ - for (int i = 0; i < senders.count(); ++i) { - Sender &s = senders[i]; - if (s.sender == sender && s.signal == signal) { - if (--s.ref == 0) { - senders.removeAt(i); - break; - } - } - } - // Q_ASSERT_X(false, "QObjectPrivate::derefSender", "sender not found"); -} - QObjectPrivate::Sender *QObjectPrivate::setCurrentSender(QObject *receiver, Sender *sender) { @@ -382,6 +360,7 @@ void QMetaObject::addGuard(QObject **ptr) return; } QMutexLocker locker(guardHashLock()); + QObjectPrivate::get(*ptr)->hasGuards = true; hash->insert(*ptr, ptr); } @@ -399,12 +378,17 @@ void QMetaObject::removeGuard(QObject **ptr) QMutexLocker locker(guardHashLock()); GuardHash::iterator it = hash->find(*ptr); const GuardHash::iterator end = hash->end(); + bool more = false; //if the QObject has more pointer attached to it. for (; it.key() == *ptr && it != end; ++it) { if (it.value() == ptr) { - (void) hash->erase(it); + it = hash->erase(it); + if (!more) more = (it != end && it.key() == *ptr); break; } + more = true; } + if (!more) + QObjectPrivate::get(*ptr)->hasGuards = false; } /*!\internal @@ -418,24 +402,33 @@ void QMetaObject::changeGuard(QObject **ptr, QObject *o) } QMutexLocker locker(guardHashLock()); if (*ptr) { + bool more = false; //if the QObject has more pointer attached to it. GuardHash::iterator it = hash->find(*ptr); const GuardHash::iterator end = hash->end(); for (; it.key() == *ptr && it != end; ++it) { if (it.value() == ptr) { - (void) hash->erase(it); + it = hash->erase(it); + if (!more) more = (it != end && it.key() == *ptr); break; } + more = true; } + if (!more) + QObjectPrivate::get(*ptr)->hasGuards = false; } *ptr = o; - if (*ptr) + if (*ptr) { hash->insert(*ptr, ptr); + QObjectPrivate::get(*ptr)->hasGuards = true; + } } /*! \internal */ void QObjectPrivate::clearGuards(QObject *object) { + if (!QObjectPrivate::get(object)->hasGuards) + return; GuardHash *hash = 0; QMutex *mutex = 0; QT_TRY { @@ -776,7 +769,7 @@ QObject::~QObject() } { - QMutexLocker locker(&d->threadData->mutex); + QMutexLocker locker(signalSlotLock(this)); // set ref to zero to indicate that this object has been deleted if (d->currentSender != 0) @@ -789,23 +782,23 @@ QObject::~QObject() for (int signal = -1; signal < d->connectionLists->count(); ++signal) { QObjectPrivate::ConnectionList &connectionList = (*d->connectionLists)[signal]; for (int i = 0; i < connectionList.count(); ++i) { - QObjectPrivate::Connection *c = &connectionList[i]; - if (!c->receiver) + QObjectPrivate::Connection *c = connectionList[i]; + if (!c->receiver) { + delete c; continue; + } - QMutex *m = &c->receiver->d_func()->threadData->mutex; + QMutex *m = signalSlotLock(c->receiver); bool needToUnlock = QOrderedMutexLocker::relock(locker.mutex(), m); - c = &connectionList[i]; - if (c->receiver) - c->receiver->d_func()->derefSender(this, signal); + c = connectionList[i]; + if (c->receiver) { + *c->prev = c->next; + if (c->next) c->next->prev = c->prev; + } if (needToUnlock) m->unlock(); - if (c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) { - qFree(c->argumentTypes); - c->argumentTypes = 0; - } - c->receiver = 0; + delete c; } } @@ -818,25 +811,25 @@ QObject::~QObject() } // disconnect all senders - for (int i = 0; i < d->senders.count(); ) { - QObjectPrivate::Sender *s = &d->senders[i]; - - QMutex *m = &s->sender->d_func()->threadData->mutex; + QObjectPrivate::Connection *node = d->senders; + while (node) { + QMutex *m = signalSlotLock(node->sender); + node->prev = &node; bool needToUnlock = QOrderedMutexLocker::relock(locker.mutex(), m); - if (m < locker.mutex()) { - if (i >= d->senders.count() || s != &d->senders[i]) { - if (needToUnlock) - m->unlock(); - continue; - } + //the node has maybe been removed while the mutex was unlocked in relock? + if (!node || signalSlotLock(node->sender) != m) { + m->unlock(); + continue; } - s->sender->d_func()->removeReceiver(s->signal, this); + node->receiver = 0; + QObjectConnectionListVector *senderLists = node->sender->d_func()->connectionLists; + if (senderLists) + senderLists->dirty = true; + if (needToUnlock) m->unlock(); - ++i; + node = node->next; } - - d->senders.clear(); } if (d->pendTimer) { @@ -874,6 +867,12 @@ QObject::~QObject() #endif } +QObjectPrivate::Connection::~Connection() +{ + if (argumentTypes != &DIRECT_CONNECTION_ONLY) + delete [] argumentTypes; +} + /*! \fn QMetaObject *QObject::metaObject() const @@ -2261,11 +2260,11 @@ static void err_method_notfound(const QObject *object, if (strchr(method,')') == 0) // common typing mistake qWarning("Object::%s: Parentheses expected, %s %s::%s%s%s", func, type, object->metaObject()->className(), method+1, - loc ? " in ":"\0", loc ? loc : "\0"); + loc ? " in ": "", loc ? loc : ""); else qWarning("Object::%s: No such %s %s::%s%s%s", func, type, object->metaObject()->className(), method+1, - loc ? " in ":"\0", loc ? loc : "\0"); + loc ? " in ": "", loc ? loc : ""); } @@ -2308,14 +2307,14 @@ QObject *QObject::sender() const { Q_D(const QObject); - QMutexLocker(&d->threadData->mutex); + QMutexLocker(signalSlotLock(this)); if (!d->currentSender) return 0; // Return 0 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); + for (QObjectPrivate::Connection *c = d->senders; c && !found; c = c->next) + found = (c->sender == d->currentSender->sender); if (!found) return 0; return d->currentSender->sender; @@ -2364,14 +2363,14 @@ int QObject::receivers(const char *signal) const } Q_D(const QObject); - QMutexLocker locker(&d->threadData->mutex); + QMutexLocker locker(signalSlotLock(this)); if (d->connectionLists) { if (signal_index < d->connectionLists->count()) { const QObjectPrivate::ConnectionList &connectionList = d->connectionLists->at(signal_index); for (int i = 0; i < connectionList.count(); ++i) { - const QObjectPrivate::Connection &c = connectionList.at(i); - receivers += c.receiver ? 1 : 0; + const QObjectPrivate::Connection *c = connectionList.at(i); + receivers += c->receiver ? 1 : 0; } } } @@ -2410,7 +2409,8 @@ int QObject::receivers(const char *signal) const can be connected to one slot. If a signal is connected to several slots, the slots are activated - in an arbitrary order when the signal is emitted. + in the same order as the order the connection was made, when the + signal is emitted. The function returns true if it successfully connects the signal to the slot. It will return false if it cannot create the @@ -2418,9 +2418,13 @@ int QObject::receivers(const char *signal) const existence of either \a signal or \a method, or if their signatures aren't compatible. - For every connection you make, a signal is emitted; two signals are emitted - for duplicate connections. You can break all of these connections with a - single disconnect() call. + By default, a signal is emitted for every connection you make; + two signals are emitted for duplicate connections. You can break + all of these connections with a single disconnect() call. + If you pass the Qt::UniqueConnection \a type, the connection will only + be made if it is not a duplicate. If there is already a duplicate + (exact same signal to the exact same slot on the same objects), + the connection will fail and connect will return false The optional \a type parameter describes the type of connection to establish. In particular, it determines whether a particular @@ -2553,7 +2557,8 @@ bool QObject::connect(const QObject *sender, const char *signal, } } #endif - QMetaObject::connect(sender, signal_index, receiver, method_index, type, types); + if (!QMetaObject::connect(sender, signal_index, receiver, method_index, type, types)) + return false; const_cast<QObject*>(sender)->connectNotify(signal - 1); return true; } @@ -2804,20 +2809,35 @@ bool QMetaObject::connect(const QObject *sender, int signal_index, QObject *s = const_cast<QObject *>(sender); QObject *r = const_cast<QObject *>(receiver); - QOrderedMutexLocker locker(&s->d_func()->threadData->mutex, - &r->d_func()->threadData->mutex); + QOrderedMutexLocker locker(signalSlotLock(sender), + signalSlotLock(receiver)); -#if defined(Q_CC_HPACC) && defined(QT_ARCH_PARISC) - QObjectPrivate::Connection c; - c.receiver = r; - c.method = method_index; - c.connectionType = type; - c.argumentTypes = types; -#else - QObjectPrivate::Connection c = { r, method_index, type, Q_BASIC_ATOMIC_INITIALIZER(types) }; -#endif - s->d_func()->addConnection(signal_index, &c); - r->d_func()->refSender(s, signal_index); + if (type & Qt::UniqueConnection) { + QObjectConnectionListVector *connectionLists = s->d_func()->connectionLists; + if (connectionLists && connectionLists->count() > signal_index) { + QObjectPrivate::ConnectionList &connectionList = (*connectionLists)[signal_index]; + for (int i = 0; i < connectionList.count(); ++i) { + QObjectPrivate::Connection *c2 = connectionList.at(i); + if (c2->receiver == receiver && c2->method == method_index) + return false; + } + } + type &= Qt::UniqueConnection - 1; + } + + QObjectPrivate::Connection *c = new QObjectPrivate::Connection; + c->sender = s; + c->receiver = r; + c->method = method_index; + c->connectionType = type; + c->argumentTypes = types; + c->prev = &r->d_func()->senders; + c->next = *c->prev; + *c->prev = c; + if (c->next) + c->next->prev = &c->next; + + s->d_func()->addConnection(signal_index, c); if (signal_index < 0) sender->d_func()->connectedSignals = ~0u; @@ -2839,8 +2859,8 @@ bool QMetaObject::disconnect(const QObject *sender, int signal_index, QObject *s = const_cast<QObject *>(sender); QObject *r = const_cast<QObject *>(receiver); - QMutex *senderMutex = &s->d_func()->threadData->mutex; - QMutex *receiverMutex = r ? &r->d_func()->threadData->mutex : 0; + QMutex *senderMutex = signalSlotLock(sender); + QMutex *receiverMutex = receiver ? signalSlotLock(receiver) : 0; QOrderedMutexLocker locker(senderMutex, receiverMutex); QObjectConnectionListVector *connectionLists = s->d_func()->connectionLists; @@ -2856,27 +2876,25 @@ bool QMetaObject::disconnect(const QObject *sender, int signal_index, for (signal_index = -1; signal_index < connectionLists->count(); ++signal_index) { QObjectPrivate::ConnectionList &connectionList = (*connectionLists)[signal_index]; for (int i = 0; i < connectionList.count(); ++i) { - QObjectPrivate::Connection *c = &connectionList[i]; + QObjectPrivate::Connection *c = connectionList[i]; if (c->receiver && (r == 0 || (c->receiver == r && (method_index < 0 || c->method == method_index)))) { - QMutex *m = &c->receiver->d_func()->threadData->mutex; + QMutex *m = signalSlotLock(c->receiver); + bool needToUnlock = false; if (!receiverMutex && senderMutex != m) { // need to relock this receiver and sender in the correct order - bool needToUnlock = QOrderedMutexLocker::relock(senderMutex, m); - c = &connectionList[i]; - if (c->receiver) - c->receiver->d_func()->derefSender(s, signal_index); - if (needToUnlock) - m->unlock(); - } else { - // no need to unlock - c->receiver->d_func()->derefSender(s, signal_index); + needToUnlock = QOrderedMutexLocker::relock(senderMutex, m); + c = connectionList[i]; } - if (c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) { - qFree(c->argumentTypes); - c->argumentTypes = 0; + if (c->receiver) { + *c->prev = c->next; + if (c->next) c->next->prev = c->prev; } + + if (needToUnlock) + m->unlock(); + c->receiver = 0; success = true; @@ -2887,27 +2905,24 @@ bool QMetaObject::disconnect(const QObject *sender, int signal_index, } else if (signal_index < connectionLists->count()) { QObjectPrivate::ConnectionList &connectionList = (*connectionLists)[signal_index]; for (int i = 0; i < connectionList.count(); ++i) { - QObjectPrivate::Connection *c = &connectionList[i]; + QObjectPrivate::Connection *c = connectionList[i]; if (c->receiver && (r == 0 || (c->receiver == r && (method_index < 0 || c->method == method_index)))) { - QMutex *m = &c->receiver->d_func()->threadData->mutex; + QMutex *m = signalSlotLock(c->receiver); + bool needToUnlock = false; if (!receiverMutex && senderMutex != m) { // need to relock this receiver and sender in the correct order - bool needToUnlock = QOrderedMutexLocker::relock(senderMutex, m); - c = &connectionList[i]; - if (c->receiver) - c->receiver->d_func()->derefSender(s, signal_index); - if (needToUnlock) - m->unlock(); - } else { - // no need to unlock - c->receiver->d_func()->derefSender(s, signal_index); + needToUnlock = QOrderedMutexLocker::relock(senderMutex, m); + c = connectionList[i]; } - if (c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) { - qFree(c->argumentTypes); - c->argumentTypes = 0; + if (c->receiver) { + *c->prev = c->next; + if (c->next) c->next->prev = c->prev; } + + if (needToUnlock) + m->unlock(); c->receiver = 0; success = true; @@ -2990,32 +3005,31 @@ void QMetaObject::connectSlotsByName(QObject *o) } } -static void queued_activate(QObject *sender, int signal, const QObjectPrivate::Connection &c, +static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv, QSemaphore *semaphore = 0) { - if (!c.argumentTypes || c.argumentTypes != &DIRECT_CONNECTION_ONLY) { + if (!c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) { QMetaMethod m = sender->metaObject()->method(signal); - QObjectPrivate::Connection &x = const_cast<QObjectPrivate::Connection &>(c); int *tmp = queuedConnectionTypes(m.parameterTypes()); if (!tmp) // cannot queue arguments tmp = &DIRECT_CONNECTION_ONLY; - if (!x.argumentTypes.testAndSetOrdered(0, tmp)) { + if (!c->argumentTypes.testAndSetOrdered(0, tmp)) { if (tmp != &DIRECT_CONNECTION_ONLY) - qFree(tmp); + delete [] tmp; } } - if (c.argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate + if (c->argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate return; int nargs = 1; // include return type - while (c.argumentTypes[nargs-1]) + while (c->argumentTypes[nargs-1]) ++nargs; int *types = (int *) qMalloc(nargs*sizeof(int)); void **args = (void **) qMalloc(nargs*sizeof(void *)); types[0] = 0; // return type args[0] = 0; // return value for (int n = 1; n < nargs; ++n) - args[n] = QMetaType::construct((types[n] = c.argumentTypes[n-1]), argv[n]); - QCoreApplication::postEvent(c.receiver, new QMetaCallEvent(c.method, + args[n] = QMetaType::construct((types[n] = c->argumentTypes[n-1]), argv[n]); + QCoreApplication::postEvent(c->receiver, new QMetaCallEvent(c->method, sender, signal, nargs, @@ -3024,13 +3038,13 @@ static void queued_activate(QObject *sender, int signal, const QObjectPrivate::C semaphore)); } -static void blocking_activate(QObject *sender, int signal, const QObjectPrivate::Connection &c, void **argv) +static void blocking_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv) { - if (QThread::currentThread() == c.receiver->thread()) { + if (QThread::currentThread() == c->receiver->thread()) { qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: " "Sender is %s(%p), receiver is %s(%p)", sender->metaObject()->className(), sender, - c.receiver->metaObject()->className(), c.receiver); + c->receiver->metaObject()->className(), c->receiver); } #ifdef QT_NO_THREAD @@ -3038,7 +3052,7 @@ static void blocking_activate(QObject *sender, int signal, const QObjectPrivate: #else QSemaphore semaphore; queued_activate(sender, signal, c, argv, &semaphore); - QMutex *mutex = &QThreadData::get2(sender->thread())->mutex; + QMutex *mutex = signalSlotLock(sender); mutex->unlock(); semaphore.acquire(); mutex->lock(); @@ -3058,7 +3072,7 @@ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal argv ? argv : empty_argv); } - QMutexLocker locker(&sender->d_func()->threadData->mutex); + QMutexLocker locker(signalSlotLock(sender)); QThreadData *currentThreadData = QThreadData::current(); QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists; @@ -3080,7 +3094,7 @@ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal } int count = connectionLists->at(signal).count(); for (int i = 0; i < count; ++i) { - const QObjectPrivate::Connection *c = &connectionLists->at(signal)[i]; + QObjectPrivate::Connection *c = connectionLists->at(signal)[i]; if (!c->receiver) continue; @@ -3092,10 +3106,10 @@ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal && (currentThreadData != sender->d_func()->threadData || receiver->d_func()->threadData != sender->d_func()->threadData)) || (c->connectionType == Qt::QueuedConnection)) { - queued_activate(sender, signal, *c, argv); + queued_activate(sender, signal, c, argv); continue; } else if (c->connectionType == Qt::BlockingQueuedConnection) { - blocking_activate(sender, signal, *c, argv); + blocking_activate(sender, signal, c, argv); continue; } @@ -3410,7 +3424,7 @@ void QObject::dumpObjectInfo() objectName().isEmpty() ? "unnamed" : objectName().toLocal8Bit().data()); Q_D(QObject); - QMutexLocker locker(&d->threadData->mutex); + QMutexLocker locker(signalSlotLock(this)); // first, look for connections where this object is the sender qDebug(" SIGNALS OUT"); @@ -3423,16 +3437,16 @@ void QObject::dumpObjectInfo() // receivers const QObjectPrivate::ConnectionList &connectionList = d->connectionLists->at(signal_index); for (int i = 0; i < connectionList.count(); ++i) { - const QObjectPrivate::Connection &c = connectionList.at(i); - if (!c.receiver) { + const QObjectPrivate::Connection *c = connectionList.at(i); + if (!c->receiver) { qDebug(" <Disconnected receiver>"); continue; } - const QMetaObject *receiverMetaObject = c.receiver->metaObject(); - const QMetaMethod method = receiverMetaObject->method(c.method); + const QMetaObject *receiverMetaObject = c->receiver->metaObject(); + const QMetaMethod method = receiverMetaObject->method(c->method); qDebug(" --> %s::%s %s", receiverMetaObject->className(), - c.receiver->objectName().isEmpty() ? "unnamed" : qPrintable(c.receiver->objectName()), + c->receiver->objectName().isEmpty() ? "unnamed" : qPrintable(c->receiver->objectName()), method.signature()); } } @@ -3443,18 +3457,16 @@ void QObject::dumpObjectInfo() // now look for connections where this object is the receiver qDebug(" SIGNALS IN"); - if (!d->senders.isEmpty()) { - for (int i = 0; i < d->senders.count(); ++i) { - const QObjectPrivate::Sender &s = d->senders.at(i); - const QMetaObject *senderMetaObject = s.sender->metaObject(); - const QMetaMethod signal = senderMetaObject->method(s.signal); - qDebug(" <-- %s::%s %s", - senderMetaObject->className(), - s.sender->objectName().isEmpty() ? "unnamed" : qPrintable(s.sender->objectName()), - signal.signature()); + if (d->senders) { + for (QObjectPrivate::Connection *s = d->senders; s; s = s->next) { + const QMetaMethod slot = metaObject()->method(s->method); + qDebug(" <-- %s::%s %s", + s->sender->metaObject()->className(), + s->sender->objectName().isEmpty() ? "unnamed" : qPrintable(s->sender->objectName()), + slot.signature()); } } else { - qDebug(" <None>"); + qDebug(" <None>"); } #endif } @@ -3507,7 +3519,7 @@ QDebug operator<<(QDebug dbg, const QObject *o) { #ifndef Q_BROKEN_DEBUG_STREAM if (!o) return dbg << "QObject(0x0) "; - dbg.nospace() << o->metaObject()->className() << "(" << (void *)o; + dbg.nospace() << o->metaObject()->className() << '(' << (void *)o; if (!o->objectName().isEmpty()) dbg << ", name = " << o->objectName(); dbg << ')'; diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 8d5c4e3..72f2a65 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -107,7 +107,8 @@ public: uint receiveChildEvents : 1; uint inEventHandler : 1; uint inThreadChangeEvent : 1; - uint unused : 23; + uint hasGuards : 1; //true iff there is one or more QPointer attached to this object + uint unused : 22; int postedEvents; }; diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index 0eed938..d699061 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -114,7 +114,6 @@ public: int signal; int ref; }; - // object currently activating the object Sender *currentSender; @@ -148,21 +147,23 @@ public: // Note: you must hold the signalSlotLock() before accessing the lists below or calling the functions struct Connection { + QObject *sender; QObject *receiver; int method; uint connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking QBasicAtomicPointer<int> argumentTypes; + //senders linked list + Connection *next; + Connection **prev; + ~Connection(); }; - typedef QList<Connection> ConnectionList; + typedef QList<Connection *> ConnectionList; QObjectConnectionListVector *connectionLists; void addConnection(int signal, Connection *c); - void removeReceiver(int signal, QObject *receiver); void cleanConnectionLists(); - QList<Sender> senders; - void refSender(QObject *sender, int signal); - void derefSender(QObject *sender, int signal); + Connection *senders; //linked list; static Sender *setCurrentSender(QObject *receiver, Sender *sender); @@ -207,7 +208,7 @@ private: QSemaphore *semaphore_; }; -class Q_CORE_EXPORT QBoolBlocker +class QBoolBlocker { public: inline QBoolBlocker(bool &b, bool value=true):block(b), reset(b){block = value;} diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 3a22323..f5d08ce 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -55,7 +55,7 @@ class QByteArray; class QString; #ifndef Q_MOC_OUTPUT_REVISION -#define Q_MOC_OUTPUT_REVISION 61 +#define Q_MOC_OUTPUT_REVISION 62 #endif // The following macros are our "extensions" to C++ diff --git a/src/corelib/kernel/qtimer.cpp b/src/corelib/kernel/qtimer.cpp index 4b3feb0..29bdf7e 100644 --- a/src/corelib/kernel/qtimer.cpp +++ b/src/corelib/kernel/qtimer.cpp @@ -211,7 +211,7 @@ void QTimer::start() */ void QTimer::start(int msec) { - setInterval(msec); + inter = msec; start(); } @@ -251,7 +251,7 @@ void QTimer::stop() /*! - \reimp + \reimp */ void QTimer::timerEvent(QTimerEvent *e) { @@ -269,7 +269,7 @@ class QSingleShotTimer : public QObject public: ~QSingleShotTimer(); QSingleShotTimer(int msec, QObject *r, const char * m); -signals: +Q_SIGNALS: void timeout(); protected: void timerEvent(QTimerEvent *); diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp index df904a6..38fa216 100644 --- a/src/corelib/kernel/qtranslator.cpp +++ b/src/corelib/kernel/qtranslator.cpp @@ -209,8 +209,6 @@ static int numerusHelper(int n, const uchar *rules, int rulesSize) return -1; } -extern bool qt_detectRTLLanguage(); - class QTranslatorPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QTranslator) @@ -351,7 +349,7 @@ QTranslator::QTranslator(QObject * parent, const char * name) QTranslator::~QTranslator() { if (QCoreApplication::instance()) - QCoreApplication::instance()->removeTranslator(this); + QCoreApplication::removeTranslator(this); Q_D(QTranslator); d->clear(); } diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 0d85b93..9fc230e 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -71,27 +71,6 @@ QT_BEGIN_NAMESPACE # define FLT_DIG 6 #endif - -static const void *constDataHelper(const QVariant::Private &d) -{ - switch (d.type) { - case QVariant::Int: - return &d.data.i; - case QVariant::UInt: - return &d.data.u; - case QVariant::Bool: - return &d.data.b; - case QVariant::LongLong: - return &d.data.ll; - case QVariant::ULongLong: - return &d.data.ull; - case QVariant::Double: - return &d.data.d; - default: - return d.is_shared ? d.data.shared->ptr : reinterpret_cast<const void *>(&d.data.ptr); - } -} - static void construct(QVariant::Private *x, const void *copy) { x->is_shared = false; @@ -188,6 +167,9 @@ static void construct(QVariant::Private *x, const void *copy) x->data.d = copy ? *static_cast<const double*>(copy) : 0.0; #endif break; + case QMetaType::Float: + x->data.f = copy ? *static_cast<const float*>(copy) : 0.0f; + break; case QVariant::LongLong: #if defined(Q_CC_RVCT) // Using trinary operator with 64bit constants crashes when ran on Symbian device @@ -301,6 +283,7 @@ static void clear(QVariant::Private *d) case QVariant::LongLong: case QVariant::ULongLong: case QVariant::Double: + case QMetaType::Float: break; case QVariant::Invalid: case QVariant::UserType: @@ -369,6 +352,7 @@ static bool isNull(const QVariant::Private *d) case QVariant::ULongLong: case QVariant::Bool: case QVariant::Double: + case QMetaType::Float: break; } return d->is_null; @@ -460,6 +444,8 @@ static bool compare(const QVariant::Private *a, const QVariant::Private *b) return a->data.b == b->data.b; case QVariant::Double: return a->data.d == b->data.d; + case QMetaType::Float: + return a->data.f == b->data.f; case QVariant::Date: return *v_cast<QDate>(a) == *v_cast<QDate>(b); case QVariant::Time: @@ -518,7 +504,7 @@ static qlonglong qMetaTypeNumber(const QVariant::Private *d) case QMetaType::Long: return qlonglong(*static_cast<long *>(d->data.shared->ptr)); case QMetaType::Float: - return qRound64(*static_cast<float *>(d->data.shared->ptr)); + return qRound64(d->data.f); case QVariant::Double: return qRound64(d->data.d); } @@ -632,6 +618,15 @@ static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, ok = &dummy; switch (uint(t)) { + case QVariant::Url: + switch (d->type) { + case QVariant::String: + *static_cast<QUrl *>(result) = QUrl(*v_cast<QString>(d)); + break; + default: + return false; + } + break; case QVariant::String: { QString *str = static_cast<QString *>(result); switch (d->type) { @@ -655,7 +650,7 @@ static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, *str = QString::number(qMetaTypeUNumber(d)); break; case QMetaType::Float: - *str = QString::number(*static_cast<float *>(d->data.shared->ptr), 'g', FLT_DIG); + *str = QString::number(d->data.f, 'g', FLT_DIG); break; case QVariant::Double: *str = QString::number(d->data.d, 'g', DBL_DIG); @@ -681,6 +676,8 @@ static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, if (v_cast<QStringList>(d)->count() == 1) *str = v_cast<QStringList>(d)->at(0); break; + case QVariant::Url: + *str = v_cast<QUrl>(d)->toString(); default: return false; } @@ -826,7 +823,7 @@ static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, *ba = QByteArray::number(d->data.d, 'g', DBL_DIG); break; case QMetaType::Float: - *ba = QByteArray::number(*static_cast<float *>(d->data.shared->ptr), 'g', FLT_DIG); + *ba = QByteArray::number(d->data.f, 'g', FLT_DIG); break; case QMetaType::Char: case QMetaType::UChar: @@ -928,7 +925,7 @@ static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, *f = double(d->data.b); break; case QMetaType::Float: - *f = *static_cast<float *>(d->data.shared->ptr); + *f = double(d->data.f); break; case QVariant::LongLong: case QVariant::Int: @@ -1062,7 +1059,7 @@ static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, #if !defined(QT_NO_DEBUG_STREAM) && !defined(Q_BROKEN_DEBUG_STREAM) static void streamDebug(QDebug dbg, const QVariant &v) { - switch (v.type()) { + switch (v.userType()) { case QVariant::Int: dbg.nospace() << v.toInt(); break; @@ -1075,6 +1072,9 @@ static void streamDebug(QDebug dbg, const QVariant &v) case QVariant::ULongLong: dbg.nospace() << v.toULongLong(); break; + case QMetaType::Float: + dbg.nospace() << qVariantValue<float>(v); + break; case QVariant::Double: dbg.nospace() << v.toDouble(); break; @@ -1383,7 +1383,7 @@ void QVariant::create(int type, const void *copy) QVariant::~QVariant() { - if (d.type > Char && (!d.is_shared || !d.data.shared->ref.deref())) + if (d.type > Char && d.type != QMetaType::Float && (!d.is_shared || !d.data.shared->ref.deref())) handler->clear(&d); } @@ -1399,7 +1399,7 @@ QVariant::QVariant(const QVariant &p) { if (d.is_shared) { d.data.shared->ref.ref(); - } else if (p.d.type > Char) { + } else if (p.d.type > Char && p.d.type != QMetaType::Float) { handler->construct(&d, p.constData()); d.is_null = p.d.is_null; } @@ -1593,6 +1593,12 @@ QVariant::QVariant(const char *val) */ /*! + \fn QVariant::QVariant(float val) + + Constructs a new variant with a floating point value, \a val. +*/ + +/*! \fn QVariant::QVariant(const QList<QVariant> &val) Constructs a new variant with a list value, \a val. @@ -1647,44 +1653,44 @@ QVariant::QVariant(double val) { d.is_null = false; d.type = Double; d.data.d = val; } QVariant::QVariant(const QByteArray &val) -{ create(ByteArray, &val); } +{ d.is_null = false; d.type = ByteArray; v_construct<QByteArray>(&d, val); } QVariant::QVariant(const QBitArray &val) -{ create(BitArray, &val); } +{ d.is_null = false; d.type = BitArray; v_construct<QBitArray>(&d, val); } QVariant::QVariant(const QString &val) -{ create(String, &val); } +{ d.is_null = false; d.type = String; v_construct<QString>(&d, val); } QVariant::QVariant(const QChar &val) -{ create (Char, &val); } +{ d.is_null = false; d.type = Char; v_construct<QChar>(&d, val); } QVariant::QVariant(const QLatin1String &val) -{ QString str(val); create(String, &str); } +{ QString str(val); d.is_null = false; d.type = String; v_construct<QString>(&d, str); } QVariant::QVariant(const QStringList &val) -{ create(StringList, &val); } +{ d.is_null = false; d.type = StringList; v_construct<QStringList>(&d, val); } QVariant::QVariant(const QDate &val) -{ create(Date, &val); } +{ d.is_null = false; d.type = Date; v_construct<QDate>(&d, val); } QVariant::QVariant(const QTime &val) -{ create(Time, &val); } +{ d.is_null = false; d.type = Time; v_construct<QTime>(&d, val); } QVariant::QVariant(const QDateTime &val) -{ create(DateTime, &val); } +{ d.is_null = false; d.type = DateTime; v_construct<QDateTime>(&d, val); } QVariant::QVariant(const QList<QVariant> &list) -{ create(List, &list); } +{ d.is_null = false; d.type = List; v_construct<QVariantList>(&d, list); } QVariant::QVariant(const QMap<QString, QVariant> &map) -{ create(Map, &map); } +{ d.is_null = false; d.type = Map; v_construct<QVariantMap>(&d, map); } QVariant::QVariant(const QHash<QString, QVariant> &hash) -{ create(Hash, &hash); } +{ d.is_null = false; d.type = Hash; v_construct<QVariantHash>(&d, hash); } #ifndef QT_NO_GEOM_VARIANT -QVariant::QVariant(const QPoint &pt) { create(Point, &pt); } -QVariant::QVariant(const QPointF &pt) { create (PointF, &pt); } -QVariant::QVariant(const QRectF &r) { create (RectF, &r); } -QVariant::QVariant(const QLineF &l) { create (LineF, &l); } -QVariant::QVariant(const QLine &l) { create (Line, &l); } -QVariant::QVariant(const QRect &r) { create(Rect, &r); } -QVariant::QVariant(const QSize &s) { create(Size, &s); } -QVariant::QVariant(const QSizeF &s) { create(SizeF, &s); } +QVariant::QVariant(const QPoint &pt) { d.is_null = false; d.type = Point; v_construct<QPoint>(&d, pt); } +QVariant::QVariant(const QPointF &pt) { d.is_null = false; d.type = PointF; v_construct<QPointF>(&d, pt); } +QVariant::QVariant(const QRectF &r) { d.is_null = false; d.type = RectF; v_construct<QRectF>(&d, r); } +QVariant::QVariant(const QLineF &l) { d.is_null = false; d.type = LineF; v_construct<QLineF>(&d, l); } +QVariant::QVariant(const QLine &l) { d.is_null = false; d.type = Line; v_construct<QLine>(&d, l); } +QVariant::QVariant(const QRect &r) { d.is_null = false; d.type = Rect; v_construct<QRect>(&d, r); } +QVariant::QVariant(const QSize &s) { d.is_null = false; d.type = Size; v_construct<QSize>(&d, s); } +QVariant::QVariant(const QSizeF &s) { d.is_null = false; d.type = SizeF; v_construct<QSizeF>(&d, s); } #endif -QVariant::QVariant(const QUrl &u) { create(Url, &u); } -QVariant::QVariant(const QLocale &l) { create(Locale, &l); } +QVariant::QVariant(const QUrl &u) { d.is_null = false; d.type = Url; v_construct<QUrl>(&d, u); } +QVariant::QVariant(const QLocale &l) { d.is_null = false; d.type = Locale; v_construct<QLocale>(&d, l); } #ifndef QT_NO_REGEXP -QVariant::QVariant(const QRegExp ®Exp) { create(RegExp, ®Exp); } +QVariant::QVariant(const QRegExp ®Exp) { d.is_null = false; d.type = RegExp; v_construct<QRegExp>(&d, regExp); } #endif QVariant::QVariant(Qt::GlobalColor color) { create(62, &color); } @@ -1749,7 +1755,7 @@ QVariant& QVariant::operator=(const QVariant &variant) if (variant.d.is_shared) { variant.d.data.shared->ref.ref(); d = variant.d; - } else if (variant.d.type > Char) { + } else if (variant.d.type > Char && variant.d.type != QMetaType::Float) { d.type = variant.d.type; handler->construct(&d, variant.constData()); d.is_null = variant.d.is_null; @@ -1855,7 +1861,7 @@ QVariant::Type QVariant::nameToType(const char *name) #ifndef QT_NO_DATASTREAM enum { MapFromThreeCount = 35 }; -static const uint map_from_three[MapFromThreeCount] = +static const ushort map_from_three[MapFromThreeCount] = { QVariant::Invalid, QVariant::Map, @@ -1935,7 +1941,7 @@ void QVariant::load(QDataStream &s) } // const cast is safe since we operate on a newly constructed variant - if (!QMetaType::load(s, d.type, const_cast<void *>(constDataHelper(d)))) { + if (!QMetaType::load(s, d.type, const_cast<void *>(constData()))) { s.setStatus(QDataStream::ReadCorruptData); qWarning("QVariant::load: unable to load type %d.", d.type); } @@ -1975,7 +1981,7 @@ void QVariant::save(QDataStream &s) const return; } - if (!QMetaType::save(s, d.type, constDataHelper(d))) { + if (!QMetaType::save(s, d.type, constData())) { Q_ASSERT_X(false, "QVariant::save", "Invalid type to save"); qWarning("QVariant::save: unable to save type %d.", d.type); } @@ -2516,7 +2522,8 @@ static const quint32 qCanConvertMatrix[QVariant::LastCoreType + 1] = /*QString*/ 1 << QVariant::StringList | 1 << QVariant::ByteArray | 1 << QVariant::Int | 1 << QVariant::UInt | 1 << QVariant::Bool | 1 << QVariant::Double | 1 << QVariant::Date | 1 << QVariant::Time | 1 << QVariant::DateTime - | 1 << QVariant::LongLong | 1 << QVariant::ULongLong | 1 << QVariant::Char, + | 1 << QVariant::LongLong | 1 << QVariant::ULongLong | 1 << QVariant::Char + | 1 << QVariant::Url, /*QStringList*/ 1 << QVariant::List | 1 << QVariant::String, @@ -2531,7 +2538,7 @@ static const quint32 qCanConvertMatrix[QVariant::LastCoreType + 1] = /*QDateTime*/ 1 << QVariant::String | 1 << QVariant::Date, -/*QUrl*/ 0, +/*QUrl*/ 1 << QVariant::String, /*QLocale*/ 0, @@ -2593,57 +2600,63 @@ static const quint32 qCanConvertMatrix[QVariant::LastCoreType + 1] = */ bool QVariant::canConvert(Type t) const { - if (d.type == uint(t)) + //we can treat floats as double + //the reason for not doing it the "proper" way is that QMetaType::Float's value is 135, + //which can't be handled by qCanConvertMatrix + //In addition QVariant::Type doesn't have a Float value, so we're using QMetaType::Float + const uint currentType = ((d.type == QMetaType::Float) ? QVariant::Double : d.type); + if (uint(t) == uint(QMetaType::Float)) t = QVariant::Double; + + if (currentType == uint(t)) return true; - if (d.type > QVariant::LastCoreType || t > QVariant::LastCoreType) { + if (currentType > QVariant::LastCoreType || t > QVariant::LastCoreType) { switch (uint(t)) { case QVariant::Int: - return d.type == QVariant::KeySequence - || d.type == QMetaType::ULong - || d.type == QMetaType::Long - || d.type == QMetaType::UShort - || d.type == QMetaType::UChar - || d.type == QMetaType::Char - || d.type == QMetaType::Short; + return currentType == QVariant::KeySequence + || currentType == QMetaType::ULong + || currentType == QMetaType::Long + || currentType == QMetaType::UShort + || currentType == QMetaType::UChar + || currentType == QMetaType::Char + || currentType == QMetaType::Short; case QVariant::Image: - return d.type == QVariant::Pixmap || d.type == QVariant::Bitmap; + return currentType == QVariant::Pixmap || currentType == QVariant::Bitmap; case QVariant::Pixmap: - return d.type == QVariant::Image || d.type == QVariant::Bitmap - || d.type == QVariant::Brush; + return currentType == QVariant::Image || currentType == QVariant::Bitmap + || currentType == QVariant::Brush; case QVariant::Bitmap: - return d.type == QVariant::Pixmap || d.type == QVariant::Image; + return currentType == QVariant::Pixmap || currentType == QVariant::Image; case QVariant::ByteArray: - return d.type == QVariant::Color; + return currentType == QVariant::Color; case QVariant::String: - return d.type == QVariant::KeySequence || d.type == QVariant::Font - || d.type == QVariant::Color; + return currentType == QVariant::KeySequence || currentType == QVariant::Font + || currentType == QVariant::Color; case QVariant::KeySequence: - return d.type == QVariant::String || d.type == QVariant::Int; + return currentType == QVariant::String || currentType == QVariant::Int; case QVariant::Font: - return d.type == QVariant::String; + return currentType == QVariant::String; case QVariant::Color: - return d.type == QVariant::String || d.type == QVariant::ByteArray - || d.type == QVariant::Brush; + return currentType == QVariant::String || currentType == QVariant::ByteArray + || currentType == QVariant::Brush; case QVariant::Brush: - return d.type == QVariant::Color || d.type == QVariant::Pixmap; + return currentType == QVariant::Color || currentType == QVariant::Pixmap; case QMetaType::Long: case QMetaType::Char: case QMetaType::UChar: case QMetaType::ULong: case QMetaType::Short: case QMetaType::UShort: - case QMetaType::Float: - return qCanConvertMatrix[QVariant::Int] & (1 << d.type) || d.type == QVariant::Int; + return qCanConvertMatrix[QVariant::Int] & (1 << currentType) || currentType == QVariant::Int; default: return false; } } - if(t == String && d.type == StringList) + if(t == String && currentType == StringList) return v_cast<QStringList>(&d)->count() == 1; else - return qCanConvertMatrix[t] & (1 << d.type); + return qCanConvertMatrix[t] & (1 << currentType); } /*! @@ -2754,7 +2767,7 @@ bool QVariant::cmp(const QVariant &v) const const void *QVariant::constData() const { - return constDataHelper(d); + return d.is_shared ? d.data.shared->ptr : reinterpret_cast<const void *>(&d.data.ptr); } /*! @@ -2767,7 +2780,7 @@ const void *QVariant::constData() const void* QVariant::data() { detach(); - return const_cast<void *>(constDataHelper(d)); + return const_cast<void *>(constData()); } diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index d7b7e3c..d73fcbc 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -181,6 +181,7 @@ class Q_CORE_EXPORT QVariant QVariant(qulonglong ull); QVariant(bool b); QVariant(double d); + QVariant(float f) { d.is_null = false; d.type = QMetaType::Float; d.data.f = f; } #ifndef QT_NO_CAST_FROM_ASCII QT_ASCII_CAST_WARN_CONSTRUCTOR QVariant(const char *str); #endif @@ -349,6 +350,7 @@ class Q_CORE_EXPORT QVariant uint u; bool b; double d; + float f; qlonglong ll; qulonglong ull; void *ptr; @@ -443,7 +445,18 @@ inline QVariant qVariantFromValue(const QVariant &t) { return t; } template <typename T> inline void qVariantSetValue(QVariant &v, const T &t) { - v = QVariant(qMetaTypeId<T>(reinterpret_cast<T *>(0)), &t); + //if possible we reuse the current QVariant private + const int type = qMetaTypeId<T>(reinterpret_cast<T *>(0)); + QVariant::Private &d = v.data_ptr(); + if (type <= int(QVariant::Char) || (type == d.type && v.isDetached())) { + d.type = type; + T *old = reinterpret_cast<T*>(d.is_shared ? d.data.shared->ptr : &d.data.ptr); + if (QTypeInfo<T>::isComplex) + old->~T(); + new (old) T(t); //call the copy constructor + } else { + v = QVariant(type, &t); + } } inline QVariant::QVariant() {} diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h index 727a390..02b507e 100644 --- a/src/corelib/kernel/qvariant_p.h +++ b/src/corelib/kernel/qvariant_p.h @@ -91,13 +91,25 @@ inline T *v_cast(QVariant::Private *d, T * = 0) #endif + +//a simple template that avoids to allocate 2 memory chunks when creating a QVariant +template <class T> class QVariantPrivateSharedEx : public QVariant::PrivateShared +{ +public: + QVariantPrivateSharedEx() : QVariant::PrivateShared(&m_t) { } + 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) { 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 = copy ? new QVariantPrivateSharedEx<T>(*static_cast<const T *>(copy)) + : new QVariantPrivateSharedEx<T>; x->is_shared = true; } else { if (copy) @@ -107,16 +119,30 @@ inline void v_construct(QVariant::Private *x, const void *copy, T * = 0) } } +template <class T> +inline void v_construct(QVariant::Private *x, const T &t) +{ + if (sizeof(T) > sizeof(QVariant::Private::Data)) { + x->data.shared = new QVariantPrivateSharedEx<T>(t); + x->is_shared = true; + } else { + new (&x->data.ptr) T(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); - delete d->data.shared; + //now we need to cast + //because QVariant::PrivateShared doesn't have a virtual destructor + delete static_cast<QVariantPrivateSharedEx<T>*>(d->data.shared); } else { v_cast<T>(d)->~T(); } + } QT_END_NAMESPACE diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp index 5163027..4740bf1 100644 --- a/src/corelib/plugin/qfactoryloader.cpp +++ b/src/corelib/plugin/qfactoryloader.cpp @@ -55,7 +55,7 @@ QT_BEGIN_NAMESPACE -Q_GLOBAL_STATIC(QList<QFactoryLoader *>, qt_factory_loaders); +Q_GLOBAL_STATIC(QList<QFactoryLoader *>, qt_factory_loaders) Q_GLOBAL_STATIC_WITH_ARGS(QMutex, qt_factoryloader_mutex, (QMutex::Recursive)) diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp index c868d4a..f6830f2 100644 --- a/src/corelib/plugin/qlibrary_unix.cpp +++ b/src/corelib/plugin/qlibrary_unix.cpp @@ -71,7 +71,7 @@ static QString qdlerror() #else const char *err = strerror(errno); #endif - return err ? QLatin1String("(")+QString::fromLocal8Bit(err) + QLatin1String(")"): QString(); + return err ? QLatin1Char('(') + QString::fromLocal8Bit(err) + QLatin1Char(')'): QString(); } bool QLibraryPrivate::load_sys() diff --git a/src/corelib/plugin/qplugin.h b/src/corelib/plugin/qplugin.h index 4d0e53c..e3b9d32 100644 --- a/src/corelib/plugin/qplugin.h +++ b/src/corelib/plugin/qplugin.h @@ -63,6 +63,21 @@ typedef QObject *(*QtPluginInstanceFunction)(); void Q_CORE_EXPORT qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunction function); +struct qt_plugin_instance_deleter +{ + qt_plugin_instance_deleter(QPointer<QObject> &instance) + : instance_(instance) + { + } + + ~qt_plugin_instance_deleter() + { + delete instance_; + } + + QPointer<QObject> &instance_; +}; + #define Q_IMPORT_PLUGIN(PLUGIN) \ extern QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance_##PLUGIN(); \ class Static##PLUGIN##PluginInstance{ \ @@ -76,8 +91,10 @@ void Q_CORE_EXPORT qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunctio #define Q_PLUGIN_INSTANCE(IMPLEMENTATION) \ { \ static QT_PREPEND_NAMESPACE(QPointer)<QT_PREPEND_NAMESPACE(QObject)> _instance; \ - if (!_instance) \ + if (!_instance) { \ + static QT_PREPEND_NAMESPACE(qt_plugin_instance_deleter) deleter(_instance); \ _instance = new IMPLEMENTATION; \ + } \ return _instance; \ } @@ -114,7 +131,7 @@ void Q_CORE_EXPORT qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunctio "pattern=""QT_PLUGIN_VERIFICATION_DATA""\n" \ "version="QT_VERSION_STR"\n" \ "debug="QPLUGIN_DEBUG_STR"\n" \ - "buildkey="QT_BUILD_KEY"\0"; + "buildkey="QT_BUILD_KEY; # if defined (Q_OS_WIN32) && defined(Q_CC_BOR) # define Q_STANDARD_CALL __stdcall diff --git a/src/corelib/plugin/quuid.cpp b/src/corelib/plugin/quuid.cpp index 4365c98..a6138a1 100644 --- a/src/corelib/plugin/quuid.cpp +++ b/src/corelib/plugin/quuid.cpp @@ -405,7 +405,7 @@ QDataStream &operator>>(QDataStream &s, QUuid &id) } return s; } -#endif +#endif // QT_NO_DATASTREAM /*! Returns true if this is the null UUID diff --git a/src/corelib/statemachine/qabstractstate.cpp b/src/corelib/statemachine/qabstractstate.cpp new file mode 100644 index 0000000..973057b --- /dev/null +++ b/src/corelib/statemachine/qabstractstate.cpp @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** 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" + +#ifndef QT_NO_STATEMACHINE + +#include "qabstractstate_p.h" +#include "qstate.h" +#include "qstate_p.h" +#include "qstatemachine.h" +#include "qstatemachine_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QAbstractState + + \brief The QAbstractState class is the base class of states of a QStateMachine. + + \since 4.6 + \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 entered() signal is emitted when the state has been entered. The + exited() signal is emitted when the state has been exited. + + The parentState() function returns the state's parent state. The machine() + function returns the state machine that the state is part of. + + \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. +*/ + +QAbstractStatePrivate::QAbstractStatePrivate() +{ +} + +QAbstractStatePrivate *QAbstractStatePrivate::get(QAbstractState *q) +{ + return q->d_func(); +} + +QStateMachine *QAbstractStatePrivate::machine() const +{ + QObject *par = parent; + while (par != 0) { + if (QStateMachine *mach = qobject_cast<QStateMachine*>(par)) + return mach; + par = par->parent(); + } + return 0; +} + +void QAbstractStatePrivate::callOnEntry(QEvent *e) +{ + Q_Q(QAbstractState); + q->onEntry(e); +} + +void QAbstractStatePrivate::callOnExit(QEvent *e) +{ + Q_Q(QAbstractState); + q->onExit(e); +} + +void QAbstractStatePrivate::emitEntered() +{ + Q_Q(QAbstractState); + emit q->entered(); +} + +void QAbstractStatePrivate::emitExited() +{ + Q_Q(QAbstractState); + emit q->exited(); +} + +/*! + Constructs a new state with the given \a parent state. +*/ +QAbstractState::QAbstractState(QState *parent) + : QObject(*new QAbstractStatePrivate, parent) +{ +} + +/*! + \internal +*/ +QAbstractState::QAbstractState(QAbstractStatePrivate &dd, QState *parent) + : QObject(dd, parent) +{ +} + +/*! + Destroys this state. +*/ +QAbstractState::~QAbstractState() +{ +} + +/*! + Returns this state's parent state, or 0 if the state has no parent state. +*/ +QState *QAbstractState::parentState() const +{ + return qobject_cast<QState*>(parent()); +} + +/*! + Returns the state machine that this state is part of, or 0 if the state is + not part of a state machine. +*/ +QStateMachine *QAbstractState::machine() const +{ + Q_D(const QAbstractState); + return d->machine(); +} + +/*! + \fn QAbstractState::onExit(QEvent *event) + + This function is called when the state is exited. The given \a event is what + caused the state to be exited. Reimplement this function to perform custom + processing when the state is exited. +*/ + +/*! + \fn QAbstractState::onEntry(QEvent *event) + + This function is called when the state is entered. The given \a event is + what caused the state to be entered. Reimplement this function to perform + custom processing when the state is entered. +*/ + +/*! + \fn QAbstractState::entered() + + This signal is emitted when the state has been entered (after onEntry() has + been called). +*/ + +/*! + \fn QAbstractState::exited() + + This signal is emitted when the state has been exited (after onExit() has + been called). +*/ + +/*! + \reimp +*/ +bool QAbstractState::event(QEvent *e) +{ + return QObject::event(e); +} + +QT_END_NAMESPACE + +#endif //QT_NO_STATEMACHINE diff --git a/src/corelib/statemachine/qabstractstate.h b/src/corelib/statemachine/qabstractstate.h new file mode 100644 index 0000000..ee55541 --- /dev/null +++ b/src/corelib/statemachine/qabstractstate.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 QABSTRACTSTATE_H +#define QABSTRACTSTATE_H + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_STATEMACHINE + +class QState; +class QStateMachine; + +class QAbstractStatePrivate; +class Q_CORE_EXPORT QAbstractState : public QObject +{ + Q_OBJECT +public: + ~QAbstractState(); + + QState *parentState() const; + QStateMachine *machine() const; + +Q_SIGNALS: + void entered(); + void exited(); + +protected: + QAbstractState(QState *parent = 0); + + virtual void onEntry(QEvent *event) = 0; + virtual void onExit(QEvent *event) = 0; + + bool event(QEvent *e); + +protected: + QAbstractState(QAbstractStatePrivate &dd, QState *parent); + +private: + Q_DISABLE_COPY(QAbstractState) + Q_DECLARE_PRIVATE(QAbstractState) +}; + +#endif //QT_NO_STATEMACHINE + +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..b4f3108 --- /dev/null +++ b/src/corelib/statemachine/qabstractstate_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 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. +// + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QStateMachine; + +class QAbstractState; +class QAbstractStatePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QAbstractState) + +public: + QAbstractStatePrivate(); + + static QAbstractStatePrivate *get(QAbstractState *q); + + QStateMachine *machine() const; + + void callOnEntry(QEvent *e); + void callOnExit(QEvent *e); + + void emitEntered(); + void emitExited(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qabstracttransition.cpp b/src/corelib/statemachine/qabstracttransition.cpp new file mode 100644 index 0000000..c1b553c --- /dev/null +++ b/src/corelib/statemachine/qabstracttransition.cpp @@ -0,0 +1,345 @@ +/**************************************************************************** +** +** 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" + +#ifndef QT_NO_STATEMACHINE + +#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. + + \since 4.6 + \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 sourceState() function returns the source of the transition. The + targetStates() function returns the targets of the transition. The machine() + function returns the state machine that the transition is part of. + + 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::sourceState + + \brief the source state (parent) of this transition +*/ + +/*! + \property QAbstractTransition::targetState + + \brief the target state of this transition +*/ + +/*! + \property QAbstractTransition::targetStates + + \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(); +} + +QStateMachine *QAbstractTransitionPrivate::machine() const +{ + QObject *par = parent; + while (par != 0) { + if (QStateMachine *mach = qobject_cast<QStateMachine*>(par)) + return mach; + par = par->parent(); + } + return 0; +} + +bool QAbstractTransitionPrivate::callEventTest(QEvent *e) +{ + Q_Q(QAbstractTransition); + return q->eventTest(e); +} + +void QAbstractTransitionPrivate::callOnTransition(QEvent *e) +{ + Q_Q(QAbstractTransition); + q->onTransition(e); +} + +QState *QAbstractTransitionPrivate::sourceState() const +{ + return qobject_cast<QState*>(parent); +} + +/*! + Constructs a new QAbstractTransition object with the given \a sourceState. +*/ +QAbstractTransition::QAbstractTransition(QState *sourceState) + : QObject(*new QAbstractTransitionPrivate, sourceState) +{ +} + +/*! + Constructs a new QAbstractTransition object with the given \a targets and \a + sourceState. +*/ +QAbstractTransition::QAbstractTransition(const QList<QAbstractState*> &targets, + QState *sourceState) + : QObject(*new QAbstractTransitionPrivate, sourceState) +{ + setTargetStates(targets); +} + +/*! + \internal +*/ +QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, + QState *parent) + : QObject(dd, parent) +{ +} + +/*! + \internal +*/ +QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, + const QList<QAbstractState*> &targets, + QState *parent) + : QObject(dd, parent) +{ + setTargetStates(targets); +} + +/*! + Destroys this transition. +*/ +QAbstractTransition::~QAbstractTransition() +{ +} + +/*! + 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 + setTargetStates(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); + QList<QAbstractState*> result; + for (int i = 0; i < d->targetStates.size(); ++i) { + QAbstractState *target = d->targetStates.at(i); + if (target) + result.append(target); + } + return result; +} + +/*! + Sets the target states of this transition to be the given \a targets. +*/ +void QAbstractTransition::setTargetStates(const QList<QAbstractState*> &targets) +{ + Q_D(QAbstractTransition); + + for (int i=0; i<targets.size(); ++i) { + QAbstractState *target = targets.at(i); + if (!target) { + qWarning("QAbstractTransition::setTargetStates: target state(s) cannot be null"); + return; + } + if (target->machine() != 0 && target->machine()->rootState() == target) { + qWarning("QAbstractTransition::setTargetStates: root state cannot be target of transition"); + return; + } + } + + d->targetStates.clear(); + for (int i = 0; i < targets.size(); ++i) + d->targetStates.append(targets.at(i)); +} + +/*! + Returns the state machine that this transition is part of, or 0 if the + transition is not part of a state machine. +*/ +QStateMachine *QAbstractTransition::machine() const +{ + Q_D(const QAbstractTransition); + return d->machine(); +} + +#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) + + 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(QEvent *event) + + This function is called when the transition is triggered. The given \a event + is what caused the transition to trigger. 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 + +#endif //QT_NO_STATEMACHINE diff --git a/src/corelib/statemachine/qabstracttransition.h b/src/corelib/statemachine/qabstracttransition.h new file mode 100644 index 0000000..a1a62c9 --- /dev/null +++ b/src/corelib/statemachine/qabstracttransition.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** 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) + +#ifndef QT_NO_STATEMACHINE + +class QEvent; +class QAbstractState; +class QState; +class QStateMachine; + +#ifndef QT_NO_ANIMATION +class QAbstractAnimation; +#endif + +class QAbstractTransitionPrivate; +class Q_CORE_EXPORT QAbstractTransition : public QObject +{ + Q_OBJECT + Q_PROPERTY(QState* sourceState READ sourceState) + Q_PROPERTY(QAbstractState* targetState READ targetState WRITE setTargetState) + Q_PROPERTY(QList<QAbstractState*> targetStates 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); + + QStateMachine *machine() const; + +#ifndef QT_NO_ANIMATION + void addAnimation(QAbstractAnimation *animation); + void removeAnimation(QAbstractAnimation *animation); + QList<QAbstractAnimation*> animations() const; +#endif + +protected: + virtual bool eventTest(QEvent *event) = 0; + + virtual void onTransition(QEvent *event) = 0; + + bool event(QEvent *e); + +protected: + QAbstractTransition(QAbstractTransitionPrivate &dd, QState *parent); + QAbstractTransition(QAbstractTransitionPrivate &dd, + const QList<QAbstractState*> &targets, QState *parent); + +private: + Q_DISABLE_COPY(QAbstractTransition) + Q_DECLARE_PRIVATE(QAbstractTransition) +}; + +#endif //QT_NO_STATEMACHINE + +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..f067984 --- /dev/null +++ b/src/corelib/statemachine/qabstracttransition_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** 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. +// + +#include <private/qobject_p.h> + +#include <QtCore/qlist.h> +#include <QtCore/qpointer.h> + +QT_BEGIN_NAMESPACE + +class QAbstractState; +class QState; +class QStateMachine; + +class QAbstractTransition; +class Q_CORE_EXPORT QAbstractTransitionPrivate + : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QAbstractTransition) +public: + QAbstractTransitionPrivate(); + + static QAbstractTransitionPrivate *get(QAbstractTransition *q); + + bool callEventTest(QEvent *e); + void callOnTransition(QEvent *e); + QState *sourceState() const; + QStateMachine *machine() const; + + QList<QPointer<QAbstractState> > targetStates; + +#ifndef QT_NO_ANIMATION + QList<QAbstractAnimation*> animations; +#endif +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qeventtransition.cpp b/src/corelib/statemachine/qeventtransition.cpp new file mode 100644 index 0000000..4c40256 --- /dev/null +++ b/src/corelib/statemachine/qeventtransition.cpp @@ -0,0 +1,290 @@ +/**************************************************************************** +** +** 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" + +#ifndef QT_NO_STATEMACHINE + +#include "qeventtransition_p.h" +#include "qwrappedevent.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. + + \since 4.6 + \ingroup statemachine + + A QEventTransition object binds an event 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 + + When reimplementing the eventTest() function, you should first call the base + implementation to verify that the event is a QWrappedEvent for the proper + object and event type. You may then cast the event to a QWrappedEvent and + get the original event by calling QWrappedEvent::event(), and perform + additional checks on that object. + + \sa QState::addTransition() +*/ + +/*! + \property QEventTransition::eventObject + + \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::unregister() +{ + Q_Q(QEventTransition); + if (!registered || !machine()) + return; + QStateMachinePrivate::get(machine())->unregisterEventTransition(q); +} + +void QEventTransitionPrivate::maybeRegister() +{ + Q_Q(QEventTransition); + if (!machine() || !machine()->configuration().contains(sourceState())) + return; + QStateMachinePrivate::get(machine())->registerEventTransition(q); +} + +/*! + Constructs a new QEventTransition object with the given \a sourceState. +*/ +QEventTransition::QEventTransition(QState *sourceState) + : QAbstractTransition(*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) + : QAbstractTransition(*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) + : QAbstractTransition(*new QEventTransitionPrivate, targets, sourceState) +{ + Q_D(QEventTransition); + d->registered = false; + d->object = object; + d->eventType = type; +} + +/*! + \internal +*/ +QEventTransition::QEventTransition(QEventTransitionPrivate &dd, QState *parent) + : QAbstractTransition(dd, parent) +{ +} + +/*! + \internal +*/ +QEventTransition::QEventTransition(QEventTransitionPrivate &dd, QObject *object, + QEvent::Type type, QState *parent) + : QAbstractTransition(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) + : QAbstractTransition(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->unregister(); + d->eventType = type; + d->maybeRegister(); +} + +/*! + Returns the event source associated with this event transition. +*/ +QObject *QEventTransition::eventObject() 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::setEventObject(QObject *object) +{ + Q_D(QEventTransition); + if (d->object == object) + return; + d->unregister(); + d->object = object; + d->maybeRegister(); +} + +/*! + \reimp +*/ +bool QEventTransition::eventTest(QEvent *event) +{ + Q_D(const QEventTransition); + if (event->type() == QEvent::Wrapped) { + QWrappedEvent *we = static_cast<QWrappedEvent*>(event); + return (we->object() == d->object) + && (we->event()->type() == d->eventType); + } + return false; +} + +/*! + \reimp +*/ +void QEventTransition::onTransition(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +bool QEventTransition::event(QEvent *e) +{ + return QAbstractTransition::event(e); +} + +QT_END_NAMESPACE + +#endif //QT_NO_STATEMACHINE diff --git a/src/corelib/statemachine/qeventtransition.h b/src/corelib/statemachine/qeventtransition.h new file mode 100644 index 0000000..40ffecf --- /dev/null +++ b/src/corelib/statemachine/qeventtransition.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 QEVENTTRANSITION_H +#define QEVENTTRANSITION_H + +#include <QtCore/qabstracttransition.h> +#include <QtCore/qcoreevent.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_STATEMACHINE + +class QEventTransitionPrivate; +class Q_CORE_EXPORT QEventTransition : public QAbstractTransition +{ + Q_OBJECT + Q_PROPERTY(QObject* eventObject READ eventObject WRITE setEventObject) + Q_PROPERTY(QEvent::Type eventType READ eventType WRITE setEventType) +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 *eventObject() const; + void setEventObject(QObject *object); + + QEvent::Type eventType() const; + void setEventType(QEvent::Type type); + +protected: + bool eventTest(QEvent *event); + void onTransition(QEvent *event); + + 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) +}; + +#endif //QT_NO_STATEMACHINE + +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..ab17ad3 --- /dev/null +++ b/src/corelib/statemachine/qeventtransition_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 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 "private/qabstracttransition_p.h" + +QT_BEGIN_NAMESPACE + +class QEventTransition; +class Q_CORE_EXPORT QEventTransitionPrivate : public QAbstractTransitionPrivate +{ + Q_DECLARE_PUBLIC(QEventTransition) +public: + QEventTransitionPrivate(); + + static QEventTransitionPrivate *get(QEventTransition *q); + + void unregister(); + void maybeRegister(); + + 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..0eb531f --- /dev/null +++ b/src/corelib/statemachine/qfinalstate.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$ +** +****************************************************************************/ + +#include "qfinalstate.h" + +#ifndef QT_NO_STATEMACHINE + +#include "qabstractstate_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QFinalState + + \brief The QFinalState class provides a final state. + + \since 4.6 + \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, the parent + state's \l{QState::finished()}{finished}() signal is emitted. 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 QStateMachine::finished(), QState::finished() +*/ + +class QFinalStatePrivate : public QAbstractStatePrivate +{ + Q_DECLARE_PUBLIC(QFinalState) + +public: + QFinalStatePrivate(); +}; + +QFinalStatePrivate::QFinalStatePrivate() +{ +} + +/*! + Constructs a new QFinalState object with the given \a parent state. +*/ +QFinalState::QFinalState(QState *parent) + : QAbstractState(*new QFinalStatePrivate, parent) +{ +} + +/*! + Destroys this final state. +*/ +QFinalState::~QFinalState() +{ +} + +/*! + \reimp +*/ +void QFinalState::onEntry(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +void QFinalState::onExit(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +bool QFinalState::event(QEvent *e) +{ + return QAbstractState::event(e); +} + +QT_END_NAMESPACE + +#endif //QT_NO_STATEMACHINE diff --git a/src/corelib/statemachine/qfinalstate.h b/src/corelib/statemachine/qfinalstate.h new file mode 100644 index 0000000..865f333 --- /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 + +#include <QtCore/qabstractstate.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_STATEMACHINE + +class QFinalStatePrivate; +class Q_CORE_EXPORT QFinalState : public QAbstractState +{ + Q_OBJECT +public: + QFinalState(QState *parent = 0); + ~QFinalState(); + +protected: + void onEntry(QEvent *event); + void onExit(QEvent *event); + + bool event(QEvent *e); + +private: + Q_DISABLE_COPY(QFinalState) + Q_DECLARE_PRIVATE(QFinalState) +}; + +#endif //QT_NO_STATEMACHINE + +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..4304da3 --- /dev/null +++ b/src/corelib/statemachine/qhistorystate.cpp @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** 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" + +#ifndef QT_NO_STATEMACHINE + +#include "qhistorystate_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QHistoryState + + \brief The QHistoryState class provides a means of returning to a previously active substate. + + \since 4.6 + \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 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); + + QHistoryState *s1h = new QHistoryState(s1); + 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 + + By default a history state is shallow, meaning that it won't remember nested + states. This can be configured through the historyType property. +*/ + +/*! + \property QHistoryState::defaultState + + \brief the default state of this history state +*/ + +/*! + \property QHistoryState::historyType + + \brief the type of history that this history state records + + The default value of this property is QHistoryState::ShallowHistory. +*/ + +/*! + \enum QHistoryState::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. +*/ + +QHistoryStatePrivate::QHistoryStatePrivate() + : defaultState(0) +{ +} + +QHistoryStatePrivate *QHistoryStatePrivate::get(QHistoryState *q) +{ + return q->d_func(); +} + +/*! + Constructs a new shallow history state with the given \a parent state. +*/ +QHistoryState::QHistoryState(QState *parent) + : QAbstractState(*new QHistoryStatePrivate, parent) +{ + Q_D(QHistoryState); + d->historyType = ShallowHistory; +} +/*! + Constructs a new history state of the given \a type, with the given \a + parent state. +*/ +QHistoryState::QHistoryState(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; +} + +/*! + Returns the type of history that this history state records. +*/ +QHistoryState::HistoryType QHistoryState::historyType() const +{ + Q_D(const QHistoryState); + return d->historyType; +} + +/*! + Sets the \a type of history that this history state records. +*/ +void QHistoryState::setHistoryType(HistoryType type) +{ + Q_D(QHistoryState); + d->historyType = type; +} + +/*! + \reimp +*/ +void QHistoryState::onEntry(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +void QHistoryState::onExit(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +bool QHistoryState::event(QEvent *e) +{ + return QAbstractState::event(e); +} + +QT_END_NAMESPACE + +#endif //QT_NO_STATEMACHINE diff --git a/src/corelib/statemachine/qhistorystate.h b/src/corelib/statemachine/qhistorystate.h new file mode 100644 index 0000000..eee43d1 --- /dev/null +++ b/src/corelib/statemachine/qhistorystate.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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 + +#include <QtCore/qabstractstate.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_STATEMACHINE + +class QHistoryStatePrivate; +class Q_CORE_EXPORT QHistoryState : public QAbstractState +{ + Q_OBJECT + Q_PROPERTY(QAbstractState* defaultState READ defaultState WRITE setDefaultState) + Q_PROPERTY(HistoryType historyType READ historyType WRITE setHistoryType) + Q_ENUMS(HistoryType) +public: + enum HistoryType { + ShallowHistory, + DeepHistory + }; + + QHistoryState(QState *parent = 0); + QHistoryState(HistoryType type, QState *parent = 0); + ~QHistoryState(); + + QAbstractState *defaultState() const; + void setDefaultState(QAbstractState *state); + + HistoryType historyType() const; + void setHistoryType(HistoryType type); + +protected: + void onEntry(QEvent *event); + void onExit(QEvent *event); + + bool event(QEvent *e); + +private: + Q_DISABLE_COPY(QHistoryState) + Q_DECLARE_PRIVATE(QHistoryState) +}; + +#endif //QT_NO_STATEMACHINE + +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..875dac8 --- /dev/null +++ b/src/corelib/statemachine/qhistorystate_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 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 "private/qabstractstate_p.h" + +#include <QtCore/qlist.h> + +QT_BEGIN_NAMESPACE + +class QHistoryState; +class QHistoryStatePrivate : public QAbstractStatePrivate +{ + Q_DECLARE_PUBLIC(QHistoryState) + +public: + QHistoryStatePrivate(); + + static QHistoryStatePrivate *get(QHistoryState *q); + + QAbstractState *defaultState; + QHistoryState::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..79d1053 --- /dev/null +++ b/src/corelib/statemachine/qsignalevent.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 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) + +#ifndef QT_NO_STATEMACHINE + +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; +}; + +#endif //QT_NO_STATEMACHINE + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qsignaleventgenerator_p.h b/src/corelib/statemachine/qsignaleventgenerator_p.h new file mode 100644 index 0000000..cf0ea1e --- /dev/null +++ b/src/corelib/statemachine/qsignaleventgenerator_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 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(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: + 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..2e150a7 --- /dev/null +++ b/src/corelib/statemachine/qsignaltransition.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 "qsignaltransition.h" + +#ifndef QT_NO_STATEMACHINE + +#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. + + \since 4.6 + \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::senderObject + + \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::unregister() +{ + Q_Q(QSignalTransition); + if ((signalIndex == -1) || !machine()) + return; + QStateMachinePrivate::get(machine())->unregisterSignalTransition(q); +} + +void QSignalTransitionPrivate::maybeRegister() +{ + Q_Q(QSignalTransition); + if (!machine() || !machine()->configuration().contains(sourceState())) + return; + QStateMachinePrivate::get(machine())->registerSignalTransition(q); +} + +/*! + Constructs a new signal transition with the given \a sourceState. +*/ +QSignalTransition::QSignalTransition(QState *sourceState) + : QAbstractTransition(*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) + : QAbstractTransition(*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) + : QAbstractTransition(*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->unregister(); + d->sender = sender; + d->maybeRegister(); +} + +/*! + 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->unregister(); + d->signal = signal; + d->maybeRegister(); +} + +/*! + \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) +{ + Q_D(const QSignalTransition); + if (event->type() == QEvent::Signal) { + if (d->signalIndex == -1) + return false; + QSignalEvent *se = static_cast<QSignalEvent*>(event); + return (se->sender() == d->sender) + && (se->signalIndex() == d->signalIndex); + } + return false; +} + +/*! + \reimp +*/ +void QSignalTransition::onTransition(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +bool QSignalTransition::event(QEvent *e) +{ + return QAbstractTransition::event(e); +} + +QT_END_NAMESPACE + +#endif //QT_NO_STATEMACHINE diff --git a/src/corelib/statemachine/qsignaltransition.h b/src/corelib/statemachine/qsignaltransition.h new file mode 100644 index 0000000..02b1de9 --- /dev/null +++ b/src/corelib/statemachine/qsignaltransition.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** 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 + +#include <QtCore/qabstracttransition.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_STATEMACHINE + +class QSignalTransitionPrivate; +class Q_CORE_EXPORT QSignalTransition : public QAbstractTransition +{ + Q_OBJECT + Q_PROPERTY(QObject* senderObject 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); + void onTransition(QEvent *event); + + bool event(QEvent *e); + +private: + Q_DISABLE_COPY(QSignalTransition) + Q_DECLARE_PRIVATE(QSignalTransition) +}; + +#endif //QT_NO_STATEMACHINE + +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..aacb6fc --- /dev/null +++ b/src/corelib/statemachine/qsignaltransition_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 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 "private/qabstracttransition_p.h" + +QT_BEGIN_NAMESPACE + +class QSignalTransition; +class QSignalTransitionPrivate : public QAbstractTransitionPrivate +{ + Q_DECLARE_PUBLIC(QSignalTransition) +public: + QSignalTransitionPrivate(); + + static QSignalTransitionPrivate *get(QSignalTransition *q); + + void unregister(); + void maybeRegister(); + + 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..5dd56c0 --- /dev/null +++ b/src/corelib/statemachine/qstate.cpp @@ -0,0 +1,489 @@ +/**************************************************************************** +** +** 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" + +#ifndef QT_NO_STATEMACHINE + +#include "qstate_p.h" +#include "qhistorystate.h" +#include "qhistorystate_p.h" +#include "qabstracttransition.h" +#include "qabstracttransition_p.h" +#include "qsignaltransition.h" +#include "qstatemachine.h" +#include "qstatemachine_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QState + + \brief The QState class provides a general-purpose state for QStateMachine. + + \since 4.6 + \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. + + The assignProperty() function is used for defining property assignments that + should be performed when a state is entered. + + Top-level states must be passed QStateMachine::rootState() as their parent + state, or added to a state machine using QStateMachine::addState(). + + \section1 States with Child States + + The childMode property determines how child states are treated. 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 state emits the QState::finished() signal when a final child state + (QFinalState) is entered. + + 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). + +*/ + +/*! + \property QState::initialState + + \brief the initial state of this state (one of its child states) +*/ + +/*! + \property QState::errorState + + \brief the error state of this state +*/ + +/*! + \property QState::childMode + + \brief the child mode of this state + + The default value of this property is QState::ExclusiveStates. +*/ + +/*! + \enum QState::ChildMode + + This enum specifies how a state's child states are treated. + + \value ExclusiveStates The child states are mutually exclusive and an + initial state must be set by calling QState::setInitialState(). + + \value ParallelStates The child states are parallel. When the parent state + is entered, all its child states are entered in parallel. +*/ + +QStatePrivate::QStatePrivate() + : errorState(0), initialState(0), childMode(QState::ExclusiveStates) +{ +} + +QStatePrivate::~QStatePrivate() +{ +} + +void QStatePrivate::emitFinished() +{ + Q_Q(QState); + emit q->finished(); +} + +void QStatePrivate::emitPolished() +{ + Q_Q(QState); + emit q->polished(); +} + +/*! + Constructs a new state with the given \a parent state. +*/ +QState::QState(QState *parent) + : QAbstractState(*new QStatePrivate, parent) +{ +} + +/*! + Constructs a new state with the given \a childMode and the given \a parent + state. +*/ +QState::QState(ChildMode childMode, QState *parent) + : QAbstractState(*new QStatePrivate, parent) +{ + Q_D(QState); + d->childMode = childMode; +} + +/*! + \internal +*/ +QState::QState(QStatePrivate &dd, QState *parent) + : QAbstractState(dd, parent) +{ +} + +/*! + Destroys this state. +*/ +QState::~QState() +{ +} + +QList<QAbstractState*> QStatePrivate::childStates() const +{ + QList<QAbstractState*> result; + QList<QObject*>::const_iterator it; + 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; + 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; + for (it = children.constBegin(); it != children.constEnd(); ++it) { + QAbstractTransition *t = qobject_cast<QAbstractTransition*>(*it); + if (t) + result.append(t); + } + return result; +} + +/*! + 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. + + \sa polished() +*/ +void QState::assignProperty(QObject *object, const char *name, + const QVariant &value) +{ + Q_D(QState); + if (!object) { + qWarning("QState::assignProperty: cannot assign property '%s' of null object", name); + return; + } + 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)); +} + +/*! + 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 && state->machine() != machine()) { + qWarning("QState::setErrorState: error state cannot belong " + "to a different state machine"); + return; + } + if (state != 0 && state->machine() != 0 && state->machine()->rootState() == state) { + qWarning("QStateMachine::setErrorState: root state cannot be error state"); + return; + } + + d->errorState = state; +} + +/*! + Adds the given \a transition. The transition has this state as the source. + This state takes ownership of the transition. If the transition is successfully + added, the function will return the \a transition pointer. Otherwise it will return null. +*/ +QAbstractTransition *QState::addTransition(QAbstractTransition *transition) +{ + Q_D(QState); + if (!transition) { + qWarning("QState::addTransition: cannot add null transition"); + return 0; + } + + // machine() will always be non-null for root state + if (machine() != 0 && machine()->rootState() == this) { + qWarning("QState::addTransition: cannot add transition from root state"); + return 0; + } + + const QList<QPointer<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 0; + } + 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 0; + } + } + transition->setParent(this); + if (machine() != 0 && machine()->configuration().contains(this)) + QStateMachinePrivate::get(machine())->registerTransitions(this); + return transition; +} + +/*! + 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; + } + if (!target) { + qWarning("QState::addTransition: cannot add transition to null state"); + return 0; + } + int offset = (*signal == '0'+QSIGNAL_CODE) ? 1 : 0; + if (sender->metaObject()->indexOfSignal(signal+offset) == -1) { + qWarning("QState::addTransition: no such signal %s::%s", + sender->metaObject()->className(), signal+offset); + return 0; + } + QSignalTransition *trans = new QSignalTransition(sender, signal, 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(QEvent *) {} + bool eventTest(QEvent *) { 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) +{ + if (!target) { + qWarning("QState::addTransition: cannot add transition to null state"); + return 0; + } + UnconditionalTransition *trans = new UnconditionalTransition(target); + return addTransition(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); +} + +/*! + \reimp +*/ +void QState::onEntry(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +void QState::onExit(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + 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->childMode == QState::ParallelStates) { + 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; +} + +/*! + Returns the child mode of this state. +*/ +QState::ChildMode QState::childMode() const +{ + Q_D(const QState); + return d->childMode; +} + +/*! + Sets the child \a mode of this state. +*/ +void QState::setChildMode(ChildMode mode) +{ + Q_D(QState); + d->childMode = mode; +} + +/*! + \reimp +*/ +bool QState::event(QEvent *e) +{ + return QAbstractState::event(e); +} + +/*! + \fn QState::finished() + + This signal is emitted when a final child state of this state is entered. + + \sa QFinalState +*/ + +/*! + \fn QState::polished() + + This signal is emitted when all properties have been assigned their final value. + + \sa QState::assignProperty(), QAbstractTransition::addAnimation() +*/ + +QT_END_NAMESPACE + +#endif //QT_NO_STATEMACHINE diff --git a/src/corelib/statemachine/qstate.h b/src/corelib/statemachine/qstate.h new file mode 100644 index 0000000..c98bb64 --- /dev/null +++ b/src/corelib/statemachine/qstate.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 QSTATE_H +#define QSTATE_H + +#include <QtCore/qabstractstate.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_STATEMACHINE + +class QAbstractTransition; +class QSignalTransition; + +class QStatePrivate; +class Q_CORE_EXPORT QState : public QAbstractState +{ + Q_OBJECT + Q_PROPERTY(QAbstractState* initialState READ initialState WRITE setInitialState) + Q_PROPERTY(QAbstractState* errorState READ errorState WRITE setErrorState) + Q_PROPERTY(ChildMode childMode READ childMode WRITE setChildMode) + Q_ENUMS(ChildMode) +public: + enum ChildMode { + ExclusiveStates, + ParallelStates + }; + + QState(QState *parent = 0); + QState(ChildMode childMode, QState *parent = 0); + ~QState(); + + QAbstractState *errorState() const; + void setErrorState(QAbstractState *state); + + QAbstractTransition *addTransition(QAbstractTransition *transition); + QSignalTransition *addTransition(QObject *sender, const char *signal, QAbstractState *target); + QAbstractTransition *addTransition(QAbstractState *target); + void removeTransition(QAbstractTransition *transition); + + QAbstractState *initialState() const; + void setInitialState(QAbstractState *state); + + ChildMode childMode() const; + void setChildMode(ChildMode mode); + + void assignProperty(QObject *object, const char *name, + const QVariant &value); + +Q_SIGNALS: + void finished(); + void polished(); + +protected: + void onEntry(QEvent *event); + void onExit(QEvent *event); + + bool event(QEvent *e); + +protected: + QState(QStatePrivate &dd, QState *parent); + +private: + Q_DISABLE_COPY(QState) + Q_DECLARE_PRIVATE(QState) +}; + +#endif //QT_NO_STATEMACHINE + +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..eddd831 --- /dev/null +++ b/src/corelib/statemachine/qstate_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** 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 "private/qabstractstate_p.h" + +#include <QtCore/qlist.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_NAMESPACE + +struct QPropertyAssignment +{ + QPropertyAssignment() + : object(0) {} + 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 QAbstractTransition; +class QHistoryState; + +class QState; +class Q_AUTOTEST_EXPORT QStatePrivate : public QAbstractStatePrivate +{ + Q_DECLARE_PUBLIC(QState) +public: + QStatePrivate(); + ~QStatePrivate(); + + static QStatePrivate *get(QState *q) { return q ? q->d_func() : 0; } + static const QStatePrivate *get(const QState *q) { return q? q->d_func() : 0; } + + QList<QAbstractState*> childStates() const; + QList<QHistoryState*> historyStates() const; + QList<QAbstractTransition*> transitions() const; + + void emitFinished(); + void emitPolished(); + + QAbstractState *errorState; + QAbstractState *initialState; + QState::ChildMode childMode; + + QList<QPropertyAssignment> propertyAssignments; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp new file mode 100644 index 0000000..64b33ac --- /dev/null +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -0,0 +1,2214 @@ +/**************************************************************************** +** +** 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" + +#ifndef QT_NO_STATEMACHINE + +#include "qstate.h" +#include "qstate_p.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 "qfinalstate.h" +#include "qhistorystate.h" +#include "qhistorystate_p.h" +#include "private/qobject_p.h" +#include "private/qthread_p.h" + +#ifndef QT_NO_STATEMACHINE_EVENTFILTER +#include "qeventtransition.h" +#include "qeventtransition_p.h" +#include "qwrappedevent.h" +#endif + +#ifndef QT_NO_ANIMATION +#include "qpropertyanimation.h" +#include "qanimationgroup.h" +#include <private/qvariantanimation_p.h> +#endif + +#include <QtCore/qmetaobject.h> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QStateMachine + \reentrant + + \brief The QStateMachine class provides a hierarchical finite state machine. + + \since 4.6 + \ingroup statemachine + + QStateMachine is based on the concepts and notation of + \l{Statecharts: A visual formalism for complex + systems}{Statecharts}. QStateMachine is part of \l{The State + Machine Framework}. + + A state machine manages a set of states (classes that inherit from + QAbstractState) and transitions (descendants of + QAbstractTransition) between those states; these states and + transitions define a state graph. Once a state graph has been + built, 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 framework's \l{The State Machine + Framework}{overview} gives several state graphs and the code to + build them. + + The rootState() is the parent of all top-level states in the + machine; it is used, for instance, when the state graph is + deleted. It is created by the machine. + + Use the addState() function to add a state to the state machine. + All top-level states are added to the root state. States are + removed with the removeState() function. Removing states while the + machine is running is discouraged. + + Before the machine can be started, the \l{initialState}{initial + state} must be set. The initial state is the state that the + machine enters when started. You can then start() the state + machine. The started() signal is emitted when the initial state is + entered. + + The machine is event driven and keeps its own event loop. Events + are posted to the machine through postEvent(). Note that this + means that it executes asynchronously, and that it will not + progress without a running event loop. You will normally not have + to post events to the machine directly as Qt's transitions, e.g., + QEventTransition and its subclasses, handle this. But for custom + transitions triggered by events, postEvent() is useful. + + The state machine processes events and takes transitions until a + top-level final state is entered; the state machine then emits the + finished() signal. You can also stop() the state machine + explicitly. The stopped() signal is emitted in this case. + + 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->assignProperty(&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 + + This code example uses QState, which inherits QAbstractState. The + QState class provides a state that you can use to set properties + and invoke methods on \l{QObject}s when the state is entered or + exited. It also contains convenience functions for adding + transitions, e.g., \l{QSignalTransition}s as in this example. See + the QState class description for further details. + + If an error is encountered, the machine will enter the + \l{errorState}{error state}, which is a special state created by + the machine. The types of errors possible are described by the + \l{QStateMachine::}{Error} enum. After the error state is entered, + the type of the error can be retrieved with error(). The execution + of the state graph will not stop when the error state is entered. + So it is possible to handle the error, for instance, by connecting + to the \l{QAbstractState::}{entered()} signal of the error state. + It is also possible to set a custom error state with + setErrorState(). + + \omit This stuff will be moved elsewhere +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 + assignments and method invocations are the actions. + + The postEvent() function posts an event to the state machine. This is useful + when you are using custom events to trigger transitions. + \endomit + + \sa QAbstractState, QAbstractTransition, QState, {The State Machine Framework} +*/ + +/*! + \property QStateMachine::rootState + + \brief the root state of this state machine +*/ + +/*! + \property QStateMachine::initialState + + \brief the initial state of this state machine + + The initial state must be one of the rootState()'s child states. +*/ + +/*! + \property QStateMachine::errorState + + \brief the error state of this state machine +*/ + +/*! + \property QStateMachine::errorString + + \brief the error string of this state machine +*/ + +/*! + \property QStateMachine::globalRestorePolicy + + \brief the restore policy for states of this state machine. + + The default value of this property is + QStateMachine::DoNotRestoreProperties. +*/ + +#ifndef QT_NO_ANIMATION +/*! + \property QStateMachine::animationsEnabled + + \brief whether animations are enabled + + The default value of this property is true. + + \sa QAbstractTransition::addAnimation() +*/ +#endif + +// #define QSTATEMACHINE_DEBUG + +QStateMachinePrivate::QStateMachinePrivate() +{ + state = NotRunning; + processing = false; + processingScheduled = false; + stop = false; + error = QStateMachine::NoError; + globalRestorePolicy = QStateMachine::DoNotRestoreProperties; + rootState = 0; + initialErrorStateForRoot = 0; + signalEventGenerator = 0; +#ifndef QT_NO_ANIMATION + animationsEnabled = true; +#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; +} + +static int indexOfDescendant(QState *s, QAbstractState *desc) +{ + QList<QAbstractState*> childStates = QStatePrivate::get(s)->childStates(); + for (int i = 0; i < childStates.size(); ++i) { + QAbstractState *c = childStates.at(i); + if ((c == desc) || QStateMachinePrivate::isDescendantOf(desc, c)) { + return i; + } + } + return -1; +} + +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 { + QState *lca = findLCA(QList<QAbstractState*>() << s1 << s2); + Q_ASSERT(lca != 0); + return (indexOfDescendant(lca, s1) < indexOfDescendant(lca, 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 { + QState *lca = findLCA(QList<QAbstractState*>() << s1 << s2); + Q_ASSERT(lca != 0); + return (indexOfDescendant(lca, s1) < indexOfDescendant(lca, s2)); + } +} + +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(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions) +{ +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": begin microstep( enabledTransitions:" << enabledTransitions << ')'; + qDebug() << q_func() << ": configuration before exiting states:" << configuration; +#endif + QList<QAbstractState*> exitedStates = exitStates(event, enabledTransitions); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": configuration after exiting states:" << configuration; +#endif + executeTransitionContent(event, enabledTransitions); + QList<QAbstractState*> enteredStates = enterStates(event, enabledTransitions); + applyProperties(enabledTransitions, exitedStates, enteredStates); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": configuration after entering states:" << configuration; + qDebug() << q_func() << ": end microstep"; +#endif +} + +QList<QAbstractState*> QStateMachinePrivate::exitStates(QEvent *event, 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); + if (lca == 0) { + setError(QStateMachine::NoCommonAncestorForTransitionError, t->sourceState()); + lst = pendingErrorStates.toList(); + lst.prepend(t->sourceState()); + + lca = findLCA(lst); + Q_ASSERT(lca != 0); + } + + { + 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 == QHistoryState::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 == QHistoryState::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(event); + configuration.remove(s); + QAbstractStatePrivate::get(s)->emitExited(); + } + return statesToExit_sorted; +} + +void QStateMachinePrivate::executeTransitionContent(QEvent *event, 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(event); + } +} + +QList<QAbstractState*> QStateMachinePrivate::enterStates(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions) +{ +#ifdef QSTATEMACHINE_DEBUG + Q_Q(QStateMachine); +#endif +// qDebug() << "enterStates(" << enabledTransitions << ')'; + QSet<QAbstractState*> statesToEnter; + QSet<QAbstractState*> statesForDefaultEntry; + + if (pendingErrorStates.isEmpty()) { + 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); + addStatesToEnter(s, lca, statesToEnter, statesForDefaultEntry); + if (isParallel(lca)) { + QList<QAbstractState*> lcac = QStatePrivate::get(lca)->childStates(); + foreach (QAbstractState* child,lcac) { + if (!statesToEnter.contains(child)) + addStatesToEnter(child,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(event); + QAbstractStatePrivate::get(s)->emitEntered(); + 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 << ": emitting finished signal for" << parent; +#endif + QStatePrivate::get(parent)->emitFinished(); + 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 << ": emitting finished signal for" << grandparent; +#endif + QStatePrivate::get(grandparent)->emitFinished(); + } + } + } + } + } + { + 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) +{ + 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, root, statesToEnter, statesForDefaultEntry); + } + #ifdef QSTATEMACHINE_DEBUG + qDebug() <<q_func() << ": restoring" + << ((QHistoryStatePrivate::get(h)->historyType == QHistoryState::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::NoDefaultStateInHistoryStateError, h); + } else { + for (int k = 0; k < hlst.size(); ++k) { + QAbstractState *s0 = hlst.at(k); + addStatesToEnter(s0, root, statesToEnter, statesForDefaultEntry); + } + #ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": initial history targets for" << s << ':' << hlst; + #endif + } + } + } else { + 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*> &exitedStates, + const QList<QAbstractState*> &enteredStates) +{ +#ifdef QT_NO_ANIMATION + Q_UNUSED(transitionList); + Q_UNUSED(exitedStates); +#else + Q_Q(QStateMachine); +#endif + // Process the property assignments of the entered states. + QHash<QAbstractState*, QList<QPropertyAssignment> > propertyAssignmentsForState; + QHash<RestorableId, QVariant> pendingRestorables = registeredRestorables; + for (int i = 0; i < enteredStates.size(); ++i) { + QState *s = qobject_cast<QState*>(enteredStates.at(i)); + if (!s) + continue; + + QList<QPropertyAssignment> assignments = QStatePrivate::get(s)->propertyAssignments; + for (int j = 0; j < assignments.size(); ++j) { + const QPropertyAssignment &assn = assignments.at(j); + if (globalRestorePolicy == QStateMachine::RestoreProperties) { + registerRestorable(assn.object, assn.propertyName); + } + pendingRestorables.remove(RestorableId(assn.object, assn.propertyName)); + propertyAssignmentsForState[s].append(assn); + } + + // Remove pending restorables for all parent states to avoid restoring properties + // before the state that assigned them is exited. If state does not explicitly + // assign a property which is assigned by the parent, it inherits the parent's assignment. + QState *parentState = s; + while ((parentState = parentState->parentState()) != 0) { + assignments = QStatePrivate::get(parentState)->propertyAssignments; + for (int j=0; j<assignments.size(); ++j) { + const QPropertyAssignment &assn = assignments.at(j); + int c = pendingRestorables.remove(RestorableId(assn.object, assn.propertyName)); + if (c > 0) + propertyAssignmentsForState[s].append(assn); + } + } + } + if (!pendingRestorables.isEmpty()) { + QAbstractState *s; + if (!enteredStates.isEmpty()) + s = enteredStates.last(); // ### handle if parallel + else + s = 0; + propertyAssignmentsForState[s] << restorablesToPropertyList(pendingRestorables); + } + +#ifndef QT_NO_ANIMATION + // Gracefully terminate playing animations for states that are exited. + for (int i = 0; i < exitedStates.size(); ++i) { + QAbstractState *s = exitedStates.at(i); + QList<QAbstractAnimation*> animations = animationsForState.take(s); + for (int j = 0; j < animations.size(); ++j) { + QAbstractAnimation *anim = animations.at(j); + QObject::disconnect(anim, SIGNAL(finished()), q, SLOT(_q_animationFinished())); + stateForAnimation.remove(anim); + + // Stop the (top-level) animation. + // ### Stopping nested animation has weird behavior. + QAbstractAnimation *topLevelAnim = anim; + while (QAnimationGroup *group = topLevelAnim->group()) + topLevelAnim = group; + topLevelAnim->stop(); + + if (resetAnimationEndValues.contains(anim)) { + qobject_cast<QVariantAnimation*>(anim)->setEndValue(QVariant()); // ### generalize + resetAnimationEndValues.remove(anim); + } + QPropertyAssignment assn = propertyForAnimation.take(anim); + Q_ASSERT(assn.object != 0); + // If there is no property assignment that sets this property, + // set the property to its target value. + bool found = false; + QHash<QAbstractState*, QList<QPropertyAssignment> >::const_iterator it; + for (it = propertyAssignmentsForState.constBegin(); it != propertyAssignmentsForState.constEnd(); ++it) { + const QList<QPropertyAssignment> &assignments = it.value(); + for (int k = 0; k < assignments.size(); ++k) { + if ((assignments.at(k).object == assn.object) + && (assignments.at(k).propertyName == assn.propertyName)) { + found = true; + break; + } + } + } + if (!found) { + assn.object->setProperty(assn.propertyName, assn.value); + } + } + } + + // Find the animations to use for the state change. + QList<QAbstractAnimation*> selectedAnimations; + if (animationsEnabled) { + for (int i = 0; i < transitionList.size(); ++i) { + QAbstractTransition *transition = transitionList.at(i); + + selectedAnimations << transition->animations(); + selectedAnimations << defaultAnimationsForSource.values(transition->sourceState()); + + QList<QAbstractState *> targetStates = transition->targetStates(); + for (int j=0; j<targetStates.size(); ++j) + selectedAnimations << defaultAnimationsForTarget.values(targetStates.at(j)); + } + selectedAnimations << defaultAnimations; + } + + // Initialize animations from property assignments. + for (int i = 0; i < selectedAnimations.size(); ++i) { + QAbstractAnimation *anim = selectedAnimations.at(i); + QHash<QAbstractState*, QList<QPropertyAssignment> >::iterator it; + for (it = propertyAssignmentsForState.begin(); it != propertyAssignmentsForState.end(); ) { + QList<QPropertyAssignment>::iterator it2; + QAbstractState *s = it.key(); + QList<QPropertyAssignment> &assignments = it.value(); + for (it2 = assignments.begin(); it2 != assignments.end(); ) { + QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> > ret; + ret = initializeAnimation(anim, *it2); + QList<QAbstractAnimation*> handlers = ret.first; + if (!handlers.isEmpty()) { + for (int j = 0; j < handlers.size(); ++j) { + QAbstractAnimation *a = handlers.at(j); + propertyForAnimation.insert(a, *it2); + stateForAnimation.insert(a, s); + animationsForState[s].append(a); + // ### connect to just the top-level animation? + QObject::connect(a, SIGNAL(finished()), q, SLOT(_q_animationFinished()), Qt::UniqueConnection); + } + it2 = assignments.erase(it2); + } else { + ++it2; + } + for (int j = 0; j < ret.second.size(); ++j) + resetAnimationEndValues.insert(ret.second.at(j)); + } + if (assignments.isEmpty()) + it = propertyAssignmentsForState.erase(it); + else + ++it; + } + // 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) { + anim->start(); + } + } +#endif // !QT_NO_ANIMATION + + // Immediately set the properties that are not animated. + { + QHash<QAbstractState*, QList<QPropertyAssignment> >::const_iterator it; + for (it = propertyAssignmentsForState.constBegin(); it != propertyAssignmentsForState.constEnd(); ++it) { + const QList<QPropertyAssignment> &assignments = it.value(); + for (int i = 0; i < assignments.size(); ++i) { + const QPropertyAssignment &assn = assignments.at(i); + assn.object->setProperty(assn.propertyName, assn.value); + } + } + } + + // Emit polished signal for entered states that have no animated properties. + for (int i = 0; i < enteredStates.size(); ++i) { + QState *s = qobject_cast<QState*>(enteredStates.at(i)); + if (s +#ifndef QT_NO_ANIMATION + && !animationsForState.contains(s) +#endif + ) + QStatePrivate::get(s)->emitPolished(); + } +} + +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)->childMode == QState::ParallelStates); +} + +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)->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(context->parentState()); + + 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::NoDefaultStateInHistoryStateError: + Q_ASSERT(currentContext != 0); + + errorString = QStateMachine::tr("Missing default state in history state '%1'") + .arg(currentContext->objectName()); + break; + + case QStateMachine::NoCommonAncestorForTransitionError: + Q_ASSERT(currentContext != 0); + + errorString = QStateMachine::tr("No common ancestor for targets and source of transition from 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; + } + + Q_ASSERT(currentErrorState != 0); + Q_ASSERT(currentErrorState != rootState); + + 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()) { + + // 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); +} + +void QStateMachinePrivate::_q_animationFinished() +{ + Q_Q(QStateMachine); + QAbstractAnimation *anim = qobject_cast<QAbstractAnimation*>(q->sender()); + Q_ASSERT(anim != 0); + QObject::disconnect(anim, SIGNAL(finished()), q, SLOT(_q_animationFinished())); + if (resetAnimationEndValues.contains(anim)) { + qobject_cast<QVariantAnimation*>(anim)->setEndValue(QVariant()); // ### generalize + resetAnimationEndValues.remove(anim); + } + + // Set the final property value. + QPropertyAssignment assn = propertyForAnimation.take(anim); + Q_ASSERT(assn.object != 0); + assn.object->setProperty(assn.propertyName, assn.value); + if (!assn.explicitlySet) + unregisterRestorable(assn.object, assn.propertyName); + + QAbstractState *state = stateForAnimation.take(anim); + Q_ASSERT(state != 0); + QHash<QAbstractState*, QList<QAbstractAnimation*> >::iterator it; + it = animationsForState.find(state); + Q_ASSERT(it != animationsForState.end()); + QList<QAbstractAnimation*> &animations = it.value(); + animations.removeOne(anim); + if (animations.isEmpty()) { + animationsForState.erase(it); + QStatePrivate::get(qobject_cast<QState*>(state))->emitPolished(); + } +} + +#endif // !QT_NO_ANIMATION + +namespace { + +class StartState : public QState +{ +public: + StartState(QState *parent) + : QState(parent) {} +protected: + void onEntry(QEvent *) {} + void onExit(QEvent *) {} +}; + +class InitialTransition : public QAbstractTransition +{ +public: + InitialTransition(QAbstractState *target) + : QAbstractTransition(QList<QAbstractState*>() << target) {} +protected: + virtual bool eventTest(QEvent *) { return true; } + virtual void onTransition(QEvent *) {} +}; + +} // 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); + QEvent nullEvent(QEvent::None); + executeTransitionContent(&nullEvent, transitions); + enterStates(&nullEvent, transitions); + applyProperties(transitions, QList<QAbstractState*>() << start, + 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(e, 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; + unregisterAllTransitions(); + emit q->finished(); + break; + case Stopped: + state = NotRunning; + unregisterAllTransitions(); + 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 +} + +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; + } + QVector<int> &connectedSignalIndexes = connections[sender]; + if (connectedSignalIndexes.size() <= signalIndex) + connectedSignalIndexes.resize(signalIndex+1); + if (connectedSignalIndexes.at(signalIndex) == 0) { + if (!signalEventGenerator) + signalEventGenerator = new QSignalEventGenerator(q); + 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 + << ", targets =" << transition->targetStates() << ')'; +#endif + return; + } + } + ++connectedSignalIndexes[signalIndex]; + QSignalTransitionPrivate::get(transition)->signalIndex = signalIndex; +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": added signal transition from" << transition->sourceState() + << ": ( sender =" << sender << ", signal =" << signal + << ", targets =" << transition->targetStates() << ')'; +#endif +} + +void QStateMachinePrivate::unregisterSignalTransition(QSignalTransition *transition) +{ + int signalIndex = QSignalTransitionPrivate::get(transition)->signalIndex; + if (signalIndex == -1) + return; // not registered + QSignalTransitionPrivate::get(transition)->signalIndex = -1; + const QObject *sender = QSignalTransitionPrivate::get(transition)->sender; + QVector<int> &connectedSignalIndexes = connections[sender]; + Q_ASSERT(connectedSignalIndexes.size() > signalIndex); + Q_ASSERT(connectedSignalIndexes.at(signalIndex) != 0); + if (--connectedSignalIndexes[signalIndex] == 0) { + Q_ASSERT(signalEventGenerator != 0); + QMetaObject::disconnect(sender, signalIndex, signalEventGenerator, + signalEventGenerator->metaObject()->methodOffset()); + int sum = 0; + for (int i = 0; i < connectedSignalIndexes.size(); ++i) + sum += connectedSignalIndexes.at(i); + if (sum == 0) + connections.remove(sender); + } +} + +void QStateMachinePrivate::unregisterAllTransitions() +{ + { + QList<QSignalTransition*> transitions = qFindChildren<QSignalTransition*>(rootState); + for (int i = 0; i < transitions.size(); ++i) + unregisterSignalTransition(transitions.at(i)); + } + { + QList<QEventTransition*> transitions = qFindChildren<QEventTransition*>(rootState); + for (int i = 0; i < transitions.size(); ++i) + unregisterEventTransition(transitions.at(i)); + } +} + +#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; + QObjectPrivate *od = QObjectPrivate::get(object); + if (!od->eventFilters.contains(q)) + object->installEventFilter(q); + ++qobjectEvents[object][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; + QHash<QEvent::Type, int> &events = qobjectEvents[object]; + Q_ASSERT(events.value(transition->eventType()) > 0); + if (--events[transition->eventType()] == 0) { + events.remove(transition->eventType()); + int sum = 0; + QHash<QEvent::Type, int>::const_iterator it; + for (it = events.constBegin(); it != events.constEnd(); ++it) + sum += it.value(); + if (sum == 0) { + qobjectEvents.remove(object); + object->removeEventFilter(q); + } + } + QEventTransitionPrivate::get(transition)->registered = false; +} +#endif + +void QStateMachinePrivate::handleTransitionSignal(const QObject *sender, int signalIndex, + void **argv) +{ + Q_ASSERT(connections[sender].at(signalIndex) != 0); + 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(*new QStateMachinePrivate, parent) +{ +} + +/*! + \internal +*/ +QStateMachine::QStateMachine(QStateMachinePrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + Destroys this state machine. +*/ +QStateMachine::~QStateMachine() +{ +} + +namespace { + +class RootErrorState : public QAbstractState +{ +public: + RootErrorState(QState *parent) + : QAbstractState(parent) + { + setObjectName(QString::fromLatin1("DefaultErrorState")); + } + + void onEntry(QEvent *) + { + QAbstractStatePrivate *d = QAbstractStatePrivate::get(this); + QStateMachine *machine = d->machine(); + + qWarning("Unrecoverable error detected in running state machine: %s", + qPrintable(machine->errorString())); + } + + void onExit(QEvent *) {} +}; + +class RootState : public QState +{ +public: + RootState(QState *parent) + : QState(parent) + { + } + + void onEntry(QEvent *) {} + void onExit(QEvent *) {} +}; + +} // 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 NoDefaultStateInHistoryStateError 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. + \value NoCommonAncestorForTransitionError The machine has selected a transition whose source + and targets are not part of the same tree of states, and thus are not part of the same + state machine. Commonly, this could mean that one of the states has not been given + any parent or added to any machine. The context of this error is the source state of + the transition. + + \sa setErrorState() +*/ + +/*! + \enum QStateMachine::RestorePolicy + + This enum specifies the restore policy type. The restore policy + takes effect when the machine enters a state which sets one or more + properties. If the restore policy 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 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. + + \value DoNotRestoreProperties The state machine should not save the initial values of properties + and restore them later. + \value RestoreProperties The state machine should save the initial values of properties + and restore them later. + + \sa QStateMachine::globalRestorePolicy QState::assignProperty() +*/ + + +/*! + 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 restore policy of the state machine. + + \sa setGlobalRestorePolicy() +*/ +QStateMachine::RestorePolicy QStateMachine::globalRestorePolicy() const +{ + Q_D(const QStateMachine); + return d->globalRestorePolicy; +} + +/*! + Sets the restore policy of the state machine to \a restorePolicy. The default + restore policy is QAbstractState::DoNotRestoreProperties. + + \sa globalRestorePolicy() +*/ +void QStateMachine::setGlobalRestorePolicy(QStateMachine::RestorePolicy restorePolicy) +{ + Q_D(QStateMachine); + 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(), setInitialState() +*/ +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); +} + +/*! + Returns whether this state machine is running. + + start(), stop() +*/ +bool QStateMachine::isRunning() const +{ + Q_D(const QStateMachine); + return (d->state == QStateMachinePrivate::Running); +} + +/*! + Starts this state machine. The machine will reset its configuration and + transition to the initial state. When a final top-level state (QFinalState) + 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. The state machine will stop processing events and + then emit the stopped() signal. + + \sa stopped(), start() +*/ +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(); +} + +/*! + \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 + (QStateMachine::initialState). + + \sa QStateMachine::finished(), QStateMachine::start() +*/ + +/*! + \fn QStateMachine::finished() + + This signal is emitted when the state machine has reached a top-level final + state (QFinalState). + + \sa QStateMachine::started() +*/ + +/*! + \fn QStateMachine::stopped() + + This signal is emitted when the state machine has stopped. + + \sa QStateMachine::stop(), QStateMachine::finished() +*/ + +/*! + \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 QWrappedEvent(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); +} + +#ifndef QT_NO_ANIMATION + +/*! + Returns whether animations are enabled for this state machine. +*/ +bool QStateMachine::animationsEnabled() const +{ + Q_D(const QStateMachine); + return d->animationsEnabled; +} + +/*! + Sets whether animations are \a enabled for this state machine. +*/ +void QStateMachine::setAnimationsEnabled(bool enabled) +{ + Q_D(QStateMachine); + d->animationsEnabled = enabled; +} + +/*! + Adds a default \a animation to be considered for any transition. +*/ +void QStateMachine::addDefaultAnimation(QAbstractAnimation *animation) +{ + Q_D(QStateMachine); + d->defaultAnimations.append(animation); +} + +/*! + Returns the list of default animations that will be considered for any transition. +*/ +QList<QAbstractAnimation*> QStateMachine::defaultAnimations() const +{ + Q_D(const QStateMachine); + return d->defaultAnimations; +} + +/*! + Removes \a animation from the list of default animations. +*/ +void QStateMachine::removeDefaultAnimation(QAbstractAnimation *animation) +{ + Q_D(QStateMachine); + d->defaultAnimations.removeAll(animation); +} + +#endif // QT_NO_ANIMATION + + +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: { +// ### in Qt 4.6 we can use QObject::senderSignalIndex() + QObjectPrivate *d = static_cast<QObjectPrivate *>(d_ptr); + int signalIndex = -1; + QObject *sender = this->sender(); + if (sender && d->currentSender) + signalIndex = d->currentSender->signal; + + Q_ASSERT(signalIndex != -1); + QStateMachine *machine = qobject_cast<QStateMachine*>(parent()); + QStateMachinePrivate::get(machine)->handleTransitionSignal(sender, signalIndex, _a); + break; + } + default: ; + } + _id -= 1; + } + return _id; +} + +QSignalEventGenerator::QSignalEventGenerator(QStateMachine *parent) + : QObject(parent) +{ +} + +/*! + \class QSignalEvent + + \brief The QSignalEvent class represents a Qt signal event. + + \since 4.6 + \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 +*/ + +/*! + \internal + + 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) + : QEvent(QEvent::Signal), 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(), QMetaObject::method() +*/ + +/*! + \fn QSignalEvent::arguments() const + + Returns the arguments of the signal. +*/ + + +/*! + \class QWrappedEvent + + \brief The QWrappedEvent class holds a clone of an event associated with a QObject. + + \since 4.6 + \ingroup statemachine + + A wrapped event is generated by a QStateMachine in response to a Qt + event. The QEventTransition class provides a transition associated with a + such an event. QWrappedEvent is part of \l{The State Machine Framework}. + + The object() function returns the object that generated the event. The + event() function returns a clone of the original event. + + \sa QEventTransition +*/ + +/*! + \internal + + Constructs a new QWrappedEvent object with the given \a object + and \a event. +*/ +QWrappedEvent::QWrappedEvent(QObject *object, QEvent *event) + : QEvent(QEvent::Wrapped), m_object(object), m_event(event) +{ +} + +/*! + Destroys this QWrappedEvent. +*/ +QWrappedEvent::~QWrappedEvent() +{ +} + +/*! + \fn QWrappedEvent::object() const + + Returns the object that the event is associated with. +*/ + +/*! + \fn QWrappedEvent::event() const + + Returns a clone of the original event. +*/ + +QT_END_NAMESPACE + +#include "moc_qstatemachine.cpp" + +#endif //QT_NO_STATEMACHINE diff --git a/src/corelib/statemachine/qstatemachine.h b/src/corelib/statemachine/qstatemachine.h new file mode 100644 index 0000000..0b3c728 --- /dev/null +++ b/src/corelib/statemachine/qstatemachine.h @@ -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$ +** +****************************************************************************/ + +#ifndef QSTATEMACHINE_H +#define QSTATEMACHINE_H + +#include <QtCore/qabstractstate.h> + +#include <QtCore/qlist.h> +#include <QtCore/qobject.h> +#include <QtCore/qset.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_STATEMACHINE + +class QEvent; +class QAbstractState; +class QState; + +class QStateMachinePrivate; +class QAbstractAnimation; +class QAbstractState; +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) + Q_PROPERTY(RestorePolicy globalRestorePolicy READ globalRestorePolicy WRITE setGlobalRestorePolicy) + Q_ENUMS(RestorePolicy) +#ifndef QT_NO_ANIMATION + Q_PROPERTY(bool animationsEnabled READ animationsEnabled WRITE setAnimationsEnabled) +#endif +public: + enum RestorePolicy { + DoNotRestoreProperties, + RestoreProperties + }; + + enum Error { + NoError, + NoInitialStateError, + NoDefaultStateInHistoryStateError, + NoCommonAncestorForTransitionError + }; + + 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(); + + bool isRunning() const; + +#ifndef QT_NO_ANIMATION + bool animationsEnabled() const; + void setAnimationsEnabled(bool enabled); + + void addDefaultAnimation(QAbstractAnimation *animation); + QList<QAbstractAnimation *> defaultAnimations() const; + void removeDefaultAnimation(QAbstractAnimation *animation); +#endif // QT_NO_ANIMATION + + QStateMachine::RestorePolicy globalRestorePolicy() const; + void setGlobalRestorePolicy(QStateMachine::RestorePolicy restorePolicy); + + void postEvent(QEvent *event, int delay = 0); + + 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(); + +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: + 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 +}; + +#endif //QT_NO_STATEMACHINE + +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..24073ca --- /dev/null +++ b/src/corelib/statemachine/qstatemachine_p.h @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** 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. +// + +#include <private/qobject_p.h> +#include <QtCore/qcoreevent.h> +#include <QtCore/qhash.h> +#include <QtCore/qlist.h> +#include <QtCore/qpair.h> +#include <QtCore/qset.h> +#include <QtCore/qvector.h> + +#include "qstate.h" +#include "private/qstate_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 QStateMachinePrivate : public QObjectPrivate +{ + 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(QEvent *event, const QList<QAbstractTransition*> &transitionList); + bool isPreempted(const QAbstractState *s, const QSet<QAbstractTransition*> &transitions) const; + QSet<QAbstractTransition*> selectTransitions(QEvent *event) const; + QList<QAbstractState*> exitStates(QEvent *event, const QList<QAbstractTransition*> &transitionList); + void executeTransitionContent(QEvent *event, const QList<QAbstractTransition*> &transitionList); + QList<QAbstractState*> enterStates(QEvent *event, 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*> &exitedStates, + 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 unregisterAllTransitions(); + 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; + QStateMachine::RestorePolicy globalRestorePolicy; + + QString errorString; + QSet<QAbstractState *> pendingErrorStates; + QSet<QAbstractState *> pendingErrorStatesForDefaultEntry; + QAbstractState *initialErrorStateForRoot; + +#ifndef QT_NO_ANIMATION + bool animationsEnabled; + + QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> > + initializeAnimation(QAbstractAnimation *abstractAnimation, + const QPropertyAssignment &prop); + + QHash<QAbstractState*, QList<QAbstractAnimation*> > animationsForState; + QHash<QAbstractAnimation*, QPropertyAssignment> propertyForAnimation; + QHash<QAbstractAnimation*, QAbstractState*> stateForAnimation; + QSet<QAbstractAnimation*> resetAnimationEndValues; + + QList<QAbstractAnimation *> defaultAnimations; + QMultiHash<QAbstractState *, QAbstractAnimation *> defaultAnimationsForSource; + QMultiHash<QAbstractState *, QAbstractAnimation *> defaultAnimationsForTarget; + +#endif // QT_NO_ANIMATION + + QSignalEventGenerator *signalEventGenerator; + + QHash<const QObject*, QVector<int> > connections; +#ifndef QT_NO_STATEMACHINE_EVENTFILTER + QHash<QObject*, QHash<QEvent::Type, int> > qobjectEvents; +#endif + QHash<int, QEvent*> delayedEvents; + + typedef QEvent* (*f_cloneEvent)(QEvent*); + struct Handler { + f_cloneEvent cloneEvent; + }; + + static Q_CORE_EXPORT const Handler *handler; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qwrappedevent.h b/src/corelib/statemachine/qwrappedevent.h new file mode 100644 index 0000000..cb4261b --- /dev/null +++ b/src/corelib/statemachine/qwrappedevent.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 QWRAPPEDEVENT_H +#define QWRAPPEDEVENT_H + +#include <QtCore/qcoreevent.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_STATEMACHINE + +class QObject; + +class Q_CORE_EXPORT QWrappedEvent : public QEvent +{ +public: + QWrappedEvent(QObject *object, QEvent *event); + ~QWrappedEvent(); + + inline QObject *object() const { return m_object; } + inline QEvent *event() const { return m_event; } + +private: + QObject *m_object; + QEvent *m_event; + +private: + Q_DISABLE_COPY(QWrappedEvent) +}; + +#endif //QT_NO_STATEMACHINE + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/statemachine.pri b/src/corelib/statemachine/statemachine.pri new file mode 100644 index 0000000..5b19bc1 --- /dev/null +++ b/src/corelib/statemachine/statemachine.pri @@ -0,0 +1,30 @@ +HEADERS += $$PWD/qstatemachine.h \ + $$PWD/qstatemachine_p.h \ + $$PWD/qsignaleventgenerator_p.h \ + $$PWD/qabstractstate.h \ + $$PWD/qabstractstate_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/qsignalevent.h \ + $$PWD/qsignaltransition.h \ + $$PWD/qsignaltransition_p.h + +SOURCES += $$PWD/qstatemachine.cpp \ + $$PWD/qabstractstate.cpp \ + $$PWD/qstate.cpp \ + $$PWD/qfinalstate.cpp \ + $$PWD/qhistorystate.cpp \ + $$PWD/qabstracttransition.cpp \ + $$PWD/qsignaltransition.cpp + +!contains(DEFINES, QT_NO_STATEMACHINE_EVENTFILTER) { +HEADERS += $$PWD/qwrappedevent.h \ + $$PWD/qeventtransition.h \ + $$PWD/qeventtransition_p.h +SOURCES += $$PWD/qeventtransition.cpp +} diff --git a/src/corelib/thread/qmutexpool.cpp b/src/corelib/thread/qmutexpool.cpp index 96a9940..0d7c890 100644 --- a/src/corelib/thread/qmutexpool.cpp +++ b/src/corelib/thread/qmutexpool.cpp @@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE // qt_global_mutexpool is here for backwards compatability only, // use QMutexpool::instance() in new clode. Q_CORE_EXPORT QMutexPool *qt_global_mutexpool = 0; -Q_GLOBAL_STATIC_WITH_ARGS(QMutexPool, globalMutexPool, (true)) +Q_GLOBAL_STATIC_WITH_ARGS(QMutexPool, globalMutexPool, (QMutex::Recursive)) /*! \class QMutexPool @@ -88,17 +88,17 @@ Q_GLOBAL_STATIC_WITH_ARGS(QMutexPool, globalMutexPool, (true)) */ /*! - Constructs a QMutexPool, reserving space for \a size QMutexes. If - \a recursive is true, all QMutexes in the pool will be recursive - mutexes; otherwise they will all be non-recursive (the default). + Constructs a QMutexPool, reserving space for \a size QMutexes. All + mutexes in the pool are created with \a recursionMode. By default, + all mutexes are non-recursive. The QMutexes are created when needed, and deleted when the QMutexPool is destructed. */ -QMutexPool::QMutexPool(bool recursive, int size) - : mutexes(size), count(size), recurs(recursive) +QMutexPool::QMutexPool(QMutex::RecursionMode recursionMode, int size) + : mutexes(size), recursionMode(recursionMode) { - for (int index = 0; index < count; ++index) { + for (int index = 0; index < mutexes.count(); ++index) { mutexes[index] = 0; } } @@ -109,7 +109,7 @@ QMutexPool::QMutexPool(bool recursive, int size) */ QMutexPool::~QMutexPool() { - for (int index = 0; index < count; ++index) { + for (int index = 0; index < mutexes.count(); ++index) { delete mutexes[index]; mutexes[index] = 0; } @@ -130,11 +130,11 @@ QMutexPool *QMutexPool::instance() QMutex *QMutexPool::get(const void *address) { Q_ASSERT_X(address != 0, "QMutexPool::get()", "'address' argument cannot be zero"); - int index = int((quintptr(address) >> (sizeof(address) >> 1)) % count); + int index = int((quintptr(address) >> (sizeof(address) >> 1)) % mutexes.count()); if (!mutexes[index]) { // mutex not created, create one - QMutex *newMutex = new QMutex(recurs ? QMutex::Recursive : QMutex::NonRecursive); + QMutex *newMutex = new QMutex(recursionMode); if (!mutexes[index].testAndSetOrdered(0, newMutex)) delete newMutex; } diff --git a/src/corelib/thread/qmutexpool_p.h b/src/corelib/thread/qmutexpool_p.h index 1009ebb..30a16d4 100644 --- a/src/corelib/thread/qmutexpool_p.h +++ b/src/corelib/thread/qmutexpool_p.h @@ -64,7 +64,7 @@ QT_BEGIN_NAMESPACE class Q_CORE_EXPORT QMutexPool { public: - explicit QMutexPool(bool recursive = false, int size = 128); + explicit QMutexPool(QMutex::RecursionMode recursionMode = QMutex::NonRecursive, int size = 131); ~QMutexPool(); QMutex *get(const void *address); @@ -72,9 +72,8 @@ public: static QMutex *globalInstanceGet(const void *address); private: - QVarLengthArray<QAtomicPointer<QMutex>, 128> mutexes; - int count; - bool recurs; + QVarLengthArray<QAtomicPointer<QMutex>, 131> mutexes; + QMutex::RecursionMode recursionMode; }; extern Q_CORE_EXPORT QMutexPool *qt_global_mutexpool; diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index f1bdc64..a2b944d 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -113,12 +113,6 @@ QThreadData::~QThreadData() // fprintf(stderr, "QThreadData %p destroyed\n", this); } -QThreadData *QThreadData::get2(QThread *thread) -{ - Q_ASSERT_X(thread != 0, "QThread", "internal error"); - return thread->d_func()->data; -} - void QThreadData::ref() { #ifndef QT_NO_THREAD diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h index 0bf773c..c575005 100644 --- a/src/corelib/thread/qthread_p.h +++ b/src/corelib/thread/qthread_p.h @@ -111,51 +111,6 @@ public: { } }; -class Q_CORE_EXPORT QThreadData -{ - QAtomicInt _ref; - -public: - QThreadData(int initialRefCount = 1); - ~QThreadData(); - - static QThreadData *current(); - static QThreadData *get2(QThread *thread); - - void ref(); - void deref(); - - QThread *thread; - bool quitNow; - int loopLevel; - QAbstractEventDispatcher *eventDispatcher; - QStack<QEventLoop *> eventLoops; - QPostEventList postEventList; - bool canWait; - QMap<int, void *> tls; - - QMutex mutex; - -# ifdef Q_OS_SYMBIAN - RThread symbian_thread_handle; -# endif -}; - -// thread wrapper for the main() thread -class QAdoptedThread : public QThread -{ - Q_DECLARE_PRIVATE(QThread) - -public: - QAdoptedThread(QThreadData *data = 0); - ~QAdoptedThread(); - void init(); - - static QThread *createThreadForAdoption(); -private: - void run(); -}; - #ifndef QT_NO_THREAD class QThreadPrivate : public QObjectPrivate { @@ -224,6 +179,38 @@ public: #endif // QT_NO_THREAD +class QThreadData +{ + QAtomicInt _ref; + +public: + QThreadData(int initialRefCount = 1); + ~QThreadData(); + + static QThreadData *current(); + static QThreadData *get2(QThread *thread) + { Q_ASSERT_X(thread != 0, "QThread", "internal error"); return thread->d_func()->data; } + + + void ref(); + void deref(); + + QThread *thread; + bool quitNow; + int loopLevel; + QAbstractEventDispatcher *eventDispatcher; + QStack<QEventLoop *> eventLoops; + QPostEventList postEventList; + bool canWait; + QMap<int, void *> tls; + + QMutex mutex; + +# ifdef Q_OS_SYMBIAN + RThread symbian_thread_handle; +# endif +}; + QT_END_NAMESPACE #endif // QTHREAD_P_H diff --git a/src/corelib/tools/qbitarray.cpp b/src/corelib/tools/qbitarray.cpp index 7168a9a..5653842 100644 --- a/src/corelib/tools/qbitarray.cpp +++ b/src/corelib/tools/qbitarray.cpp @@ -655,6 +655,7 @@ QBitArray operator^(const QBitArray &a1, const QBitArray &a2) QBitArray stream functions *****************************************************************************/ +#ifndef QT_NO_DATASTREAM /*! \relates QBitArray @@ -662,7 +663,7 @@ QBitArray operator^(const QBitArray &a1, const QBitArray &a2) \sa \link datastreamformat.html Format of the QDataStream operators \endlink */ -#ifndef QT_NO_DATASTREAM + QDataStream &operator<<(QDataStream &out, const QBitArray &ba) { quint32 len = ba.size(); @@ -715,7 +716,7 @@ QDataStream &operator>>(QDataStream &in, QBitArray &ba) *ba.d.data() = ba.d.size() * 8 - len; return in; } -#endif +#endif // QT_NO_DATASTREAM /*! \fn DataPtr &QBitArray::data_ptr() diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index ae6561f..f73adf8 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -579,7 +579,7 @@ static inline char qToLower(char c) return c; } -Q_CORE_EXPORT QByteArray::Data QByteArray::shared_null = {Q_BASIC_ATOMIC_INITIALIZER(1), +QByteArray::Data QByteArray::shared_null = {Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, shared_null.array, {0} }; QByteArray::Data QByteArray::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, shared_empty.array, {0} }; @@ -2581,6 +2581,8 @@ void QByteArray::clear() d->ref.ref(); } +#ifndef QT_NO_DATASTREAM + /*! \relates QByteArray Writes byte array \a ba to the stream \a out and returns a reference @@ -2588,7 +2590,6 @@ void QByteArray::clear() \sa {Format of the QDataStream operators} */ -#ifndef QT_NO_DATASTREAM QDataStream &operator<<(QDataStream &out, const QByteArray &ba) { @@ -2631,7 +2632,7 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) return in; } -#endif //QT_NO_DATASTREAM +#endif // QT_NO_DATASTREAM /*! \fn bool QByteArray::operator==(const QString &str) const diff --git a/src/corelib/tools/qchar.cpp b/src/corelib/tools/qchar.cpp index a940cda..250dad0 100644 --- a/src/corelib/tools/qchar.cpp +++ b/src/corelib/tools/qchar.cpp @@ -1321,7 +1321,7 @@ QDataStream &operator>>(QDataStream &in, QChar &chr) chr.unicode() = ushort(u); return in; } -#endif +#endif // QT_NO_DATASTREAM /*! \fn ushort & QChar::unicode() diff --git a/src/corelib/tools/qcontiguouscache.cpp b/src/corelib/tools/qcontiguouscache.cpp new file mode 100644 index 0000000..fc75cc0 --- /dev/null +++ b/src/corelib/tools/qcontiguouscache.cpp @@ -0,0 +1,432 @@ +/**************************************************************************** +** +** 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 "qcontiguouscache.h" +#ifdef QT_QCONTIGUOUSCACHE_DEBUG +#include <QDebug> +#endif + +QT_BEGIN_NAMESPACE + +#ifdef QT_QCONTIGUOUSCACHE_DEBUG +void QContiguousCacheData::dump() const +{ + qDebug() << "capacity:" << alloc; + qDebug() << "count:" << count; + qDebug() << "start:" << start; + qDebug() << "offset:" << offset; +} +#endif + +/*! \class QContiguousCache + \brief The QContiguousCache class is a template class that provides a contiguous cache. + \ingroup tools + \ingroup shared + \reentrant + + The QContiguousCache class provides an efficient way of caching items for + display in a user interface view. Unlike QCache, it adds a restriction + that elements within the cache are contiguous. This has the advantage + of matching how user interface views most commonly request data, as + a set of rows localized around the current scrolled position. This + restriction allows the cache to consume less memory and processor + cycles than QCache. The QContiguousCache class also can provide + an upper bound on memory usage via setCapacity(). + + The simplest way of using a contiguous cache is to use the append() + and prepend(). + +\code +MyRecord record(int row) const +{ + Q_ASSERT(row >= 0 && row < count()); + + while(row > cache.lastIndex()) + cache.append(slowFetchRecord(cache.lastIndex()+1)); + while(row < cache.firstIndex()) + cache.prepend(slowFetchRecord(cache.firstIndex()-1)); + + return cache.at(row); +} +\endcode + + If the cache is full then the item at the opposite end of the cache from + where the new item is appended or prepended will be removed. + + This usage can be further optimized by using the insert() function + in the case where the requested row is a long way from the currently cached + items. If there is a gap between where the new item is inserted and the currently + cached items then the existing cached items are first removed to retain + the contiguous nature of the cache. Hence it is important to take some care then + when using insert() in order to avoid unwanted clearing of the cache. + + The range of valid indexes for the QContiguousCache class are from + 0 to INT_MAX. Calling prepend() such that the first index would become less + than 0 or append() such that the last index would become greater + than INT_MAX can result in the indexes of the cache being invalid. + When the cache indexes are invalid it is important to call + normalizeIndexes() before calling any of containsIndex(), firstIndex(), + lastIndex(), at() or the [] operator. Calling these + functions when the cache has invalid indexes will result in undefined + behavior. The indexes can be checked by using areIndexesValid() + + In most cases the indexes will not exceed 0 to INT_MAX, and + normalizeIndexes() will not need to be used. + + See the \l{Contiguous Cache Example}{Contiguous Cache} example. +*/ + +/*! \fn QContiguousCache::QContiguousCache(int capacity) + + Constructs a cache with the given \a capacity. + + \sa setCapacity() +*/ + +/*! \fn QContiguousCache::QContiguousCache(const QContiguousCache<T> &other) + + Constructs a copy of \a other. + + This operation takes \l{constant time}, because QContiguousCache is + \l{implicitly shared}. This makes returning a QContiguousCache from a + function very fast. If a shared instance is modified, it will be + copied (copy-on-write), and that takes \l{linear time}. + + \sa operator=() +*/ + +/*! \fn QContiguousCache::~QContiguousCache() + + Destroys the cache. +*/ + +/*! \fn void QContiguousCache::detach() + + \internal +*/ + +/*! \fn bool QContiguousCache::isDetached() const + + \internal +*/ + +/*! \fn void QContiguousCache::setSharable(bool sharable) + + \internal +*/ + +/*! \fn QContiguousCache<T> &QContiguousCache::operator=(const QContiguousCache<T> &other) + + Assigns \a other to this cache and returns a reference to this cache. +*/ + +/*! \fn bool QContiguousCache::operator==(const QContiguousCache<T> &other) const + + Returns true if \a other is equal to this cache; otherwise returns false. + + Two caches are considered equal if they contain the same values at the same + indexes. This function requires the value type to implement the \c operator==(). + + \sa operator!=() +*/ + +/*! \fn bool QContiguousCache::operator!=(const QContiguousCache<T> &other) const + + Returns true if \a other is not equal to this cache; otherwise + returns false. + + Two caches are considered equal if they contain the same values at the same + indexes. This function requires the value type to implement the \c operator==(). + + \sa operator==() +*/ + +/*! \fn int QContiguousCache::capacity() const + + Returns the number of items the cache can store before it is full. + When a cache contains a number of items equal to its capacity, adding new + items will cause items farthest from the added item to be removed. + + \sa setCapacity(), size() +*/ + +/*! \fn int QContiguousCache::count() const + + \overload + + Same as size(). +*/ + +/*! \fn int QContiguousCache::size() const + + Returns the number of items contained within the cache. + + \sa capacity() +*/ + +/*! \fn bool QContiguousCache::isEmpty() const + + Returns true if no items are stored within the cache. + + \sa size(), capacity() +*/ + +/*! \fn bool QContiguousCache::isFull() const + + Returns true if the number of items stored within the cache is equal + to the capacity of the cache. + + \sa size(), capacity() +*/ + +/*! \fn int QContiguousCache::available() const + + Returns the number of items that can be added to the cache before it becomes full. + + \sa size(), capacity(), isFull() +*/ + +/*! \fn void QContiguousCache::clear() + + Removes all items from the cache. The capacity is unchanged. +*/ + +/*! \fn void QContiguousCache::setCapacity(int size) + + Sets the capacity of the cache to the given \a size. A cache can hold a + number of items equal to its capacity. When inserting, appending or prepending + items to the cache, if the cache is already full then the item farthest from + the added item will be removed. + + If the given \a size is smaller than the current count of items in the cache + then only the last \a size items from the cache will remain. + + \sa capacity(), isFull() +*/ + +/*! \fn const T &QContiguousCache::at(int i) const + + Returns the item at index position \a i in the cache. \a i must + be a valid index position in the cache (i.e, firstIndex() <= \a i <= lastIndex()). + + The indexes in the cache refer to the number of positions the item is from the + first item appended into the cache. That is to say a cache with a capacity of + 100, that has had 150 items appended will have a valid index range of + 50 to 149. This allows inserting and retrieving items into the cache based + on a theoretical infinite list + + \sa firstIndex(), lastIndex(), insert(), operator[]() +*/ + +/*! \fn T &QContiguousCache::operator[](int i) + + Returns the item at index position \a i as a modifiable reference. If + the cache does not contain an item at the given index position \a i + then it will first insert an empty item at that position. + + In most cases it is better to use either at() or insert(). + + Note that using non-const operators can cause QContiguousCache to do a deep + copy. + + \sa insert(), at() +*/ + +/*! \fn const T &QContiguousCache::operator[](int i) const + + \overload + + Same as at(\a i). +*/ + +/*! \fn void QContiguousCache::append(const T &value) + + Inserts \a value at the end of the cache. If the cache is already full + the item at the start of the cache will be removed. + + \sa prepend(), insert(), isFull() +*/ + +/*! \fn void QContiguousCache::prepend(const T &value) + + Inserts \a value at the start of the cache. If the cache is already full + the item at the end of the cache will be removed. + + \sa append(), insert(), isFull() +*/ + +/*! \fn void QContiguousCache::insert(int i, const T &value) + + Inserts the \a value at the index position \a i. If the cache already contains + an item at \a i then that value is replaced. If \a i is either one more than + lastIndex() or one less than firstIndex() it is the equivalent to an append() + or a prepend(). + + If the given index \a i is not within the current range of the cache nor adjacent + to the bounds of the cache's index range, the cache is first cleared before + inserting the item. At this point the cache will have a size of 1. It is + worthwhile taking effort to insert items in an order that starts adjacent + to the current index range for the cache. + + The range of valid indexes for the QContiguousCache class are from + 0 to INT_MAX. Inserting outside of this range has undefined behavior. + + + \sa prepend(), append(), isFull(), firstIndex(), lastIndex() +*/ + +/*! \fn bool QContiguousCache::containsIndex(int i) const + + Returns true if the cache's index range includes the given index \a i. + + \sa firstIndex(), lastIndex() +*/ + +/*! \fn int QContiguousCache::firstIndex() const + + Returns the first valid index in the cache. The index will be invalid if the + cache is empty. + + \sa capacity(), size(), lastIndex() +*/ + +/*! \fn int QContiguousCache::lastIndex() const + + Returns the last valid index in the cache. The index will be invalid if the cache is empty. + + \sa capacity(), size(), firstIndex() +*/ + + +/*! \fn T &QContiguousCache::first() + + Returns a reference to the first item in the cache. This function + assumes that the cache isn't empty. + + \sa last(), isEmpty() +*/ + +/*! \fn T &QContiguousCache::last() + + Returns a reference to the last item in the cache. This function + assumes that the cache isn't empty. + + \sa first(), isEmpty() +*/ + +/*! \fn const T& QContiguousCache::first() const + + \overload +*/ + +/*! \fn const T& QContiguousCache::last() const + + \overload +*/ + +/*! \fn void QContiguousCache::removeFirst() + + Removes the first item from the cache. This function assumes that + the cache isn't empty. + + \sa removeLast() +*/ + +/*! \fn void QContiguousCache::removeLast() + + Removes the last item from the cache. This function assumes that + the cache isn't empty. + + \sa removeFirst() +*/ + +/*! \fn T QContiguousCache::takeFirst() + + Removes the first item in the cache and returns it. This function + assumes that the cache isn't empty. + + If you don't use the return value, removeFirst() is more efficient. + + \sa takeLast(), removeFirst() +*/ + +/*! \fn T QContiguousCache::takeLast() + + Removes the last item in the cache and returns it. This function + assumes that the cache isn't empty. + + If you don't use the return value, removeLast() is more efficient. + + \sa takeFirst(), removeLast() +*/ + +/*! \fn void QContiguousCache::normalizeIndexes() + + Moves the first index and last index of the cache + such that they point to valid indexes. The function does not modify + the contents of the cache or the ordering of elements within the cache. + + It is provided so that index overflows can be corrected when using the + cache as a circular buffer. + + \code + QContiguousCache<int> cache(10); + cache.insert(INT_MAX, 1); // cache contains one value and has valid indexes, INT_MAX to INT_MAX + cache.append(2); // cache contains two values but does not have valid indexes. + cache.normalizeIndexes(); // cache has two values, 1 and 2. New first index will be in the range of 0 to capacity(). + \endcode + + \sa areIndexesValid(), append(), prepend() +*/ + +/*! \fn bool QContiguousCache::areIndexesValid() const + + Returns whether the indexes for items stored in the cache are valid. + Indexes can become invalid if items are appended after the index position + INT_MAX or prepended before the index position 0. This is only expected + to occur in very long lived circular buffer style usage of the + contiguous cache. Indexes can be made valid again by calling + normalizeIndexs(). + + \sa normalizeIndexes(), append(), prepend() +*/ + +QT_END_NAMESPACE diff --git a/src/corelib/tools/qcontiguouscache.h b/src/corelib/tools/qcontiguouscache.h new file mode 100644 index 0000000..2437f63 --- /dev/null +++ b/src/corelib/tools/qcontiguouscache.h @@ -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$ +** +****************************************************************************/ + +#ifndef QCONTIGUOUSCACHE_H +#define QCONTIGUOUSCACHE_H + +#include <QtCore/qatomic.h> +#include <limits.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +#undef QT_QCONTIGUOUSCACHE_DEBUG +QT_MODULE(Core) + + +struct Q_CORE_EXPORT QContiguousCacheData +{ + QBasicAtomicInt ref; + int alloc; + int count; + int start; + int offset; + uint sharable : 1; + +#ifdef QT_QCONTIGUOUSCACHE_DEBUG + void dump() const; +#endif +}; + +template <typename T> +struct QContiguousCacheTypedData +{ + QBasicAtomicInt ref; + int alloc; + int count; + int start; + int offset; + uint sharable : 1; + + T array[1]; +}; + +template<typename T> +class QContiguousCache { + typedef QContiguousCacheTypedData<T> Data; + union { QContiguousCacheData *p; QContiguousCacheTypedData<T> *d; }; +public: + explicit QContiguousCache(int capacity = 0); + QContiguousCache(const QContiguousCache<T> &v) : d(v.d) { d->ref.ref(); if (!d->sharable) detach_helper(); } + + inline ~QContiguousCache() { if (!d) return; if (!d->ref.deref()) free(d); } + + inline void detach() { if (d->ref != 1) detach_helper(); } + inline bool isDetached() const { return d->ref == 1; } + inline void setSharable(bool sharable) { if (!sharable) detach(); d->sharable = sharable; } + + QContiguousCache<T> &operator=(const QContiguousCache<T> &other); + bool operator==(const QContiguousCache<T> &other) const; + inline bool operator!=(const QContiguousCache<T> &other) const { return !(*this == other); } + + inline int capacity() const {return d->alloc; } + inline int count() const { return d->count; } + inline int size() const { return d->count; } + + inline bool isEmpty() const { return d->count == 0; } + inline bool isFull() const { return d->count == d->alloc; } + inline int available() const { return d->alloc - d->count; } + + void clear(); + void setCapacity(int size); + + const T &at(int pos) const; + T &operator[](int i); + const T &operator[](int i) const; + + void append(const T &value); + void prepend(const T &value); + void insert(int pos, const T &value); + + inline bool containsIndex(int pos) const { return pos >= d->offset && pos - d->offset < d->count; } + inline int firstIndex() const { return d->offset; } + inline int lastIndex() const { return d->offset + d->count - 1; } + + inline const T &first() const { Q_ASSERT(!isEmpty()); return d->array[d->start]; } + inline const T &last() const { Q_ASSERT(!isEmpty()); return d->array[(d->start + d->count -1) % d->alloc]; } + inline T &first() { Q_ASSERT(!isEmpty()); detach(); return d->array[d->start]; } + inline T &last() { Q_ASSERT(!isEmpty()); detach(); return d->array[(d->start + d->count -1) % d->alloc]; } + + void removeFirst(); + T takeFirst(); + void removeLast(); + T takeLast(); + + inline bool areIndexesValid() const + { return d->offset >= 0 && d->offset < INT_MAX - d->count && (d->offset % d->alloc) == d->start; } + + inline void normalizeIndexes() { d->offset = d->start; } + +#ifdef QT_QCONTIGUOUSCACHE_DEBUG + void dump() const { p->dump(); } +#endif +private: + void detach_helper(); + + QContiguousCacheData *malloc(int aalloc); + void free(Data *x); + int sizeOfTypedData() { + // this is more or less the same as sizeof(Data), except that it doesn't + // count the padding at the end + return reinterpret_cast<const char *>(&(reinterpret_cast<const Data *>(this))->array[1]) - reinterpret_cast<const char *>(this); + } +}; + +template <typename T> +void QContiguousCache<T>::detach_helper() +{ + union { QContiguousCacheData *p; QContiguousCacheTypedData<T> *d; } x; + + x.p = malloc(d->alloc); + x.d->ref = 1; + x.d->count = d->count; + x.d->start = d->start; + x.d->offset = d->offset; + x.d->alloc = d->alloc; + x.d->sharable = true; + + T *dest = x.d->array + x.d->start; + T *src = d->array + d->start; + int count = x.d->count; + while (count--) { + if (QTypeInfo<T>::isComplex) { + new (dest) T(*src); + } else { + *dest = *src; + } + dest++; + if (dest == x.d->array + x.d->alloc) + dest = x.d->array; + src++; + if (src == d->array + d->alloc) + src = d->array; + } + + if (!d->ref.deref()) + free(d); + d = x.d; +} + +template <typename T> +void QContiguousCache<T>::setCapacity(int asize) +{ + if (asize == d->alloc) + return; + detach(); + union { QContiguousCacheData *p; QContiguousCacheTypedData<T> *d; } x; + x.p = malloc(asize); + x.d->alloc = asize; + x.d->count = qMin(d->count, asize); + x.d->offset = d->offset + d->count - x.d->count; + x.d->start = x.d->offset % x.d->alloc; + T *dest = x.d->array + (x.d->start + x.d->count-1) % x.d->alloc; + T *src = d->array + (d->start + d->count-1) % d->alloc; + int count = x.d->count; + while (count--) { + if (QTypeInfo<T>::isComplex) { + new (dest) T(*src); + } else { + *dest = *src; + } + if (dest == x.d->array) + dest = x.d->array + x.d->alloc; + dest--; + if (src == d->array) + src = d->array + d->alloc; + src--; + } + /* free old */ + free(d); + d = x.d; +} + +template <typename T> +void QContiguousCache<T>::clear() +{ + if (d->ref == 1) { + if (QTypeInfo<T>::isComplex) { + int count = d->count; + T * i = d->array + d->start; + T * e = d->array + d->alloc; + while (count--) { + i->~T(); + i++; + if (i == e) + i = d->array; + } + } + d->count = d->start = d->offset = 0; + } else { + union { QContiguousCacheData *p; QContiguousCacheTypedData<T> *d; } x; + x.p = malloc(d->alloc); + x.d->ref = 1; + x.d->alloc = d->alloc; + x.d->count = x.d->start = x.d->offset = 0; + x.d->sharable = true; + if (!d->ref.deref()) free(d); + d = x.d; + } +} + +template <typename T> +inline QContiguousCacheData *QContiguousCache<T>::malloc(int aalloc) +{ + return static_cast<QContiguousCacheData *>(qMalloc(sizeOfTypedData() + (aalloc - 1) * sizeof(T))); +} + +template <typename T> +QContiguousCache<T>::QContiguousCache(int capacity) +{ + p = malloc(capacity); + d->ref = 1; + d->alloc = capacity; + d->count = d->start = d->offset = 0; + d->sharable = true; +} + +template <typename T> +QContiguousCache<T> &QContiguousCache<T>::operator=(const QContiguousCache<T> &other) +{ + other.d->ref.ref(); + if (!d->ref.deref()) + free(d); + d = other.d; + if (!d->sharable) + detach_helper(); + return *this; +} + +template <typename T> +bool QContiguousCache<T>::operator==(const QContiguousCache<T> &other) const +{ + if (other.d == d) + return true; + if (other.d->start != d->start + || other.d->count != d->count + || other.d->offset != d->offset + || other.d->alloc != d->alloc) + return false; + for (int i = firstIndex(); i <= lastIndex(); ++i) + if (!(at(i) == other.at(i))) + return false; + return true; +} + +template <typename T> +void QContiguousCache<T>::free(Data *x) +{ + if (QTypeInfo<T>::isComplex) { + int count = d->count; + T * i = d->array + d->start; + T * e = d->array + d->alloc; + while (count--) { + i->~T(); + i++; + if (i == e) + i = d->array; + } + } + qFree(x); +} +template <typename T> +void QContiguousCache<T>::append(const T &value) +{ + detach(); + if (QTypeInfo<T>::isComplex) { + if (d->count == d->alloc) + (d->array + (d->start+d->count) % d->alloc)->~T(); + new (d->array + (d->start+d->count) % d->alloc) T(value); + } else { + d->array[(d->start+d->count) % d->alloc] = value; + } + + if (d->count == d->alloc) { + d->start++; + d->start %= d->alloc; + d->offset++; + } else { + d->count++; + } +} + +template<typename T> +void QContiguousCache<T>::prepend(const T &value) +{ + detach(); + if (d->start) + d->start--; + else + d->start = d->alloc-1; + d->offset--; + + if (d->count != d->alloc) + d->count++; + else + if (d->count == d->alloc) + (d->array + d->start)->~T(); + + if (QTypeInfo<T>::isComplex) + new (d->array + d->start) T(value); + else + d->array[d->start] = value; +} + +template<typename T> +void QContiguousCache<T>::insert(int pos, const T &value) +{ + Q_ASSERT_X(pos >= 0 && pos < INT_MAX, "QContiguousCache<T>::insert", "index out of range"); + detach(); + if (containsIndex(pos)) { + if(QTypeInfo<T>::isComplex) + new (d->array + pos % d->alloc) T(value); + else + d->array[pos % d->alloc] = value; + } else if (pos == d->offset-1) + prepend(value); + else if (pos == d->offset+d->count) + append(value); + else { + // we don't leave gaps. + clear(); + d->offset = pos; + d->start = pos % d->alloc; + d->count = 1; + if (QTypeInfo<T>::isComplex) + new (d->array + d->start) T(value); + else + d->array[d->start] = value; + } +} + +template <typename T> +inline const T &QContiguousCache<T>::at(int pos) const +{ Q_ASSERT_X(pos >= d->offset && pos - d->offset < d->count, "QContiguousCache<T>::at", "index out of range"); return d->array[pos % d->alloc]; } +template <typename T> +inline const T &QContiguousCache<T>::operator[](int pos) const +{ Q_ASSERT_X(pos >= d->offset && pos - d->offset < d->count, "QContiguousCache<T>::at", "index out of range"); return d->array[pos % d->alloc]; } + +template <typename T> +inline T &QContiguousCache<T>::operator[](int pos) +{ + detach(); + if (!containsIndex(pos)) + insert(pos, T()); + return d->array[pos % d->alloc]; +} + +template <typename T> +inline void QContiguousCache<T>::removeFirst() +{ + Q_ASSERT(d->count > 0); + detach(); + d->count--; + if (QTypeInfo<T>::isComplex) + (d->array + d->start)->~T(); + d->start = (d->start + 1) % d->alloc; + d->offset++; +} + +template <typename T> +inline void QContiguousCache<T>::removeLast() +{ + Q_ASSERT(d->count > 0); + detach(); + d->count--; + if (QTypeInfo<T>::isComplex) + (d->array + (d->start + d->count) % d->alloc)->~T(); +} + +template <typename T> +inline T QContiguousCache<T>::takeFirst() +{ T t = first(); removeFirst(); return t; } + +template <typename T> +inline T QContiguousCache<T>::takeLast() +{ T t = last(); removeLast(); return t; } + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index b43891c..356c56e 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -3831,19 +3831,19 @@ void QDateTimePrivate::getUTC(QDate &outDate, QTime &outTime) const #if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_NO_DATESTRING) QDebug operator<<(QDebug dbg, const QDate &date) { - dbg.nospace() << "QDate(" << date.toString() << ")"; + dbg.nospace() << "QDate(" << date.toString() << ')'; return dbg.space(); } QDebug operator<<(QDebug dbg, const QTime &time) { - dbg.nospace() << "QTime(" << time.toString() << ")"; + dbg.nospace() << "QTime(" << time.toString() << ')'; return dbg.space(); } QDebug operator<<(QDebug dbg, const QDateTime &date) { - dbg.nospace() << "QDateTime(" << date.toString() << ")"; + dbg.nospace() << "QDateTime(" << date.toString() << ')'; return dbg.space(); } #endif @@ -4686,7 +4686,7 @@ QDateTimeParser::StateNode QDateTimeParser::parse(QString &input, int &cursorPos if (fixup && tmpstate == Intermediate && used < sn.count) { const FieldInfo fi = fieldInfo(index); if ((fi & (Numeric|FixedWidth)) == (Numeric|FixedWidth)) { - const QString newText = QString(QLatin1String("%1")).arg(num, sn.count, 10, QLatin1Char('0')); + const QString newText = QString::fromLatin1("%1").arg(num, sn.count, 10, QLatin1Char('0')); input.replace(pos, used, newText); used = sn.count; } diff --git a/src/corelib/tools/qdumper.cpp b/src/corelib/tools/qdumper.cpp deleted file mode 100644 index c3b8524..0000000 --- a/src/corelib/tools/qdumper.cpp +++ /dev/null @@ -1,1157 +0,0 @@ -/**************************************************************************** -** -** 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 <qdatetime.h> -#include <qdebug.h> -#include <qdir.h> -#include <qfileinfo.h> -#include <qhash.h> -#include <qmap.h> -#include <qmetaobject.h> -#include <qobject.h> -#include <qstring.h> -#include <qvariant.h> -#include <qvector.h> - -#if !defined(Q_OS_WINCE) && !defined(QT_NO_DUMPER) - -#include <stdlib.h> -#include <stdio.h> - -#ifdef Q_OS_WIN -# include <windows.h> -#endif - -QT_BEGIN_NAMESPACE - -namespace { - -// This is used to abort evaluation of custom data dumpers in a "coordinated" -// way. Abortion will happen anyway when we try to access a non-initialized -// non-trivial object, so there is no way to prevent this from occuring at all -// conceptionally. Gdb will catch SIGSEGV and return to the calling frame. -// This is just fine provided we only _read_ memory in the custom handlers -// below. - -volatile int qProvokeSegFaultHelper; - -static void qCheckAccess(const void *d) -{ - // provoke segfault when address is not readable - qProvokeSegFaultHelper = *(char*)d; -} - -static void qCheckPointer(const void *d) -{ - if (!d) - return; - qProvokeSegFaultHelper = *(char*)d; -} - -static void qProvokeSegFault() -{ - // provoke segfault unconditionally - qCheckAccess(0); -} - -static char qDumpInBuffer[100]; -static char qDumpBuffer[1000]; -#ifdef Q_OS_WIN -static char qDumpBuffer2[sizeof(qDumpBuffer) + 100]; -#endif - -static char toHex(int n) -{ - return n < 10 ? '0' + n : 'a' - 10 + n; -} - - -struct QDumper -{ - explicit QDumper(); - ~QDumper(); - void flush(); - QDumper &operator<<(long c); - QDumper &operator<<(int i); - QDumper &operator<<(unsigned long c); - QDumper &operator<<(unsigned int i); - QDumper &operator<<(const void *p); - void put(char c); - void addCommaIfNeeded(); - void putEncoded(unsigned c); - QDumper &operator<<(const char *str); - QDumper &operator<<(const QString &str); - void disarm(); - - void beginHash(); // start of data hash output - void endHash(); // start of data hash output - - // the dumper arguments - int protocolVersion; // dumper protocol version - int token; // some token to show on success - const char *outertype; // object type - const char *iname; // object name used for display - const char *exp; // object expression - const char *innertype; // 'inner type' for class templates - const void *data; // pointer to raw data - bool dumpChildren; // do we want to see children? - - // handling of nested templates - void setupTemplateParameters(); - enum { maxTemplateParameters = 10 }; - const char *templateParameters[maxTemplateParameters + 1]; - int templateParametersCount; - - // internal state - bool success; // are we finished? - size_t pos; -}; - - -QDumper::QDumper() -{ - success = false; - pos = 0; -} - -QDumper::~QDumper() -{ - flush(); - put(0); // our end marker -#ifdef Q_OS_WIN - sprintf(qDumpBuffer2, "@@CDD/%d/done\n", token); - OutputDebugStringA(qDumpBuffer2); -#else - fprintf(stderr, "%d/done\n", token); -#endif - qDumpInBuffer[0] = 0; -} - -void QDumper::flush() -{ - qDumpBuffer[pos++] = 0; -#ifdef Q_OS_WIN - sprintf(qDumpBuffer2, "@@CDD#%d#%d,%s\n", token, int(pos - 1), qDumpBuffer); - OutputDebugStringA(qDumpBuffer2); -#else - fprintf(stderr, "%d#%d,%s\n", token, int(pos - 1), qDumpBuffer); -#endif - pos = 0; -} - -void QDumper::setupTemplateParameters() -{ - char *s = const_cast<char *>(innertype); - - templateParametersCount = 1; - templateParameters[0] = s; - for (int i = 1; i != maxTemplateParameters + 1; ++i) - templateParameters[i] = 0; - - while (*s) { - while (*s && *s != '@') - ++s; - if (*s) { - *s = '\0'; - ++s; - templateParameters[templateParametersCount++] = s; - } - } -} - -QDumper &QDumper::operator<<(unsigned long c) -{ - static char buf[100]; - sprintf(buf, "%lu", c); - return (*this) << buf; -} - -QDumper &QDumper::operator<<(unsigned int i) -{ - static char buf[100]; - sprintf(buf, "%u", i); - return (*this) << buf; -} - -QDumper &QDumper::operator<<(long c) -{ - static char buf[100]; - sprintf(buf, "%ld", c); - return (*this) << buf; -} - -QDumper &QDumper::operator<<(int i) -{ - static char buf[100]; - sprintf(buf, "%d", i); - return (*this) << buf; -} - -QDumper &QDumper::operator<<(const void *p) -{ - static char buf[100]; - sprintf(buf, "%p", p); - // we get a '0x' prefix only on some implementations. - // if it isn't there, write it out manually. - if (buf[1] != 'x') { - put('0'); - put('x'); - } - return (*this) << buf; -} - -void QDumper::put(char c) -{ - if (pos >= sizeof(qDumpBuffer) - 100) - flush(); - qDumpBuffer[pos++] = c; -} - -void QDumper::addCommaIfNeeded() -{ - if (pos == 0) - return; - if (qDumpBuffer[pos - 1] == '}' || qDumpBuffer[pos - 1] == '"') - put(','); -} - -void QDumper::putEncoded(unsigned c) -{ - if (c >= 32 && c <= 126 && c != '"' && c != '\\') { - put(c); - } else { - put('\\'); - put('u'); - put(toHex((c >> 12) & 0xf)); - put(toHex((c >> 8) & 0xf)); - put(toHex((c >> 4) & 0xf)); - put(toHex( c & 0xf)); - } -} - -QDumper &QDumper::operator<<(const char *str) -{ - while (*str) - put(*(str++)); - return *this; -} - -QDumper &QDumper::operator<<(const QString &str) -{ - int n = str.size(); - if (n < 0) { - qProvokeSegFault(); - } else { - //(*this) << "[" << n << "]"; - if (n > 1000000) - n = 1000000; - //put(' '); - put('\\'); - put('"'); - for (int i = 0; i != n; ++i) - putEncoded(str[i].unicode()); - put('\\'); - put('"'); - if (n < str.size()) - (*this) << "<incomplete string>"; - } - return *this; -} - -void QDumper::disarm() -{ - flush(); - success = true; -} - -void QDumper::beginHash() -{ - addCommaIfNeeded(); - put('{'); -} - -void QDumper::endHash() -{ - put('}'); -} - - -// -// Some helpers to keep the dumper code short -// - -// dump property=value pair -#undef P -#define P(dumper,name,value) \ - do { \ - dumper.addCommaIfNeeded(); \ - dumper << (name) << "=\"" << value << "\""; \ - } while (0) - -// simple string property -#undef S -#define S(dumper, name, value) \ - dumper.beginHash(); \ - P(dumper, "name", name); \ - P(dumper, "value", value); \ - P(dumper, "type", "QString"); \ - P(dumper, "numchild", "0"); \ - dumper.endHash(); - -// simple integer property -#undef I -#define I(dumper, name, value) \ - dumper.beginHash(); \ - P(dumper, "name", name); \ - P(dumper, "value", value); \ - P(dumper, "type", "int"); \ - P(dumper, "numchild", "0"); \ - dumper.endHash(); - -// simple boolean property -#undef BL -#define BL(dumper, name, value) \ - dumper.beginHash(); \ - P(dumper, "name", name); \ - P(dumper, "value", (value ? "true" : "false")); \ - P(dumper, "type", "bool"); \ - P(dumper, "numchild", "0"); \ - dumper.endHash(); - -#undef TT -#define TT(type, value) \ - "<tr><td>" << type << "</td><td> : </td><td>" << value << "</td></tr>" - -static void qDumpUnknown(QDumper &d) -{ - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "<internal error>"); - P(d, "type", d.outertype); - P(d, "numchild", "0"); - d.disarm(); -} - -static void qDumpQPropertyList(QDumper &d) -{ - const QObject *ob = (const QObject *)d.data; - const QMetaObject *mo = ob->metaObject(); - P(d, "iname", d.iname); - P(d, "addr", "<synthetic>"); - P(d, "type", "QObject"); - P(d, "numchild", mo->propertyCount()); - if (d.dumpChildren) { - d << ",children=["; - for (int i = mo->propertyCount(); --i >= 0; ) { - const QMetaProperty & prop = mo->property(i); - d.beginHash(); - P(d, "name", prop.name()); - if (QLatin1String(prop.typeName()) == QLatin1String("QString")) { - P(d, "value", prop.read(ob).toString()); - P(d, "numchild", "0"); - } else if (QLatin1String(prop.typeName()) == QLatin1String("bool")) { - P(d, "value", (prop.read(ob).toBool() ? "true" : "false")); - P(d, "numchild", "0"); - } else if (QLatin1String(prop.typeName()) == QLatin1String("int")) { - P(d, "value", prop.read(ob).toInt()); - P(d, "numchild", "0"); - } else { - P(d, "exp", "((" << mo->className() << "*)" << ob - << ")->" << prop.name() << "()"); - } - P(d, "type", prop.typeName()); - P(d, "numchild", "1"); - d.endHash(); - } - d << "]"; - } - d.disarm(); -} - -static void qDumpQObject(QDumper &d) -{ - const QObject *ob = reinterpret_cast<const QObject *>(d.data); - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", (void*)d.data); - P(d, "type", "QObject"); - P(d, "numchild", 4); - if (d.dumpChildren) { - const QMetaObject *mo = ob->metaObject(); - const QObjectList &children = ob->children(); - d << ",children=["; - S(d, "objectName", ob->objectName()); - d.beginHash(); - P(d, "name", "properties"); - // FIXME: Note that when simply using '(QObject*)' - // in the cast below, Gdb/MI _sometimes misparses - // expressions further down in the tree. - P(d, "exp", "*(class QObject*)" << d.data); - P(d, "type", "QPropertyList"); - P(d, "value", "<" << mo->propertyCount() << " items>"); - P(d, "numchild", mo->propertyCount()); - d.endHash(); - d.beginHash(); - P(d, "name", "children"); - P(d, "exp", "((class QObject*)" << d.data << ")->children()"); - P(d, "type", "QList<QObject *>"); - P(d, "value", "<" << children.size() << " items>"); - P(d, "numchild", children.size()); - d.endHash(); - d.beginHash(); - P(d, "name", "parent"); - P(d, "exp", "((class QObject*)" << d.data << ")->parent()"); - P(d, "type", "QObject *"); - P(d, "numchild", (ob->parent() ? "1" : "0")); - d.endHash(); - d << "]"; - } - d.disarm(); -} - -static void qDumpQDir(QDumper &d) -{ - const QDir &dir = *reinterpret_cast<const QDir *>(d.data); - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", dir.path()); - P(d, "type", "QDir"); - P(d, "numchild", "3"); - if (d.dumpChildren) { - d << ",children=["; - S(d, "absolutePath", dir.absolutePath()); - S(d, "canonicalPath", dir.canonicalPath()); - d << "]"; - } - d.disarm(); -} - -static void qDumpQFileInfo(QDumper &d) -{ - const QFileInfo &info = *reinterpret_cast<const QFileInfo *>(d.data); - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", info.filePath()); - P(d, "type", "QDir"); - P(d, "numchild", "3"); - if (d.dumpChildren) { - d << ",children=["; - S(d, "absolutePath", info.absolutePath()); - S(d, "absoluteFilePath", info.absoluteFilePath()); - S(d, "canonicalPath", info.canonicalPath()); - S(d, "canonicalFilePath", info.canonicalFilePath()); - S(d, "completeBaseName", info.completeBaseName()); - S(d, "completeSuffix", info.completeSuffix()); - S(d, "baseName", info.baseName()); -#ifdef Q_OS_MACX - BL(d, "isBundle", info.isBundle()); - S(d, "bundleName", info.bundleName()); -#endif - S(d, "completeSuffix", info.completeSuffix()); - S(d, "fileName", info.fileName()); - S(d, "filePath", info.filePath()); - S(d, "group", info.group()); - S(d, "owner", info.owner()); - S(d, "path", info.path()); - - I(d, "groupid", (long)info.groupId()); - I(d, "ownerid", (long)info.ownerId()); - //QFile::Permissions permissions () const - I(d, "permissions", info.permissions()); - - //QDir absoluteDir () const - //QDir dir () const - - BL(d, "caching", info.caching()); - BL(d, "exists", info.exists()); - BL(d, "isAbsolute", info.isAbsolute()); - BL(d, "isDir", info.isDir()); - BL(d, "isExecutable", info.isExecutable()); - BL(d, "isFile", info.isFile()); - BL(d, "isHidden", info.isHidden()); - BL(d, "isReadable", info.isReadable()); - BL(d, "isRelative", info.isRelative()); - BL(d, "isRoot", info.isRoot()); - BL(d, "isSymLink", info.isSymLink()); - BL(d, "isWritable", info.isWritable()); - -#ifndef QT_NO_DATESTRING - d.beginHash(); - P(d, "name", "created"); - P(d, "value", info.created().toString()); - P(d, "exp", "((QFileInfo*)" << d.data << ")->created()"); - P(d, "type", "QDateTime"); - P(d, "numchild", "1"); - d.endHash(); - - d.beginHash(); - P(d, "name", "lastModified"); - P(d, "value", info.lastModified().toString()); - P(d, "exp", "((QFileInfo*)" << d.data << ")->lastModified()"); - P(d, "type", "QDateTime"); - P(d, "numchild", "1"); - d.endHash(); - - d.beginHash(); - P(d, "name", "lastRead"); - P(d, "value", info.lastRead().toString()); - P(d, "exp", "((QFileInfo*)" << d.data << ")->lastRead()"); - P(d, "type", "QDateTime"); - P(d, "numchild", "1"); - d.endHash(); -#endif - - d << "]"; - } - d.disarm(); -} - -static void qDumpQDateTime(QDumper &d) -{ -#ifdef QT_NO_DATESTRING - qDumpUnknown(d); -#else - const QDateTime &date = *reinterpret_cast<const QDateTime *>(d.data); - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", date.toString()); - P(d, "type", "QDateTime"); - P(d, "numchild", "3"); - if (d.dumpChildren) { - d << ",children=["; - BL(d, "isNull", date.isNull()); - I(d, "toTime_t", (long)date.toTime_t()); - S(d, "toString", date.toString()); - S(d, "toString_(ISO)", date.toString(Qt::ISODate)); - S(d, "toString_(SystemLocale)", date.toString(Qt::SystemLocaleDate)); - S(d, "toString_(Locale)", date.toString(Qt::LocaleDate)); - S(d, "toString", date.toString()); - - d.beginHash(); - P(d, "name", "toUTC"); - P(d, "exp", "((QDateTime*)" << d.data << ")->toTimeSpec(Qt::UTC)"); - P(d, "type", "QDateTime"); - P(d, "numchild", "1"); - d.endHash(); - - d.beginHash(); - P(d, "name", "toLocalTime"); - P(d, "exp", "((QDateTime*)" << d.data << ")->toTimeSpec(Qt::LocalTime)"); - P(d, "type", "QDateTime"); - P(d, "numchild", "1"); - d.endHash(); - - d << "]"; - } - d.disarm(); -#endif // ifdef QT_NO_DATESTRING -} - -static void qDumpQString(QDumper &d) -{ - const QString &str = *reinterpret_cast<const QString *>(d.data); - - // Try to provoke segfaults early to prevent the frontend - // from asking for unavailable child details - if (!str.isEmpty()) { - volatile ushort dummy = 0; - dummy += str.at(0).unicode(); - dummy += str.at(str.size() - 1).unicode(); - } - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", str); - P(d, "type", "QString"); - P(d, "numchild", "0"); - d.disarm(); -} - -static void qDumpQStringList(QDumper &d) -{ - const QStringList &list = *reinterpret_cast<const QStringList *>(d.data); - int n = list.size(); - if (n < 0) - qProvokeSegFault(); - if (n > 0) { - qCheckAccess(&list.front()); - qCheckAccess(&list.back()); - } - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "<" << n << " items>"); - P(d, "valuedisabled", "true"); - P(d, "numchild", n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - for (int i = 0; i != n; ++i) { - S(d, "[" << i << "]", list[i]); - } - if (n < list.size()) { - d.beginHash(); - P(d, "value", "<incomplete>"); - P(d, "type", " "); - P(d, "numchild", "0"); - d.endHash(); - } - d << "]"; - } - d.disarm(); -} - -static void qDumpQVariantHelper(const void *data, QString *value, - QString *exp, int *numchild) -{ - const QVariant &v = *reinterpret_cast<const QVariant *>(data); - switch (v.type()) { - case QVariant::Invalid: - *value = QLatin1String("<invalid>"); - *numchild = 0; - break; - case QVariant::String: - *value = QLatin1Char('"') + v.toString() + QLatin1Char('"'); - *numchild = 0; - break; - case QVariant::StringList: - *exp = QString(QLatin1String("((QVariant*)%1)->d.data.c")) - .arg((qulonglong)data); - *numchild = v.toStringList().size(); - break; - case QVariant::Int: - *value = QString::number(v.toInt()); - *numchild= 0; - break; - case QVariant::Double: - *value = QString::number(v.toDouble()); - *numchild = 0; - break; - default: - // FIXME - //*exp = QString("qVariantValue<" << v.typeName() << ">" - // << "(*(QVariant*)" << data << ")"); - break; - } -} - -static void qDumpQVariant(QDumper &d) -{ - const QVariant &v = *reinterpret_cast<const QVariant *>(d.data); - QString value; - QString exp; - int numchild = 0; - qDumpQVariantHelper(d.data, &value, &exp, &numchild); - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "(" << v.typeName() << ") " << qPrintable(value)); - P(d, "type", "QVariant"); - P(d, "numchild", 1); - if (d.dumpChildren) { - d << ",children=["; - d.beginHash(); - P(d, "name", "value"); - if (!exp.isEmpty()) - P(d, "exp", qPrintable(exp)); - if (!value.isEmpty()) - P(d, "value", qPrintable(value)); - P(d, "type", v.typeName()); - P(d, "numchild", numchild); - d.endHash(); - d << "]"; - } - d.disarm(); -} - -static void qDumpQList(QDumper &d) -{ - // This uses the knowledge that QList<T> has only a single member - // of type union { QListData p; QListData::Data *d; }; - const QListData &ldata = *reinterpret_cast<const QListData*>(d.data); - const QListData::Data *pdata = *reinterpret_cast<const QListData::Data* const*>(d.data); - int nn = ldata.size(); - if (nn < 0) - qProvokeSegFault(); - if (nn > 0) { - qCheckAccess(ldata.d->array); - //qCheckAccess(ldata.d->array[0]); - //qCheckAccess(ldata.d->array[nn - 1]); - } - - int n = nn; - P(d, "iname", d.iname); - P(d, "value", "<" << n << " items>"); - P(d, "valuedisabled", "true"); - P(d, "numchild", n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - for (int i = 0; i != n; ++i) { - d.beginHash(); - P(d, "name", "[" << i << "]"); - // The exact condition here is: - // QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic - // but this data is not available in the compiled binary. - // So as first approximation only do the 'isLarge' check: - void *p = &(ldata.d->array[i + pdata->begin]); - unsigned long voidpsize = sizeof(void*); - P(d, "exp", "(sizeof(" << d.innertype << ")>" << voidpsize << - "?(**(" << d.innertype << "**)(" << p << "))" - ":(*(" << d.innertype << "*)(" << p << ")))"); - P(d, "type", d.innertype); - d.endHash(); - } - if (n < nn) { - d << ",{"; - P(d, "value", "<incomplete>"); - d.endHash(); - } - d << "]"; - } - d.disarm(); -} - -static void qDumpQVector(QDumper &d) -{ - // Use 'int' as representative value. No way (and no need) - // to deduce proper type here. - const QVector<int> &vec = *reinterpret_cast<const QVector<int> *>(d.data); - const int nn = vec.size(); - - // Try to provoke segfaults early to prevent the frontend - // from asking for unavailable child details - if (nn < 0) - qProvokeSegFault(); - if (nn > 0) { - qCheckAccess(&vec.front()); - qCheckAccess(&vec.back()); - } - - //int innersize = 0; - //scanf(qDumpInBuffer, "%d", &innersize); - - int n = nn; - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "<" << n << " items>"); - P(d, "valuedisabled", "true"); - P(d, "numchild", n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - for (int i = 0; i != n; ++i) { - if (i) - d << ","; - d.beginHash(); - P(d, "name", "[" << i << "]"); - P(d, "exp", "(" << d.exp << ".d->array[" << i << "])"); - P(d, "type", d.innertype); - d.endHash(); - } - if (n < nn) { - d << ",{"; - P(d, "value", "<incomplete>"); - d.endHash(); - } - d << "]"; - } - d.disarm(); -} - -static void qDumpQHashNode(QDumper &d) -{ - struct NodeOS { void *next; uint k; uint v; } nodeOS; // int-key optimization, small value - struct NodeOL { void *next; uint k; void *v; } nodeOL; // int-key optimiatzion, large value - struct NodeNS { void *next; uint h; uint k; uint v; } nodeNS; // no optimization, small value - struct NodeNL { void *next; uint h; uint k; void *v; } nodeNL; // no optimization, large value - struct NodeL { void *next; uint h; void *k; void *v; } nodeL; // complex key - - // offsetof(...,...) not yet in Standard C++ - const ulong nodeOSk ( (char *)&nodeOS.k - (char *)&nodeOS ); - const ulong nodeOSv ( (char *)&nodeOS.v - (char *)&nodeOS ); - const ulong nodeOLk ( (char *)&nodeOL.k - (char *)&nodeOL ); - const ulong nodeOLv ( (char *)&nodeOL.v - (char *)&nodeOL ); - const ulong nodeNSk ( (char *)&nodeNS.k - (char *)&nodeNS ); - const ulong nodeNSv ( (char *)&nodeNS.v - (char *)&nodeNS ); - const ulong nodeNLk ( (char *)&nodeNL.k - (char *)&nodeNL ); - const ulong nodeNLv ( (char *)&nodeNL.v - (char *)&nodeNL ); - const ulong nodeLk ( (char *)&nodeL.k - (char *)&nodeL ); - const ulong nodeLv ( (char *)&nodeL.v - (char *)&nodeL ); - - const QHashData *h = reinterpret_cast<const QHashData *>(d.data); - const char *keyType = d.templateParameters[0]; - const char *valueType = d.templateParameters[1]; - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", ""); - P(d, "numchild", 2); - if (d.dumpChildren) { - // there is a hash specialization in cast the key are integers or shorts - bool isOptimizedIntKey = qstrcmp(keyType, "int") == 0 -#if defined(Q_BYTE_ORDER) && Q_BYTE_ORDER == Q_LITTLE_ENDIAN - || qstrcmp(keyType, "short") == 0 - || qstrcmp(keyType, "ushort") == 0 -#endif - || qstrcmp(keyType, "uint") == 0; - - d << ",children=["; - d.beginHash(); - P(d, "name", "key"); - P(d, "type", keyType); - unsigned long intsize = sizeof(int); - if (isOptimizedIntKey) { - P(d, "exp", "*(" << keyType << "*)" - "(((sizeof(" << valueType << ")>" << intsize << ")?" - << nodeOLk << ":" << nodeOSk << - ")+(char*)" << h << ")"); - } else { - P(d, "exp", "*(" << keyType << "*)" - "(((sizeof(" << keyType << ")>" << intsize << ")?" - << nodeLk << ":" - "((sizeof(" << valueType << ")>" << intsize << ")?" - << nodeNLk << ":" << nodeNSk << "))+(char*)" << h << ")"); - } - d.endHash(); - d.beginHash(); - P(d, "name", "value"); - P(d, "type", valueType); - if (isOptimizedIntKey) { - P(d, "exp", "*(" << valueType << "*)" - "(((sizeof(" << valueType << ")>" << intsize << ")?" - << nodeOLv << ":" << nodeOSv << ")+(char*)" << h << ")"); - } else { - P(d, "exp", "*(" << valueType << "*)" - "(((sizeof(" << keyType << ")>" << intsize << ")?" << nodeLv << ":" - "((sizeof(" << valueType << ")>" << intsize << ")?" - << nodeNLv << ":" << nodeNSv << "))+(char*)" << h << ")"); - } - d.endHash(); - d << "]"; - } - d.disarm(); -} - -static void qDumpQHash(QDumper &d) -{ - QHashData *h = *reinterpret_cast<QHashData *const*>(d.data); - const char *keyType = d.templateParameters[0]; - const char *valueType = d.templateParameters[1]; - - qCheckPointer(h->fakeNext); - qCheckPointer(h->buckets); - - int n = h->size; - - if (n < 0) - qProvokeSegFault(); - if (n > 0) { - qCheckPointer(h->fakeNext); - qCheckPointer(*h->buckets); - } - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "<" << n << " items>"); - P(d, "numchild", n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - - QHashData::Node *node = h->firstNode(); - QHashData::Node *end = reinterpret_cast<QHashData::Node *>(h); - int i = 0; - - while (node != end) { - d.beginHash(); - P(d, "name", "[" << i << "]"); - P(d, "type", "QHashNode<" << keyType << "," << valueType << " >"); - P(d, "exp", "*(QHashNode<" << keyType << "," << valueType << " >*)" << node); - d.endHash(); - - ++i; - node = QHashData::nextNode(node); - } - d << "]"; - } - d.disarm(); -} - -static void qDumpQMapNode(QDumper &d) -{ - const QMapData *h = reinterpret_cast<const QMapData *>(d.data); - const char *keyType = d.templateParameters[0]; - const char *valueType = d.templateParameters[1]; - - qCheckAccess(h->backward); - qCheckAccess(h->forward[0]); - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", ""); - P(d, "numchild", 2); - if (d.dumpChildren) { - unsigned long voidpsize = sizeof(void*); - d << ",children=["; - d.beginHash(); - P(d, "name", "key"); - P(d, "type", keyType); - P(d, "exp", "*(" << keyType << "*)" - << "(" - << 2 * voidpsize - << "-sizeof('QMap<" << keyType << "," << valueType << ">::Node')" - << "+(char*)" << h - << ")"); - d.endHash(); - d.beginHash(); - P(d, "name", "value"); - P(d, "type", valueType); - P(d, "exp", "*(" << valueType << "*)" - << "(" - << "(size_t)&(('QMap<" << keyType << "," << valueType << ">::Node'*)0)->value" - << "+" << 2 * voidpsize - << "-sizeof('QMap<" << keyType << "," << valueType << ">::Node')" - << "+(char*)" << h - << ")"); - d.endHash(); - d << "]"; - } - - d.disarm(); -} - -static void qDumpQMap(QDumper &d) -{ - QMapData *h = *reinterpret_cast<QMapData *const*>(d.data); - const char *keyType = d.templateParameters[0]; - const char *valueType = d.templateParameters[1]; - - int n = h->size; - - if (n < 0) - qProvokeSegFault(); - if (n > 0) { - qCheckAccess(h->backward); - qCheckAccess(h->forward[0]); - qCheckPointer(h->backward->backward); - qCheckPointer(h->forward[0]->backward); - } - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "<" << n << " items>"); - P(d, "numchild", n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - - QMapData::Node *node = reinterpret_cast<QMapData::Node *>(h->forward[0]); - QMapData::Node *end = reinterpret_cast<QMapData::Node *>(h); - int i = 0; - - while (node != end) { - d.beginHash(); - P(d, "name", "[" << i << "]"); - P(d, "type", "QMap<" << keyType << "," << valueType << ">::Node"); - P(d, "exp", "*('QMap<" << keyType << "," << valueType << ">::Node'*)" << node); - d.endHash(); - - ++i; - node = node->forward[0]; - } - d << "]"; - } - - d.disarm(); -} - -static void qDumpQSet(QDumper &d) -{ - // This uses the knowledge that QHash<T> has only a single member - // of union { QHashData *d; QHashNode<Key, T> *e; }; - QHashData *hd = *(QHashData**)d.data; - QHashData::Node *node = hd->firstNode(); - - int n = hd->size; - if (n < 0) - qProvokeSegFault(); - if (n > 0) { - qCheckAccess(node); - qCheckPointer(node->next); - } - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "<" << n << " items>"); - P(d, "valuedisabled", "true"); - P(d, "numchild", 2 * n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - int i = 0; - for (int bucket = 0; bucket != hd->numBuckets; ++bucket) { - for (node = hd->buckets[bucket]; node->next; node = node->next) { - d.beginHash(); - P(d, "name", "[" << i << "]"); - P(d, "type", d.innertype); - P(d, "exp", "(('QHashNode<" << d.innertype - << ",QHashDummyValue>'*)" - << static_cast<const void*>(node) << ")->key" - ); - d.endHash(); - ++i; - } - } - d << "]"; - } - d.disarm(); -} - -static void handleProtocolVersion2(QDumper & d) -{ - if (!d.outertype[0]) { - qDumpUnknown(d); - return; - } - - d.setupTemplateParameters(); - // d.outertype[0] is usally 'Q', so don't use it - switch (d.outertype[1]) { - case 'D': - if (qstrcmp(d.outertype, "QDateTime") == 0) - qDumpQDateTime(d); - else if (qstrcmp(d.outertype, "QDir") == 0) - qDumpQDir(d); - break; - case 'F': - if (qstrcmp(d.outertype, "QFileInfo") == 0) - qDumpQFileInfo(d); - break; - case 'H': - if (qstrcmp(d.outertype, "QHash") == 0) - qDumpQHash(d); - else if (qstrcmp(d.outertype, "QHashNode") == 0) - qDumpQHashNode(d); - break; - case 'L': - if (qstrcmp(d.outertype, "QList") == 0) - qDumpQList(d); - break; - case 'M': - if (qstrcmp(d.outertype, "QMap") == 0) - qDumpQMap(d); - else if (qstrcmp(d.outertype, "QMap::Node") == 0) - qDumpQMapNode(d); - break; - case 'O': - if (qstrcmp(d.outertype, "QObject") == 0) - qDumpQObject(d); - break; - case 'P': - if (qstrcmp(d.outertype, "QPropertyList") == 0) - qDumpQPropertyList(d); - break; - case 'S': - if (qstrcmp(d.outertype, "QSet") == 0) - qDumpQSet(d); - else if (qstrcmp(d.outertype, "QString") == 0) - qDumpQString(d); - else if (qstrcmp(d.outertype, "QStringList") == 0) - qDumpQStringList(d); - break; - case 'V': - if (qstrcmp(d.outertype, "QVariant") == 0) - qDumpQVariant(d); - else if (qstrcmp(d.outertype, "QVector") == 0) - qDumpQVector(d); - break; - } - - if (!d.success) - qDumpUnknown(d); -} - -} // anonymous namespace - - -extern "C" Q_CORE_EXPORT void qDumpObjectData( - int protocolVersion, - int token, - const char *outertype, - const char *iname, - const char *exp, - const char *innertype, - const void *data, - bool dumpChildren) -{ - if (protocolVersion == 1) { - // used to test whether error output gets through - //fprintf(stderr, "using stderr, qDebug follows: %d\n", token); - //qDebug() << "using qDebug, stderr already used: " << token; - } - - else if (protocolVersion == 2) { - QDumper d; - d.protocolVersion = protocolVersion; - d.token = token; - d.outertype = outertype ? outertype : ""; - d.iname = iname ? iname : ""; - d.exp = exp ? exp : ""; - d.innertype = innertype ? innertype : ""; - d.data = data ? data : ""; - d.dumpChildren = dumpChildren; - handleProtocolVersion2(d); - } - - else { - qDebug() << "Unsupported protocol version" << protocolVersion; - } -} - -QT_END_NAMESPACE - -#endif // !Q_OS_WINCE && !QT_NO_QDUMPER diff --git a/src/corelib/tools/qeasingcurve.cpp b/src/corelib/tools/qeasingcurve.cpp new file mode 100644 index 0000000..2b027fc --- /dev/null +++ b/src/corelib/tools/qeasingcurve.cpp @@ -0,0 +1,846 @@ +/**************************************************************************** +** +** 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 + \since 4.6 + \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" + +#ifndef QT_NO_DEBUG_STREAM +#include <QtCore/qdebug.h> +#include <QtCore/QString> +#endif + +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; +} + +QT_BEGIN_INCLUDE_NAMESPACE +#include "../../3rdparty/easing/easing.cpp" +QT_END_INCLUDE_NAMESPACE + +class QEasingCurvePrivate +{ +public: + QEasingCurvePrivate() + : type(QEasingCurve::Linear), + config(0), + func(&easeNone) + { } + ~QEasingCurvePrivate() { delete config; } + 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 +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/qline.cpp b/src/corelib/tools/qline.cpp index f615dcd..88825a3 100644 --- a/src/corelib/tools/qline.cpp +++ b/src/corelib/tools/qline.cpp @@ -260,7 +260,7 @@ QT_BEGIN_NAMESPACE #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const QLine &p) { - d << "QLine(" << p.p1() << "," << p.p2() << ")"; + d << "QLine(" << p.p1() << ',' << p.p2() << ')'; return d; } #endif @@ -822,7 +822,7 @@ qreal QLineF::angle(const QLineF &l) const #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const QLineF &p) { - d << "QLineF(" << p.p1() << "," << p.p2() << ")"; + d << "QLineF(" << p.p1() << ',' << p.p2() << ')'; return d; } #endif diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp index 4317933..357b5d7 100644 --- a/src/corelib/tools/qlocale.cpp +++ b/src/corelib/tools/qlocale.cpp @@ -42,7 +42,6 @@ #include "qglobal.h" #ifndef QT_NO_SYSTEMLOCALE -#define QLOCALE_CPP QT_BEGIN_NAMESPACE class QSystemLocale; static QSystemLocale *QSystemLocale_globalSystemLocale(); @@ -120,7 +119,7 @@ static char *_qdtoa( NEEDS_VOLATILE double d, int mode, int ndigits, int *decpt, Q_CORE_EXPORT char *qdtoa(double d, int mode, int ndigits, int *decpt, int *sign, char **rve, char **digits_str); Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok); -Q_CORE_EXPORT qlonglong qstrtoll(const char *nptr, const char **endptr, register int base, bool *ok); +static qlonglong qstrtoll(const char *nptr, const char **endptr, register int base, bool *ok); static qulonglong qstrtoull(const char *nptr, const char **endptr, register int base, bool *ok); #if defined(Q_CC_MWERKS) && defined(Q_OS_WIN32) @@ -328,7 +327,7 @@ static QString readEscapedFormatString(const QString &format, int *idx) { int &i = *idx; - Q_ASSERT(format.at(i).unicode() == '\''); + Q_ASSERT(format.at(i) == QLatin1Char('\'')); ++i; if (i == format.size()) return QString(); @@ -642,7 +641,7 @@ static QLocale::MeasurementSystem winSystemMeasurementSystem() QString iMeasure = QT_WA_INLINE( QString::fromUtf16(reinterpret_cast<ushort*>(output)), QString::fromLocal8Bit(reinterpret_cast<char*>(output))); - if (iMeasure == QString::fromLatin1("1")) { + if (iMeasure == QLatin1String("1")) { return QLocale::ImperialSystem; } } @@ -780,7 +779,7 @@ QVariant QSystemLocale::query(QueryType type, QVariant in = QVariant()) const Instead it can return a "Windows code". This maps windows codes to ISO country names. */ struct WindowsToISOListElt { - int windows_code; + ushort windows_code; char iso_name[6]; }; @@ -1143,7 +1142,7 @@ static QString macToQtFormat(const QString &sys_fmt) break; case 'S': // fractional second if (repeat < 3) - result += QLatin1String("z"); + result += QLatin1Char('z'); else result += QLatin1String("zzz"); break; @@ -1157,7 +1156,7 @@ static QString macToQtFormat(const QString &sys_fmt) if (repeat >= 2) result += QLatin1String("dd"); else - result += QLatin1String("d"); + result += QLatin1Char('d'); break; case 'a': result += QLatin1String("AP"); @@ -1541,7 +1540,7 @@ QDataStream &operator>>(QDataStream &ds, QLocale &l) l = QLocale(s); return ds; } -#endif +#endif // QT_NO_DATASTREAM /*! @@ -1609,6 +1608,8 @@ QDataStream &operator>>(QDataStream &ds, QLocale &l) This constructor converts the locale name to a language/country pair; it does not use the system locale database. + QLocale's data is based on Common Locale Data Repository v1.6.1. + The double-to-string and string-to-double conversion functions are covered by the following licenses: @@ -1629,8 +1630,6 @@ QDataStream &operator>>(QDataStream &ds, QLocale &l) This product includes software developed by the University of California, Berkeley and its contributors. - QLocale's data is based on Common Locale Data Repository v1.6.1. - \sa QString::arg(), QString::toInt(), QString::toDouble() */ @@ -4678,7 +4677,7 @@ static qulonglong qstrtoull(const char *nptr, const char **endptr, register int * Ignores `locale' stuff. Assumes that the upper and lower case * alphabets and digits are each contiguous. */ -Q_CORE_EXPORT qlonglong qstrtoll(const char *nptr, const char **endptr, register int base, bool *ok) +static qlonglong qstrtoll(const char *nptr, const char **endptr, register int base, bool *ok) { register const char *s; register qulonglong acc; @@ -5375,6 +5374,14 @@ static Bigint *mult(Bigint *a, Bigint *b) static Bigint *p5s; +struct p5s_deleter +{ + ~p5s_deleter() + { + Bfree(p5s); + } +}; + static Bigint *pow5mult(Bigint *b, int k) { Bigint *b1, *p5, *p51; @@ -5396,6 +5403,7 @@ static Bigint *pow5mult(Bigint *b, int k) return b; if (!(p5 = p5s)) { /* first time */ + static p5s_deleter deleter; p5 = p5s = i2b(625); p5->next = 0; } diff --git a/src/corelib/tools/qlocale.h b/src/corelib/tools/qlocale.h index 5b611eb..987ab4e 100644 --- a/src/corelib/tools/qlocale.h +++ b/src/corelib/tools/qlocale.h @@ -99,11 +99,9 @@ public: virtual QVariant query(QueryType type, QVariant in) const; virtual QLocale fallbackLocale() const; -#ifdef QLOCALE_CPP private: QSystemLocale(bool); friend QSystemLocale *QSystemLocale_globalSystemLocale(); -#endif }; #endif diff --git a/src/corelib/tools/qlocale_data_p.h b/src/corelib/tools/qlocale_data_p.h index 37f59a4..e0eecf3 100644 --- a/src/corelib/tools/qlocale_data_p.h +++ b/src/corelib/tools/qlocale_data_p.h @@ -60,8 +60,8 @@ QT_BEGIN_NAMESPACE */ struct CountryLanguage { - quint32 languageId; - quint32 countryId; + quint16 languageId; + quint16 countryId; }; static const CountryLanguage ImperialMeasurementSystems[] = { { 31, 225 }, @@ -83,7 +83,7 @@ static const int ImperialMeasurementSystemsCount = */ -static const uint locale_index[] = { +static const quint16 locale_index[] = { 0, // unused 0, // C 0, // Abkhazian @@ -2313,7 +2313,7 @@ static const char language_name_list[] = "Chewa\0" ; -static const uint language_name_index[] = { +static const quint16 language_name_index[] = { 0, // Unused 8, // C 10, // Abkhazian @@ -2727,7 +2727,7 @@ static const char country_name_list[] = "SerbiaAndMontenegro\0" ; -static const uint country_name_index[] = { +static const quint16 country_name_index[] = { 0, // AnyCountry 8, // Afghanistan 20, // Albania diff --git a/src/corelib/tools/qlocale_p.h b/src/corelib/tools/qlocale_p.h index 9d36a83..ed7fc10 100644 --- a/src/corelib/tools/qlocale_p.h +++ b/src/corelib/tools/qlocale_p.h @@ -96,7 +96,7 @@ public: ShowBase = 0x80, UppercaseBase = 0x100, - ForcePoint = Alternate, + ForcePoint = Alternate }; enum GroupSeparatorMode { @@ -140,29 +140,29 @@ public: QString dateTimeToString(const QString &format, const QDate *date, const QTime *time, const QLocale *q) const; - quint32 m_language_id, m_country_id; + quint16 m_language_id, m_country_id; quint16 m_decimal, m_group, m_list, m_percent, m_zero, m_minus, m_plus, m_exponential; - quint32 m_short_date_format_idx, m_short_date_format_size; - quint32 m_long_date_format_idx, m_long_date_format_size; - quint32 m_short_time_format_idx, m_short_time_format_size; - quint32 m_long_time_format_idx, m_long_time_format_size; - quint32 m_standalone_short_month_names_idx, m_standalone_short_month_names_size; - quint32 m_standalone_long_month_names_idx, m_standalone_long_month_names_size; - quint32 m_standalone_narrow_month_names_idx, m_standalone_narrow_month_names_size; - quint32 m_short_month_names_idx, m_short_month_names_size; - quint32 m_long_month_names_idx, m_long_month_names_size; - quint32 m_narrow_month_names_idx, m_narrow_month_names_size; - quint32 m_standalone_short_day_names_idx, m_standalone_short_day_names_size; - quint32 m_standalone_long_day_names_idx, m_standalone_long_day_names_size; - quint32 m_standalone_narrow_day_names_idx, m_standalone_narrow_day_names_size; - quint32 m_short_day_names_idx, m_short_day_names_size; - quint32 m_long_day_names_idx, m_long_day_names_size; - quint32 m_narrow_day_names_idx, m_narrow_day_names_size; - quint32 m_am_idx, m_am_size; - quint32 m_pm_idx, m_pm_size; + quint16 m_short_date_format_idx, m_short_date_format_size; + quint16 m_long_date_format_idx, m_long_date_format_size; + quint16 m_short_time_format_idx, m_short_time_format_size; + quint16 m_long_time_format_idx, m_long_time_format_size; + quint16 m_standalone_short_month_names_idx, m_standalone_short_month_names_size; + quint16 m_standalone_long_month_names_idx, m_standalone_long_month_names_size; + quint16 m_standalone_narrow_month_names_idx, m_standalone_narrow_month_names_size; + quint16 m_short_month_names_idx, m_short_month_names_size; + quint16 m_long_month_names_idx, m_long_month_names_size; + quint16 m_narrow_month_names_idx, m_narrow_month_names_size; + quint16 m_standalone_short_day_names_idx, m_standalone_short_day_names_size; + quint16 m_standalone_long_day_names_idx, m_standalone_long_day_names_size; + quint16 m_standalone_narrow_day_names_idx, m_standalone_narrow_day_names_size; + quint16 m_short_day_names_idx, m_short_day_names_size; + quint16 m_long_day_names_idx, m_long_day_names_size; + quint16 m_narrow_day_names_idx, m_narrow_day_names_size; + quint16 m_am_idx, m_am_size; + quint16 m_pm_idx, m_pm_size; }; inline char QLocalePrivate::digitToCLocale(const QChar &in) const diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index bd9ba74..65247cd 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -1016,7 +1016,7 @@ Q_INLINE_TEMPLATE int QMultiMap<Key, T>::remove(const Key &key, const T &value) { int n = 0; typename QMap<Key, T>::iterator i(find(key)); - typename QMap<Key, T>::const_iterator end(QMap<Key, T>::constEnd()); + typename QMap<Key, T>::iterator end(QMap<Key, T>::end()); while (i != end && !qMapLessThanKey<Key>(key, i.key())) { if (i.value() == value) { #if defined(Q_CC_RVCT) diff --git a/src/corelib/tools/qpoint.cpp b/src/corelib/tools/qpoint.cpp index feea473..90b44b1 100644 --- a/src/corelib/tools/qpoint.cpp +++ b/src/corelib/tools/qpoint.cpp @@ -373,7 +373,7 @@ QDebug operator<<(QDebug dbg, const QPoint &p) { QDebug operator<<(QDebug d, const QPointF &p) { - d.nospace() << "QPointF(" << p.x() << ", " << p.y() << ")"; + d.nospace() << "QPointF(" << p.x() << ", " << p.y() << ')'; return d; } #endif @@ -442,6 +442,19 @@ QDebug operator<<(QDebug d, const QPointF &p) otherwise returns false. */ + +/*! + Returns the sum of the absolute values of x() and y(), + traditionally known as the "Manhattan length" of the vector from + the origin to the point. + + \sa QPoint::manhattanLength() +*/ +qreal QPointF::manhattanLength() const +{ + return qAbs(x())+qAbs(y()); +} + /*! \fn qreal QPointF::x() const diff --git a/src/corelib/tools/qpoint.h b/src/corelib/tools/qpoint.h index 1dab7e2..e716bf7 100644 --- a/src/corelib/tools/qpoint.h +++ b/src/corelib/tools/qpoint.h @@ -192,6 +192,8 @@ public: QPointF(const QPoint &p); QPointF(qreal xpos, qreal ypos); + qreal manhattanLength() const; + bool isNull() const; qreal x() const; diff --git a/src/corelib/tools/qregexp.cpp b/src/corelib/tools/qregexp.cpp index f357956..92ac3ba 100644 --- a/src/corelib/tools/qregexp.cpp +++ b/src/corelib/tools/qregexp.cpp @@ -4076,6 +4076,6 @@ QDataStream &operator>>(QDataStream &in, QRegExp ®Exp) regExp = newRegExp; return in; } -#endif +#endif // QT_NO_DATASTREAM QT_END_NAMESPACE diff --git a/src/corelib/tools/qringbuffer_p.h b/src/corelib/tools/qringbuffer_p.h index a1bfab0..939f4e0 100644 --- a/src/corelib/tools/qringbuffer_p.h +++ b/src/corelib/tools/qringbuffer_p.h @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE -class Q_CORE_EXPORT QRingBuffer +class QRingBuffer { public: inline QRingBuffer(int growth = 4096) : basicBlockSize(growth) { @@ -74,6 +74,52 @@ public: return buffers.isEmpty() ? 0 : (buffers.first().constData() + head); } + // access the bytes at a specified position + // the out-variable length will contain the amount of bytes readable + // from there, e.g. the amount still the same QByteArray + inline const char *readPointerAtPosition(qint64 pos, qint64 &length) const { + if (buffers.isEmpty()) { + length = 0; + return 0; + } + + if (pos >= bufferSize) { + length = 0; + return 0; + } + + // special case: it is in the first buffer + int nextDataBlockSizeValue = nextDataBlockSize(); + if (pos - head < nextDataBlockSizeValue) { + length = nextDataBlockSizeValue - pos; + return buffers.at(0).constData() + head + pos; + } + + // special case: we only had one buffer and tried to read over it + if (buffers.length() == 1) { + length = 0; + return 0; + } + + // skip the first + pos -= nextDataBlockSizeValue; + + // normal case: it is somewhere in the second to the-one-before-the-tailBuffer + for (int i = 1; i < tailBuffer; i++) { + if (pos >= buffers[i].size()) { + pos -= buffers[i].size(); + continue; + } + + length = buffers[i].length() - pos; + return buffers[i].constData() + pos; + } + + // it is in the tail buffer + length = tail - pos; + return buffers[tailBuffer].constData() + pos; + } + inline void free(int bytes) { bufferSize -= bytes; if (bufferSize < 0) diff --git a/src/corelib/tools/qset.h b/src/corelib/tools/qset.h index 4b19adc..993ce48 100644 --- a/src/corelib/tools/qset.h +++ b/src/corelib/tools/qset.h @@ -85,6 +85,8 @@ public: inline bool contains(const T &value) const { return q_hash.contains(value); } + bool contains(const QSet<T> &set) const; + class const_iterator; class iterator @@ -274,6 +276,18 @@ Q_INLINE_TEMPLATE QSet<T> &QSet<T>::subtract(const QSet<T> &other) return *this; } +template <class T> +Q_INLINE_TEMPLATE bool QSet<T>::contains(const QSet<T> &other) const +{ + typename QSet<T>::const_iterator i = other.constBegin(); + while (i != other.constEnd()) { + if (!contains(*i)) + return false; + ++i; + } + return true; +} + template <typename T> Q_OUTOFLINE_TEMPLATE QList<T> QSet<T>::toList() const { diff --git a/src/corelib/tools/qsize.h b/src/corelib/tools/qsize.h index 57c55ca..237625e 100644 --- a/src/corelib/tools/qsize.h +++ b/src/corelib/tools/qsize.h @@ -98,8 +98,10 @@ Q_DECLARE_TYPEINFO(QSize, Q_MOVABLE_TYPE); QSize stream functions *****************************************************************************/ +#ifndef QT_NO_DATASTREAM Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QSize &); Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QSize &); +#endif /***************************************************************************** @@ -171,14 +173,14 @@ inline const QSize operator*(qreal c, const QSize &s) inline QSize &QSize::operator/=(qreal c) { - Q_ASSERT(!qFuzzyCompare(c + 1, 1)); + Q_ASSERT(!qFuzzyIsNull(c)); wd = qRound(wd/c); ht = qRound(ht/c); return *this; } inline const QSize operator/(const QSize &s, qreal c) { - Q_ASSERT(!qFuzzyCompare(c + 1, 1)); + Q_ASSERT(!qFuzzyIsNull(c)); return QSize(qRound(s.wd/c), qRound(s.ht/c)); } @@ -249,8 +251,10 @@ Q_DECLARE_TYPEINFO(QSizeF, Q_MOVABLE_TYPE); QSizeF stream functions *****************************************************************************/ +#ifndef QT_NO_DATASTREAM Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QSizeF &); Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QSizeF &); +#endif /***************************************************************************** @@ -327,14 +331,14 @@ inline const QSizeF operator*(qreal c, const QSizeF &s) inline QSizeF &QSizeF::operator/=(qreal c) { - Q_ASSERT(!qFuzzyCompare(c + 1, 1)); + Q_ASSERT(!qFuzzyIsNull(c)); wd = wd/c; ht = ht/c; return *this; } inline const QSizeF operator/(const QSizeF &s, qreal c) { - Q_ASSERT(!qFuzzyCompare(c + 1, 1)); + Q_ASSERT(!qFuzzyIsNull(c)); return QSizeF(s.wd/c, s.ht/c); } diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index e61a568..6ae3c38 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -195,6 +195,68 @@ static int ucstrnicmp(const ushort *a, const ushort *b, int l) return ucstricmp(a, a + l, b, b + l); } +static bool qMemEquals(const quint16 *a, const quint16 *b, int length) +{ + // Benchmarking indicates that doing memcmp is much slower than + // executing the comparison ourselves. + // To make it even faster, we do a 32-bit comparison, comparing + // twice the amount of data as a normal word-by-word comparison. + // + // Benchmarking results on a 2.33 GHz Core2 Duo, with a 64-QChar + // block of data, with 4194304 iterations (per iteration): + // operation usec cpu ticks + // memcmp 330 710 + // 16-bit 79 167-171 + // 32-bit aligned 49 105-109 + // + // Testing also indicates that unaligned 32-bit loads are as + // performant as 32-bit aligned. + if (a == b || !length) + return true; + + register union { + const quint16 *w; + const quint32 *d; + quintptr value; + } sa, sb; + sa.w = a; + sb.w = b; + + // check alignment + if ((sa.value & 2) == (sb.value & 2)) { + // both addresses have the same alignment + if (sa.value & 2) { + // both addresses are not aligned to 4-bytes boundaries + // compare the first character + if (*sa.w != *sb.w) + return false; + --length; + ++sa.w; + ++sb.w; + + // now both addresses are 4-bytes aligned + } + + // both addresses are 4-bytes aligned + // do a fast 32-bit comparison + register const quint32 *e = sa.d + (length >> 1); + for ( ; sa.d != e; ++sa.d, ++sb.d) { + if (*sa.d != *sb.d) + return false; + } + + // do we have a tail? + return (length & 1) ? *sa.w == *sb.w : true; + } else { + // one of the addresses isn't 4-byte aligned but the other is + register const quint16 *e = sa.w + length; + for ( ; sa.w != e; ++sa.w, ++sb.w) { + if (*sa.w != *sb.w) + return false; + } + } + return true; +} /*! \internal @@ -884,6 +946,23 @@ QString::QString(int size, QChar ch) } } +/*! + Constructs a string of the given \a size without initializing the + characters. This is only used in \c QStringBuilder::toString(). + + \internal +*/ + +QString::QString(int size, Uninitialized) +{ + d = (Data*) qMalloc(sizeof(Data)+size*sizeof(QChar)); + d->ref = 1; + d->alloc = d->size = size; + d->clean = d->asciiCache = d->simpletext = d->righttoleft = d->capacity = 0; + d->data = d->array; + d->array[size] = '\0'; +} + /*! \fn QString::QString(const QLatin1String &str) Constructs a copy of the Latin-1 string \a str. @@ -1910,8 +1989,10 @@ QString &QString::replace(QChar c, const QLatin1String &after, Qt::CaseSensitivi */ bool QString::operator==(const QString &other) const { - return (size() == other.size()) && - (memcmp((char*)unicode(),(char*)other.unicode(), size()*sizeof(QChar))==0); + if (d->size != other.d->size) + return false; + + return qMemEquals(d->data, other.d->data, d->size); } /*! @@ -3138,7 +3219,7 @@ bool QString::startsWith(const QString& s, Qt::CaseSensitivity cs) const if (s.d->size > d->size) return false; if (cs == Qt::CaseSensitive) { - return memcmp((char*)d->data, (char*)s.d->data, s.d->size*sizeof(QChar)) == 0; + return qMemEquals(d->data, s.d->data, s.d->size); } else { uint last = 0; uint olast = 0; @@ -3209,7 +3290,7 @@ bool QString::endsWith(const QString& s, Qt::CaseSensitivity cs) const if (pos < 0) return false; if (cs == Qt::CaseSensitive) { - return memcmp((char*)&d->data[pos], (char*)s.d->data, s.d->size*sizeof(QChar)) == 0; + return qMemEquals(d->data + pos, s.d->data, s.d->size); } else { uint last = 0; uint olast = 0; @@ -7696,7 +7777,8 @@ QString QStringRef::toString() const { */ bool operator==(const QStringRef &s1,const QStringRef &s2) { return (s1.size() == s2.size() && - (memcmp((char*)s1.unicode(), (char*)s2.unicode(), s1.size()*sizeof(QChar))==0)); } + qMemEquals((const ushort *)s1.unicode(), (const ushort *)s2.unicode(), s1.size())); +} /*! \relates QStringRef @@ -7705,7 +7787,8 @@ bool operator==(const QStringRef &s1,const QStringRef &s2) */ bool operator==(const QString &s1,const QStringRef &s2) { return (s1.size() == s2.size() && - (memcmp((char*)s1.unicode(), (char*)s2.unicode(), s1.size()*sizeof(QChar))==0)); } + qMemEquals((const ushort *)s1.unicode(), (const ushort *)s2.unicode(), s1.size())); +} /*! \relates QStringRef diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 69c4f2f..7c0d6a3 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -579,6 +579,9 @@ public: bool isSimpleText() const { if (!d->clean) updateProperties(); return d->simpletext; } bool isRightToLeft() const { if (!d->clean) updateProperties(); return d->righttoleft; } + struct Uninitialized {}; + QString(int size, Uninitialized); + private: #if defined(QT_NO_CAST_FROM_ASCII) && !defined(Q_NO_DECLARED_NOT_DEFINED) QString &operator+=(const char *s); @@ -1001,13 +1004,15 @@ inline int QByteArray::findRev(const QString &s, int from) const # endif // QT3_SUPPORT #endif // QT_NO_CAST_TO_ASCII +#ifndef QT_USE_FAST_OPERATOR_PLUS +# ifndef QT_USE_FAST_CONCATENATION inline const QString operator+(const QString &s1, const QString &s2) { QString t(s1); t += s2; return t; } inline const QString operator+(const QString &s1, QChar s2) { QString t(s1); t += s2; return t; } inline const QString operator+(QChar s1, const QString &s2) { QString t(s1); t += s2; return t; } -#ifndef QT_NO_CAST_FROM_ASCII +# ifndef QT_NO_CAST_FROM_ASCII inline QT_ASCII_CAST_WARN const QString operator+(const QString &s1, const char *s2) { QString t(s1); t += QString::fromAscii(s2); return t; } inline QT_ASCII_CAST_WARN const QString operator+(const char *s1, const QString &s2) @@ -1020,7 +1025,9 @@ inline QT_ASCII_CAST_WARN const QString operator+(const QByteArray &ba, const QS { QString t = QString::fromAscii(ba.constData(), qstrnlen(ba.constData(), ba.size())); t += s; return t; } inline QT_ASCII_CAST_WARN const QString operator+(const QString &s, const QByteArray &ba) { QString t(s); t += QString::fromAscii(ba.constData(), qstrnlen(ba.constData(), ba.size())); return t; } -#endif +# endif // QT_NO_CAST_FROM_ASCII +# endif // QT_USE_FAST_CONCATENATION +#endif // QT_USE_FAST_OPERATOR_PLUS #ifndef QT_NO_STL inline std::string QString::toStdString() const @@ -1229,6 +1236,8 @@ inline int QStringRef::localeAwareCompare(const QStringRef &s1, const QStringRef QT_END_NAMESPACE -QT_END_HEADER +#ifdef QT_USE_FAST_CONCATENATION +#include <QtCore/qstringbuilder.h> +#endif #endif // QSTRING_H diff --git a/src/corelib/tools/qstringbuilder.cpp b/src/corelib/tools/qstringbuilder.cpp new file mode 100644 index 0000000..366a07b --- /dev/null +++ b/src/corelib/tools/qstringbuilder.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** 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 "qstringbuilder.h" + +/*! + \class QLatin1Literal + \reentrant + \since 4.6 + + \brief The QLatin1Literal class provides a thin wrapper around string + literals used in source code. + + \ingroup tools + \ingroup shared + \ingroup text + \mainclass + + Unlike \c QLatin1String, a \c QLatin1Literal can retrieve its size + without iterating over the literal. + + The main use of \c QLatin1Literal is in conjunction with \c QStringBuilder + to reduce the number of reallocations needed to build up a string from + smaller chunks. + + \sa QStringBuilder, QLatin1String, QString, QStringRef +*/ + +/*! \fn int QLatin1Literal::size() const + + Returns the number of characters in the literal \e{excluding} the trailing + NULL char. +*/ + +/*! \fn QLatin1Literal::QLatin1Literal(const char(&string)[N]) + + Constructs a new literal from the given \a string. +*/ + +/*! \fn char *QLatin1Literal::data() const + + Returns a pointer to the first character of the string literal. + The string literal is terminated by a NUL character. +*/ + +/*! \fn QLatin1Literal::operator QString() const + + Converts the \c QLatin1Literal into a \c QString object. +*/ + + + +/*! + \class QStringBuilder + \reentrant + \since 4.6 + + \brief The QStringBuilder class is a template class that provides a facility to build + up QStrings from smaller chunks. + + \ingroup tools + \ingroup shared + \ingroup text + \mainclass + + When creating strings from smaller chunks, typically \c QString::operator+() + is used, resulting in \e{n - 1} reallocations when operating on \e{n} chunks. + + QStringBuilder uses expression templates to collect the individual parts, + compute the total size, allocate memory for the resulting QString object, + and copy the contents of the chunks into the result. + + The QStringBuilder class is not to be used explicitly in user code. + Instances of the class are created as return values of the operator%() + function, acting on objects of type \c QString, \c QLatin1String, + \c QLatin1Literal, \c \QStringRef, \c QChar, \c QCharRef, + \c QLatin1Char, and \c char. + + Concatenating strings with operator%() generally yields better + performance then using \c QString::operator+() on the same chunks + if there are three or more of them, and performs equally well in other + cases. + + \sa QLatin1Literal, QString +*/ + +/* \fn template <class A, class B> QStringBuilder<A, B> operator%(const A &a, const B &b) + + Returns a \c QStringBuilder object that is converted to a QString object + when assigned to a variable of QString type or passed to a function that + takes a QString parameter. + + This function is usable with arguments of type \c QString, + \c QLatin1String, \c QLatin1Literal, \c QStringRef, + \c QChar, \c QCharRef, \c QLatin1Char, and \c char. +*/ + diff --git a/src/corelib/tools/qstringbuilder.h b/src/corelib/tools/qstringbuilder.h new file mode 100644 index 0000000..30a7c67 --- /dev/null +++ b/src/corelib/tools/qstringbuilder.h @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** 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 QSTRINGBUILDER_H +#define QSTRINGBUILDER_H + +#include <QtCore/qstring.h> + +#include <string.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +// ### Qt 5: merge with QLatin1String +class QLatin1Literal +{ +public: + template <int N> + QLatin1Literal(const char (&str)[N]) : m_size(N - 1), m_data(str) {} + + inline int size() const { return m_size; } + inline const char *data() const { return m_data; } + +private: + const int m_size; + const char *m_data; +}; + + +template <typename T> class QConcatenable {}; + +template <typename A, typename B> +class QStringBuilder +{ +public: + QStringBuilder(const A &a_, const B &b_) : a(a_), b(b_) {} + + operator QString() const + { + QString s(QConcatenable< QStringBuilder<A, B> >::size(*this), + QString::Uninitialized()); + + QChar *d = s.data(); + QConcatenable< QStringBuilder<A, B> >::appendTo(*this, d); + return s; + } + QByteArray toLatin1() const { return QString(*this).toLatin1(); } + + const A &a; + const B &b; +}; + + +template <> struct QConcatenable<char> +{ + typedef char type; + static int size(const char) { return 1; } + static inline void appendTo(const char c, QChar *&out) + { + *out++ = QLatin1Char(c); + } +}; + +template <> struct QConcatenable<QLatin1Char> +{ + typedef QLatin1Char type; + static int size(const QLatin1Char) { return 1; } + static inline void appendTo(const QLatin1Char c, QChar *&out) + { + *out++ = c; + } +}; + +template <> struct QConcatenable<QChar> +{ + typedef QChar type; + static int size(const QChar) { return 1; } + static inline void appendTo(const QChar c, QChar *&out) + { + *out++ = c; + } +}; + +template <> struct QConcatenable<QCharRef> +{ + typedef QCharRef type; + static int size(const QCharRef &) { return 1; } + static inline void appendTo(const QCharRef &c, QChar *&out) + { + *out++ = QChar(c); + } +}; + +template <> struct QConcatenable<QLatin1String> +{ + typedef QLatin1String type; + static int size(const QLatin1String &a) { return qstrlen(a.latin1()); } + static inline void appendTo(const QLatin1String &a, QChar *&out) + { + for (const char *s = a.latin1(); *s; ) + *out++ = QLatin1Char(*s++); + } + +}; + +template <> struct QConcatenable<QLatin1Literal> +{ + typedef QLatin1Literal type; + static int size(const QLatin1Literal &a) { return a.size(); } + static inline void appendTo(const QLatin1Literal &a, QChar *&out) + { + for (const char *s = a.data(); *s; ) + *out++ = QLatin1Char(*s++); + } +}; + +template <> struct QConcatenable<QString> +{ + typedef QString type; + static int size(const QString &a) { return a.size(); } + static inline void appendTo(const QString &a, QChar *&out) + { + const int n = a.size(); + memcpy(out, (char*)a.constData(), sizeof(QChar) * n); + out += n; + } +}; + +template <> struct QConcatenable<QStringRef> +{ + typedef QStringRef type; + static int size(const QStringRef &a) { return a.size(); } + static inline void appendTo(QStringRef a, QChar *&out) + { + const int n = a.size(); + memcpy(out, (char*)a.constData(), sizeof(QChar) * n); + out += n; + } +}; + +#ifndef QT_NO_CAST_FROM_ASCII +template <int N> struct QConcatenable<char[N]> +{ + typedef char type[N]; + static int size(const char[N]) { return N - 1; } + static inline void appendTo(const char a[N], QChar *&out) + { + for (int i = 0; i < N - 1; ++i) + *out++ = QLatin1Char(a[i]); + } +}; + +template <> struct QConcatenable<const char *> +{ + typedef char const *type; + static int size(const char *a) { return qstrlen(a); } + static inline void appendTo(const char *a, QChar *&out) + { + while (*a) + *out++ = QLatin1Char(*a++); + } +}; +#endif + +template <typename A, typename B> +struct QConcatenable< QStringBuilder<A, B> > +{ + typedef QStringBuilder<A, B> type; + static int size(const type &p) + { + return QConcatenable<A>::size(p.a) + QConcatenable<B>::size(p.b); + } + static inline void appendTo(const QStringBuilder<A, B> &p, QChar *&out) + { + QConcatenable<A>::appendTo(p.a, out); + QConcatenable<B>::appendTo(p.b, out); + } +}; + +template <typename A, typename B> +QStringBuilder<typename QConcatenable<A>::type, typename QConcatenable<B>::type> +operator%(const A &a, const B &b) +{ + return QStringBuilder<A, B>(a, b); +} + +#ifdef QT_USE_FAST_OPERATOR_PLUS +template <typename A, typename B> +QStringBuilder<typename QConcatenable<A>::type, typename QConcatenable<B>::type> +operator+(const A &a, const B &b) +{ + return QStringBuilder<A, B>(a, b); +} +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSTRINGBUILDER_H diff --git a/src/corelib/tools/qtimeline.cpp b/src/corelib/tools/qtimeline.cpp index 3a03558..ee4f73c 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) { @@ -523,12 +509,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; } /*! @@ -608,42 +650,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..48c9232 100644 --- a/src/corelib/tools/qtimeline.h +++ b/src/corelib/tools/qtimeline.h @@ -42,6 +42,7 @@ #ifndef QTIMELINE_H #define QTIMELINE_H +#include <QtCore/qeasingcurve.h> #include <QtCore/qobject.h> QT_BEGIN_HEADER @@ -60,6 +61,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 +107,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/qunicodetables_p.h b/src/corelib/tools/qunicodetables_p.h index e588313..5f696dd 100644 --- a/src/corelib/tools/qunicodetables_p.h +++ b/src/corelib/tools/qunicodetables_p.h @@ -178,7 +178,7 @@ namespace QUnicodeTables { } Q_CORE_EXPORT int QT_FASTCALL script(uint ucs4); - Q_CORE_EXPORT_INLINE int QT_FASTCALL script(const QChar &ch) { + inline int script(const QChar &ch) { return script(ch.unicode()); } diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 610b8e7..d9fad3a 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -83,19 +83,9 @@ struct Q_CORE_EXPORT QVectorData }; template <typename T> -struct QVectorTypedData -{ - QBasicAtomicInt ref; - int alloc; - int size; -#if defined(QT_ARCH_SPARC) && defined(Q_CC_GNU) && defined(__LP64__) && defined(QT_BOOTSTRAPPED) - // workaround for bug in gcc 3.4.2 - uint sharable; - uint capacity; -#else - uint sharable : 1; - uint capacity : 1; -#endif +struct QVectorTypedData : private QVectorData +{ // private inheritance as we must not access QVectorData member thought QVectorTypedData + // as this would break strict aliasing rules. (in the case of shared_null) T array[1]; }; @@ -105,14 +95,21 @@ template <typename T> class QVector { typedef QVectorTypedData<T> Data; - union { QVectorData *p; QVectorTypedData<T> *d; }; + union { + QVectorData *d; +#if defined(Q_CC_SUN) && (__SUNPRO_CC <= 0x550) + QVectorTypedData<T> *p; +#else + Data *p; +#endif + }; public: - inline QVector() : p(&QVectorData::shared_null) { d->ref.ref(); } + inline QVector() : d(&QVectorData::shared_null) { d->ref.ref(); } explicit QVector(int size); QVector(int size, const T &t); inline QVector(const QVector<T> &v) : d(v.d) { d->ref.ref(); if (!d->sharable) detach_helper(); } - inline ~QVector() { if (!d) return; if (!d->ref.deref()) free(d); } + inline ~QVector() { if (!d) return; if (!d->ref.deref()) free(p); } QVector<T> &operator=(const QVector<T> &v); bool operator==(const QVector<T> &v) const; inline bool operator!=(const QVector<T> &v) const { return !(*this == v); } @@ -131,9 +128,9 @@ public: inline bool isDetached() const { return d->ref == 1; } inline void setSharable(bool sharable) { if (!sharable) detach(); d->sharable = sharable; } - inline T *data() { detach(); return d->array; } - inline const T *data() const { return d->array; } - inline const T *constData() const { return d->array; } + inline T *data() { detach(); return p->array; } + inline const T *data() const { return p->array; } + inline const T *constData() const { return p->array; } void clear(); const T &at(int i) const; @@ -226,12 +223,12 @@ public: typedef T* iterator; typedef const T* const_iterator; #endif - inline iterator begin() { detach(); return d->array; } - inline const_iterator begin() const { return d->array; } - inline const_iterator constBegin() const { return d->array; } - inline iterator end() { detach(); return d->array + d->size; } - inline const_iterator end() const { return d->array + d->size; } - inline const_iterator constEnd() const { return d->array + d->size; } + inline iterator begin() { detach(); return p->array; } + inline const_iterator begin() const { return p->array; } + inline const_iterator constBegin() const { return p->array; } + inline iterator end() { detach(); return p->array + d->size; } + inline const_iterator end() const { return p->array + d->size; } + inline const_iterator constEnd() const { return p->array + d->size; } iterator insert(iterator before, int n, const T &x); inline iterator insert(iterator before, const T &x) { return insert(before, 1, x); } iterator erase(iterator begin, iterator end); @@ -328,11 +325,11 @@ inline void QVector<T>::clear() template <typename T> inline const T &QVector<T>::at(int i) const { Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::at", "index out of range"); - return d->array[i]; } + return p->array[i]; } template <typename T> inline const T &QVector<T>::operator[](int i) const { Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::operator[]", "index out of range"); - return d->array[i]; } + return p->array[i]; } template <typename T> inline T &QVector<T>::operator[](int i) { Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::operator[]", "index out of range"); @@ -370,7 +367,7 @@ QVector<T> &QVector<T>::operator=(const QVector<T> &v) { v.d->ref.ref(); if (!d->ref.deref()) - free(d); + free(p); d = v.d; if (!d->sharable) detach_helper(); @@ -388,31 +385,31 @@ inline QVectorData *QVector<T>::malloc(int aalloc) template <typename T> QVector<T>::QVector(int asize) { - p = malloc(asize); + d = malloc(asize); d->ref = 1; d->alloc = d->size = asize; d->sharable = true; d->capacity = false; if (QTypeInfo<T>::isComplex) { - T* b = d->array; - T* i = d->array + d->size; + T* b = p->array; + T* i = p->array + d->size; while (i != b) new (--i) T; } else { - qMemSet(d->array, 0, asize * sizeof(T)); + qMemSet(p->array, 0, asize * sizeof(T)); } } template <typename T> QVector<T>::QVector(int asize, const T &t) { - p = malloc(asize); + d = malloc(asize); d->ref = 1; d->alloc = d->size = asize; d->sharable = true; d->capacity = false; - T* i = d->array + d->size; - while (i != d->array) + T* i = p->array + d->size; + while (i != p->array) new (--i) T(t); } @@ -421,7 +418,7 @@ void QVector<T>::free(Data *x) { if (QTypeInfo<T>::isComplex) { T* b = x->array; - T* i = b + x->size; + T* i = b + reinterpret_cast<QVectorData *>(x)->size; while (i-- != b) i->~T(); } @@ -433,7 +430,7 @@ void QVector<T>::realloc(int asize, int aalloc) { T *pOld; T *pNew; - union { QVectorData *p; Data *d; } x; + union { QVectorData *d; Data *p; } x; x.d = d; if (QTypeInfo<T>::isComplex && asize < d->size && d->ref == 1 ) { @@ -506,7 +503,7 @@ void QVector<T>::realloc(int asize, int aalloc) if (d != x.d) { if (!d->ref.deref()) - free(d); + free(p); d = x.d; } } @@ -514,15 +511,15 @@ void QVector<T>::realloc(int asize, int aalloc) template<typename T> Q_OUTOFLINE_TEMPLATE T QVector<T>::value(int i) const { - if (i < 0 || i >= p->size) { + if (i < 0 || i >= d->size) { return T(); } - return d->array[i]; + return p->array[i]; } template<typename T> Q_OUTOFLINE_TEMPLATE T QVector<T>::value(int i, const T &defaultValue) const { - return ((i < 0 || i >= p->size) ? defaultValue : d->array[i]); + return ((i < 0 || i >= d->size) ? defaultValue : p->array[i]); } template <typename T> @@ -533,14 +530,14 @@ void QVector<T>::append(const T &t) realloc(d->size, QVectorData::grow(sizeOfTypedData(), d->size + 1, sizeof(T), QTypeInfo<T>::isStatic)); if (QTypeInfo<T>::isComplex) - new (d->array + d->size) T(copy); + new (p->array + d->size) T(copy); else - d->array[d->size] = copy; + p->array[d->size] = copy; } else { if (QTypeInfo<T>::isComplex) - new (d->array + d->size) T(t); + new (p->array + d->size) T(t); else - d->array[d->size] = t; + p->array[d->size] = t; } ++d->size; } @@ -548,27 +545,27 @@ void QVector<T>::append(const T &t) template <typename T> Q_TYPENAME QVector<T>::iterator QVector<T>::insert(iterator before, size_type n, const T &t) { - int offset = before - d->array; + int offset = before - p->array; if (n != 0) { const T copy(t); if (d->ref != 1 || d->size + n > d->alloc) realloc(d->size, QVectorData::grow(sizeOfTypedData(), d->size + n, sizeof(T), QTypeInfo<T>::isStatic)); if (QTypeInfo<T>::isStatic) { - T *b = d->array + d->size; - T *i = d->array + d->size + n; + T *b = p->array + d->size; + T *i = p->array + d->size + n; while (i != b) new (--i) T; - i = d->array + d->size; + i = p->array + d->size; T *j = i + n; - b = d->array + offset; + b = p->array + offset; while (i != b) *--j = *--i; i = b+n; while (i != b) *--i = copy; } else { - T *b = d->array + offset; + T *b = p->array + offset; T *i = b + n; memmove(i, b, (d->size - offset) * sizeof(T)); while (i != b) @@ -576,29 +573,29 @@ Q_TYPENAME QVector<T>::iterator QVector<T>::insert(iterator before, size_type n, } d->size += n; } - return d->array + offset; + return p->array + offset; } template <typename T> Q_TYPENAME QVector<T>::iterator QVector<T>::erase(iterator abegin, iterator aend) { - int f = abegin - d->array; - int l = aend - d->array; + int f = abegin - p->array; + int l = aend - p->array; int n = l - f; detach(); if (QTypeInfo<T>::isComplex) { - qCopy(d->array+l, d->array+d->size, d->array+f); - T *i = d->array+d->size; - T* b = d->array+d->size-n; + qCopy(p->array+l, p->array+d->size, p->array+f); + T *i = p->array+d->size; + T* b = p->array+d->size-n; while (i != b) { --i; i->~T(); } } else { - memmove(d->array + f, d->array + l, (d->size-l)*sizeof(T)); + memmove(p->array + f, p->array + l, (d->size-l)*sizeof(T)); } d->size -= n; - return d->array + f; + return p->array + f; } template <typename T> @@ -608,9 +605,9 @@ bool QVector<T>::operator==(const QVector<T> &v) const return false; if (d == v.d) return true; - T* b = d->array; + T* b = p->array; T* i = b + d->size; - T* j = v.d->array + d->size; + T* j = v.p->array + d->size; while (i != b) if (!(*--i == *--j)) return false; @@ -623,8 +620,8 @@ QVector<T> &QVector<T>::fill(const T &from, int asize) const T copy(from); resize(asize < 0 ? d->size : asize); if (d->size) { - T *i = d->array + d->size; - T *b = d->array; + T *i = p->array + d->size; + T *b = p->array; while (i != b) *--i = copy; } @@ -637,9 +634,9 @@ QVector<T> &QVector<T>::operator+=(const QVector &l) int newSize = d->size + l.d->size; realloc(d->size, newSize); - T *w = d->array + newSize; - T *i = l.d->array + l.d->size; - T *b = l.d->array; + T *w = p->array + newSize; + T *i = l.p->array + l.d->size; + T *b = l.p->array; while (i != b) { if (QTypeInfo<T>::isComplex) new (--w) T(*--i); @@ -656,11 +653,11 @@ int QVector<T>::indexOf(const T &t, int from) const if (from < 0) from = qMax(from + d->size, 0); if (from < d->size) { - T* n = d->array + from - 1; - T* e = d->array + d->size; + T* n = p->array + from - 1; + T* e = p->array + d->size; while (++n != e) if (*n == t) - return n - d->array; + return n - p->array; } return -1; } @@ -673,8 +670,8 @@ int QVector<T>::lastIndexOf(const T &t, int from) const else if (from >= d->size) from = d->size-1; if (from >= 0) { - T* b = d->array; - T* n = d->array + from + 1; + T* b = p->array; + T* n = p->array + from + 1; while (n != b) { if (*--n == t) return n - b; @@ -686,8 +683,8 @@ int QVector<T>::lastIndexOf(const T &t, int from) const template <typename T> bool QVector<T>::contains(const T &t) const { - T* b = d->array; - T* i = d->array + d->size; + T* b = p->array; + T* i = p->array + d->size; while (i != b) if (*--i == t) return true; @@ -698,8 +695,8 @@ template <typename T> int QVector<T>::count(const T &t) const { int c = 0; - T* b = d->array; - T* i = d->array + d->size; + T* b = p->array; + T* i = p->array + d->size; while (i != b) if (*--i == t) ++c; diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index 8d27100..cf13cc9 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 \ @@ -19,6 +20,7 @@ HEADERS += \ tools/qlocale_p.h \ tools/qlocale_data_p.h \ tools/qmap.h \ + tools/qcontiguouscache.h \ tools/qpodlist_p.h \ tools/qpoint.h \ tools/qqueue.h \ @@ -30,6 +32,7 @@ HEADERS += \ tools/qsize.h \ tools/qstack.h \ tools/qstring.h \ + tools/qstringbuilder.h \ tools/qstringlist.h \ tools/qstringmatcher.h \ tools/qtextboundaryfinder.h \ @@ -46,7 +49,7 @@ SOURCES += \ tools/qbytearraymatcher.cpp \ tools/qcryptographichash.cpp \ tools/qdatetime.cpp \ - tools/qdumper.cpp \ + tools/qeasingcurve.cpp \ tools/qhash.cpp \ tools/qline.cpp \ tools/qlinkedlist.cpp \ @@ -54,12 +57,14 @@ SOURCES += \ tools/qlocale.cpp \ tools/qpoint.cpp \ tools/qmap.cpp \ + tools/qcontiguouscache.cpp \ tools/qrect.cpp \ tools/qregexp.cpp \ tools/qshareddata.cpp \ tools/qsharedpointer.cpp \ tools/qsize.cpp \ tools/qstring.cpp \ + tools/qstringbuilder.cpp \ tools/qstringlist.cpp \ tools/qtextboundaryfinder.cpp \ tools/qtimeline.cpp \ diff --git a/src/corelib/xml/make-parser.sh b/src/corelib/xml/make-parser.sh index 0c2ba12..0e2cbe1 100755 --- a/src/corelib/xml/make-parser.sh +++ b/src/corelib/xml/make-parser.sh @@ -6,10 +6,8 @@ mkdir -p $me/out for f in $me/out/*.h; do n=$(basename $f) - p4 open $n cp $f $n done -p4 revert -a ... -p4 diff -dub ... +git diff . diff --git a/src/corelib/xml/qxmlstream.cpp b/src/corelib/xml/qxmlstream.cpp index 6fe6ebf..bce7161 100644 --- a/src/corelib/xml/qxmlstream.cpp +++ b/src/corelib/xml/qxmlstream.cpp @@ -628,7 +628,7 @@ while (<STDIN>) { $sizes[$i++] = $counter; $counter += length 1 + $_; } -print " \"\\0\";\n\nstatic const int QXmlStreamReader_tokenTypeString_indices[] = {\n "; +print " \"\\0\";\n\nstatic const short QXmlStreamReader_tokenTypeString_indices[] = {\n "; for ($j = 0; $j < $i; ++$j) { printf "$sizes[$j], "; } @@ -659,10 +659,9 @@ static const char QXmlStreamReader_tokenTypeString_string[] = "Comment\0" "DTD\0" "EntityReference\0" - "ProcessingInstruction\0" - "\0"; + "ProcessingInstruction\0"; -static const int QXmlStreamReader_tokenTypeString_indices[] = { +static const short QXmlStreamReader_tokenTypeString_indices[] = { 0, 8, 16, 30, 42, 55, 66, 77, 85, 89, 105, 0 }; @@ -3059,7 +3058,7 @@ QXmlStreamPrivateTagStack::NamespaceDeclaration &QXmlStreamWriterPrivate::findNa QString s; int n = ++namespacePrefixCount; forever { - s = QLatin1String("n") + QString::number(n++); + s = QLatin1Char('n') + QString::number(n++); int j = namespaceDeclarations.size() - 2; while (j >= 0 && namespaceDeclarations.at(j).prefix != s) --j; diff --git a/src/corelib/xml/qxmlstream.g b/src/corelib/xml/qxmlstream.g index d5e64a6..0cc744e 100644 --- a/src/corelib/xml/qxmlstream.g +++ b/src/corelib/xml/qxmlstream.g @@ -1533,8 +1533,8 @@ attribute ::= qname space_opt EQ space_opt attribute_value; QStringRef namespacePrefix = symString(attribute.key); QStringRef namespaceUri = symString(attribute.value); attributeStack.pop(); - if ((namespacePrefix == QLatin1String("xml") - ^ namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace")) + if (((namespacePrefix == QLatin1String("xml")) + ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace"))) || namespaceUri == QLatin1String("http://www.w3.org/2000/xmlns/") || namespaceUri.isEmpty() || namespacePrefix == QLatin1String("xmlns")) diff --git a/src/corelib/xml/qxmlstream_p.h b/src/corelib/xml/qxmlstream_p.h index 201f0c1..26509ff 100644 --- a/src/corelib/xml/qxmlstream_p.h +++ b/src/corelib/xml/qxmlstream_p.h @@ -1,3 +1,4 @@ +// This file was generated by qlalr - DO NOT EDIT! /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). @@ -126,27 +127,22 @@ public: GOTO_CHECK_OFFSET = 1017 }; - static const char *const spell []; - static const int lhs []; - static const int rhs []; - static const int goto_default []; - static const int action_default []; - static const int action_index []; - static const int action_info []; - static const int action_check []; + static const char *const spell []; + static const qint16 lhs []; + static const qint16 rhs []; + static const qint16 goto_default []; + static const qint16 action_default []; + static const qint16 action_index []; + static const qint16 action_info []; + static const qint16 action_check []; static inline int nt_action (int state, int nt) { - const int *const goto_index = &action_index [GOTO_INDEX_OFFSET]; - const int *const goto_check = &action_check [GOTO_CHECK_OFFSET]; - - const int yyn = goto_index [state] + nt; - - if (yyn < 0 || goto_check [yyn] != nt) + const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt; + if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt) return goto_default [nt]; - const int *const goto_info = &action_info [GOTO_INFO_OFFSET]; - return goto_info [yyn]; + return action_info [GOTO_INFO_OFFSET + yyn]; } static inline int t_action (int state, int token) @@ -169,7 +165,7 @@ const char *const QXmlStreamReader_Table::spell [] = { "EMPTY", "ANY", "PCDATA", 0, 0, 0, 0, "CDATA", "ID", "IDREF", "IDREFS", "ENTITIES", "NMTOKEN", "NMTOKENS", "<?xml", "version", 0}; -const int QXmlStreamReader_Table::lhs [] = { +const qint16 QXmlStreamReader_Table::lhs [] = { 57, 57, 59, 59, 59, 59, 59, 59, 59, 59, 67, 68, 64, 72, 72, 72, 75, 66, 66, 66, 66, 79, 78, 80, 80, 80, 80, 80, 80, 81, @@ -198,7 +194,7 @@ const int QXmlStreamReader_Table::lhs [] = { 58, 58, 58, 58, 58, 58, 58, 58, 74, 69, 69, 77, 111, 102, 102, 102, 102, 102, 140}; -const int QXmlStreamReader_Table:: rhs[] = { +const qint16 QXmlStreamReader_Table:: rhs[] = { 2, 1, 4, 2, 2, 2, 2, 2, 2, 0, 1, 1, 9, 2, 4, 0, 4, 4, 6, 6, 4, 1, 3, 1, 1, 1, 2, 2, 2, 1, @@ -227,7 +223,7 @@ const int QXmlStreamReader_Table:: rhs[] = { 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2}; -const int QXmlStreamReader_Table::action_default [] = { +const qint16 QXmlStreamReader_Table::action_default [] = { 10, 258, 0, 2, 1, 0, 124, 116, 118, 119, 126, 128, 122, 11, 113, 107, 0, 108, 127, 110, 114, 112, 120, 123, 125, 106, 109, 111, 117, 115, @@ -272,7 +268,7 @@ const int QXmlStreamReader_Table::action_default [] = { 260, 0, 230, 0, 0, 13, 269, 9, 5, 8, 4, 0, 7, 258, 6, 0, 3}; -const int QXmlStreamReader_Table::goto_default [] = { +const qint16 QXmlStreamReader_Table::goto_default [] = { 2, 4, 3, 49, 388, 43, 37, 52, 47, 41, 249, 53, 127, 84, 393, 81, 85, 126, 42, 46, 169, 130, 131, 146, 145, 149, 138, 136, 140, 147, @@ -283,7 +279,7 @@ const int QXmlStreamReader_Table::goto_default [] = { 264, 252, 251, 250, 339, 326, 325, 329, 398, 399, 50, 51, 59, 0}; -const int QXmlStreamReader_Table::action_index [] = { +const qint16 QXmlStreamReader_Table::action_index [] = { -21, -57, 33, 119, 960, 70, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, 105, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, @@ -372,7 +368,7 @@ const int QXmlStreamReader_Table::action_index [] = { 28, 15, 82, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, 3, -84, 98, -84}; -const int QXmlStreamReader_Table::action_info [] = { +const qint16 QXmlStreamReader_Table::action_info [] = { 65, 332, 65, 405, 392, 385, 377, 65, 414, 410, 415, 55, 397, 374, 373, 217, 206, 408, 65, 65, 207, 211, 216, 1, 55, 199, 182, 192, 70, 70, @@ -506,7 +502,7 @@ const int QXmlStreamReader_Table::action_info [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -const int QXmlStreamReader_Table::action_check [] = { +const qint16 QXmlStreamReader_Table::action_check [] = { 26, 18, 26, 14, 4, 4, 4, 26, 24, 14, 4, 26, 4, 4, 4, 4, 22, 55, 26, 26, 42, 4, 4, 44, 26, 22, 19, 12, 2, 2, |