browsercore/appfw/Common/PictureFlow.cpp
changeset 0 1450b09d0cfd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/browsercore/appfw/Common/PictureFlow.cpp	Tue May 04 12:39:35 2010 +0300
@@ -0,0 +1,1637 @@
+/*
+  PictureFlow - animated image show widget
+  http://pictureflow.googlecode.com
+  
+  Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). 
+  Copyright (C) 2008 Ariya Hidayat (ariya@kde.org)
+  Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "PictureFlow.h"
+
+// detect Qt version
+#if QT_VERSION >= 0x040000
+#define PICTUREFLOW_QT4
+#elif QT_VERSION >= 0x030000
+#define PICTUREFLOW_QT3
+#elif QT_VERSION >= 235
+#define PICTUREFLOW_QT2
+#else
+#error PictureFlow widgets need Qt 2, Qt 3 or Qt 4
+#endif
+
+#include <QGraphicsSceneResizeEvent>
+#include <QGraphicsSceneMouseEvent>
+#include <QGraphicsSceneMoveEvent>
+
+#ifdef PICTUREFLOW_QT4
+#include <QApplication>
+#include <QCache>
+#include <QHash>
+#include <QImage>
+#include <QKeyEvent>
+#include <QPainter>
+#include <QPixmap>
+#include <QTimer>
+#include <QVector>
+#include <QWidget>
+#endif
+
+#include <QDebug>
+
+#ifdef PICTUREFLOW_QT3
+#include <qapplication.h>
+#include <qcache.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qdatetime.h>
+#include <qtimer.h>
+#include <qvaluevector.h>
+#include <qwidget.h>
+
+#define qMax(x,y) ((x) > (y)) ? (x) : (y)
+#define qMin(x,y) ((x) < (y)) ? (x) : (y)
+
+#define QVector QValueVector
+
+#define toImage convertToImage
+#define contains find
+#define modifiers state
+#define ControlModifier ControlButton
+#endif
+
+#ifdef PICTUREFLOW_QT2
+#include <qapplication.h>
+#include <qarray.h>
+#include <qcache.h>
+#include <qimage.h>
+#include <qintdict.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qdatetime.h>
+#include <qtimer.h>
+#include <qwidget.h>
+
+#define qMax(x,y) ((x) > (y)) ? (x) : (y)
+#define qMin(x,y) ((x) < (y)) ? (x) : (y)
+
+#define QVector QArray
+
+#define toImage convertToImage
+#define contains find
+#define modifiers state
+#define ControlModifier ControlButton
+#define flush flushX
+#endif
+
+// for fixed-point arithmetic, we need minimum 32-bit long
+// long long (64-bit) might be useful for multiplication and division
+typedef long PFreal;
+#define PFREAL_SHIFT 10
+#define PFREAL_ONE (1 << PFREAL_SHIFT)
+
+#define IANGLE_MAX 1024
+#define IANGLE_MASK 1023
+
+namespace WRT {
+
+const int slideRatio1 = 2;
+const int slideRatio2 = 5;
+const int KScrollTimeout = 250;
+
+inline PFreal fmul(PFreal a, PFreal b)
+{
+  return ((long long)(a))*((long long)(b)) >> PFREAL_SHIFT;
+}
+
+inline PFreal fdiv(PFreal num, PFreal den)
+{
+  long long p = (long long)(num) << (PFREAL_SHIFT*2);
+  long long q = p / (long long)den;
+  long long r = q >> PFREAL_SHIFT;
+
+  return r;
+}
+
+inline PFreal fsin(int iangle)
+{
+  // warning: regenerate the table if IANGLE_MAX and PFREAL_SHIFT are changed!
+  static const PFreal tab[] = {
+     3,    103,    202,    300,    394,    485,    571,    652,
+   726,    793,    853,    904,    947,    980,   1004,   1019,
+  1023,   1018,   1003,    978,    944,    901,    849,    789,
+   721,    647,    566,    479,    388,    294,    196,     97,
+    -4,   -104,   -203,   -301,   -395,   -486,   -572,   -653,
+  -727,   -794,   -854,   -905,   -948,   -981,  -1005,  -1020,
+ -1024,  -1019,  -1004,   -979,   -945,   -902,   -850,   -790,
+  -722,   -648,   -567,   -480,   -389,   -295,   -197,    -98,
+  3
+  };
+
+  while(iangle < 0)
+    iangle += IANGLE_MAX;
+  iangle &= IANGLE_MASK;
+
+  int i = (iangle >> 4);
+  PFreal p = tab[i];
+  PFreal q = tab[(i+1)];
+  PFreal g = (q - p);
+  return p + g * (iangle-i*16)/16;
+}
+
+inline PFreal fcos(int iangle)
+{
+  return fsin(iangle + (IANGLE_MAX >> 2));
+}
+
+/* ----------------------------------------------------------
+
+PictureFlowState stores the state of all slides, i.e. all the necessary
+information to be able to render them.
+
+PictureFlowAnimator is responsible to move the slides during the
+transition between slides, to achieve the effect similar to Cover Flow,
+by changing the state.
+
+PictureFlowSoftwareRenderer (or PictureFlowOpenGLRenderer) is
+the actual 3-d renderer. It should render all slides given the state
+(an instance of PictureFlowState).
+
+Instances of all the above three classes are stored in
+PictureFlowPrivate.
+
+------------------------------------------------------- */
+
+struct SlideInfo
+{
+  int slideIndex;
+  int angle;
+  PFreal cx;
+  PFreal cy;
+  int blend;
+};
+
+class PictureFlowState
+{
+public:
+  PictureFlowState();
+  ~PictureFlowState();
+
+  void reposition();
+  void reset();
+
+  QRgb backgroundColor;
+  int slideWidth;
+  int slideHeight;
+  ReflectionEffect reflectionEffect;
+  QVector<QImage*> slideImages;
+
+  int angle;
+  int spacing;
+  PFreal offsetX;
+  PFreal offsetY;
+
+  SlideInfo centerSlide;
+  QVector<SlideInfo> leftSlides;
+  QVector<SlideInfo> rightSlides;
+  int centerIndex;
+};
+
+class PictureFlowAnimator
+{
+public:
+  PictureFlowAnimator();
+  PictureFlowState* state;
+
+  void start(int slide);
+  void stop(int slide);
+  void update();
+
+  int target;
+  int step;
+  int frame;
+  QTimer animateTimer;
+};
+
+class PictureFlowAbstractRenderer
+{
+public:
+  PictureFlowAbstractRenderer(): state(0), dirty(false), widget(0), gWidget(0), gPainter(0) {}
+  virtual ~PictureFlowAbstractRenderer() {}
+
+  PictureFlowState* state;
+  bool dirty;
+  QWidget* widget;
+  QGraphicsWidget* gWidget;
+  QPainter* gPainter;
+  QRect cRect; // central rect
+
+  virtual void init() = 0;
+  virtual void paint() = 0;
+};
+
+class PictureFlowSoftwareRenderer: public PictureFlowAbstractRenderer
+{
+public:
+  PictureFlowSoftwareRenderer();
+  ~PictureFlowSoftwareRenderer();
+
+  virtual void init();
+  virtual void paint();
+
+  QRect renderSlide(const SlideInfo &slide, int col1 = -1, int col2 = -1);
+private:
+  QSize size;
+  QRgb bgcolor;
+  int effect;
+  QImage buffer;
+  QVector<PFreal> rays;
+  QImage* blankSurface;
+#ifdef PICTUREFLOW_QT4
+  QCache<int,QImage> surfaceCache;
+  QHash<int,QImage*> imageHash;
+#endif
+#ifdef PICTUREFLOW_QT3
+  QCache<QImage> surfaceCache;
+  QMap<int,QImage*> imageHash;
+#endif
+#ifdef PICTUREFLOW_QT2
+  QCache<QImage> surfaceCache;
+  QIntDict<QImage> imageHash;
+#endif
+
+  void render();
+  void renderSlides();
+  QImage* surface(int slideIndex);
+};
+
+// ------------- PictureFlowState ---------------------------------------
+
+PictureFlowState::PictureFlowState():
+backgroundColor(0), slideWidth(150), slideHeight(200),
+reflectionEffect(BlurredReflection), centerIndex(0)
+{
+}
+
+PictureFlowState::~PictureFlowState()
+{
+  for(int i = 0; i < (int)slideImages.count(); i++)
+    delete slideImages[i];
+}
+
+// readjust the settings, call this when slide dimension is changed
+void PictureFlowState::reposition()
+{
+  angle = 60*IANGLE_MAX / 360;
+
+  offsetX = slideWidth/2 * (PFREAL_ONE-fcos(angle));
+  offsetY = slideWidth/2 * fsin(angle);
+  offsetX += slideWidth * PFREAL_ONE;
+  offsetY += slideWidth * PFREAL_ONE / 4;
+  spacing = 20;
+}
+
+// adjust slides so that they are in "steady state" position
+void PictureFlowState::reset()
+{
+  centerSlide.angle = 0;
+  centerSlide.cx = 0;
+  centerSlide.cy = 0;
+  centerSlide.slideIndex = centerIndex;
+  centerSlide.blend = 256;
+
+  leftSlides.resize(6);
+  for(int i = 0; i < (int)leftSlides.count(); i++)
+  {
+    SlideInfo& si = leftSlides[i];
+    si.angle = angle;
+    si.cx = -(offsetX + spacing*i*PFREAL_ONE);
+    si.cy = offsetY;
+    si.slideIndex = centerIndex-1-i;
+    si.blend = 256;
+    if(i == (int)leftSlides.count()-2)
+      si.blend = 128;
+    if(i == (int)leftSlides.count()-1)
+      si.blend = 0;
+  }
+
+  rightSlides.resize(6);
+  for(int i = 0; i < (int)rightSlides.count(); i++)
+  {
+    SlideInfo& si = rightSlides[i];
+    si.angle = -angle;
+    si.cx = offsetX + spacing*i*PFREAL_ONE;
+    si.cy = offsetY;
+    si.slideIndex = centerIndex+1+i;
+    si.blend = 256;
+    if(i == (int)rightSlides.count()-2)
+      si.blend = 128;
+    if(i == (int)rightSlides.count()-1)
+      si.blend = 0;
+  }
+}
+
+// ------------- PictureFlowAnimator  ---------------------------------------
+
+PictureFlowAnimator::PictureFlowAnimator():
+state(0), target(0), step(0), frame(0)
+{
+}
+
+void PictureFlowAnimator::start(int slide)
+{
+  target = slide;
+  if(!animateTimer.isActive() && state)
+  {
+    step = (target < state->centerSlide.slideIndex) ? -1 : 1;
+    animateTimer.start(30);
+  }
+}
+
+void PictureFlowAnimator::stop(int slide)
+{
+  step = 0;
+  target = slide;
+  frame = slide << 16;
+  animateTimer.stop();
+}
+
+void PictureFlowAnimator::update()
+{
+  if(!animateTimer.isActive())
+    return;
+  if(step == 0)
+    return;
+  if(!state)
+    return;
+
+  int speed = 16384/4;
+
+#if 1
+  // deaccelerate when approaching the target
+  // we disabled clicking until the animation is done so this has to be fast enough to not annoy users
+  const int max = 65536 + 16384; // was 65536*2 but that was too slow when we disabled clicks
+
+  int fi = frame;
+  fi -= (target << 16);
+  if(fi < 0)
+    fi = -fi;
+  fi = qMin(fi, max);
+
+  int ia = IANGLE_MAX * (fi-max/2) / (max*2);
+  speed = 512 + 16384 * (PFREAL_ONE+fsin(ia))/PFREAL_ONE;
+#endif
+
+  frame += speed*step;
+
+  int index = frame >> 16;
+  int pos = frame & 0xffff;
+  int neg = 65536 - pos;
+  int tick = (step < 0) ? neg : pos;
+  PFreal ftick = (tick * PFREAL_ONE) >> 16;
+
+  if(step < 0)
+    index++;
+
+  if(state->centerIndex != index)
+  {
+    state->centerIndex = index;
+    frame = index << 16;
+    state->centerSlide.slideIndex = state->centerIndex;
+    for(int i = 0; i < (int)state->leftSlides.count(); i++)
+      state->leftSlides[i].slideIndex = state->centerIndex-1-i;
+    for(int i = 0; i < (int)state->rightSlides.count(); i++)
+      state->rightSlides[i].slideIndex = state->centerIndex+1+i;
+  }
+
+  state->centerSlide.angle = (step * tick * state->angle) >> 16;
+  state->centerSlide.cx = -step * fmul(state->offsetX, ftick);
+  state->centerSlide.cy = fmul(state->offsetY, ftick);
+
+  if(state->centerIndex == target)
+  {
+    stop(target);
+    state->reset();
+    return;
+  }
+
+  for(int i = 0; i < (int)state->leftSlides.count(); i++)
+  {
+    SlideInfo& si = state->leftSlides[i];
+    si.angle = state->angle;
+    si.cx = -(state->offsetX + state->spacing*i*PFREAL_ONE + step*state->spacing*ftick);
+    si.cy = state->offsetY;
+  }
+
+  for(int i = 0; i < (int)state->rightSlides.count(); i++)
+  {
+    SlideInfo& si = state->rightSlides[i];
+    si.angle = -state->angle;
+    si.cx = state->offsetX + state->spacing*i*PFREAL_ONE - step*state->spacing*ftick;
+    si.cy = state->offsetY;
+  }
+
+  if(step > 0)
+  {
+    PFreal ftick = (neg * PFREAL_ONE) >> 16;
+    state->rightSlides[0].angle = -(neg * state->angle) >> 16;
+    state->rightSlides[0].cx = fmul(state->offsetX, ftick);
+    state->rightSlides[0].cy = fmul(state->offsetY, ftick);
+  }
+  else
+  {
+    PFreal ftick = (pos * PFREAL_ONE) >> 16;
+    state->leftSlides[0].angle = (pos * state->angle) >> 16;
+    state->leftSlides[0].cx = -fmul(state->offsetX, ftick);
+    state->leftSlides[0].cy = fmul(state->offsetY, ftick);
+  }
+
+  // must change direction ?
+  if(target < index) if(step > 0)
+    step = -1;
+  if(target > index) if(step < 0)
+    step = 1;
+
+  // the first and last slide must fade in/fade out
+  int nleft = state->leftSlides.count();
+  int nright = state->rightSlides.count();
+  int fade = pos / 256;
+
+  for(int index = 0; index < nleft; index++)
+  {
+    int blend = 256;
+    if(index == nleft-1)
+      blend = (step > 0) ? 0 : 128-fade/2;
+    if(index == nleft-2)
+      blend = (step > 0) ? 128-fade/2 : 256-fade/2;
+    if(index == nleft-3)
+      blend = (step > 0) ? 256-fade/2 : 256;
+    state->leftSlides[index].blend = blend;
+  }
+  for(int index = 0; index < nright; index++)
+  {
+    int blend = (index < nright-2) ? 256 : 128;
+    if(index == nright-1)
+      blend = (step > 0) ? fade/2 : 0;
+    if(index == nright-2)
+      blend = (step > 0) ? 128+fade/2 : fade/2;
+    if(index == nright-3)
+      blend = (step > 0) ? 256 : 128+fade/2;
+    state->rightSlides[index].blend = blend;
+  }
+}
+
+// ------------- PictureFlowSoftwareRenderer ---------------------------------------
+
+PictureFlowSoftwareRenderer::PictureFlowSoftwareRenderer():
+PictureFlowAbstractRenderer(), size(0,0), bgcolor(0), effect(-1), blankSurface(0)
+{
+#ifdef PICTUREFLOW_QT3
+  surfaceCache.setAutoDelete(true);
+#endif
+}
+
+PictureFlowSoftwareRenderer::~PictureFlowSoftwareRenderer()
+{
+  surfaceCache.clear();
+  buffer = QImage();
+  delete blankSurface;
+}
+
+void PictureFlowSoftwareRenderer::paint()
+{
+  if(!widget && !gWidget)
+    return;
+
+  if(widget && widget->size() != size)
+    init();
+  else if (gWidget && gWidget->size().toSize() != size)
+    init();
+
+  if(state->backgroundColor != bgcolor)
+  {
+    bgcolor = state->backgroundColor;
+    surfaceCache.clear();
+  }
+
+  if((int)(state->reflectionEffect) != effect)
+  {
+    effect = (int)state->reflectionEffect;
+    surfaceCache.clear();
+  }
+
+  if(dirty)
+    render();
+
+  if (widget) {
+      QPainter painter(widget);
+      painter.drawImage(QPoint(0,0), buffer);
+  } else if (gWidget && gPainter) {
+      gPainter->drawImage(QPoint(0,0), buffer);
+  }
+}
+
+void PictureFlowSoftwareRenderer::init()
+{
+  if(!widget && !gWidget)
+    return;
+
+  surfaceCache.clear();
+  blankSurface = 0;
+
+  if (widget)
+    size = widget->size();
+  else
+    size = gWidget->size().toSize();
+  
+  int ww = size.width();
+  int wh = size.height();
+  int w = (ww+1)/2;
+  int h = (wh+1)/2;
+
+#ifdef PICTUREFLOW_QT4
+  buffer = QImage(ww, wh, QImage::Format_RGB32);
+#endif
+#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2)
+  buffer.create(ww, wh, 32);
+#endif
+  buffer.fill(bgcolor);
+
+  rays.resize(w*2);
+  for(int i = 0; i < w; i++)
+  {
+    PFreal gg = ((PFREAL_ONE >> 1) + i * PFREAL_ONE) / (2*h);
+    rays[w-i-1] = -gg;
+    rays[w+i] = gg;
+  }
+
+  dirty = true;
+}
+
+// TODO: optimize this with lookup tables
+static QRgb blendColor(QRgb c1, QRgb c2, int blend)
+{
+  int r = qRed(c1) * blend/256 + qRed(c2)*(256-blend)/256;
+  int g = qGreen(c1) * blend/256 + qGreen(c2)*(256-blend)/256;
+  int b = qBlue(c1) * blend/256 + qBlue(c2)*(256-blend)/256;
+  return qRgb(r, g, b);
+}
+
+
+static QImage* prepareSurface(const QImage* slideImage, int w, int h, QRgb bgcolor,
+ReflectionEffect reflectionEffect)
+{
+#ifdef PICTUREFLOW_QT4
+  Qt::TransformationMode mode = Qt::SmoothTransformation;
+  QImage img = slideImage->scaled(w, h, Qt::IgnoreAspectRatio, mode);
+#endif
+#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2)
+  QImage img = slideImage->smoothScale(w, h);
+#endif
+
+  // slightly larger, to accomodate for the reflection
+  int hs = h * 2;
+  int hofs = h / 3;
+
+  // offscreen buffer: black is sweet
+#ifdef PICTUREFLOW_QT4
+  QImage* result = new QImage(hs, w, QImage::Format_RGB32);
+#endif
+#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2)
+  QImage* result = new QImage;
+  result->create(hs, w, 32);
+#endif
+  result->fill(bgcolor);
+
+  // transpose the image, this is to speed-up the rendering
+  // because we process one column at a time
+  // (and much better and faster to work row-wise, i.e in one scanline)
+  for(int x = 0; x < w; x++)
+    for(int y = 0; y < h; y++)
+      result->setPixel(hofs + y, x, img.pixel(x, y));
+
+  if(reflectionEffect != NoReflection)
+  {
+    // create the reflection
+    int ht = hs - h - hofs;
+    int hte = ht;
+    for(int x = 0; x < w; x++)
+      for(int y = 0; y < ht; y++)
+      {
+        QRgb color = img.pixel(x, img.height()-y-1);
+        result->setPixel(h+hofs+y, x, blendColor(color,bgcolor,128*(hte-y)/hte));
+      }
+
+    if(reflectionEffect == BlurredReflection)
+    {
+      // blur the reflection everything first
+      // Based on exponential blur algorithm by Jani Huhtanen
+      QRect rect(hs/2, 0, hs/2, w);
+      rect &= result->rect();
+
+      int r1 = rect.top();
+      int r2 = rect.bottom();
+      int c1 = rect.left();
+      int c2 = rect.right();
+
+      int bpl = result->bytesPerLine();
+      int rgba[4];
+      unsigned char* p;
+
+      // how many times blur is applied?
+      // for low-end system, limit this to only 1 loop
+      for(int loop = 0; loop < 2; loop++)
+      {
+        for(int col = c1; col <= c2; col++)
+        {
+          p = result->scanLine(r1) + col*4;
+          for(int i = 0; i < 3; i++)
+            rgba[i] = p[i] << 4;
+
+          p += bpl;
+          for(int j = r1; j < r2; j++, p += bpl)
+            for(int i = 0; i < 3; i++)
+              p[i] = (rgba[i] += (((p[i]<<4)-rgba[i])) >> 1) >> 4;
+        }
+
+        for(int row = r1; row <= r2; row++)
+        {
+          p = result->scanLine(row) + c1*4;
+          for(int i = 0; i < 3; i++)
+            rgba[i] = p[i] << 4;
+
+          p += 4;
+          for(int j = c1; j < c2; j++, p+=4)
+            for(int i = 0; i < 3; i++)
+              p[i] = (rgba[i] += (((p[i]<<4)-rgba[i])) >> 1) >> 4;
+        }
+
+        for(int col = c1; col <= c2; col++)
+        {
+          p = result->scanLine(r2) + col*4;
+          for(int i = 0; i < 3; i++)
+            rgba[i] = p[i] << 4;
+
+          p -= bpl;
+          for(int j = r1; j < r2; j++, p -= bpl)
+            for(int i = 0; i < 3; i++)
+              p[i] = (rgba[i] += (((p[i]<<4)-rgba[i])) >> 1) >> 4;
+        }
+
+        for(int row = r1; row <= r2; row++)
+        {
+          p = result->scanLine(row) + c2*4;
+          for(int i = 0; i < 3; i++)
+            rgba[i] = p[i] << 4;
+
+          p -= 4;
+          for(int j = c1; j < c2; j++, p-=4)
+            for(int i = 0; i < 3; i++)
+              p[i] = (rgba[i] += (((p[i]<<4)-rgba[i])) >> 1) >> 4;
+        }
+      }
+
+      // overdraw to leave only the reflection blurred (but not the actual image)
+      for(int x = 0; x < w; x++)
+        for(int y = 0; y < h; y++)
+          result->setPixel(hofs + y, x, img.pixel(x, y));
+    }
+  }
+
+  return result;
+}
+
+QImage* PictureFlowSoftwareRenderer::surface(int slideIndex)
+{
+  if(!state)
+    return 0;
+  if(slideIndex < 0)
+    return 0;
+  if(slideIndex >= (int)state->slideImages.count())
+    return 0;
+
+#ifdef PICTUREFLOW_QT4
+  int key = slideIndex;
+#endif
+#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2)
+  QString key = QString::number(slideIndex);
+#endif
+
+  QImage* img = state->slideImages.at(slideIndex);
+  bool empty = img ? img->isNull() : true;
+  if(empty)
+  {
+    surfaceCache.remove(key);
+    imageHash.remove(slideIndex);
+    if(!blankSurface)
+    {
+      int sw = state->slideWidth;
+      int sh = state->slideHeight;
+
+#ifdef PICTUREFLOW_QT4
+      QImage img = QImage(sw, sh, QImage::Format_RGB32);
+
+      QPainter painter(&img);
+      QPoint p1(sw*4/10, 0);
+      QPoint p2(sw*6/10, sh);
+      QLinearGradient linearGrad(p1, p2);
+      linearGrad.setColorAt(0, Qt::black);
+      linearGrad.setColorAt(1, Qt::white);
+      painter.setBrush(linearGrad);
+      painter.fillRect(0, 0, sw, sh, QBrush(linearGrad));
+
+      painter.setPen(QPen(QColor(64,64,64), 4));
+      painter.setBrush(QBrush());
+      painter.drawRect(2, 2, sw-3, sh-3);
+      painter.end();
+#endif
+#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2)
+      QPixmap pixmap(sw, sh, 32);
+      QPainter painter(&pixmap);
+      painter.fillRect(pixmap.rect(), QColor(192,192,192));
+      painter.fillRect(5, 5, sw-10, sh-10, QColor(64,64,64));
+      painter.end();
+      QImage img = pixmap.convertToImage();
+#endif
+
+      blankSurface = prepareSurface(&img, sw, sh, bgcolor, state->reflectionEffect);
+    }
+    return blankSurface;
+  }
+
+#ifdef PICTUREFLOW_QT4
+  bool exist = imageHash.contains(slideIndex);
+  if(exist)
+  if(img == imageHash.find(slideIndex).value())
+#endif
+#ifdef PICTUREFLOW_QT3
+  bool exist = imageHash.find(slideIndex) != imageHash.end();
+  if(exist)
+  if(img == imageHash.find(slideIndex).data())
+#endif
+#ifdef PICTUREFLOW_QT2
+  if(img == imageHash[slideIndex])
+#endif
+    if(surfaceCache.contains(key))
+        return surfaceCache[key];
+
+  QImage* sr = prepareSurface(img, state->slideWidth, state->slideHeight, bgcolor, state->reflectionEffect);
+  surfaceCache.insert(key, sr);
+  imageHash.insert(slideIndex, img);
+
+  return sr;
+}
+
+// Renders a slide to offscreen buffer. Returns a rect of the rendered area.
+// col1 and col2 limit the column for rendering.
+QRect PictureFlowSoftwareRenderer::renderSlide(const SlideInfo &slide, int col1, int col2)
+{
+  int blend = slide.blend;
+  if(!blend)
+    return QRect();
+
+  QImage* src = surface(slide.slideIndex);
+  if(!src)
+    return QRect();
+
+  QRect rect(0, 0, 0, 0);
+
+  int sw = src->height();
+  int sh = src->width();
+  int h = buffer.height();
+  int w = buffer.width();
+
+  if(col1 > col2)
+  {
+    int c = col2;
+    col2 = col1;
+    col1 = c;
+  }
+
+  col1 = (col1 >= 0) ? col1 : 0;
+  col2 = (col2 >= 0) ? col2 : w-1;
+  col1 = qMin(col1, w-1);
+  col2 = qMin(col2, w-1);
+
+  int zoom = 100;
+  int distance = h * 100 / zoom;
+  PFreal sdx = fcos(slide.angle);
+  PFreal sdy = fsin(slide.angle);
+  PFreal xs = slide.cx - state->slideWidth * sdx/2;
+  PFreal ys = slide.cy - state->slideWidth * sdy/2;
+  PFreal dist = distance * PFREAL_ONE;
+  int xi = qMax((PFreal)0, (w*PFREAL_ONE/2) + fdiv(xs*h, dist+ys) >> PFREAL_SHIFT);
+  if(xi >= w)
+    return rect;
+
+  bool flag = false;
+  rect.setLeft(xi);
+  
+  int centerY = 0;
+  for(int x = qMax(xi, col1); x <= col2; x++)
+  {
+    PFreal hity = 0;
+    PFreal fk = rays[x];
+    if(sdy)
+    {
+      fk = fk - fdiv(sdx,sdy);
+      hity = -fdiv((rays[x]*distance - slide.cx + slide.cy*sdx/sdy), fk);
+    }
+
+    dist = distance*PFREAL_ONE + hity;
+    if(dist < 0)
+      continue;
+
+    PFreal hitx = fmul(dist, rays[x]);
+    PFreal hitdist = fdiv(hitx - slide.cx, sdx);
+
+    int column = sw/2 + (hitdist >> PFREAL_SHIFT);
+    if(column >= sw)
+      break;
+    if(column < 0)
+      continue;
+
+    rect.setRight(x);
+    if(!flag)
+      rect.setLeft(x);
+    flag = true;
+
+    int y1 = h/2;
+    int y2 = y1+ 1;
+    centerY = y1;
+    QRgb* pixel1 = (QRgb*)(buffer.scanLine(y1)) + x;
+    QRgb* pixel2 = (QRgb*)(buffer.scanLine(y2)) + x;
+    QRgb pixelstep = pixel2 - pixel1;
+
+    int center = (sh/2);
+    int dy = dist / h;
+    int p1 = center*PFREAL_ONE - dy/2;
+    int p2 = center*PFREAL_ONE + dy/2;
+
+    const QRgb *ptr = (const QRgb*)(src->scanLine(column));
+    if(blend == 256)
+      while((y1 >= 0) && (y2 < h) && (p1 >= 0))
+      {
+        *pixel1 = ptr[p1 >> PFREAL_SHIFT];
+        *pixel2 = ptr[p2 >> PFREAL_SHIFT];
+        p1 -= dy;
+        p2 += dy;
+        y1--;
+        y2++;
+        pixel1 -= pixelstep;
+        pixel2 += pixelstep;
+      }
+    else
+      while((y1 >= 0) && (y2 < h) && (p1 >= 0))
+      {
+        QRgb c1 = ptr[p1 >> PFREAL_SHIFT];
+        QRgb c2 = ptr[p2 >> PFREAL_SHIFT];
+        *pixel1 = blendColor(c1, bgcolor, blend);
+        *pixel2 = blendColor(c2, bgcolor, blend);
+        p1 -= dy;
+        p2 += dy;
+        y1--;
+        y2++;
+        pixel1 -= pixelstep;
+        pixel2 += pixelstep;
+     }
+   }
+
+   int yTop = (3 * centerY - 2 * state->slideHeight) / 3;
+   rect.setTop(yTop);
+   rect.setBottom(state->slideHeight + yTop);
+   return rect;
+}
+
+void PictureFlowSoftwareRenderer::renderSlides()
+{
+  int nleft = state->leftSlides.count();
+  int nright = state->rightSlides.count();
+
+  QRect r = renderSlide(state->centerSlide);
+  int c1 = r.left();
+  int c2 = r.right();
+  cRect = r;
+  for(int index = 0; index < nleft; index++)
+  {
+    QRect rs = renderSlide(state->leftSlides[index], 0, c1-1);
+    if(!rs.isEmpty())
+      c1 = rs.left();
+  }
+  for(int index = 0; index < nright; index++)
+  {
+    QRect rs = renderSlide(state->rightSlides[index], c2+1, buffer.width());
+    if(!rs.isEmpty())
+      c2 = rs.right();
+  }
+}
+
+// Render the slides. Updates only the offscreen buffer.
+void PictureFlowSoftwareRenderer::render()
+{
+  buffer.fill(state->backgroundColor);
+  renderSlides();
+  dirty = false;
+}
+
+// -----------------------------------------
+
+class PictureFlowPrivate
+{
+public:
+  PictureFlowState* state;
+  PictureFlowAnimator* animator;
+  PictureFlowAbstractRenderer* renderer;
+  QTimer triggerTimer;
+  QTimer scrollTimer;
+};
+
+
+PictureFlow::PictureFlow(QWidget* parent): FlowInterface(parent)
+{
+  d = new PictureFlowPrivate;
+
+  d->state = new PictureFlowState;
+  d->state->reset();
+  d->state->reposition();
+
+  d->renderer = new PictureFlowSoftwareRenderer;
+  d->renderer->state = d->state;
+  d->renderer->widget = this;
+  d->renderer->init();
+
+  d->animator = new PictureFlowAnimator;
+  d->animator->state = d->state;
+  QObject::connect(&d->animator->animateTimer, SIGNAL(timeout()), this, SLOT(updateAnimation()));
+
+  QObject::connect(&d->triggerTimer, SIGNAL(timeout()), this, SLOT(render()));
+  QObject::connect(&d->scrollTimer, SIGNAL(timeout()), this, SLOT(scroll()));
+
+#ifdef PICTUREFLOW_QT4
+  setAttribute(Qt::WA_StaticContents, true);
+  setAttribute(Qt::WA_OpaquePaintEvent, true);
+  setAttribute(Qt::WA_NoSystemBackground, true);
+#endif
+#ifdef PICTUREFLOW_QT3
+  setWFlags(getWFlags() | Qt::WStaticContents);
+  setWFlags(getWFlags() | Qt::WNoAutoErase);
+#endif
+#ifdef PICTUREFLOW_QT2
+  setWFlags(getWFlags() | Qt::WPaintClever);
+  setWFlags(getWFlags() | Qt::WRepaintNoErase);
+  setWFlags(getWFlags() | Qt::WResizeNoErase);
+#endif
+}
+
+PictureFlow::~PictureFlow()
+{
+  delete d->renderer;
+  delete d->animator;
+  delete d->state;
+  delete d;
+}
+
+int PictureFlow::slideCount() const
+{
+  return d->state->slideImages.count();
+}
+
+QColor PictureFlow::backgroundColor() const
+{
+  return QColor(d->state->backgroundColor);
+}
+
+void PictureFlow::setBackgroundColor(const QColor& c)
+{
+  d->state->backgroundColor = c.rgb();
+  triggerRender();
+}
+
+QSize PictureFlow::slideSize() const
+{
+  return QSize(d->state->slideWidth, d->state->slideHeight);
+}
+
+void PictureFlow::setSlideSize(QSize size)
+{
+  d->state->slideWidth = size.width();
+  d->state->slideHeight = size.height();
+  d->state->reposition();
+  triggerRender();
+}
+
+ReflectionEffect PictureFlow::reflectionEffect() const
+{
+  return d->state->reflectionEffect;
+}
+
+void PictureFlow::setReflectionEffect(ReflectionEffect effect)
+{
+  d->state->reflectionEffect = effect;
+  triggerRender();
+}
+
+QImage PictureFlow::slide(int index) const
+{
+  QImage* i = 0;
+  if((index >= 0) && (index < slideCount()))
+    i = d->state->slideImages[index];
+  return i ? QImage(*i) : QImage();
+}
+
+void PictureFlow::addSlide(const QImage& image)
+{
+  int c = d->state->slideImages.count();
+  d->state->slideImages.resize(c+1);
+  d->state->slideImages[c] = new QImage(image);
+  triggerRender();
+}
+
+void PictureFlow::addSlide(const QPixmap& pixmap)
+{
+  addSlide(pixmap.toImage());
+}
+
+void PictureFlow::setSlide(int index, const QImage& image)
+{
+  if((index >= 0) && (index < slideCount()))
+  {
+    QImage* i = image.isNull() ? 0 : new QImage(image);
+    delete d->state->slideImages[index];
+    d->state->slideImages[index] = i;
+    triggerRender();
+  }
+}
+
+void PictureFlow::setSlide(int index, const QPixmap& pixmap)
+{
+  setSlide(index, pixmap.toImage());
+}
+
+int PictureFlow::centerIndex() const
+{
+  return d->state->centerIndex;
+}
+
+bool PictureFlow::slideAnimationOngoing() const
+{
+    return d->animator->animateTimer.isActive();
+}
+
+void PictureFlow::setCenterIndex(int index)
+{
+  index = qMin(index, slideCount()-1);
+  index = qMax(index, 0);
+  d->state->centerIndex = index;
+  d->state->reset();
+  d->animator->stop(index);
+  triggerRender();
+}
+
+void PictureFlow::clear()
+{
+  int c = d->state->slideImages.count();
+  for(int i = 0; i < c; i++)
+    delete d->state->slideImages[i];
+  d->state->slideImages.resize(0);
+
+  d->state->reset();
+  triggerRender();
+}
+
+void PictureFlow::render()
+{
+  d->renderer->dirty = true;
+  update();
+}
+
+void PictureFlow::triggerRender()
+{
+#ifdef PICTUREFLOW_QT4
+  d->triggerTimer.setSingleShot(true);
+  d->triggerTimer.start(0);
+#endif
+#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2)
+  d->triggerTimer.start(0, true);
+#endif
+}
+
+void PictureFlow::showPrevious()
+{
+  int step = d->animator->step;
+  int center = d->state->centerIndex;
+
+  if(step > 0)
+    d->animator->start(center);
+
+  if(step == 0)
+    if(center > 0)
+      d->animator->start(center - 1);
+
+  if(step < 0)
+    d->animator->target = qMax(0, center - 2);
+}
+
+void PictureFlow::showNext()
+{
+  int step = d->animator->step;
+  int center = d->state->centerIndex;
+
+  if(step < 0)
+    d->animator->start(center);
+
+  if(step == 0)
+    if(center < slideCount()-1)
+      d->animator->start(center + 1);
+
+  if(step > 0)
+    d->animator->target = qMin(center + 2, slideCount()-1);
+}
+
+void PictureFlow::showSlide(int index)
+{
+  index = qMax(index, 0);
+  index = qMin(slideCount()-1, index);
+  if(index == d->state->centerSlide.slideIndex)
+    return;
+
+  d->animator->start(index);
+}
+
+void PictureFlow::keyPressEvent(QKeyEvent* event)
+{
+    switch (event->key()) {
+        case Qt::Key_Escape:
+            emit cancel();
+            return;
+
+        case Qt::Key_Enter:
+        case Qt::Key_Return:
+        case Qt::Key_Select:
+            emit ok(centerIndex());
+            return;
+        case Qt::Key_Left:
+            if(event->modifiers() == Qt::ControlModifier)
+                showSlide(centerIndex()-10);
+            else
+                showPrevious();
+            event->accept();
+            return;
+        case Qt::Key_Right:
+            if(event->modifiers() == Qt::ControlModifier)
+                showSlide(centerIndex()+10);
+            else
+                showNext();
+            event->accept();
+            return;
+    }
+    event->ignore();
+}
+
+void PictureFlow::mousePressEvent(QMouseEvent* event)
+{
+    m_lastMoveEventPos = event->pos();
+    if (d->scrollTimer.isActive())
+        d->scrollTimer.stop();
+	d->scrollTimer.start(KScrollTimeout);
+    scroll();
+}
+
+void PictureFlow::mouseMoveEvent(QMouseEvent* event)
+{
+    m_lastMoveEventPos = event->pos();
+}
+
+void PictureFlow::mouseReleaseEvent(QMouseEvent* event)
+{
+    d->scrollTimer.stop();        
+	if (slideAnimationOngoing()) {
+//		qDebug() << "pf:mouseReleaseEvent slideanimation running, ignoring click";
+		return;
+	}
+    if(event->x() > ((width() * (slideRatio2 - slideRatio1)) / (2 * slideRatio2)) && event->x() < ((width() * (slideRatio2 + slideRatio1)) / (2 * slideRatio2))) {
+        emit ok(centerIndex());
+        return;
+    }
+}
+
+void PictureFlow::scroll()
+{
+    if(m_lastMoveEventPos.x() < ((width() * (slideRatio2 - slideRatio1)) / (2 * slideRatio2))) {
+        showPrevious();
+    }
+    else if (m_lastMoveEventPos.x() > ((width() * (slideRatio2 + slideRatio1)) / (2 * slideRatio2))) {
+        showNext();
+    }
+}
+
+
+void PictureFlow::paintEvent(QPaintEvent* event)
+{
+  Q_UNUSED(event);
+  d->renderer->paint();
+}
+
+void PictureFlow::resizeEvent(QResizeEvent* event)
+{
+    QWidget::resizeEvent(event);
+
+    QSize s = event->size(); //parentWidget()->rect().size();
+    setSlideSize(QSize((s.width() * slideRatio1) / slideRatio2, (s.height() * slideRatio1) / slideRatio2));
+}
+
+void PictureFlow::updateAnimation()
+{
+  int old_center = d->state->centerIndex;
+  d->animator->update();
+  triggerRender();
+  if(d->state->centerIndex != old_center)
+    emit centerIndexChanged(d->state->centerIndex);
+}
+
+void PictureFlow::init()
+{   
+    QSize s = size(); //parentWidget()->rect().size();
+ 
+    setSlideSize(QSize((s.width() * slideRatio1) / slideRatio2, (s.height() * slideRatio1) / slideRatio2));
+    //resize(s);
+//TODO: Disable refrection ?
+//    setReflectionEffect(PictureFlow::NoReflection); 
+    setBackgroundColor(Qt::black);
+    // ensure that system cursor is an arrow, not a random icon
+    // This is not an issue if the platform does not have a system cursor
+#ifndef __SYMBIAN32__
+    setCursor(Qt::ArrowCursor);
+#endif
+    setFocusPolicy(Qt::WheelFocus);
+    setFocus(Qt::OtherFocusReason);
+}
+
+QRect PictureFlow::centralRect() const 
+{
+    if (d->renderer) {
+        /* Render the slide to get the rectangle */
+        SlideInfo s = d->state->centerSlide;
+        QRect r = ((PictureFlowSoftwareRenderer*)d->renderer)->renderSlide(s);
+        return r;
+    }
+    else
+        return QRect();
+}
+
+//-----------------------------------------------
+// GraphicsPictureFlow class
+
+GraphicsPictureFlow::GraphicsPictureFlow(QObject* parent): GraphicsFlowInterface(NULL)
+{
+  setParent(parent);
+
+  d = new PictureFlowPrivate;
+
+  d->state = new PictureFlowState;
+  d->state->reset();
+  d->state->reposition();
+
+  d->renderer = new PictureFlowSoftwareRenderer;
+  d->renderer->state = d->state;
+  d->renderer->gWidget = this;
+  d->renderer->init();
+
+  d->animator = new PictureFlowAnimator;
+  d->animator->state = d->state;
+  QObject::connect(&d->animator->animateTimer, SIGNAL(timeout()), this, SLOT(updateAnimation()));
+
+  QObject::connect(&d->triggerTimer, SIGNAL(timeout()), this, SLOT(render()));
+  QObject::connect(&d->scrollTimer, SIGNAL(timeout()), this, SLOT(scroll()));
+
+#ifdef PICTUREFLOW_QT4
+  setAttribute(Qt::WA_StaticContents, true);
+  setAttribute(Qt::WA_OpaquePaintEvent, true);
+  setAttribute(Qt::WA_NoSystemBackground, true);
+#endif
+#ifdef PICTUREFLOW_QT3
+  setWFlags(getWFlags() | Qt::WStaticContents);
+  setWFlags(getWFlags() | Qt::WNoAutoErase);
+#endif
+#ifdef PICTUREFLOW_QT2
+  setWFlags(getWFlags() | Qt::WPaintClever);
+  setWFlags(getWFlags() | Qt::WRepaintNoErase);
+  setWFlags(getWFlags() | Qt::WResizeNoErase);
+#endif
+}
+
+GraphicsPictureFlow::~GraphicsPictureFlow()
+{
+  delete d->renderer;
+  delete d->animator;
+  delete d->state;
+  delete d;
+}
+
+int GraphicsPictureFlow::slideCount() const
+{
+  return d->state->slideImages.count();
+}
+
+QColor GraphicsPictureFlow::backgroundColor() const
+{
+  return QColor(d->state->backgroundColor);
+}
+
+void GraphicsPictureFlow::setBackgroundColor(const QColor& c)
+{
+  d->state->backgroundColor = c.rgb();
+  triggerRender();
+}
+
+QSize GraphicsPictureFlow::slideSize() const
+{
+  return QSize(d->state->slideWidth, d->state->slideHeight);
+}
+
+void GraphicsPictureFlow::setSlideSize(QSize size)
+{
+  d->state->slideWidth = size.width();
+  d->state->slideHeight = size.height();
+  d->state->reposition();
+  triggerRender();
+}
+
+ReflectionEffect GraphicsPictureFlow::reflectionEffect() const
+{
+  return d->state->reflectionEffect;
+}
+
+void GraphicsPictureFlow::setReflectionEffect(ReflectionEffect effect)
+{
+  d->state->reflectionEffect = effect;
+  triggerRender();
+}
+
+QImage GraphicsPictureFlow::slide(int index) const
+{
+  QImage* i = 0;
+  if((index >= 0) && (index < slideCount()))
+    i = d->state->slideImages[index];
+  return i ? QImage(*i) : QImage();
+}
+
+void GraphicsPictureFlow::addSlide(const QImage& image)
+{
+  int c = d->state->slideImages.count();
+  d->state->slideImages.resize(c+1);
+  d->state->slideImages[c] = new QImage(image);
+  triggerRender();
+}
+
+void GraphicsPictureFlow::addSlide(const QPixmap& pixmap)
+{
+  addSlide(pixmap.toImage());
+}
+
+void GraphicsPictureFlow::setSlide(int index, const QImage& image)
+{
+  if((index >= 0) && (index < slideCount()))
+  {
+    QImage* i = image.isNull() ? 0 : new QImage(image);
+    delete d->state->slideImages[index];
+    d->state->slideImages[index] = i;
+    triggerRender();
+  }
+}
+
+void GraphicsPictureFlow::setSlide(int index, const QPixmap& pixmap)
+{
+  setSlide(index, pixmap.toImage());
+}
+
+int GraphicsPictureFlow::centerIndex() const
+{
+  return d->state->centerIndex;
+}
+
+bool GraphicsPictureFlow::slideAnimationOngoing() const
+{
+    return d->animator->animateTimer.isActive();
+}
+
+void GraphicsPictureFlow::setCenterIndex(int index)
+{
+  index = qMin(index, slideCount()-1);
+  index = qMax(index, 0);
+  d->state->centerIndex = index;
+  d->state->reset();
+  d->animator->stop(index);
+  triggerRender();
+}
+
+void GraphicsPictureFlow::clear()
+{
+  int c = d->state->slideImages.count();
+  for(int i = 0; i < c; i++)
+    delete d->state->slideImages[i];
+  d->state->slideImages.resize(0);
+
+  d->state->reset();
+  triggerRender();
+}
+
+void GraphicsPictureFlow::render()
+{
+  d->renderer->dirty = true;
+  update();
+}
+
+void GraphicsPictureFlow::triggerRender()
+{
+#ifdef PICTUREFLOW_QT4
+  d->triggerTimer.setSingleShot(true);
+  d->triggerTimer.start(0);
+#endif
+#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2)
+  d->triggerTimer.start(0, true);
+#endif
+}
+
+void GraphicsPictureFlow::showPrevious()
+{
+  int step = d->animator->step;
+  int center = d->state->centerIndex;
+
+  if(step > 0)
+    d->animator->start(center);
+
+  if(step == 0)
+    if(center > 0)
+      d->animator->start(center - 1);
+
+  if(step < 0)
+    d->animator->target = qMax(0, center - 2);
+}
+
+void GraphicsPictureFlow::showNext()
+{
+  int step = d->animator->step;
+  int center = d->state->centerIndex;
+
+  if(step < 0)
+    d->animator->start(center);
+
+  if(step == 0)
+    if(center < slideCount()-1)
+      d->animator->start(center + 1);
+
+  if(step > 0)
+    d->animator->target = qMin(center + 2, slideCount()-1);
+}
+
+void GraphicsPictureFlow::showSlide(int index)
+{
+  index = qMax(index, 0);
+  index = qMin(slideCount()-1, index);
+  if(index == d->state->centerSlide.slideIndex)
+    return;
+
+  d->animator->start(index);
+}
+
+void GraphicsPictureFlow::keyPressEvent(QKeyEvent* event)
+{
+    switch (event->key()) {
+        case Qt::Key_Escape:
+            emit cancel();
+            return;
+
+        case Qt::Key_Enter:
+        case Qt::Key_Return:
+        case Qt::Key_Select:
+            emit ok(centerIndex());
+            return;
+        case Qt::Key_Left:
+            if(event->modifiers() == Qt::ControlModifier)
+                showSlide(centerIndex()-10);
+            else
+                showPrevious();
+            event->accept();
+            return;
+        case Qt::Key_Right:
+            if(event->modifiers() == Qt::ControlModifier)
+                showSlide(centerIndex()+10);
+            else
+                showNext();
+            event->accept();
+            return;
+    }
+    event->ignore();
+}
+
+void GraphicsPictureFlow::mousePressEvent(QGraphicsSceneMouseEvent* event)
+{
+    m_lastMoveEventPos = event->pos().toPoint();
+    if (d->scrollTimer.isActive())
+        d->scrollTimer.stop();
+	d->scrollTimer.start(KScrollTimeout);
+    scroll();
+}
+
+void GraphicsPictureFlow::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
+{
+    m_lastMoveEventPos = event->pos().toPoint();
+}
+
+void GraphicsPictureFlow::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
+{
+    d->scrollTimer.stop();        
+	if (slideAnimationOngoing()) {
+//		qDebug() << "pf:mouseReleaseEvent slideanimation running, ignoring click";
+		return;
+	}
+    if(event->pos().x() > ((size().width() * (slideRatio2 - slideRatio1)) / (2 * slideRatio2)) && event->pos().x() < ((size().width() * (slideRatio2 + slideRatio1)) / (2 * slideRatio2))) {
+        emit ok(centerIndex());
+        return;
+    }
+}
+
+void GraphicsPictureFlow::scroll()
+{
+    if(m_lastMoveEventPos.x() < ((size().width() * (slideRatio2 - slideRatio1)) / (2 * slideRatio2))) {
+        showPrevious();
+    }
+    else if (m_lastMoveEventPos.x() > ((size().width() * (slideRatio2 + slideRatio1)) / (2 * slideRatio2))) {
+        showNext();
+    }
+}
+
+void GraphicsPictureFlow::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
+{
+    d->renderer->gPainter = painter;
+    d->renderer->paint();
+    d->renderer->gPainter = NULL;
+}
+
+void GraphicsPictureFlow::resizeEvent(QGraphicsSceneResizeEvent* event)
+{
+    QGraphicsWidget::resizeEvent(event);
+
+    QSize s = event->newSize().toSize();
+    setSlideSize(QSize((s.width() * slideRatio1) / slideRatio2, (s.height() * slideRatio1) / slideRatio2));
+}
+
+void GraphicsPictureFlow::updateAnimation()
+{
+  int old_center = d->state->centerIndex;
+  d->animator->update();
+  triggerRender();
+  if(d->state->centerIndex != old_center)
+    emit centerIndexChanged(d->state->centerIndex);
+}
+
+void GraphicsPictureFlow::init()
+{   
+    QSize s = size().toSize(); //parentWidget()->rect().size();
+ 
+    setSlideSize(QSize((s.width() * slideRatio1) / slideRatio2, (s.height() * slideRatio1) / slideRatio2));
+    //resize(s);
+//TODO: Disable refrection ?
+//    setReflectionEffect(PictureFlow::NoReflection); 
+    setBackgroundColor(Qt::black);
+    // ensure that system cursor is an arrow, not a random icon
+    // This is not an issue if the platform does not have a system cursor
+#ifndef __SYMBIAN32__
+    setCursor(Qt::ArrowCursor);
+#endif
+    setFocusPolicy(Qt::WheelFocus);
+    setFocus(Qt::OtherFocusReason);
+}
+
+QRect GraphicsPictureFlow::centralRect() const 
+{
+    if (d->renderer) {
+        /* Render the slide to get the rectangle */
+        SlideInfo s = d->state->centerSlide;
+        QRect r = ((PictureFlowSoftwareRenderer*)d->renderer)->renderSlide(s);
+        return r;
+    }
+    else
+        return QRect();
+}
+}