tests/qtp/qtp_anomaly/src/flickcharm.cpp
changeset 0 1918ee327afb
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the demos of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "flickcharm.h"
       
    43 
       
    44 #include <QAbstractScrollArea>
       
    45 #include <QApplication>
       
    46 #include <QBasicTimer>
       
    47 #include <QEvent>
       
    48 #include <QHash>
       
    49 #include <QList>
       
    50 #include <QMouseEvent>
       
    51 #include <QScrollBar>
       
    52 #include <QWebFrame>
       
    53 #include <QWebView>
       
    54 
       
    55 #include <QDebug>
       
    56 
       
    57 struct FlickData {
       
    58     typedef enum { Steady, Pressed, ManualScroll, AutoScroll, Stop } State;
       
    59     State state;
       
    60     QWidget *widget;
       
    61     QPoint pressPos;
       
    62     QPoint offset;
       
    63     QPoint dragPos;
       
    64     QPoint speed;
       
    65     QList<QEvent*> ignored;
       
    66 };
       
    67 
       
    68 class FlickCharmPrivate
       
    69 {
       
    70 public:
       
    71     QHash<QWidget*, FlickData*> flickData;
       
    72     QBasicTimer ticker;
       
    73 };
       
    74 
       
    75 FlickCharm::FlickCharm(QObject *parent): QObject(parent)
       
    76 {
       
    77     d = new FlickCharmPrivate;
       
    78 }
       
    79 
       
    80 FlickCharm::~FlickCharm()
       
    81 {
       
    82     delete d;
       
    83 }
       
    84 
       
    85 void FlickCharm::activateOn(QWidget *widget)
       
    86 {
       
    87     QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget);
       
    88     if (scrollArea) {
       
    89         scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
       
    90         scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
       
    91 
       
    92         QWidget *viewport = scrollArea->viewport();
       
    93 
       
    94         viewport->installEventFilter(this);
       
    95         scrollArea->installEventFilter(this);
       
    96 
       
    97         d->flickData.remove(viewport);
       
    98         d->flickData[viewport] = new FlickData;
       
    99         d->flickData[viewport]->widget = widget;
       
   100         d->flickData[viewport]->state = FlickData::Steady;
       
   101 
       
   102         return;
       
   103     }
       
   104 
       
   105     QWebView *webView = qobject_cast<QWebView*>(widget);
       
   106     if (webView) {
       
   107         QWebFrame *frame = webView->page()->mainFrame();
       
   108         frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
       
   109         frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
       
   110 
       
   111         webView->installEventFilter(this);
       
   112 
       
   113         d->flickData.remove(webView);
       
   114         d->flickData[webView] = new FlickData;
       
   115         d->flickData[webView]->widget = webView;
       
   116         d->flickData[webView]->state = FlickData::Steady;
       
   117 
       
   118         return;
       
   119     }
       
   120 
       
   121     qWarning() << "FlickCharm only works on QAbstractScrollArea (and derived classes)";
       
   122     qWarning() << "or QWebView (and derived classes)";
       
   123 }
       
   124 
       
   125 void FlickCharm::deactivateFrom(QWidget *widget)
       
   126 {
       
   127     QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget);
       
   128     if (scrollArea) {
       
   129         QWidget *viewport = scrollArea->viewport();
       
   130 
       
   131         viewport->removeEventFilter(this);
       
   132         scrollArea->removeEventFilter(this);
       
   133 
       
   134         delete d->flickData[viewport];
       
   135         d->flickData.remove(viewport);
       
   136 
       
   137         return;
       
   138     }
       
   139 
       
   140     QWebView *webView = qobject_cast<QWebView*>(widget);
       
   141     if (webView) {
       
   142         webView->removeEventFilter(this);
       
   143 
       
   144         delete d->flickData[webView];
       
   145         d->flickData.remove(webView);
       
   146 
       
   147         return;
       
   148     }
       
   149 }
       
   150 
       
   151 static QPoint scrollOffset(QWidget *widget)
       
   152 {
       
   153     int x = 0, y = 0;
       
   154 
       
   155     QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget);
       
   156     if (scrollArea) {
       
   157         x = scrollArea->horizontalScrollBar()->value();
       
   158         y = scrollArea->verticalScrollBar()->value();
       
   159     }
       
   160 
       
   161     QWebView *webView = qobject_cast<QWebView*>(widget);
       
   162     if (webView) {
       
   163         QWebFrame *frame = webView->page()->mainFrame();
       
   164         x = frame->evaluateJavaScript("window.scrollX").toInt();
       
   165         y = frame->evaluateJavaScript("window.scrollY").toInt();
       
   166     }
       
   167 
       
   168     return QPoint(x, y);
       
   169 }
       
   170 
       
   171 static void setScrollOffset(QWidget *widget, const QPoint &p)
       
   172 {
       
   173     QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget);
       
   174     if (scrollArea) {
       
   175         scrollArea->horizontalScrollBar()->setValue(p.x());
       
   176         scrollArea->verticalScrollBar()->setValue(p.y());
       
   177     }
       
   178 
       
   179     QWebView *webView = qobject_cast<QWebView*>(widget);
       
   180     QWebFrame *frame = webView ? webView->page()->mainFrame() : 0;
       
   181     if (frame)
       
   182         frame->evaluateJavaScript(QString("window.scrollTo(%1,%2);").arg(p.x()).arg(p.y()));
       
   183 }
       
   184 
       
   185 static QPoint deaccelerate(const QPoint &speed, int a = 1, int max = 64)
       
   186 {
       
   187     int x = qBound(-max, speed.x(), max);
       
   188     int y = qBound(-max, speed.y(), max);
       
   189     x = (x == 0) ? x : (x > 0) ? qMax(0, x - a) : qMin(0, x + a);
       
   190     y = (y == 0) ? y : (y > 0) ? qMax(0, y - a) : qMin(0, y + a);
       
   191     return QPoint(x, y);
       
   192 }
       
   193 
       
   194 bool FlickCharm::eventFilter(QObject *object, QEvent *event)
       
   195 {
       
   196     if (!object->isWidgetType())
       
   197         return false;
       
   198 
       
   199     QEvent::Type type = event->type();
       
   200     if (type != QEvent::MouseButtonPress &&
       
   201             type != QEvent::MouseButtonRelease &&
       
   202             type != QEvent::MouseMove)
       
   203         return false;
       
   204 
       
   205     QMouseEvent *mouseEvent = 0;
       
   206     switch (event->type()) {
       
   207         case QEvent::MouseButtonPress:
       
   208         case QEvent::MouseButtonRelease:
       
   209         case QEvent::MouseMove:
       
   210             mouseEvent = static_cast<QMouseEvent*>(event);
       
   211             break;
       
   212     }
       
   213 
       
   214     if (!mouseEvent || mouseEvent->modifiers() != Qt::NoModifier)
       
   215         return false;
       
   216 
       
   217     QWidget *viewport = qobject_cast<QWidget*>(object);
       
   218     FlickData *data = d->flickData.value(viewport);
       
   219     if (!viewport || !data || data->ignored.removeAll(event))
       
   220         return false;
       
   221 
       
   222     bool consumed = false;
       
   223     switch (data->state) {
       
   224 
       
   225     case FlickData::Steady:
       
   226         if (mouseEvent->type() == QEvent::MouseButtonPress)
       
   227             if (mouseEvent->buttons() == Qt::LeftButton) {
       
   228                 consumed = true;
       
   229                 data->state = FlickData::Pressed;
       
   230                 data->pressPos = mouseEvent->pos();
       
   231                 data->offset = scrollOffset(data->widget);
       
   232             }
       
   233         break;
       
   234 
       
   235     case FlickData::Pressed:
       
   236         if (mouseEvent->type() == QEvent::MouseButtonRelease) {
       
   237             consumed = true;
       
   238             data->state = FlickData::Steady;
       
   239 
       
   240             QMouseEvent *event1 = new QMouseEvent(QEvent::MouseButtonPress,
       
   241                                                   data->pressPos, Qt::LeftButton,
       
   242                                                   Qt::LeftButton, Qt::NoModifier);
       
   243             QMouseEvent *event2 = new QMouseEvent(*mouseEvent);
       
   244 
       
   245             data->ignored << event1;
       
   246             data->ignored << event2;
       
   247             QApplication::postEvent(object, event1);
       
   248             QApplication::postEvent(object, event2);
       
   249         }
       
   250         if (mouseEvent->type() == QEvent::MouseMove) {
       
   251             consumed = true;
       
   252             data->state = FlickData::ManualScroll;
       
   253             data->dragPos = QCursor::pos();
       
   254             if (!d->ticker.isActive())
       
   255                 d->ticker.start(20, this);
       
   256         }
       
   257         break;
       
   258 
       
   259     case FlickData::ManualScroll:
       
   260         if (mouseEvent->type() == QEvent::MouseMove) {
       
   261             consumed = true;
       
   262             QPoint delta = mouseEvent->pos() - data->pressPos;
       
   263             setScrollOffset(data->widget, data->offset - delta);
       
   264         }
       
   265         if (mouseEvent->type() == QEvent::MouseButtonRelease) {
       
   266             consumed = true;
       
   267             data->state = FlickData::AutoScroll;
       
   268         }
       
   269         break;
       
   270 
       
   271     case FlickData::AutoScroll:
       
   272         if (mouseEvent->type() == QEvent::MouseButtonPress) {
       
   273             consumed = true;
       
   274             data->state = FlickData::Stop;
       
   275             data->speed = QPoint(0, 0);
       
   276             data->pressPos = mouseEvent->pos();
       
   277             data->offset = scrollOffset(data->widget);
       
   278         }
       
   279         if (mouseEvent->type() == QEvent::MouseButtonRelease) {
       
   280             consumed = true;
       
   281             data->state = FlickData::Steady;
       
   282             data->speed = QPoint(0, 0);
       
   283         }
       
   284         break;
       
   285 
       
   286     case FlickData::Stop:
       
   287         if (mouseEvent->type() == QEvent::MouseButtonRelease) {
       
   288             consumed = true;
       
   289             data->state = FlickData::Steady;
       
   290         }
       
   291         if (mouseEvent->type() == QEvent::MouseMove) {
       
   292             consumed = true;
       
   293             data->state = FlickData::ManualScroll;
       
   294             data->dragPos = QCursor::pos();
       
   295             if (!d->ticker.isActive())
       
   296                 d->ticker.start(20, this);
       
   297         }
       
   298         break;
       
   299 
       
   300     default:
       
   301         break;
       
   302     }
       
   303 
       
   304     return consumed;
       
   305 }
       
   306 
       
   307 void FlickCharm::timerEvent(QTimerEvent *event)
       
   308 {
       
   309     int count = 0;
       
   310     QHashIterator<QWidget*, FlickData*> item(d->flickData);
       
   311     while (item.hasNext()) {
       
   312         item.next();
       
   313         FlickData *data = item.value();
       
   314 
       
   315         if (data->state == FlickData::ManualScroll) {
       
   316             count++;
       
   317             data->speed = QCursor::pos() - data->dragPos;
       
   318             data->dragPos = QCursor::pos();
       
   319         }
       
   320 
       
   321         if (data->state == FlickData::AutoScroll) {
       
   322             count++;
       
   323             data->speed = deaccelerate(data->speed);
       
   324             QPoint p = scrollOffset(data->widget);
       
   325             setScrollOffset(data->widget, p - data->speed);
       
   326             if (data->speed == QPoint(0, 0))
       
   327                 data->state = FlickData::Steady;
       
   328         }
       
   329     }
       
   330 
       
   331     if (!count)
       
   332         d->ticker.stop();
       
   333 
       
   334     QObject::timerEvent(event);
       
   335 }