00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "animator.h"
00022
00023 #include <QGraphicsItem>
00024 #include <QTimeLine>
00025
00026 #include <kconfig.h>
00027 #include <kconfiggroup.h>
00028 #include <kservice.h>
00029 #include <kservicetypetrader.h>
00030 #include <kglobalsettings.h>
00031
00032 #include "animationdriver.h"
00033
00034 namespace Plasma
00035 {
00036
00037 static const int MIN_TICK_RATE_INT = 10;
00038 static const qreal MIN_TICK_RATE = 10;
00039
00040 struct AnimationState
00041 {
00042 QGraphicsItem *item;
00043 QObject *qobj;
00044 Animator::Animation animation;
00045 Animator::CurveShape curve;
00046 int interval;
00047 int currentInterval;
00048 int frames;
00049 int currentFrame;
00050 int id;
00051 };
00052
00053 struct ElementAnimationState
00054 {
00055 QGraphicsItem *item;
00056 QObject *qobj;
00057 Animator::CurveShape curve;
00058 Animator::Animation animation;
00059 int interval;
00060 int currentInterval;
00061 int frames;
00062 int currentFrame;
00063 int id;
00064 QPixmap pixmap;
00065 };
00066
00067 struct MovementState
00068 {
00069 QGraphicsItem *item;
00070 QObject *qobj;
00071 Animator::CurveShape curve;
00072 Animator::Movement movement;
00073 int interval;
00074 int currentInterval;
00075 int frames;
00076 int currentFrame;
00077 QPoint start;
00078 QPoint destination;
00079 int id;
00080 };
00081
00082 struct CustomAnimationState
00083 {
00084 Animator::CurveShape curve;
00085 int frames;
00086 int currentFrame;
00087 int interval;
00088 int currentInterval;
00089 int id;
00090 QObject *receiver;
00091 char *slot;
00092 };
00093
00094 class AnimatorPrivate
00095 {
00096 public:
00097
00098 AnimatorPrivate()
00099 : driver(0),
00100 animId(0),
00101 timerId(0)
00102 {
00103 }
00104
00105 ~AnimatorPrivate()
00106 {
00107 qDeleteAll(animatedItems);
00108 qDeleteAll(animatedElements);
00109 qDeleteAll(movingItems);
00110
00111 QMutableMapIterator<int, CustomAnimationState*> it(customAnims);
00112 while (it.hasNext()) {
00113 it.next();
00114 delete[] it.value()->slot;
00115 delete it.value();
00116 it.remove();
00117 }
00118
00119
00120
00121 }
00122
00123 qreal calculateProgress(int time, int duration, Animator::CurveShape curve)
00124 {
00125 if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects)) {
00126 return qreal(1.0);
00127 }
00128
00129 timeline.setCurveShape(static_cast<QTimeLine::CurveShape>(curve));
00130 timeline.setDuration(duration);
00131 qreal progress = timeline.valueForTime(time);
00132 return progress;
00133 }
00134
00135 void performAnimation(qreal amount, const AnimationState *state)
00136 {
00137 switch (state->animation) {
00138 case Animator::AppearAnimation:
00139 driver->itemAppear(amount, state->item);
00140 break;
00141 case Animator::DisappearAnimation:
00142 driver->itemDisappear(amount, state->item);
00143 if (amount >= 1) {
00144 state->item->hide();
00145 }
00146 break;
00147 case Animator::ActivateAnimation:
00148 driver->itemActivated(amount, state->item);
00149 break;
00150 }
00151 }
00152
00153 void performMovement(qreal amount, const MovementState *state)
00154 {
00155 switch (state->movement) {
00156 case Animator::SlideInMovement:
00157 case Animator::FastSlideInMovement:
00158
00159 driver->itemSlideIn(amount, state->item, state->start, state->destination);
00160 break;
00161 case Animator::SlideOutMovement:
00162 case Animator::FastSlideOutMovement:
00163
00164 driver->itemSlideOut(amount, state->item, state->start, state->destination);
00165 break;
00166 }
00167 }
00168
00169 void init(Animator *q);
00170 void animatedItemDestroyed(QObject*);
00171 void movingItemDestroyed(QObject*);
00172 void animatedElementDestroyed(QObject*);
00173 void customAnimReceiverDestroyed(QObject*);
00174
00175 AnimationDriver *driver;
00176 int animId;
00177 int timerId;
00178 QTime time;
00179 QTimeLine timeline;
00180
00181
00182
00183
00184 QMap<QGraphicsItem*, AnimationState*> animatedItems;
00185 QMap<QGraphicsItem*, MovementState*> movingItems;
00186 QMap<int, ElementAnimationState*> animatedElements;
00187 QMap<int, CustomAnimationState*> customAnims;
00188 };
00189
00190 class AnimatorSingleton
00191 {
00192 public:
00193 Animator self;
00194 };
00195
00196 K_GLOBAL_STATIC(AnimatorSingleton, privateSelf)
00197
00198 Animator *Animator::self()
00199 {
00200 return &privateSelf->self;
00201 }
00202
00203 Animator::Animator(QObject *parent)
00204 : QObject(parent),
00205 d(new AnimatorPrivate)
00206 {
00207 d->init(this);
00208 }
00209
00210 Animator::~Animator()
00211 {
00212 delete d;
00213 }
00214
00215 void AnimatorPrivate::animatedItemDestroyed(QObject *o)
00216 {
00217
00218 QMutableMapIterator<QGraphicsItem*, AnimationState*> it(animatedItems);
00219 while (it.hasNext()) {
00220 it.next();
00221
00222 if (it.value()->qobj == o) {
00223 kDebug() << "found deleted animated item";
00224 delete it.value();
00225 it.remove();
00226 }
00227 }
00228 }
00229
00230 void AnimatorPrivate::movingItemDestroyed(QObject *o)
00231 {
00232 QMutableMapIterator<QGraphicsItem*, MovementState*> it(movingItems);
00233 while (it.hasNext()) {
00234 it.next();
00235 if (it.value()->qobj == o) {
00236 delete it.value();
00237 it.remove();
00238 }
00239 }
00240 }
00241
00242 void AnimatorPrivate::animatedElementDestroyed(QObject *o)
00243 {
00244 QMutableMapIterator<int, ElementAnimationState*> it(animatedElements);
00245 while (it.hasNext()) {
00246 it.next();
00247 if (it.value()->qobj == o) {
00248 delete it.value();
00249 it.remove();
00250 }
00251 }
00252 }
00253
00254 void AnimatorPrivate::customAnimReceiverDestroyed(QObject *o)
00255 {
00256 QMutableMapIterator<int, CustomAnimationState*> it(customAnims);
00257 while (it.hasNext()) {
00258 if (it.next().value()->receiver == o) {
00259 delete[] it.value()->slot;
00260 delete it.value();
00261 it.remove();
00262 }
00263 }
00264 }
00265
00266 int Animator::animateItem(QGraphicsItem *item, Animation animation)
00267 {
00268
00269
00270
00271 QMap<QGraphicsItem*, AnimationState*>::iterator it = d->animatedItems.find(item);
00272 if (it != d->animatedItems.end()) {
00273 delete it.value();
00274 d->animatedItems.erase(it);
00275 }
00276
00277 int frames = d->driver->animationFps(animation);
00278
00279 if (frames < 1) {
00280
00281
00282 return -1;
00283 }
00284
00285 int duration = d->driver->animationDuration(animation);
00286
00287 AnimationState *state = new AnimationState;
00288 state->id = ++d->animId;
00289 state->item = item;
00290 state->animation = animation;
00291 state->curve = d->driver->animationCurve(animation);
00292 state->frames = qMax(1.0, frames * (duration / 1000.0));
00293 state->currentFrame = 0;
00294 state->interval = d->driver->animationDuration(animation) / qreal(state->frames);
00295 state->interval = qMax(MIN_TICK_RATE_INT, state->interval - (state->interval % MIN_TICK_RATE_INT));
00296 state->currentInterval = state->interval;
00297 state->qobj = dynamic_cast<QObject*>(item);
00298
00299 if (state->qobj) {
00300
00301 disconnect(state->qobj, SIGNAL(destroyed(QObject*)),
00302 this, SLOT(animatedItemDestroyed(QObject*)));
00303 connect(state->qobj, SIGNAL(destroyed(QObject*)),
00304 this, SLOT(animatedItemDestroyed(QObject*)));
00305 }
00306
00307 d->animatedItems[item] = state;
00308 d->performAnimation(0, state);
00309
00310 if (!d->timerId) {
00311 d->timerId = startTimer(MIN_TICK_RATE);
00312 d->time.restart();
00313 }
00314
00315 return state->id;
00316 }
00317
00318 int Animator::moveItem(QGraphicsItem *item, Movement movement, const QPoint &destination)
00319 {
00320
00321 QMap<QGraphicsItem*, MovementState*>::iterator it = d->movingItems.find(item);
00322 if (it != d->movingItems.end()) {
00323 delete it.value();
00324 d->movingItems.erase(it);
00325 }
00326
00327 int frames = d->driver->movementAnimationFps(movement);
00328 if (frames <= 1) {
00329
00330
00331 return -1;
00332 }
00333
00334 MovementState *state = new MovementState;
00335 state->id = ++d->animId;
00336 state->destination = destination;
00337 state->start = item->pos().toPoint();
00338 state->item = item;
00339 state->movement = movement;
00340 state->curve = d->driver->movementAnimationCurve(movement);
00341
00342 int duration = d->driver->movementAnimationDuration(movement);
00343 state->frames = qMax(1.0, frames * (duration / 1000.0));
00344 state->currentFrame = 0;
00345 state->interval = duration / qreal(state->frames);
00346 state->interval = qMax(MIN_TICK_RATE_INT, state->interval - (state->interval % MIN_TICK_RATE_INT));
00347
00348
00349 state->currentInterval = state->interval;
00350 state->qobj = dynamic_cast<QObject*>(item);
00351
00352 if (state->qobj) {
00353 disconnect(state->qobj, SIGNAL(destroyed(QObject*)), this, SLOT(movingItemDestroyed(QObject*)));
00354 connect(state->qobj, SIGNAL(destroyed(QObject*)), this, SLOT(movingItemDestroyed(QObject*)));
00355 }
00356
00357 d->movingItems[item] = state;
00358 d->performMovement(0, state);
00359
00360 if (!d->timerId) {
00361 d->timerId = startTimer(MIN_TICK_RATE);
00362 d->time.restart();
00363 }
00364
00365 return state->id;
00366 }
00367
00368 int Animator::customAnimation(int frames, int duration, Animator::CurveShape curve,
00369 QObject *receiver, const char *slot)
00370 {
00371 if (frames < 1 || duration < 1 || !receiver || !slot) {
00372 return -1;
00373 }
00374
00375 CustomAnimationState *state = new CustomAnimationState;
00376 state->id = ++d->animId;
00377 state->frames = frames;
00378 state->currentFrame = 0;
00379 state->curve = curve;
00380 state->interval = duration / qreal(state->frames);
00381 state->interval = qMax(MIN_TICK_RATE_INT, state->interval - (state->interval % MIN_TICK_RATE_INT));
00382 state->currentInterval = state->interval;
00383 state->receiver = receiver;
00384 state->slot = qstrdup(slot);
00385
00386 d->customAnims[state->id] = state;
00387
00388 disconnect(receiver, SIGNAL(destroyed(QObject*)),
00389 this, SLOT(customAnimReceiverDestroyed(QObject*)));
00390 connect(receiver, SIGNAL(destroyed(QObject*)),
00391 this, SLOT(customAnimReceiverDestroyed(QObject*)));
00392
00393
00394 if (!QMetaObject::invokeMethod(receiver, slot, Q_ARG(qreal, 0))) {
00395
00396 QMetaObject::invokeMethod(receiver, slot, Q_ARG(qreal, 0), Q_ARG(int, state->id));
00397 }
00398
00399 if (!d->timerId) {
00400 d->timerId = startTimer(MIN_TICK_RATE);
00401 d->time.restart();
00402 }
00403
00404 return state->id;
00405 }
00406
00407 void Animator::stopCustomAnimation(int id)
00408 {
00409 QMap<int, CustomAnimationState*>::iterator it = d->customAnims.find(id);
00410 if (it != d->customAnims.end()) {
00411 delete [] it.value()->slot;
00412 delete it.value();
00413 d->customAnims.erase(it);
00414 }
00415
00416 }
00417
00418 void Animator::stopItemAnimation(int id)
00419 {
00420 QMutableMapIterator<QGraphicsItem*, AnimationState*> it(d->animatedItems);
00421 while (it.hasNext()) {
00422 it.next();
00423 if (it.value()->id == id) {
00424 delete it.value();
00425 it.remove();
00426 return;
00427 }
00428 }
00429 }
00430
00431 void Animator::stopItemMovement(int id)
00432 {
00433 QMutableMapIterator<QGraphicsItem*, MovementState*> it(d->movingItems);
00434 while (it.hasNext()) {
00435 it.next();
00436 if (it.value()->id == id) {
00437 delete it.value();
00438 it.remove();
00439 return;
00440 }
00441 }
00442 }
00443
00444 int Animator::animateElement(QGraphicsItem *item, Animation animation)
00445 {
00446
00447 int frames = d->driver->elementAnimationFps(animation);
00448 int duration = d->driver->animationDuration(animation);
00449
00450 ElementAnimationState *state = new ElementAnimationState;
00451 state->item = item;
00452 state->curve = d->driver->elementAnimationCurve(animation);
00453 state->animation = animation;
00454 state->frames = qMax(1.0, frames * (duration / 1000.0));
00455 state->currentFrame = 0;
00456 state->interval = duration / qreal(state->frames);
00457 state->interval = qMax(MIN_TICK_RATE_INT, state->interval - (state->interval % MIN_TICK_RATE_INT));
00458 state->currentInterval = state->interval;
00459 state->id = ++d->animId;
00460 state->qobj = dynamic_cast<QObject*>(item);
00461
00462 if (state->qobj) {
00463 disconnect(state->qobj, SIGNAL(destroyed(QObject*)),
00464 this, SLOT(animatedElementDestroyed(QObject*)));
00465 connect(state->qobj, SIGNAL(destroyed(QObject*)),
00466 this, SLOT(animatedElementDestroyed(QObject*)));
00467 }
00468
00469
00470
00471 bool needTimer = true;
00472 if (state->frames < 1) {
00473 state->frames = 1;
00474 state->currentFrame = 1;
00475 needTimer = false;
00476 }
00477
00478 d->animatedElements[state->id] = state;
00479
00480
00481 if (needTimer && !d->timerId) {
00482
00483
00484 d->timerId = startTimer(MIN_TICK_RATE);
00485 d->time.restart();
00486 }
00487 return state->id;
00488 }
00489
00490 void Animator::stopElementAnimation(int id)
00491 {
00492 QMap<int, ElementAnimationState*>::iterator it = d->animatedElements.find(id);
00493 if (it != d->animatedElements.end()) {
00494 delete it.value();
00495 d->animatedElements.erase(it);
00496 }
00497
00498 }
00499
00500 void Animator::setInitialPixmap(int id, const QPixmap &pixmap)
00501 {
00502 QMap<int, ElementAnimationState*>::iterator it = d->animatedElements.find(id);
00503
00504 if (it == d->animatedElements.end()) {
00505 kDebug() << "No entry found for id " << id;
00506 return;
00507 }
00508
00509 it.value()->pixmap = pixmap;
00510 }
00511
00512 QPixmap Animator::currentPixmap(int id)
00513 {
00514 QMap<int, ElementAnimationState*>::const_iterator it = d->animatedElements.constFind(id);
00515
00516 if (it == d->animatedElements.constEnd()) {
00517
00518 return QPixmap();
00519 }
00520
00521 ElementAnimationState *state = it.value();
00522 qreal progress = d->calculateProgress(state->currentFrame * state->interval,
00523 state->frames * state->interval,
00524 state->curve);
00525
00526
00527 switch (state->animation) {
00528 case AppearAnimation:
00529 return d->driver->elementAppear(progress, state->pixmap);
00530 break;
00531 case DisappearAnimation:
00532 return d->driver->elementDisappear(progress, state->pixmap);
00533 break;
00534 case ActivateAnimation:
00535 break;
00536 }
00537
00538 return state->pixmap;
00539 }
00540
00541 bool Animator::isAnimating() const
00542 {
00543 return (!d->animatedItems.isEmpty() ||
00544 !d->movingItems.isEmpty() ||
00545 !d->animatedElements.isEmpty() ||
00546 !d->customAnims.isEmpty());
00547 }
00548
00549 void Animator::timerEvent(QTimerEvent *event)
00550 {
00551 Q_UNUSED(event)
00552 bool animationsRemain = false;
00553 int elapsed = MIN_TICK_RATE;
00554 if (d->time.elapsed() > elapsed) {
00555 elapsed = d->time.elapsed();
00556 }
00557 d->time.restart();
00558
00559
00560 foreach (AnimationState *state, d->animatedItems) {
00561 if (state->currentInterval <= elapsed) {
00562
00563 state->currentFrame +=
00564 (KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) ?
00565 qMax(1, elapsed / state->interval) : state->frames - state->currentFrame;
00566
00567 if (state->currentFrame < state->frames) {
00568 qreal progress = d->calculateProgress(state->currentFrame * state->interval,
00569 state->frames * state->interval,
00570 state->curve);
00571 d->performAnimation(progress, state);
00572 state->currentInterval = state->interval;
00573 animationsRemain = true;
00574 } else {
00575 d->performAnimation(1, state);
00576 d->animatedItems.erase(d->animatedItems.find(state->item));
00577 emit animationFinished(state->item, state->animation);
00578 delete state;
00579 }
00580 } else {
00581 state->currentInterval -= elapsed;
00582 animationsRemain = true;
00583 }
00584 }
00585
00586 foreach (MovementState *state, d->movingItems) {
00587 if (state->currentInterval <= elapsed) {
00588
00589 state->currentFrame +=
00590 (KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) ?
00591 qMax(1, elapsed / state->interval) : state->frames - state->currentFrame;
00592
00593 if (state->currentFrame < state->frames) {
00594
00595 qreal progress = d->calculateProgress(state->currentFrame * state->interval,
00596 state->frames * state->interval,
00597 state->curve);
00598 d->performMovement(progress, state);
00599 animationsRemain = true;
00600 } else {
00601
00602 d->performMovement(1, state);
00603 d->movingItems.erase(d->movingItems.find(state->item));
00604 emit movementFinished(state->item);
00605 delete state;
00606 }
00607 } else {
00608 state->currentInterval -= elapsed;
00609 animationsRemain = true;
00610 }
00611 }
00612
00613 foreach (ElementAnimationState *state, d->animatedElements) {
00614 if (state->currentFrame == state->frames) {
00615
00616
00617
00618
00619
00620
00621 continue;
00622 }
00623
00624 if (state->currentInterval <= elapsed) {
00625
00626
00627
00628
00629
00630 state->currentFrame +=
00631 (KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) ?
00632 qMax(1, elapsed / state->interval) : state->frames - state->currentFrame;
00633
00634 state->item->update();
00635 if (state->currentFrame < state->frames) {
00636 state->currentInterval = state->interval;
00637 animationsRemain = true;
00638 } else {
00639 d->animatedElements.remove(state->id);
00640 emit elementAnimationFinished(state->id);
00641 delete state;
00642 }
00643 } else {
00644 state->currentInterval -= elapsed;
00645 animationsRemain = true;
00646 }
00647 }
00648
00649 foreach (CustomAnimationState *state, d->customAnims) {
00650 if (state->currentInterval <= elapsed) {
00651
00652 state->currentFrame +=
00653 (KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) ?
00654 qMax(1, elapsed / state->interval) : state->frames - state->currentFrame;
00655
00656
00657
00658
00659
00660 if (state->currentFrame < state->frames) {
00661
00662
00663 state->currentInterval = state->interval;
00664 animationsRemain = true;
00665
00666
00667 qreal progress = d->calculateProgress(state->currentFrame * state->interval,
00668 state->frames * state->interval,
00669 state->curve);
00670 if (!QMetaObject::invokeMethod(state->receiver, state->slot, Q_ARG(qreal, progress))) {
00671
00672 QMetaObject::invokeMethod(state->receiver, state->slot, Q_ARG(qreal, progress),
00673 Q_ARG(int, state->id));
00674 }
00675 } else {
00676 if (!QMetaObject::invokeMethod(state->receiver, state->slot, Q_ARG(qreal, 1))) {
00677 QMetaObject::invokeMethod(state->receiver, state->slot, Q_ARG(qreal, 1), Q_ARG(int, state->id));
00678 }
00679 d->customAnims.erase(d->customAnims.find(state->id));
00680 emit customAnimationFinished(state->id);
00681 delete [] state->slot;
00682 delete state;
00683 }
00684 } else {
00685 state->currentInterval -= elapsed;
00686 animationsRemain = true;
00687 }
00688 }
00689
00690 if (!animationsRemain && d->timerId) {
00691 killTimer(d->timerId);
00692 d->timerId = 0;
00693 }
00694 }
00695
00696 void AnimatorPrivate::init(Animator *q)
00697 {
00698
00699 KConfig c("plasmarc");
00700 KConfigGroup cg(&c, "Animator");
00701 QString pluginName = cg.readEntry("driver", "default");
00702
00703 if (!pluginName.isEmpty()) {
00704 QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(pluginName);
00705 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Animator", constraint);
00706
00707 if (!offers.isEmpty()) {
00708 QString error;
00709
00710 KPluginLoader plugin(*offers.first());
00711
00712 if (Plasma::isPluginVersionCompatible(plugin.pluginVersion())) {
00713 driver = offers.first()->createInstance<Plasma::AnimationDriver>(q, QVariantList(), &error);
00714 }
00715
00716 if (!driver) {
00717 kDebug() << "Could not load requested animator "
00718 << offers.first() << ". Error given: " << error;
00719 }
00720 }
00721 }
00722
00723 if (!driver) {
00724 driver = new AnimationDriver(q);
00725 }
00726 }
00727
00728 }
00729
00730 #include <animator.moc>