--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hbcore/effects/hbeffectgroup.cpp Mon Apr 19 14:02:13 2010 +0300
@@ -0,0 +1,528 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (developer.feedback@nokia.com)
+**
+** This file is part of the HbCore module of the UI Extensions for Mobile.
+**
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at developer.feedback@nokia.com.
+**
+****************************************************************************/
+
+#include "hbeffectgroup_p.h"
+#include "hbeffectabstract_p.h"
+#include "hbeffect.h"
+#include "hbtimer_p.h"
+#include "hbeffectdef_p.h"
+#include "hbeffectinternal_p.h"
+#include "hbmainwindow.h"
+#include "hbinstance.h"
+
+#include <qglobal.h>
+#include <QMetaObject>
+#include <QTransform>
+#include <QGraphicsItem>
+#include <QGraphicsWidget>
+#include <QGraphicsView>
+#include <QTimer>
+
+#ifdef HB_FILTER_EFFECTS
+#include "hbvgeffect_p.h"
+#include "hbvgchainedeffect_p.h"
+#endif
+
+
+HbEffectGroup::HbEffectGroup(
+ const QString &effectEventType,
+ QGraphicsItem *registrationItem,
+ QGraphicsItem *targetItem,
+ const QString &itemType)
+ : mRegistrationItem(registrationItem),
+ mTargetItem(targetItem),
+ mEffectEventType(effectEventType),
+ mItemType(itemType),
+ mDirty(false),
+ mVgEffect(0),
+ mVgEffectActivated(false),
+ mFinishedCount(0),
+ mObserver(0),
+ mRunningState(NotRunning),
+ mLooping(false),
+ mView(0),
+ mHideWhenFinished(false)
+{
+}
+
+HbEffectGroup::~HbEffectGroup()
+{
+ qDeleteAll(mEffects);
+
+#ifdef HB_FILTER_EFFECTS
+ // Delete the vg effect if its ownership has not been transferred to the graphics item
+ if (!mVgEffectActivated && !mVgEffectGuard.isNull()) {
+ delete mVgEffect;
+ }
+#endif
+}
+
+QString HbEffectGroup::effectEventType() const
+{
+ return mEffectEventType;
+}
+
+QString HbEffectGroup::itemType() const
+{
+ return mItemType;
+}
+
+QGraphicsItem *HbEffectGroup::registrationItem() const
+{
+ return mRegistrationItem;
+}
+
+QGraphicsItem *HbEffectGroup::targetItem() const
+{
+ return mTargetItem;
+}
+
+void HbEffectGroup::addEffect(HbEffectAbstract *effect)
+{
+ mEffects.append(effect);
+}
+
+void HbEffectGroup::removeEffect(HbEffectAbstract *effect)
+{
+ mEffects.removeAll(effect);
+}
+
+/*
+* Fixes the order of the effects to be optimal for item transformations.
+* E.g. translate needs to be after scale because translating a scaled matrix would not lead to the wanted result.
+* Also there are some problems with rotation effect if it's done after scale effect
+*/
+void HbEffectGroup::fixEffectOrder()
+{
+ // If there is only one effect, no need to change order
+ if (mEffects.count() > 1) {
+ int last = mEffects.count() - 1;
+
+ for (int i = last; i >= 0; --i) {
+ HbEffectAbstract *effect = mEffects[i];
+ if (effect->name() == HB_EFFECT_NAME_TRANSLATE) {
+ // Move translate effect last in the effect list
+ mEffects.takeAt(i);
+ mEffects.append(effect);
+ }
+ }
+
+ for (int i = last; i >= 0; --i) {
+ HbEffectAbstract *effect = mEffects[i];
+ if (effect->name() == HB_EFFECT_NAME_SCALE) {
+ // Move scale effect second last in the effect list
+ mEffects.takeAt(i);
+ mEffects.insert(mEffects.size()-1, effect);
+ }
+ }
+
+ }
+}
+
+void HbEffectGroup::setObserver(QObject *observer, const QString &effectFinishedSlotName)
+{
+ mObserver = observer;
+ mEffectFinishedSlotName = effectFinishedSlotName;
+}
+
+void HbEffectGroup::updateItemTransform()
+{
+ QGraphicsView *gv(0);
+ // support for graphics view transforms
+ if (mTargetItem->type() == HbGVWrapperItemType) {
+ HbGVWrapperItem *gvw = static_cast<HbGVWrapperItem*>(mTargetItem);
+ if (gvw)
+ gv = gvw->mainWindow();
+ }
+ QTransform transform;
+
+ Q_FOREACH(HbEffectAbstract *effect, mEffects) {
+ if (effect)
+ effect->updateItemTransform(transform);
+ }
+ if (!gv)
+ mTargetItem->setTransform(transform);
+ else
+ gv->setTransform(transform);
+}
+
+bool HbEffectGroup::dirty() const
+{
+ return mDirty;
+}
+
+void HbEffectGroup::setDirty(bool dirty)
+{
+ mDirty = dirty;
+}
+
+int HbEffectGroup::effectCount() const
+{
+ return mEffects.count();
+}
+
+bool HbEffectGroup::isRunning() const
+{
+ return mRunningState != NotRunning;
+}
+
+void HbEffectGroup::setLooping(bool looping)
+{
+ mLooping = looping;
+}
+
+bool HbEffectGroup::isLooping() const
+{
+ return mLooping;
+}
+
+void HbEffectGroup::pause()
+{
+ Q_FOREACH(HbEffectAbstract *effect, mEffects) {
+ effect->pause();
+ }
+}
+
+void HbEffectGroup::resume()
+{
+ Q_FOREACH(HbEffectAbstract *effect, mEffects) {
+ effect->resume();
+ }
+}
+
+const QVariant &HbEffectGroup::userData() const
+{
+ return mUserData;
+}
+
+void HbEffectGroup::setUserData(const QVariant &userData)
+{
+ mUserData = userData;
+}
+
+QRectF HbEffectGroup::extRect() const
+{
+ return mExtRect;
+}
+
+void HbEffectGroup::setExtRect(const QRectF &extRect)
+{
+ mExtRect = extRect;
+
+ // This is needed to make scaling work from an extrect that has an equal size with the item's rect
+ if (mTargetItem) {
+ QRectF itemRect = mTargetItem->boundingRect();
+ qreal width = itemRect.width();
+ if (qFuzzyCompare(width, mExtRect.width())) {
+ mExtRect.setWidth(width + 0.00001);
+ }
+ qreal height = itemRect.height();
+ if (qFuzzyCompare(height, mExtRect.height())) {
+ mExtRect.setHeight(height + 0.00001);
+ }
+ }
+}
+
+HbView *HbEffectGroup::view() const
+{
+ return mView;
+}
+
+void HbEffectGroup::setView(HbView *view)
+{
+ mView = view;
+}
+
+#ifdef HB_FILTER_EFFECTS
+
+HbVgChainedEffect *HbEffectGroup::vgEffect()
+{
+ if (!mVgEffect) {
+ mVgEffect = new HbVgChainedEffect;
+ }
+ return mVgEffect;
+}
+
+void HbEffectGroup::activateVgEffect()
+{
+ if (!mVgEffectActivated) {
+ mVgEffectGuard = QPointer<QGraphicsEffect>();
+ vgEffect()->install(mTargetItem);
+ mVgEffectActivated = true;
+ }
+}
+
+/**
+* Removes the VG effect from the graphics item without deleting it.
+* The ownership is moved back to the effect group.
+*/
+void HbEffectGroup::deactivateVgEffect()
+{
+ if (mVgEffectActivated) {
+ // This does not delete the effect so ownership is transferred back to
+ // the effect group. However this is believed to be a bug in Qt 4.6.0
+ // so use a QPointer to make sure that we do not do double deletion in
+ // case Qt starts deleting the effect correctly in the future.
+ mVgEffectGuard = QPointer<QGraphicsEffect>(mTargetItem->graphicsEffect());
+ mTargetItem->setGraphicsEffect(0);
+ mVgEffectActivated = false;
+ }
+}
+
+#endif // HB_FILTER_EFFECTS
+
+void HbEffectGroup::startAll()
+{
+ if (!mEffects.empty()) {
+ mRunningState = Running;
+ mFinishedCount = 0;
+ }
+
+ // First resolve parameters and set the start states for all the effects.
+ // This is done before starting the effect animations to avoid screen flickering.
+
+ QTransform transform;
+
+ Q_FOREACH(HbEffectAbstract *effect, mEffects) {
+ // Resolve parameters etc.
+ effect->init();
+ if (effect->interval() == 0) {
+ // Set start state if effect starts immediately
+ effect->setStartState(transform);
+ }
+ }
+
+ mTargetItem->setTransform(transform);
+
+ if (mEffects.empty()) {
+ // No effect exists but user wants notification when effect finishes.
+ // Let the user do whatever he wanted to do when effect finishes.
+ invokeObserver(Hb::EffectNotStarted);
+ }
+ else {
+ // Start state has been set for all the effects,
+ // next step is to start the effect animations.
+ // Before that, resolve the view where the effect belongs if the effect is looping.
+ // This is needed for being able to pause looping effects when their view is inactive.
+ if (isLooping()) {
+ resolveView();
+ }
+
+ Q_FOREACH(HbEffectAbstract *effect, mEffects) {
+ // If the starttime is zero, start effect immediately
+ if (effect->interval() == 0) {
+ effect->start(); // This may call group's effectFinished if the effect was empty.
+ } else {
+ //Else register the effect to timeline to wait its turn.
+ HbTimer::instance()->registerEntry(effect);
+ }
+ }
+ }
+}
+
+void HbEffectGroup::resolveView() {
+ if (!mView) {
+ if (mTargetItem) {
+ QGraphicsScene *scene = mTargetItem->scene();
+ if (scene) {
+ // Resolve the main window having the same scene that the item belongs to
+ QList<HbMainWindow *> windowList = hbInstance->allMainWindows();
+ Q_FOREACH(const HbMainWindow *window, windowList) {
+ if (window->scene() == scene) {
+ mView = window->currentView();
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+bool HbEffectGroup::hasTranslateEffect() const
+{
+ foreach(HbEffectAbstract *effect, mEffects) {
+ if (effect->name() == HB_EFFECT_NAME_TRANSLATE) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool HbEffectGroup::hasRotateEffect() const
+{
+ foreach(HbEffectAbstract *effect, mEffects) {
+ if (effect->name() == HB_EFFECT_NAME_ROTATE) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool HbEffectGroup::hasScaleEffect() const
+{
+ foreach(HbEffectAbstract *effect, mEffects) {
+ if (effect->name() == HB_EFFECT_NAME_SCALE) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool HbEffectGroup::hasOpacityEffect() const
+{
+ foreach(HbEffectAbstract *effect, mEffects) {
+ if (effect->name() == HB_EFFECT_NAME_OPACITY) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void HbEffectGroup::doHideEffect(const QTransform *transform, bool opacityEffectUsed)
+{
+ mTargetItem->setTransform(transform ? *transform : QTransform());
+ if (opacityEffectUsed) {
+ // Hide opacity effect by setting item fully opaque regardless of what
+ // its opacity value was before the effect.
+ mTargetItem->setOpacity(1.0f);
+ }
+#ifdef HB_FILTER_EFFECTS
+ deactivateVgEffect();
+#endif
+}
+
+void HbEffectGroup::cancelAll(bool sendCallback, bool itemIsValid, bool hideEffect, const QTransform &initialItemTransform)
+{
+ QTransform transform;
+ bool opacityEffectUsed = false;
+
+ Q_FOREACH(HbEffectAbstract *effect, mEffects) {
+ if (effect) {
+ HbTimer::instance()->unregisterEntry(effect);
+ effect->cancel(transform, itemIsValid);
+ if (effect->name() == HB_EFFECT_NAME_OPACITY) {
+ opacityEffectUsed = true;
+ }
+ }
+ }
+
+ if (itemIsValid) {
+ // If effect needs to be removed, reset transform matrix and deactivate VG effect
+ if (hideEffect || mHideWhenFinished) {
+ doHideEffect(&initialItemTransform, opacityEffectUsed);
+ } else { // Otherwise set transform corresponding to the end state of the effect
+ mTargetItem->setTransform(initialItemTransform * transform);
+ }
+ }
+
+ // Do not set this to NotRunning before effect->cancel has been called because filter
+ // effects cancel does stuff that requires group->isRunning() return true.
+ mRunningState = NotRunning;
+
+ // Invoke observer with cancel signal
+ if (sendCallback)
+ invokeObserver(Hb::EffectCancelled);
+}
+
+void HbEffectGroup::effectFinished(Hb::EffectEvent reason)
+{
+ // Inform the animated item when the whole effect group has finished.
+ if (++mFinishedCount == mEffects.count()) {
+ mFinishedCount = 0;
+
+ // The animation framework funnily enough sends the finished signal before updating the animation with the final
+ // value, so here we set running state to NotRunning asynchronously so the effect's final value gets still updated.
+ mRunningState = FinishInProgress;
+ QTimer::singleShot(0, this, SLOT(clearEffectRunning()));
+
+ // Send callback if observer has been provided
+ invokeObserver(reason);
+ }
+
+ // The effect group object is not deleted here,
+ // because it would need to be removed from the list of the effect groups in
+ // the effect engine as well.
+ // It will be deleted and a new effect group created when the same effect is started again.
+}
+
+void HbEffectGroup::clearEffectRunning()
+{
+ // Comes here when effect has finished and running state is set to NotRunning asynchronously.
+ // Only set running state to 'NotRunning' if the finish is still in progress, i.e. the effect
+ // has not been restarted meanwhile.
+ if (mRunningState == FinishInProgress) {
+ mRunningState = NotRunning;
+ // We are finished either normally or with EffectNotStarted. It is now the time to
+ // get rid of all the "effects" caused by the effects in this group if the
+ // hide-when-finished flag is set.
+ if (mHideWhenFinished) {
+ doHideEffect(0, hasOpacityEffect());
+ }
+ }
+}
+
+void HbEffectGroup::invokeObserver(Hb::EffectEvent reason)
+{
+ // Send callback if observer has been provided
+ if (mRegistrationItem && mTargetItem && mObserver && !mEffectFinishedSlotName.isEmpty()) {
+ HbEffect::EffectStatus status;
+ status.item = mRegistrationItem;
+ status.effectEvent = mEffectEventType;
+ status.userData = mUserData;
+ status.reason = reason;
+
+ QObject *observer = mObserver;
+
+ // Clear the observer to make sure it is not sent more than once.
+ // This is done before invokeMethod to avoid crash if the callback
+ // deletes this object.
+ mObserver = 0;
+
+ // Send callback if observer has been provided. Use queued connection if
+ // the effect finished normally, because otherwise deleting the effect during the callback
+ // would cause crash because this function finally returns back to animation framework code
+ // which assumes the effect objects are alive.
+ QMetaObject::invokeMethod(
+ observer,
+ mEffectFinishedSlotName.toAscii().data(),
+ reason == Hb::EffectFinished ? Qt::QueuedConnection : Qt::AutoConnection,
+ QGenericReturnArgument(),
+ Q_ARG(HbEffect::EffectStatus, status));
+
+ // Do not access member variables after invoking the callback since it might
+ // have deleted this object.
+ }
+}
+
+void HbEffectGroup::setHideWhenFinished(bool hideWhenFinished)
+{
+ mHideWhenFinished = hideWhenFinished;
+}
+
+// End of File