src/hbcore/effects/hbeffectgroup.cpp
changeset 0 16d8024aca5e
child 1 f7ac710697a9
equal deleted inserted replaced
-1:000000000000 0:16d8024aca5e
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (developer.feedback@nokia.com)
       
     6 **
       
     7 ** This file is part of the HbCore module of the UI Extensions for Mobile.
       
     8 **
       
     9 ** GNU Lesser General Public License Usage
       
    10 ** This file may be used under the terms of the GNU Lesser General Public
       
    11 ** License version 2.1 as published by the Free Software Foundation and
       
    12 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
       
    13 ** Please review the following information to ensure the GNU Lesser General
       
    14 ** Public License version 2.1 requirements will be met:
       
    15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    16 **
       
    17 ** In addition, as a special exception, Nokia gives you certain additional
       
    18 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    20 **
       
    21 ** If you have questions regarding the use of this file, please contact
       
    22 ** Nokia at developer.feedback@nokia.com.
       
    23 **
       
    24 ****************************************************************************/
       
    25 
       
    26 #include "hbeffectgroup_p.h"
       
    27 #include "hbeffectabstract_p.h"
       
    28 #include "hbeffect.h"
       
    29 #include "hbtimer_p.h"
       
    30 #include "hbeffectdef_p.h"
       
    31 #include "hbeffectinternal_p.h"
       
    32 #include "hbmainwindow.h"
       
    33 #include "hbinstance.h"
       
    34 
       
    35 #include <qglobal.h>
       
    36 #include <QMetaObject>
       
    37 #include <QTransform>
       
    38 #include <QGraphicsItem>
       
    39 #include <QGraphicsWidget>
       
    40 #include <QGraphicsView>
       
    41 #include <QTimer>
       
    42 
       
    43 #ifdef HB_FILTER_EFFECTS
       
    44 #include "hbvgeffect_p.h"
       
    45 #include "hbvgchainedeffect_p.h"
       
    46 #endif
       
    47 
       
    48 
       
    49 HbEffectGroup::HbEffectGroup(
       
    50     const QString &effectEventType, 
       
    51     QGraphicsItem *registrationItem, 
       
    52     QGraphicsItem *targetItem,
       
    53     const QString &itemType)
       
    54     : mRegistrationItem(registrationItem),
       
    55       mTargetItem(targetItem),
       
    56       mEffectEventType(effectEventType),
       
    57       mItemType(itemType),
       
    58       mDirty(false),
       
    59       mVgEffect(0),
       
    60       mVgEffectActivated(false),
       
    61       mFinishedCount(0),
       
    62       mObserver(0),
       
    63       mRunningState(NotRunning),
       
    64       mLooping(false),
       
    65       mView(0),
       
    66       mHideWhenFinished(false)
       
    67 {
       
    68 }
       
    69 
       
    70 HbEffectGroup::~HbEffectGroup()
       
    71 {
       
    72     qDeleteAll(mEffects);
       
    73 
       
    74 #ifdef HB_FILTER_EFFECTS
       
    75     // Delete the vg effect if its ownership has not been transferred to the graphics item
       
    76     if (!mVgEffectActivated && !mVgEffectGuard.isNull()) {
       
    77         delete mVgEffect;
       
    78     }
       
    79 #endif
       
    80 }
       
    81 
       
    82 QString HbEffectGroup::effectEventType() const
       
    83 {
       
    84     return mEffectEventType;
       
    85 }
       
    86 
       
    87 QString HbEffectGroup::itemType() const
       
    88 {
       
    89     return mItemType;
       
    90 }
       
    91 
       
    92 QGraphicsItem *HbEffectGroup::registrationItem() const
       
    93 {
       
    94     return mRegistrationItem;
       
    95 }
       
    96 
       
    97 QGraphicsItem *HbEffectGroup::targetItem() const
       
    98 {
       
    99     return mTargetItem;
       
   100 }
       
   101 
       
   102 void HbEffectGroup::addEffect(HbEffectAbstract *effect)
       
   103 {
       
   104     mEffects.append(effect);
       
   105 }
       
   106 
       
   107 void HbEffectGroup::removeEffect(HbEffectAbstract *effect)
       
   108 {
       
   109     mEffects.removeAll(effect);
       
   110 }
       
   111 
       
   112 /*
       
   113 * Fixes the order of the effects to be optimal for item transformations.
       
   114 * E.g. translate needs to be after scale because translating a scaled matrix would not lead to the wanted result.
       
   115 * Also there are some problems with rotation effect if it's done after scale effect
       
   116 */
       
   117 void HbEffectGroup::fixEffectOrder()
       
   118 {
       
   119     // If there is only one effect, no need to change order
       
   120     if (mEffects.count() > 1) {
       
   121         int last = mEffects.count() - 1;
       
   122 
       
   123         for (int i = last; i >= 0; --i) {
       
   124             HbEffectAbstract *effect = mEffects[i];
       
   125             if (effect->name() == HB_EFFECT_NAME_TRANSLATE) {
       
   126                 // Move translate effect last in the effect list
       
   127                 mEffects.takeAt(i);
       
   128                 mEffects.append(effect);
       
   129             }
       
   130         }
       
   131 
       
   132         for (int i = last; i >= 0; --i) {
       
   133             HbEffectAbstract *effect = mEffects[i];
       
   134             if (effect->name() == HB_EFFECT_NAME_SCALE) {
       
   135                 // Move scale effect second last in the effect list
       
   136                 mEffects.takeAt(i);
       
   137                 mEffects.insert(mEffects.size()-1, effect);
       
   138             }
       
   139         }
       
   140 
       
   141     }
       
   142 }
       
   143 
       
   144 void HbEffectGroup::setObserver(QObject *observer, const QString &effectFinishedSlotName)
       
   145 {
       
   146     mObserver = observer;
       
   147     mEffectFinishedSlotName = effectFinishedSlotName;
       
   148 }
       
   149 
       
   150 void HbEffectGroup::updateItemTransform()
       
   151 {
       
   152     QGraphicsView *gv(0); 
       
   153     // support for graphics view transforms
       
   154     if (mTargetItem->type() == HbGVWrapperItemType) {
       
   155         HbGVWrapperItem *gvw = static_cast<HbGVWrapperItem*>(mTargetItem);
       
   156         if (gvw)
       
   157             gv = gvw->mainWindow();
       
   158     }
       
   159     QTransform transform;
       
   160 
       
   161     Q_FOREACH(HbEffectAbstract *effect, mEffects) {
       
   162         if (effect)
       
   163             effect->updateItemTransform(transform);
       
   164     }
       
   165     if (!gv)
       
   166         mTargetItem->setTransform(transform);	
       
   167     else 
       
   168         gv->setTransform(transform);
       
   169 }
       
   170 
       
   171 bool HbEffectGroup::dirty() const
       
   172 {
       
   173     return mDirty;
       
   174 }
       
   175 
       
   176 void HbEffectGroup::setDirty(bool dirty)
       
   177 {
       
   178     mDirty = dirty;
       
   179 }
       
   180 
       
   181 int HbEffectGroup::effectCount() const
       
   182 {
       
   183 	return mEffects.count();
       
   184 }
       
   185 
       
   186 bool HbEffectGroup::isRunning() const
       
   187 {
       
   188     return mRunningState != NotRunning;
       
   189 }
       
   190 
       
   191 void HbEffectGroup::setLooping(bool looping)
       
   192 {
       
   193     mLooping = looping;
       
   194 }
       
   195 
       
   196 bool HbEffectGroup::isLooping() const
       
   197 {
       
   198     return mLooping;
       
   199 }
       
   200 
       
   201 void HbEffectGroup::pause()
       
   202 {
       
   203     Q_FOREACH(HbEffectAbstract *effect, mEffects) {
       
   204         effect->pause();
       
   205     }
       
   206 }
       
   207 
       
   208 void HbEffectGroup::resume()
       
   209 {
       
   210     Q_FOREACH(HbEffectAbstract *effect, mEffects) {
       
   211         effect->resume();
       
   212     }
       
   213 }
       
   214 
       
   215 const QVariant &HbEffectGroup::userData() const
       
   216 {
       
   217     return mUserData;
       
   218 }
       
   219 
       
   220 void HbEffectGroup::setUserData(const QVariant &userData)
       
   221 {
       
   222     mUserData = userData;
       
   223 }
       
   224 
       
   225 QRectF HbEffectGroup::extRect() const
       
   226 {
       
   227     return mExtRect;
       
   228 }
       
   229 
       
   230 void HbEffectGroup::setExtRect(const QRectF &extRect)
       
   231 {
       
   232     mExtRect = extRect;
       
   233 
       
   234     // This is needed to make scaling work from an extrect that has an equal size with the item's rect
       
   235     if (mTargetItem) {
       
   236         QRectF itemRect = mTargetItem->boundingRect();
       
   237         qreal width = itemRect.width();
       
   238         if (qFuzzyCompare(width, mExtRect.width())) {
       
   239             mExtRect.setWidth(width + 0.00001);
       
   240         }
       
   241         qreal height = itemRect.height();
       
   242         if (qFuzzyCompare(height, mExtRect.height())) {
       
   243             mExtRect.setHeight(height + 0.00001);
       
   244         }
       
   245     }
       
   246 }
       
   247 
       
   248 HbView *HbEffectGroup::view() const
       
   249 {
       
   250     return mView;
       
   251 }
       
   252 
       
   253 void HbEffectGroup::setView(HbView *view)
       
   254 {
       
   255     mView = view;
       
   256 }
       
   257 
       
   258 #ifdef HB_FILTER_EFFECTS
       
   259 
       
   260 HbVgChainedEffect *HbEffectGroup::vgEffect()
       
   261 {
       
   262     if (!mVgEffect) {
       
   263         mVgEffect = new HbVgChainedEffect;
       
   264     }
       
   265     return mVgEffect;
       
   266 }
       
   267 
       
   268 void HbEffectGroup::activateVgEffect()
       
   269 {
       
   270     if (!mVgEffectActivated) {
       
   271         mVgEffectGuard = QPointer<QGraphicsEffect>();
       
   272         vgEffect()->install(mTargetItem);
       
   273         mVgEffectActivated = true;
       
   274     }
       
   275 }
       
   276 
       
   277 /**
       
   278 * Removes the VG effect from the graphics item without deleting it.
       
   279 * The ownership is moved back to the effect group.
       
   280 */
       
   281 void HbEffectGroup::deactivateVgEffect()
       
   282 {
       
   283     if (mVgEffectActivated) {
       
   284         // This does not delete the effect so ownership is transferred back to
       
   285         // the effect group.  However this is believed to be a bug in Qt 4.6.0
       
   286         // so use a QPointer to make sure that we do not do double deletion in
       
   287         // case Qt starts deleting the effect correctly in the future.
       
   288         mVgEffectGuard = QPointer<QGraphicsEffect>(mTargetItem->graphicsEffect());
       
   289         mTargetItem->setGraphicsEffect(0);
       
   290         mVgEffectActivated = false;
       
   291     }
       
   292 }
       
   293 
       
   294 #endif // HB_FILTER_EFFECTS
       
   295 
       
   296 void HbEffectGroup::startAll()
       
   297 {
       
   298     if (!mEffects.empty()) {
       
   299         mRunningState = Running;
       
   300         mFinishedCount = 0;
       
   301     }
       
   302 
       
   303     // First resolve parameters and set the start states for all the effects.
       
   304     // This is done before starting the effect animations to avoid screen flickering.
       
   305 
       
   306     QTransform transform;
       
   307 
       
   308     Q_FOREACH(HbEffectAbstract *effect, mEffects) {
       
   309         // Resolve parameters etc.
       
   310         effect->init();
       
   311         if (effect->interval() == 0) {
       
   312             // Set start state if effect starts immediately
       
   313             effect->setStartState(transform);
       
   314         }
       
   315     }
       
   316 
       
   317     mTargetItem->setTransform(transform);
       
   318 
       
   319     if (mEffects.empty()) {
       
   320         // No effect exists but user wants notification when effect finishes. 
       
   321         // Let the user do whatever he wanted to do when effect finishes.
       
   322         invokeObserver(Hb::EffectNotStarted);
       
   323     }        
       
   324     else {
       
   325         // Start state has been set for all the effects,
       
   326         // next step is to start the effect animations.
       
   327         // Before that, resolve the view where the effect belongs if the effect is looping.
       
   328         // This is needed for being able to pause looping effects when their view is inactive.
       
   329         if (isLooping()) {
       
   330             resolveView();
       
   331         }
       
   332 
       
   333         Q_FOREACH(HbEffectAbstract *effect, mEffects) {
       
   334             // If the starttime is zero, start effect immediately
       
   335             if (effect->interval() == 0) {
       
   336                 effect->start(); // This may call group's effectFinished if the effect was empty.
       
   337             } else {
       
   338                 //Else register the effect to timeline to wait its turn.
       
   339                 HbTimer::instance()->registerEntry(effect);
       
   340             }
       
   341         }
       
   342     }
       
   343 }
       
   344 
       
   345 void HbEffectGroup::resolveView() {
       
   346     if (!mView) {
       
   347         if (mTargetItem) {
       
   348             QGraphicsScene *scene = mTargetItem->scene();
       
   349             if (scene) {
       
   350                 // Resolve the main window having the same scene that the item belongs to
       
   351                 QList<HbMainWindow *> windowList = hbInstance->allMainWindows();
       
   352                 Q_FOREACH(const HbMainWindow *window, windowList) {
       
   353                     if (window->scene() == scene) {
       
   354                         mView = window->currentView();
       
   355                         break;
       
   356                     }
       
   357                 }
       
   358             }
       
   359         }
       
   360     }
       
   361 }
       
   362 
       
   363 bool HbEffectGroup::hasTranslateEffect() const
       
   364 {
       
   365     foreach(HbEffectAbstract *effect, mEffects) {
       
   366         if (effect->name() == HB_EFFECT_NAME_TRANSLATE) {
       
   367             return true;
       
   368         }
       
   369     }
       
   370 
       
   371     return false;
       
   372 }
       
   373 
       
   374 bool HbEffectGroup::hasRotateEffect() const
       
   375 {
       
   376     foreach(HbEffectAbstract *effect, mEffects) {
       
   377         if (effect->name() == HB_EFFECT_NAME_ROTATE) {
       
   378             return true;
       
   379         }
       
   380     }
       
   381 
       
   382     return false;
       
   383 }
       
   384 
       
   385 bool HbEffectGroup::hasScaleEffect() const
       
   386 {
       
   387     foreach(HbEffectAbstract *effect, mEffects) {
       
   388         if (effect->name() == HB_EFFECT_NAME_SCALE) {
       
   389             return true;
       
   390         }
       
   391     }
       
   392 
       
   393     return false;
       
   394 }
       
   395 
       
   396 bool HbEffectGroup::hasOpacityEffect() const
       
   397 {
       
   398     foreach(HbEffectAbstract *effect, mEffects) {
       
   399         if (effect->name() == HB_EFFECT_NAME_OPACITY) {
       
   400             return true;
       
   401         }
       
   402     }
       
   403 
       
   404     return false;
       
   405 }
       
   406 
       
   407 void HbEffectGroup::doHideEffect(const QTransform *transform, bool opacityEffectUsed)
       
   408 {
       
   409     mTargetItem->setTransform(transform ? *transform : QTransform());
       
   410     if (opacityEffectUsed) {
       
   411         // Hide opacity effect by setting item fully opaque regardless of what
       
   412         // its opacity value was before the effect.
       
   413         mTargetItem->setOpacity(1.0f);
       
   414     }
       
   415 #ifdef HB_FILTER_EFFECTS            
       
   416     deactivateVgEffect();
       
   417 #endif            
       
   418 }
       
   419 
       
   420 void HbEffectGroup::cancelAll(bool sendCallback, bool itemIsValid, bool hideEffect, const QTransform &initialItemTransform)
       
   421 {
       
   422     QTransform transform;
       
   423     bool opacityEffectUsed = false;
       
   424 
       
   425     Q_FOREACH(HbEffectAbstract *effect, mEffects) {
       
   426         if (effect) {
       
   427             HbTimer::instance()->unregisterEntry(effect);
       
   428             effect->cancel(transform, itemIsValid);
       
   429             if (effect->name() == HB_EFFECT_NAME_OPACITY) {
       
   430                 opacityEffectUsed = true;
       
   431             }
       
   432         }
       
   433     }
       
   434 
       
   435     if (itemIsValid) {
       
   436         // If effect needs to be removed, reset transform matrix and deactivate VG effect
       
   437         if (hideEffect || mHideWhenFinished) {
       
   438             doHideEffect(&initialItemTransform, opacityEffectUsed);
       
   439         } else { // Otherwise set transform corresponding to the end state of the effect
       
   440             mTargetItem->setTransform(initialItemTransform * transform);
       
   441         }
       
   442     }
       
   443 
       
   444     // Do not set this to NotRunning before effect->cancel has been called because filter
       
   445     // effects cancel does stuff that requires group->isRunning() return true.
       
   446     mRunningState = NotRunning;
       
   447 
       
   448     // Invoke observer with cancel signal
       
   449     if (sendCallback)
       
   450         invokeObserver(Hb::EffectCancelled);
       
   451 }
       
   452 
       
   453 void HbEffectGroup::effectFinished(Hb::EffectEvent reason)
       
   454 {
       
   455     // Inform the animated item when the whole effect group has finished.
       
   456     if (++mFinishedCount == mEffects.count()) {
       
   457         mFinishedCount = 0;
       
   458         
       
   459         // The animation framework funnily enough sends the finished signal before updating the animation with the final
       
   460         // value, so here we set running state to NotRunning asynchronously so the effect's final value gets still updated.
       
   461         mRunningState = FinishInProgress;
       
   462         QTimer::singleShot(0, this, SLOT(clearEffectRunning()));
       
   463 
       
   464         // Send callback if observer has been provided
       
   465         invokeObserver(reason);
       
   466     }
       
   467 
       
   468     // The effect group object is not deleted here,
       
   469     // because it would need to be removed from the list of the effect groups in
       
   470     // the effect engine as well.
       
   471     // It will be deleted and a new effect group created when the same effect is started again.
       
   472 }
       
   473 
       
   474 void HbEffectGroup::clearEffectRunning()
       
   475 {
       
   476     // Comes here when effect has finished and running state is set to NotRunning asynchronously.
       
   477     // Only set running state to 'NotRunning' if the finish is still in progress, i.e. the effect
       
   478     // has not been restarted meanwhile.
       
   479     if (mRunningState == FinishInProgress) {
       
   480         mRunningState = NotRunning;
       
   481         // We are finished either normally or with EffectNotStarted. It is now the time to
       
   482         // get rid of all the "effects" caused by the effects in this group if the
       
   483         // hide-when-finished flag is set.
       
   484         if (mHideWhenFinished) {
       
   485             doHideEffect(0, hasOpacityEffect());
       
   486         }
       
   487     }
       
   488 }
       
   489 
       
   490 void HbEffectGroup::invokeObserver(Hb::EffectEvent reason)
       
   491 {
       
   492     // Send callback if observer has been provided
       
   493     if (mRegistrationItem && mTargetItem && mObserver && !mEffectFinishedSlotName.isEmpty()) {
       
   494         HbEffect::EffectStatus status;
       
   495         status.item = mRegistrationItem;
       
   496         status.effectEvent = mEffectEventType;
       
   497         status.userData = mUserData;
       
   498         status.reason = reason;
       
   499 
       
   500         QObject *observer = mObserver;    
       
   501     
       
   502         // Clear the observer to make sure it is not sent more than once.
       
   503         // This is done before invokeMethod to avoid crash if the callback
       
   504         // deletes this object.
       
   505         mObserver = 0;
       
   506 
       
   507         // Send callback if observer has been provided. Use queued connection if
       
   508         // the effect finished normally, because otherwise deleting the effect during the callback
       
   509         // would cause crash because this function finally returns back to animation framework code
       
   510         // which assumes the effect objects are alive.
       
   511         QMetaObject::invokeMethod(
       
   512             observer,
       
   513             mEffectFinishedSlotName.toAscii().data(),
       
   514             reason == Hb::EffectFinished ? Qt::QueuedConnection : Qt::AutoConnection,
       
   515             QGenericReturnArgument(),
       
   516             Q_ARG(HbEffect::EffectStatus, status));
       
   517 
       
   518         // Do not access member variables after invoking the callback since it might
       
   519         // have deleted this object.
       
   520     }
       
   521 }
       
   522 
       
   523 void HbEffectGroup::setHideWhenFinished(bool hideWhenFinished)
       
   524 {
       
   525     mHideWhenFinished = hideWhenFinished;
       
   526 }
       
   527 
       
   528 // End of File