2 PictureFlow - animated image show widget
3 http://pictureflow.googlecode.com
5 Copyright (C) 2009 Ariya Hidayat (ariya@kde.org)
6 Copyright (C) 2008 Ariya Hidayat (ariya@kde.org)
7 Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 #include "pictureflow.hpp"
29 #include "components/playlist/ml_model.hpp"
32 #if QT_VERSION < 0x040300
33 #error PictureFlow widgets need Qt 4.3 or later
36 #include <QApplication>
45 #include "../components/playlist/playlist_model.hpp" /* getArtPixmap etc */
46 #include "../components/playlist/sorting.h" /* Columns List */
47 #include "input_manager.hpp"
49 // for fixed-point arithmetic, we need minimum 32-bit long
50 // long long (64-bit) might be useful for multiplication and division
52 #define PFREAL_SHIFT 10
53 #define PFREAL_ONE (1 << PFREAL_SHIFT)
55 #define IANGLE_MAX 1024
56 #define IANGLE_MASK 1023
58 inline PFreal fmul(PFreal a, PFreal b)
60 return ((long long)(a))*((long long)(b)) >> PFREAL_SHIFT;
63 inline PFreal fdiv(PFreal num, PFreal den)
65 long long p = (long long)(num) << (PFREAL_SHIFT * 2);
66 long long q = p / (long long)den;
67 long long r = q >> PFREAL_SHIFT;
72 inline PFreal fsin(int iangle)
74 // warning: regenerate the table if IANGLE_MAX and PFREAL_SHIFT are changed!
75 static const PFreal tab[] = {
76 3, 103, 202, 300, 394, 485, 571, 652,
77 726, 793, 853, 904, 947, 980, 1004, 1019,
78 1023, 1018, 1003, 978, 944, 901, 849, 789,
79 721, 647, 566, 479, 388, 294, 196, 97,
80 -4, -104, -203, -301, -395, -486, -572, -653,
81 -727, -794, -854, -905, -948, -981, -1005, -1020,
82 -1024, -1019, -1004, -979, -945, -902, -850, -790,
83 -722, -648, -567, -480, -389, -295, -197, -98,
89 iangle &= IANGLE_MASK;
91 int i = (iangle >> 4);
93 PFreal q = tab[(i+1)];
95 return p + g *(iangle - i*16) / 16;
98 inline PFreal fcos(int iangle)
100 return fsin(iangle + (IANGLE_MAX >> 2));
103 /* ----------------------------------------------------------
105 PictureFlowState stores the state of all slides, i.e. all the necessary
106 information to be able to render them.
108 PictureFlowAnimator is responsible to move the slides during the
109 transition between slides, to achieve the effect similar to Cover Flow,
110 by changing the state.
112 PictureFlowSoftwareRenderer (or PictureFlowOpenGLRenderer) is
113 the actual 3-d renderer. It should render all slides given the state
114 (an instance of PictureFlowState).
116 Instances of all the above three classes are stored in
119 ------------------------------------------------------- */
129 class PictureFlowState
138 QRgb backgroundColor;
141 PictureFlow::ReflectionEffect reflectionEffect;
149 SlideInfo centerSlide;
150 QVector<SlideInfo> leftSlides;
151 QVector<SlideInfo> rightSlides;
155 class PictureFlowAnimator
158 PictureFlowAnimator();
159 PictureFlowState* state;
161 void start(int slide);
162 void stop(int slide);
171 class PictureFlowAbstractRenderer
174 PictureFlowAbstractRenderer(): state(0), dirty(false), widget(0) {}
175 virtual ~PictureFlowAbstractRenderer() {}
177 PictureFlowState* state;
181 virtual void init() = 0;
182 virtual void paint() = 0;
185 class PictureFlowSoftwareRenderer: public PictureFlowAbstractRenderer
188 PictureFlowSoftwareRenderer();
189 ~PictureFlowSoftwareRenderer();
192 virtual void paint();
199 QVector<PFreal> rays;
200 QImage* blankSurface;
204 QRect renderSlide(const SlideInfo &slide, int col1 = -1, int col2 = -1);
205 QImage* surface(QModelIndex);
206 QHash<QString, QImage*> cache;
209 // ------------- PictureFlowState ---------------------------------------
211 PictureFlowState::PictureFlowState():
212 backgroundColor(0), slideWidth(150), slideHeight(200),
213 reflectionEffect(PictureFlow::BlurredReflection), centerIndex(0)
217 PictureFlowState::~PictureFlowState()
221 // readjust the settings, call this when slide dimension is changed
222 void PictureFlowState::reposition()
224 angle = 70 * IANGLE_MAX / 360; // approx. 70 degrees tilted
226 offsetX = slideWidth / 2 * (PFREAL_ONE - fcos(angle));
227 offsetY = slideWidth / 2 * fsin(angle);
228 offsetX += slideWidth * PFREAL_ONE;
229 offsetY += slideWidth * PFREAL_ONE / 4;
233 // adjust slides so that they are in "steady state" position
234 void PictureFlowState::reset()
236 centerSlide.angle = 0;
239 centerSlide.slideIndex = centerIndex;
240 centerSlide.blend = 256;
242 leftSlides.resize(6);
243 for (int i = 0; i < (int)leftSlides.count(); i++) {
244 SlideInfo& si = leftSlides[i];
246 si.cx = -(offsetX + spacing * i * PFREAL_ONE);
248 si.slideIndex = centerIndex - 1 - i;
250 if (i == (int)leftSlides.count() - 2)
252 if (i == (int)leftSlides.count() - 1)
256 rightSlides.resize(6);
257 for (int i = 0; i < (int)rightSlides.count(); i++) {
258 SlideInfo& si = rightSlides[i];
260 si.cx = offsetX + spacing * i * PFREAL_ONE;
262 si.slideIndex = centerIndex + 1 + i;
264 if (i == (int)rightSlides.count() - 2)
266 if (i == (int)rightSlides.count() - 1)
271 // ------------- PictureFlowAnimator ---------------------------------------
273 PictureFlowAnimator::PictureFlowAnimator():
274 state(0), target(0), step(0), frame(0)
278 void PictureFlowAnimator::start(int slide)
281 if (!animateTimer.isActive() && state) {
282 step = (target < state->centerSlide.slideIndex) ? -1 : 1;
283 animateTimer.start(30);
287 void PictureFlowAnimator::stop(int slide)
295 void PictureFlowAnimator::update()
297 if (!animateTimer.isActive())
307 // deaccelerate when approaching the target
308 const int max = 2 * 65536;
311 fi -= (target << 16);
316 int ia = IANGLE_MAX * (fi - max / 2) / (max * 2);
317 speed = 512 + 16384 * (PFREAL_ONE + fsin(ia)) / PFREAL_ONE;
320 frame += speed * step;
322 int index = frame >> 16;
323 int pos = frame & 0xffff;
324 int neg = 65536 - pos;
325 int tick = (step < 0) ? neg : pos;
326 PFreal ftick = (tick * PFREAL_ONE) >> 16;
331 if (state->centerIndex != index) {
332 state->centerIndex = index;
334 state->centerSlide.slideIndex = state->centerIndex;
335 for (int i = 0; i < (int)state->leftSlides.count(); i++)
336 state->leftSlides[i].slideIndex = state->centerIndex - 1 - i;
337 for (int i = 0; i < (int)state->rightSlides.count(); i++)
338 state->rightSlides[i].slideIndex = state->centerIndex + 1 + i;
341 state->centerSlide.angle = (step * tick * state->angle) >> 16;
342 state->centerSlide.cx = -step * fmul(state->offsetX, ftick);
343 state->centerSlide.cy = fmul(state->offsetY, ftick);
345 if (state->centerIndex == target) {
351 for (int i = 0; i < (int)state->leftSlides.count(); i++) {
352 SlideInfo& si = state->leftSlides[i];
353 si.angle = state->angle;
354 si.cx = -(state->offsetX + state->spacing * i * PFREAL_ONE + step * state->spacing * ftick);
355 si.cy = state->offsetY;
358 for (int i = 0; i < (int)state->rightSlides.count(); i++) {
359 SlideInfo& si = state->rightSlides[i];
360 si.angle = -state->angle;
361 si.cx = state->offsetX + state->spacing * i * PFREAL_ONE - step * state->spacing * ftick;
362 si.cy = state->offsetY;
366 PFreal ftick = (neg * PFREAL_ONE) >> 16;
367 state->rightSlides[0].angle = -(neg * state->angle) >> 16;
368 state->rightSlides[0].cx = fmul(state->offsetX, ftick);
369 state->rightSlides[0].cy = fmul(state->offsetY, ftick);
371 PFreal ftick = (pos * PFREAL_ONE) >> 16;
372 state->leftSlides[0].angle = (pos * state->angle) >> 16;
373 state->leftSlides[0].cx = -fmul(state->offsetX, ftick);
374 state->leftSlides[0].cy = fmul(state->offsetY, ftick);
377 // must change direction ?
378 if (target < index) if (step > 0)
380 if (target > index) if (step < 0)
383 // the first and last slide must fade in/fade out
384 int nleft = state->leftSlides.count();
385 int nright = state->rightSlides.count();
386 int fade = pos / 256;
388 for (int index = 0; index < nleft; index++) {
390 if (index == nleft - 1)
391 blend = (step > 0) ? 0 : 128 - fade / 2;
392 if (index == nleft - 2)
393 blend = (step > 0) ? 128 - fade / 2 : 256 - fade / 2;
394 if (index == nleft - 3)
395 blend = (step > 0) ? 256 - fade / 2 : 256;
396 state->leftSlides[index].blend = blend;
398 for (int index = 0; index < nright; index++) {
399 int blend = (index < nright - 2) ? 256 : 128;
400 if (index == nright - 1)
401 blend = (step > 0) ? fade / 2 : 0;
402 if (index == nright - 2)
403 blend = (step > 0) ? 128 + fade / 2 : fade / 2;
404 if (index == nright - 3)
405 blend = (step > 0) ? 256 : 128 + fade / 2;
406 state->rightSlides[index].blend = blend;
411 // ------------- PictureFlowSoftwareRenderer ---------------------------------------
413 PictureFlowSoftwareRenderer::PictureFlowSoftwareRenderer():
414 PictureFlowAbstractRenderer(), size(0, 0), bgcolor(0), effect(-1), blankSurface(0)
418 PictureFlowSoftwareRenderer::~PictureFlowSoftwareRenderer()
425 void PictureFlowSoftwareRenderer::paint()
430 if (widget->size() != size)
433 if (state->backgroundColor != bgcolor) {
434 bgcolor = state->backgroundColor;
437 if ((int)(state->reflectionEffect) != effect) {
438 effect = (int)state->reflectionEffect;
444 QPainter painter(widget);
445 painter.drawImage(QPoint(0, 0), buffer);
447 QModelIndex index = state->model->index( state->centerIndex, 0, state->model->currentIndex().parent() );
451 void PictureFlowSoftwareRenderer::init()
458 size = widget->size();
459 int ww = size.width();
460 int wh = size.height();
461 int w = (ww + 1) / 2;
462 int h = (wh + 1) / 2;
464 buffer = QImage(ww, wh, QImage::Format_RGB32);
465 buffer.fill(bgcolor);
468 for (int i = 0; i < w; i++) {
469 PFreal gg = ((PFREAL_ONE >> 1) + i * PFREAL_ONE) / (2 * h);
477 // TODO: optimize this with lookup tables
478 static QRgb blendColor(QRgb c1, QRgb c2, int blend)
480 int r = qRed(c1) * blend / 256 + qRed(c2) * (256 - blend) / 256;
481 int g = qGreen(c1) * blend / 256 + qGreen(c2) * (256 - blend) / 256;
482 int b = qBlue(c1) * blend / 256 + qBlue(c2) * (256 - blend) / 256;
483 return qRgb(r, g, b);
487 static QImage* prepareSurface(const QImage* slideImage, int w, int h, QRgb bgcolor,
488 PictureFlow::ReflectionEffect reflectionEffect, QModelIndex index)
490 Qt::TransformationMode mode = Qt::SmoothTransformation;
491 QImage img = slideImage->scaled(w, h, Qt::IgnoreAspectRatio, mode);
493 // slightly larger, to accomodate for the reflection
497 // offscreen buffer: black is sweet
498 QImage* result = new QImage(hs, w, QImage::Format_RGB32);
499 QFont font( index.data( Qt::FontRole ).value<QFont>() );
500 QPainter imagePainter( result );
502 imagePainter.setFont( font );
504 rotation.scale(1,-1);
505 rotation.translate( 0, hofs );
506 result->fill(bgcolor);
508 // transpose the image, this is to speed-up the rendering
509 // because we process one column at a time
510 // (and much better and faster to work row-wise, i.e in one scanline)
512 for (int x = 0; x < w; x++)
513 for (int y = 0; y < h; y++)
514 result->setPixel(hofs + y, x, img.pixel(x, y));
516 imagePainter.drawImage( hofs+h, 0, img );
517 if (reflectionEffect != PictureFlow::NoReflection) {
518 // create the reflection
519 int ht = hs - h - hofs;
521 for (int x = 0; x < w; x++)
523 QRgb *line = (QRgb*)(result->scanLine( x ));
524 for (int y = 0; y < ht; y++) {
525 QRgb color = img.pixel(x, img.height() - y - 1);
526 line[h+hofs+y] = blendColor( color, bgcolor, 128*(hte-y)/hte );
527 //result->setPixel(h + hofs + y, x, blendColor(color, bgcolor, 128*(hte - y) / hte));
531 if (reflectionEffect == PictureFlow::BlurredReflection) {
532 // blur the reflection everything first
533 // Based on exponential blur algorithm by Jani Huhtanen
534 QRect rect(hs / 2, 0, hs / 2, w);
535 rect &= result->rect();
538 int r2 = rect.bottom();
539 int c1 = rect.left();
540 int c2 = rect.right();
542 int bpl = result->bytesPerLine();
546 // how many times blur is applied?
547 // for low-end system, limit this to only 1 loop
548 for (int loop = 0; loop < 2; loop++) {
549 for (int col = c1; col <= c2; col++) {
550 p = result->scanLine(r1) + col * 4;
551 for (int i = 0; i < 3; i++)
555 for (int j = r1; j < r2; j++, p += bpl)
556 for (int i = 0; i < 3; i++)
557 p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4;
560 for (int row = r1; row <= r2; row++) {
561 p = result->scanLine(row) + c1 * 4;
562 for (int i = 0; i < 3; i++)
566 for (int j = c1; j < c2; j++, p += 4)
567 for (int i = 0; i < 3; i++)
568 p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4;
571 for (int col = c1; col <= c2; col++) {
572 p = result->scanLine(r2) + col * 4;
573 for (int i = 0; i < 3; i++)
577 for (int j = r1; j < r2; j++, p -= bpl)
578 for (int i = 0; i < 3; i++)
579 p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4;
582 for (int row = r1; row <= r2; row++) {
583 p = result->scanLine(row) + c2 * 4;
584 for (int i = 0; i < 3; i++)
588 for (int j = c1; j < c2; j++, p -= 4)
589 for (int i = 0; i < 3; i++)
590 p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4;
594 // overdraw to leave only the reflection blurred (but not the actual image)
595 imagePainter.setTransform( rotation );
596 imagePainter.drawImage( 0, 0, img );
597 imagePainter.setBrush( QBrush( Qt::lightGray ) );
598 imagePainter.setPen( QColor( Qt::lightGray ) );
599 QFontMetrics fm = imagePainter.fontMetrics();
600 imagePainter.drawText( 0, img.height()+ 13, VLCModel::getMeta( index, COLUMN_TITLE ) );
601 imagePainter.drawText( 0, img.height()+ 13 + fm.xHeight()*2, VLCModel::getMeta( index, COLUMN_ARTIST ) );
603 for (int x = 0; x < w; x++)
604 for (int y = 0; y < h; y++)
605 result->setPixel(hofs + y, x, img.pixel(x, y));
613 QImage* PictureFlowSoftwareRenderer::surface(QModelIndex index)
615 if (!state || !index.isValid())
618 QImage* img = new QImage(VLCModel::getArtPixmap( index,
619 QSize( state->slideWidth, state->slideHeight ) ).toImage());
621 QImage* sr = prepareSurface(img, state->slideWidth, state->slideHeight, bgcolor, state->reflectionEffect, index );
627 // Renders a slide to offscreen buffer. Returns a rect of the rendered area.
628 // col1 and col2 limit the column for rendering.
629 QRect PictureFlowSoftwareRenderer::renderSlide(const SlideInfo &slide, int col1, int col2)
631 int blend = slide.blend;
639 PLModel* plm = dynamic_cast<PLModel*>( state->model );
641 MLModel* mlm = dynamic_cast<MLModel*>( state->model );
645 index = ((PLModel*)state->model)->index( slide.slideIndex, 0, state->model->currentIndex().parent() );
646 if( !index.isValid() )
649 PLItem *item = static_cast<PLItem*>( index.internalPointer() );
650 artURL = InputManager::decodeArtURL( item->inputItem() );
655 index = ((MLModel*)state->model)->index( slide.slideIndex, 0, QModelIndex() );
656 if( !index.isValid() )
659 MLItem *item = static_cast<MLItem*>( index.internalPointer() );
660 artURL = qfu( item->getMedia()->psz_cover );
663 QString key = QString("%1%2%3%4").arg(VLCModel::getMeta( index, COLUMN_TITLE )).arg( VLCModel::getMeta( index, COLUMN_ARTIST ) ).arg(index.data( VLCModel::IsCurrentRole ).toBool() ).arg( artURL );
666 if( cache.contains( key ) )
667 src = cache.value( key );
670 src = surface( index );
671 cache.insert( key, src );
676 QRect rect(0, 0, 0, 0);
678 int sw = src->height();
679 int sh = src->width();
680 int h = buffer.height();
681 int w = buffer.width();
689 col1 = (col1 >= 0) ? col1 : 0;
690 col2 = (col2 >= 0) ? col2 : w - 1;
691 col1 = qMin(col1, w - 1);
692 col2 = qMin(col2, w - 1);
695 int distance = h * 100 / zoom;
696 PFreal sdx = fcos(slide.angle);
697 PFreal sdy = fsin(slide.angle);
698 PFreal xs = slide.cx - state->slideWidth * sdx / 2;
699 PFreal ys = slide.cy - state->slideWidth * sdy / 2;
700 PFreal dist = distance * PFREAL_ONE;
702 int xi = qMax((PFreal)0, (w * PFREAL_ONE / 2) + fdiv(xs * h, dist + ys) >> PFREAL_SHIFT);
710 for (int x = qMax(xi, col1); x <= col2; x++) {
714 fk = fk - fdiv(sdx, sdy);
715 hity = -fdiv((rays[x] * distance - slide.cx + slide.cy * sdx / sdy), fk);
718 dist = distance * PFREAL_ONE + hity;
722 PFreal hitx = fmul(dist, rays[x]);
723 PFreal hitdist = fdiv(hitx - slide.cx, sdx);
725 int column = sw / 2 + (hitdist >> PFREAL_SHIFT);
738 QRgb* pixel1 = (QRgb*)(buffer.scanLine(y1)) + x;
739 QRgb* pixel2 = (QRgb*)(buffer.scanLine(y2)) + x;
740 QRgb pixelstep = pixel2 - pixel1;
742 int center = (sh / 2);
744 int p1 = center * PFREAL_ONE - dy / 2;
745 int p2 = center * PFREAL_ONE + dy / 2;
747 const QRgb *ptr = (const QRgb*)(src->scanLine(column));
749 while ((y1 >= 0) && (y2 < h) && (p1 >= 0)) {
750 *pixel1 = ptr[p1 >> PFREAL_SHIFT];
751 *pixel2 = ptr[p2 >> PFREAL_SHIFT];
760 while ((y1 >= 0) && (y2 < h) && (p1 >= 0)) {
761 QRgb c1 = ptr[p1 >> PFREAL_SHIFT];
762 QRgb c2 = ptr[p2 >> PFREAL_SHIFT];
763 *pixel1 = blendColor(c1, bgcolor, blend);
764 *pixel2 = blendColor(c2, bgcolor, blend);
775 rect.setBottom(h - 1);
779 void PictureFlowSoftwareRenderer::renderSlides()
781 int nleft = state->leftSlides.count();
782 int nright = state->rightSlides.count();
784 QRect r = renderSlide(state->centerSlide);
788 for (int index = 0; index < nleft; index++) {
789 QRect rs = renderSlide(state->leftSlides[index], 0, c1 - 1);
793 for (int index = 0; index < nright; index++) {
794 QRect rs = renderSlide(state->rightSlides[index], c2 + 1, buffer.width());
800 // Render the slides. Updates only the offscreen buffer.
801 void PictureFlowSoftwareRenderer::render()
803 buffer.fill(state->backgroundColor);
808 // -----------------------------------------
810 class PictureFlowPrivate
813 PictureFlowState* state;
814 PictureFlowAnimator* animator;
815 PictureFlowAbstractRenderer* renderer;
820 PictureFlow::PictureFlow(QWidget* parent, VLCModel* _p_model): QWidget(parent)
822 d = new PictureFlowPrivate;
823 d->state = new PictureFlowState;
824 d->state->model = _p_model;
826 d->state->reposition();
828 d->renderer = new PictureFlowSoftwareRenderer;
829 d->renderer->state = d->state;
830 d->renderer->widget = this;
833 d->animator = new PictureFlowAnimator;
834 d->animator->state = d->state;
835 QObject::connect(&d->animator->animateTimer, SIGNAL(timeout()), this, SLOT(updateAnimation()));
837 QObject::connect(&d->triggerTimer, SIGNAL(timeout()), this, SLOT(render()));
839 setAttribute(Qt::WA_StaticContents, true);
840 setAttribute(Qt::WA_OpaquePaintEvent, true);
841 setAttribute(Qt::WA_NoSystemBackground, true);
844 PictureFlow::~PictureFlow()
852 int PictureFlow::slideCount() const
854 return d->state->model->rowCount( d->state->model->currentIndex().parent() );
857 QColor PictureFlow::backgroundColor() const
859 return QColor(d->state->backgroundColor);
862 void PictureFlow::setBackgroundColor(const QColor& c)
864 d->state->backgroundColor = c.rgb();
868 QSize PictureFlow::slideSize() const
870 return QSize(d->state->slideWidth, d->state->slideHeight);
873 void PictureFlow::setSlideSize(QSize size)
875 d->state->slideWidth = size.width();
876 d->state->slideHeight = size.height();
877 d->state->reposition();
881 PictureFlow::ReflectionEffect PictureFlow::reflectionEffect() const
883 return d->state->reflectionEffect;
886 void PictureFlow::setReflectionEffect(ReflectionEffect effect)
888 d->state->reflectionEffect = effect;
892 int PictureFlow::centerIndex() const
894 return d->state->centerIndex;
897 void PictureFlow::setCenterIndex(int index)
899 index = qMin(index, slideCount() - 1);
900 index = qMax(index, 0);
901 d->state->centerIndex = index;
903 d->animator->stop(index);
907 void PictureFlow::clear()
913 void PictureFlow::render()
915 d->renderer->dirty = true;
919 void PictureFlow::triggerRender()
921 d->triggerTimer.setSingleShot(true);
922 d->triggerTimer.start(0);
925 void PictureFlow::showPrevious()
927 int step = d->animator->step;
928 int center = d->state->centerIndex;
931 d->animator->start(center);
935 d->animator->start(center - 1);
938 d->animator->target = qMax(0, center - 2);
941 void PictureFlow::showNext()
943 int step = d->animator->step;
944 int center = d->state->centerIndex;
947 d->animator->start(center);
950 if (center < slideCount() - 1)
951 d->animator->start(center + 1);
954 d->animator->target = qMin(center + 2, slideCount() - 1);
957 void PictureFlow::showSlide(int index)
959 index = qMax(index, 0);
960 index = qMin(slideCount() - 1, index);
961 if (index == d->state->centerSlide.slideIndex)
964 d->animator->start(index);
967 void PictureFlow::keyPressEvent(QKeyEvent* event)
969 if (event->key() == Qt::Key_Left) {
970 if (event->modifiers() == Qt::ControlModifier)
971 showSlide(centerIndex() - 10);
978 if (event->key() == Qt::Key_Right) {
979 if (event->modifiers() == Qt::ControlModifier)
980 showSlide(centerIndex() + 10);
990 void PictureFlow::mousePressEvent(QMouseEvent* event)
992 if (event->x() > width() / 2 + d->state->slideWidth/2 )
994 else if (event->x() < width() / 2 - d->state->slideWidth/2 )
996 else if ( d->state->model->currentIndex().row() != d->state->centerIndex )
997 d->state->model->activateItem( d->state->model->index( d->state->centerIndex, 0,
998 d->state->model->currentIndex().parent() ) );
1001 void PictureFlow::paintEvent(QPaintEvent* event)
1004 d->renderer->paint();
1007 void PictureFlow::resizeEvent(QResizeEvent* event)
1010 QWidget::resizeEvent(event);
1013 void PictureFlow::updateAnimation()
1015 int old_center = d->state->centerIndex;
1016 d->animator->update();
1018 if (d->state->centerIndex != old_center)
1019 emit centerIndexChanged(d->state->centerIndex);