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"
31 #include <QApplication>
40 #include "../components/playlist/playlist_model.hpp" /* getArtPixmap etc */
41 #include "../components/playlist/sorting.h" /* Columns List */
42 #include "input_manager.hpp"
44 // for fixed-point arithmetic, we need minimum 32-bit long
45 // long long (64-bit) might be useful for multiplication and division
47 #define PFREAL_SHIFT 10
48 #define PFREAL_ONE (1 << PFREAL_SHIFT)
50 #define IANGLE_MAX 1024
51 #define IANGLE_MASK 1023
53 inline PFreal fmul(PFreal a, PFreal b)
55 return ((long long)(a))*((long long)(b)) >> PFREAL_SHIFT;
58 inline PFreal fdiv(PFreal num, PFreal den)
60 long long p = (long long)(num) << (PFREAL_SHIFT * 2);
61 long long q = p / (long long)den;
62 long long r = q >> PFREAL_SHIFT;
67 inline PFreal fsin(int iangle)
69 // warning: regenerate the table if IANGLE_MAX and PFREAL_SHIFT are changed!
70 static const PFreal tab[] = {
71 3, 103, 202, 300, 394, 485, 571, 652,
72 726, 793, 853, 904, 947, 980, 1004, 1019,
73 1023, 1018, 1003, 978, 944, 901, 849, 789,
74 721, 647, 566, 479, 388, 294, 196, 97,
75 -4, -104, -203, -301, -395, -486, -572, -653,
76 -727, -794, -854, -905, -948, -981, -1005, -1020,
77 -1024, -1019, -1004, -979, -945, -902, -850, -790,
78 -722, -648, -567, -480, -389, -295, -197, -98,
84 iangle &= IANGLE_MASK;
86 int i = (iangle >> 4);
88 PFreal q = tab[(i+1)];
90 return p + g *(iangle - i*16) / 16;
93 inline PFreal fcos(int iangle)
95 return fsin(iangle + (IANGLE_MAX >> 2));
98 /* ----------------------------------------------------------
100 PictureFlowState stores the state of all slides, i.e. all the necessary
101 information to be able to render them.
103 PictureFlowAnimator is responsible to move the slides during the
104 transition between slides, to achieve the effect similar to Cover Flow,
105 by changing the state.
107 PictureFlowSoftwareRenderer (or PictureFlowOpenGLRenderer) is
108 the actual 3-d renderer. It should render all slides given the state
109 (an instance of PictureFlowState).
111 Instances of all the above three classes are stored in
114 ------------------------------------------------------- */
124 class PictureFlowState
133 QRgb backgroundColor;
136 PictureFlow::ReflectionEffect reflectionEffect;
144 SlideInfo centerSlide;
145 QVector<SlideInfo> leftSlides;
146 QVector<SlideInfo> rightSlides;
150 class PictureFlowAnimator
153 PictureFlowAnimator();
154 PictureFlowState* state;
156 void start(int slide);
157 void stop(int slide);
166 class PictureFlowAbstractRenderer
169 PictureFlowAbstractRenderer(): state(0), dirty(false), widget(0) {}
170 virtual ~PictureFlowAbstractRenderer() {}
172 PictureFlowState* state;
176 virtual void init() = 0;
177 virtual void paint() = 0;
180 class PictureFlowSoftwareRenderer: public PictureFlowAbstractRenderer
183 PictureFlowSoftwareRenderer();
184 ~PictureFlowSoftwareRenderer();
187 virtual void paint();
194 QVector<PFreal> rays;
195 QImage* blankSurface;
199 QRect renderSlide(const SlideInfo &slide, int col1 = -1, int col2 = -1);
200 QImage* surface(QModelIndex);
201 QHash<QString, QImage*> cache;
204 // ------------- PictureFlowState ---------------------------------------
206 PictureFlowState::PictureFlowState():
207 backgroundColor(0), slideWidth(150), slideHeight(200),
208 reflectionEffect(PictureFlow::BlurredReflection), centerIndex(0)
212 PictureFlowState::~PictureFlowState()
216 // readjust the settings, call this when slide dimension is changed
217 void PictureFlowState::reposition()
219 angle = 70 * IANGLE_MAX / 360; // approx. 70 degrees tilted
221 offsetX = slideWidth / 2 * (PFREAL_ONE - fcos(angle));
222 offsetY = slideWidth / 2 * fsin(angle);
223 offsetX += slideWidth * PFREAL_ONE;
224 offsetY += slideWidth * PFREAL_ONE / 4;
228 // adjust slides so that they are in "steady state" position
229 void PictureFlowState::reset()
231 centerSlide.angle = 0;
234 centerSlide.slideIndex = centerIndex;
235 centerSlide.blend = 256;
237 leftSlides.resize(6);
238 for (int i = 0; i < (int)leftSlides.count(); i++) {
239 SlideInfo& si = leftSlides[i];
241 si.cx = -(offsetX + spacing * i * PFREAL_ONE);
243 si.slideIndex = centerIndex - 1 - i;
245 if (i == (int)leftSlides.count() - 2)
247 if (i == (int)leftSlides.count() - 1)
251 rightSlides.resize(6);
252 for (int i = 0; i < (int)rightSlides.count(); i++) {
253 SlideInfo& si = rightSlides[i];
255 si.cx = offsetX + spacing * i * PFREAL_ONE;
257 si.slideIndex = centerIndex + 1 + i;
259 if (i == (int)rightSlides.count() - 2)
261 if (i == (int)rightSlides.count() - 1)
266 // ------------- PictureFlowAnimator ---------------------------------------
268 PictureFlowAnimator::PictureFlowAnimator():
269 state(0), target(0), step(0), frame(0)
273 void PictureFlowAnimator::start(int slide)
276 if (!animateTimer.isActive() && state) {
277 step = (target < state->centerSlide.slideIndex) ? -1 : 1;
278 animateTimer.start(30);
282 void PictureFlowAnimator::stop(int slide)
290 void PictureFlowAnimator::update()
292 if (!animateTimer.isActive())
302 // deaccelerate when approaching the target
303 const int max = 2 * 65536;
306 fi -= (target << 16);
311 int ia = IANGLE_MAX * (fi - max / 2) / (max * 2);
312 speed = 512 + 16384 * (PFREAL_ONE + fsin(ia)) / PFREAL_ONE;
315 frame += speed * step;
317 int index = frame >> 16;
318 int pos = frame & 0xffff;
319 int neg = 65536 - pos;
320 int tick = (step < 0) ? neg : pos;
321 PFreal ftick = (tick * PFREAL_ONE) >> 16;
326 if (state->centerIndex != index) {
327 state->centerIndex = index;
329 state->centerSlide.slideIndex = state->centerIndex;
330 for (int i = 0; i < (int)state->leftSlides.count(); i++)
331 state->leftSlides[i].slideIndex = state->centerIndex - 1 - i;
332 for (int i = 0; i < (int)state->rightSlides.count(); i++)
333 state->rightSlides[i].slideIndex = state->centerIndex + 1 + i;
336 state->centerSlide.angle = (step * tick * state->angle) >> 16;
337 state->centerSlide.cx = -step * fmul(state->offsetX, ftick);
338 state->centerSlide.cy = fmul(state->offsetY, ftick);
340 if (state->centerIndex == target) {
346 for (int i = 0; i < (int)state->leftSlides.count(); i++) {
347 SlideInfo& si = state->leftSlides[i];
348 si.angle = state->angle;
349 si.cx = -(state->offsetX + state->spacing * i * PFREAL_ONE + step * state->spacing * ftick);
350 si.cy = state->offsetY;
353 for (int i = 0; i < (int)state->rightSlides.count(); i++) {
354 SlideInfo& si = state->rightSlides[i];
355 si.angle = -state->angle;
356 si.cx = state->offsetX + state->spacing * i * PFREAL_ONE - step * state->spacing * ftick;
357 si.cy = state->offsetY;
361 PFreal ftick = (neg * PFREAL_ONE) >> 16;
362 state->rightSlides[0].angle = -(neg * state->angle) >> 16;
363 state->rightSlides[0].cx = fmul(state->offsetX, ftick);
364 state->rightSlides[0].cy = fmul(state->offsetY, ftick);
366 PFreal ftick = (pos * PFREAL_ONE) >> 16;
367 state->leftSlides[0].angle = (pos * state->angle) >> 16;
368 state->leftSlides[0].cx = -fmul(state->offsetX, ftick);
369 state->leftSlides[0].cy = fmul(state->offsetY, ftick);
372 // must change direction ?
373 if (target < index) if (step > 0)
375 if (target > index) if (step < 0)
378 // the first and last slide must fade in/fade out
379 int nleft = state->leftSlides.count();
380 int nright = state->rightSlides.count();
381 int fade = pos / 256;
383 for (int index = 0; index < nleft; index++) {
385 if (index == nleft - 1)
386 blend = (step > 0) ? 0 : 128 - fade / 2;
387 if (index == nleft - 2)
388 blend = (step > 0) ? 128 - fade / 2 : 256 - fade / 2;
389 if (index == nleft - 3)
390 blend = (step > 0) ? 256 - fade / 2 : 256;
391 state->leftSlides[index].blend = blend;
393 for (int index = 0; index < nright; index++) {
394 int blend = (index < nright - 2) ? 256 : 128;
395 if (index == nright - 1)
396 blend = (step > 0) ? fade / 2 : 0;
397 if (index == nright - 2)
398 blend = (step > 0) ? 128 + fade / 2 : fade / 2;
399 if (index == nright - 3)
400 blend = (step > 0) ? 256 : 128 + fade / 2;
401 state->rightSlides[index].blend = blend;
406 // ------------- PictureFlowSoftwareRenderer ---------------------------------------
408 PictureFlowSoftwareRenderer::PictureFlowSoftwareRenderer():
409 PictureFlowAbstractRenderer(), size(0, 0), bgcolor(0), effect(-1), blankSurface(0)
413 PictureFlowSoftwareRenderer::~PictureFlowSoftwareRenderer()
420 void PictureFlowSoftwareRenderer::paint()
425 if (widget->size() != size)
428 if (state->backgroundColor != bgcolor) {
429 bgcolor = state->backgroundColor;
432 if ((int)(state->reflectionEffect) != effect) {
433 effect = (int)state->reflectionEffect;
439 QPainter painter(widget);
440 painter.drawImage(QPoint(0, 0), buffer);
442 QModelIndex index = state->model->index( state->centerIndex, 0, state->model->currentIndex().parent() );
446 void PictureFlowSoftwareRenderer::init()
453 size = widget->size();
454 int ww = size.width();
455 int wh = size.height();
456 int w = (ww + 1) / 2;
457 int h = (wh + 1) / 2;
459 buffer = QImage(ww, wh, QImage::Format_RGB32);
460 buffer.fill(bgcolor);
463 for (int i = 0; i < w; i++) {
464 PFreal gg = ((PFREAL_ONE >> 1) + i * PFREAL_ONE) / (2 * h);
472 // TODO: optimize this with lookup tables
473 static QRgb blendColor(QRgb c1, QRgb c2, int blend)
475 int r = qRed(c1) * blend / 256 + qRed(c2) * (256 - blend) / 256;
476 int g = qGreen(c1) * blend / 256 + qGreen(c2) * (256 - blend) / 256;
477 int b = qBlue(c1) * blend / 256 + qBlue(c2) * (256 - blend) / 256;
478 return qRgb(r, g, b);
482 static QImage* prepareSurface(const QImage* slideImage, int w, int h, QRgb bgcolor,
483 PictureFlow::ReflectionEffect reflectionEffect, QModelIndex index)
485 Qt::TransformationMode mode = Qt::SmoothTransformation;
486 QImage img = slideImage->scaled(w, h, Qt::IgnoreAspectRatio, mode);
488 // slightly larger, to accomodate for the reflection
492 // offscreen buffer: black is sweet
493 QImage* result = new QImage(hs, w, QImage::Format_RGB32);
494 QFont font( index.data( Qt::FontRole ).value<QFont>() );
495 QPainter imagePainter( result );
497 imagePainter.setFont( font );
499 rotation.scale(1,-1);
500 rotation.translate( 0, hofs );
501 result->fill(bgcolor);
503 // transpose the image, this is to speed-up the rendering
504 // because we process one column at a time
505 // (and much better and faster to work row-wise, i.e in one scanline)
507 for (int x = 0; x < w; x++)
508 for (int y = 0; y < h; y++)
509 result->setPixel(hofs + y, x, img.pixel(x, y));
511 imagePainter.drawImage( hofs+h, 0, img );
512 if (reflectionEffect != PictureFlow::NoReflection) {
513 // create the reflection
514 int ht = hs - h - hofs;
516 for (int x = 0; x < w; x++)
518 QRgb *line = (QRgb*)(result->scanLine( x ));
519 for (int y = 0; y < ht; y++) {
520 QRgb color = img.pixel(x, img.height() - y - 1);
521 line[h+hofs+y] = blendColor( color, bgcolor, 128*(hte-y)/hte );
522 //result->setPixel(h + hofs + y, x, blendColor(color, bgcolor, 128*(hte - y) / hte));
526 if (reflectionEffect == PictureFlow::BlurredReflection) {
527 // blur the reflection everything first
528 // Based on exponential blur algorithm by Jani Huhtanen
529 QRect rect(hs / 2, 0, hs / 2, w);
530 rect &= result->rect();
533 int r2 = rect.bottom();
534 int c1 = rect.left();
535 int c2 = rect.right();
537 int bpl = result->bytesPerLine();
541 // how many times blur is applied?
542 // for low-end system, limit this to only 1 loop
543 for (int loop = 0; loop < 2; loop++) {
544 for (int col = c1; col <= c2; col++) {
545 p = result->scanLine(r1) + col * 4;
546 for (int i = 0; i < 3; i++)
550 for (int j = r1; j < r2; j++, p += bpl)
551 for (int i = 0; i < 3; i++)
552 p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4;
555 for (int row = r1; row <= r2; row++) {
556 p = result->scanLine(row) + c1 * 4;
557 for (int i = 0; i < 3; i++)
561 for (int j = c1; j < c2; j++, p += 4)
562 for (int i = 0; i < 3; i++)
563 p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4;
566 for (int col = c1; col <= c2; col++) {
567 p = result->scanLine(r2) + col * 4;
568 for (int i = 0; i < 3; i++)
572 for (int j = r1; j < r2; j++, p -= bpl)
573 for (int i = 0; i < 3; i++)
574 p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4;
577 for (int row = r1; row <= r2; row++) {
578 p = result->scanLine(row) + c2 * 4;
579 for (int i = 0; i < 3; i++)
583 for (int j = c1; j < c2; j++, p -= 4)
584 for (int i = 0; i < 3; i++)
585 p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4;
589 // overdraw to leave only the reflection blurred (but not the actual image)
590 imagePainter.setTransform( rotation );
591 imagePainter.drawImage( 0, 0, img );
592 imagePainter.setBrush( QBrush( Qt::lightGray ) );
593 imagePainter.setPen( QColor( Qt::lightGray ) );
594 QFontMetrics fm = imagePainter.fontMetrics();
595 imagePainter.drawText( 0, img.height()+ 13, VLCModel::getMeta( index, COLUMN_TITLE ) );
596 imagePainter.drawText( 0, img.height()+ 13 + fm.xHeight()*2, VLCModel::getMeta( index, COLUMN_ARTIST ) );
598 for (int x = 0; x < w; x++)
599 for (int y = 0; y < h; y++)
600 result->setPixel(hofs + y, x, img.pixel(x, y));
608 QImage* PictureFlowSoftwareRenderer::surface(QModelIndex index)
610 if (!state || !index.isValid())
613 QImage* img = new QImage(VLCModel::getArtPixmap( index,
614 QSize( state->slideWidth, state->slideHeight ) ).toImage());
616 QImage* sr = prepareSurface(img, state->slideWidth, state->slideHeight, bgcolor, state->reflectionEffect, index );
622 // Renders a slide to offscreen buffer. Returns a rect of the rendered area.
623 // col1 and col2 limit the column for rendering.
624 QRect PictureFlowSoftwareRenderer::renderSlide(const SlideInfo &slide, int col1, int col2)
626 int blend = slide.blend;
634 PLModel* plm = qobject_cast<PLModel*>( state->model );
636 MLModel* mlm = qobject_cast<MLModel*>( state->model );
640 index = ((PLModel*)state->model)->index( slide.slideIndex, 0, state->model->currentIndex().parent() );
641 if( !index.isValid() )
644 PLItem *item = static_cast<PLItem*>( index.internalPointer() );
645 artURL = InputManager::decodeArtURL( item->inputItem() );
650 index = ((MLModel*)state->model)->index( slide.slideIndex, 0, QModelIndex() );
651 if( !index.isValid() )
654 MLItem *item = static_cast<MLItem*>( index.internalPointer() );
655 artURL = qfu( item->getMedia()->psz_cover );
658 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 );
661 if( cache.contains( key ) )
662 src = cache.value( key );
665 src = surface( index );
666 cache.insert( key, src );
671 QRect rect(0, 0, 0, 0);
673 int sw = src->height();
674 int sh = src->width();
675 int h = buffer.height();
676 int w = buffer.width();
684 col1 = (col1 >= 0) ? col1 : 0;
685 col2 = (col2 >= 0) ? col2 : w - 1;
686 col1 = qMin(col1, w - 1);
687 col2 = qMin(col2, w - 1);
690 int distance = h * 100 / zoom;
691 PFreal sdx = fcos(slide.angle);
692 PFreal sdy = fsin(slide.angle);
693 PFreal xs = slide.cx - state->slideWidth * sdx / 2;
694 PFreal ys = slide.cy - state->slideWidth * sdy / 2;
695 PFreal dist = distance * PFREAL_ONE;
697 int xi = qMax((PFreal)0, ((w * PFREAL_ONE / 2) + fdiv(xs * h, dist + ys)) >> PFREAL_SHIFT);
705 for (int x = qMax(xi, col1); x <= col2; x++) {
709 fk = fk - fdiv(sdx, sdy);
710 hity = -fdiv((rays[x] * distance - slide.cx + slide.cy * sdx / sdy), fk);
713 dist = distance * PFREAL_ONE + hity;
717 PFreal hitx = fmul(dist, rays[x]);
718 PFreal hitdist = fdiv(hitx - slide.cx, sdx);
720 int column = sw / 2 + (hitdist >> PFREAL_SHIFT);
733 QRgb* pixel1 = (QRgb*)(buffer.scanLine(y1)) + x;
734 QRgb* pixel2 = (QRgb*)(buffer.scanLine(y2)) + x;
735 QRgb pixelstep = pixel2 - pixel1;
737 int center = (sh / 2);
739 int p1 = center * PFREAL_ONE - dy / 2;
740 int p2 = center * PFREAL_ONE + dy / 2;
742 const QRgb *ptr = (const QRgb*)(src->scanLine(column));
744 while ((y1 >= 0) && (y2 < h) && (p1 >= 0)) {
745 *pixel1 = ptr[p1 >> PFREAL_SHIFT];
746 *pixel2 = ptr[p2 >> PFREAL_SHIFT];
755 while ((y1 >= 0) && (y2 < h) && (p1 >= 0)) {
756 QRgb c1 = ptr[p1 >> PFREAL_SHIFT];
757 QRgb c2 = ptr[p2 >> PFREAL_SHIFT];
758 *pixel1 = blendColor(c1, bgcolor, blend);
759 *pixel2 = blendColor(c2, bgcolor, blend);
770 rect.setBottom(h - 1);
774 void PictureFlowSoftwareRenderer::renderSlides()
776 int nleft = state->leftSlides.count();
777 int nright = state->rightSlides.count();
779 QRect r = renderSlide(state->centerSlide);
783 for (int index = 0; index < nleft; index++) {
784 QRect rs = renderSlide(state->leftSlides[index], 0, c1 - 1);
788 for (int index = 0; index < nright; index++) {
789 QRect rs = renderSlide(state->rightSlides[index], c2 + 1, buffer.width());
795 // Render the slides. Updates only the offscreen buffer.
796 void PictureFlowSoftwareRenderer::render()
798 buffer.fill(state->backgroundColor);
803 // -----------------------------------------
805 class PictureFlowPrivate
808 PictureFlowState* state;
809 PictureFlowAnimator* animator;
810 PictureFlowAbstractRenderer* renderer;
815 PictureFlow::PictureFlow(QWidget* parent, VLCModel* _p_model): QWidget(parent)
817 d = new PictureFlowPrivate;
818 d->state = new PictureFlowState;
819 d->state->model = _p_model;
821 d->state->reposition();
823 d->renderer = new PictureFlowSoftwareRenderer;
824 d->renderer->state = d->state;
825 d->renderer->widget = this;
828 d->animator = new PictureFlowAnimator;
829 d->animator->state = d->state;
830 QObject::connect(&d->animator->animateTimer, SIGNAL(timeout()), this, SLOT(updateAnimation()));
832 QObject::connect(&d->triggerTimer, SIGNAL(timeout()), this, SLOT(render()));
834 setAttribute(Qt::WA_StaticContents, true);
835 setAttribute(Qt::WA_OpaquePaintEvent, true);
836 setAttribute(Qt::WA_NoSystemBackground, true);
839 PictureFlow::~PictureFlow()
847 int PictureFlow::slideCount() const
849 return d->state->model->rowCount( d->state->model->currentIndex().parent() );
852 QColor PictureFlow::backgroundColor() const
854 return QColor(d->state->backgroundColor);
857 void PictureFlow::setBackgroundColor(const QColor& c)
859 d->state->backgroundColor = c.rgb();
863 QSize PictureFlow::slideSize() const
865 return QSize(d->state->slideWidth, d->state->slideHeight);
868 void PictureFlow::setSlideSize(QSize size)
870 d->state->slideWidth = size.width();
871 d->state->slideHeight = size.height();
872 d->state->reposition();
876 PictureFlow::ReflectionEffect PictureFlow::reflectionEffect() const
878 return d->state->reflectionEffect;
881 void PictureFlow::setReflectionEffect(ReflectionEffect effect)
883 d->state->reflectionEffect = effect;
887 int PictureFlow::centerIndex() const
889 return d->state->centerIndex;
892 void PictureFlow::setCenterIndex(int index)
894 index = qMin(index, slideCount() - 1);
895 index = qMax(index, 0);
896 d->state->centerIndex = index;
898 d->animator->stop(index);
902 void PictureFlow::clear()
908 void PictureFlow::render()
910 d->renderer->dirty = true;
914 void PictureFlow::triggerRender()
916 d->triggerTimer.setSingleShot(true);
917 d->triggerTimer.start(0);
920 void PictureFlow::showPrevious()
922 int step = d->animator->step;
923 int center = d->state->centerIndex;
926 d->animator->start(center);
930 d->animator->start(center - 1);
933 d->animator->target = qMax(0, center - 2);
936 void PictureFlow::showNext()
938 int step = d->animator->step;
939 int center = d->state->centerIndex;
942 d->animator->start(center);
945 if (center < slideCount() - 1)
946 d->animator->start(center + 1);
949 d->animator->target = qMin(center + 2, slideCount() - 1);
952 void PictureFlow::showSlide(int index)
954 index = qMax(index, 0);
955 index = qMin(slideCount() - 1, index);
956 if (index == d->state->centerSlide.slideIndex)
959 d->animator->start(index);
962 void PictureFlow::keyPressEvent(QKeyEvent* event)
964 if (event->key() == Qt::Key_Left) {
965 if (event->modifiers() == Qt::ControlModifier)
966 showSlide(centerIndex() - 10);
973 if (event->key() == Qt::Key_Right) {
974 if (event->modifiers() == Qt::ControlModifier)
975 showSlide(centerIndex() + 10);
985 void PictureFlow::mousePressEvent(QMouseEvent* event)
987 if (event->x() > width() / 2 + d->state->slideWidth/2 )
989 else if (event->x() < width() / 2 - d->state->slideWidth/2 )
991 else if ( d->state->model->currentIndex().row() != d->state->centerIndex )
992 d->state->model->activateItem( d->state->model->index( d->state->centerIndex, 0,
993 d->state->model->currentIndex().parent() ) );
996 void PictureFlow::paintEvent(QPaintEvent* event)
999 d->renderer->paint();
1002 void PictureFlow::resizeEvent(QResizeEvent* event)
1005 QWidget::resizeEvent(event);
1008 void PictureFlow::updateAnimation()
1010 int old_center = d->state->centerIndex;
1011 d->animator->update();
1013 if (d->state->centerIndex != old_center)
1014 emit centerIndexChanged(d->state->centerIndex);