ginebra2/FlickCharm.cpp
changeset 6 1c3b8676e58c
parent 5 0f2326c2a325
child 8 2e16851ffecd
child 10 232fbd5a2dcb
equal deleted inserted replaced
5:0f2326c2a325 6:1c3b8676e58c
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** Contact: Qt Software Information (qt-info@nokia.com)
       
     5 **
       
     6 ** This file is part of the Graphics Dojo project on Qt Labs.
       
     7 **
       
     8 ** This file may be used under the terms of the GNU General Public
       
     9 ** License version 2.0 or 3.0 as published by the Free Software Foundation
       
    10 ** and appearing in the file LICENSE.GPL included in the packaging of
       
    11 ** this file.  Please review the following information to ensure GNU
       
    12 ** General Public Licensing requirements will be met:
       
    13 ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
       
    14 ** http://www.gnu.org/copyleft/gpl.html.
       
    15 **
       
    16 ** If you are unsure which license is appropriate for your use, please
       
    17 ** contact the sales department at qt-sales@nokia.com.
       
    18 **
       
    19 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
       
    20 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
       
    21 **
       
    22 ****************************************************************************/
       
    23 
       
    24 #include "FlickCharm.h"
       
    25 
       
    26 #include <QAbstractScrollArea>
       
    27 #include <QApplication>
       
    28 #include <QBasicTimer>
       
    29 #include <QEvent>
       
    30 #include <QHash>
       
    31 #include <QList>
       
    32 #include <QMouseEvent>
       
    33 #include <QScrollBar>
       
    34 #include <QWebFrame>
       
    35 #include <QWebView>
       
    36 #include <QGraphicsWebView>
       
    37 #include <QGraphicsSceneMouseEvent>
       
    38 
       
    39 #include <QDebug>
       
    40 
       
    41 struct FlickData {
       
    42     typedef enum { Steady, Pressed, ManualScroll, AutoScroll, Stop } State;
       
    43     State state;
       
    44     QObject *widget;
       
    45     QPoint pressPos;
       
    46     QPoint offset;
       
    47     QPoint dragPos;
       
    48     QPoint speed;
       
    49     QList<QEvent*> ignored;
       
    50 };
       
    51 
       
    52 class FlickCharmPrivate
       
    53 {
       
    54 public:
       
    55     QHash<QObject*, FlickData*> flickData;
       
    56     QBasicTimer ticker;
       
    57 };
       
    58 
       
    59 FlickCharm::FlickCharm(QObject *parent): QObject(parent)
       
    60 {
       
    61     d = new FlickCharmPrivate;
       
    62 }
       
    63 
       
    64 FlickCharm::~FlickCharm()
       
    65 {
       
    66     delete d;
       
    67 }
       
    68 
       
    69 void FlickCharm::activateOn(QWidget *widget)
       
    70 {
       
    71     QAbstractScrollArea *scrollArea = dynamic_cast<QAbstractScrollArea*>(widget);
       
    72     if (scrollArea) {
       
    73         scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
       
    74         scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
       
    75 
       
    76         QWidget *viewport = scrollArea->viewport();
       
    77 
       
    78         viewport->installEventFilter(this);
       
    79         scrollArea->installEventFilter(this);
       
    80 
       
    81         d->flickData.remove(viewport);
       
    82         d->flickData[viewport] = new FlickData;
       
    83         d->flickData[viewport]->widget = widget;
       
    84         d->flickData[viewport]->state = FlickData::Steady;
       
    85 
       
    86         return;
       
    87     }
       
    88 }
       
    89 
       
    90 void FlickCharm::activateOn(QWebView *webView)
       
    91 {
       
    92     if (webView) {
       
    93         QWebFrame *frame = webView->page()->mainFrame();
       
    94         frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
       
    95         frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
       
    96 
       
    97         webView->installEventFilter(this);
       
    98 
       
    99         d->flickData.remove(webView);
       
   100         d->flickData[webView] = new FlickData;
       
   101         d->flickData[webView]->widget = webView;
       
   102         d->flickData[webView]->state = FlickData::Steady;
       
   103 
       
   104         return;
       
   105     }
       
   106 }
       
   107 
       
   108 void FlickCharm::activateOn(QGraphicsWebView *webView)
       
   109 {
       
   110     qDebug() << "FlickCharm::activateOn";
       
   111     if (webView) {
       
   112         QWebFrame *frame = webView->page()->mainFrame();
       
   113         frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
       
   114         frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
       
   115 
       
   116         webView->installEventFilter(this);
       
   117 
       
   118         d->flickData.remove(webView);
       
   119         d->flickData[webView] = new FlickData;
       
   120         d->flickData[webView]->widget = webView;
       
   121         d->flickData[webView]->state = FlickData::Steady;
       
   122 
       
   123         return;
       
   124     }
       
   125 }
       
   126 
       
   127 void FlickCharm::deactivateFrom(QWidget *widget)
       
   128 {
       
   129     QAbstractScrollArea *scrollArea = dynamic_cast<QAbstractScrollArea*>(widget);
       
   130     if (scrollArea) {
       
   131         QWidget *viewport = scrollArea->viewport();
       
   132 
       
   133         viewport->removeEventFilter(this);
       
   134         scrollArea->removeEventFilter(this);
       
   135 
       
   136         delete d->flickData[viewport];
       
   137         d->flickData.remove(viewport);
       
   138 
       
   139         return;
       
   140     }
       
   141 
       
   142     QWebView *webView = dynamic_cast<QWebView*>(widget);
       
   143     if (webView) {
       
   144         webView->removeEventFilter(this);
       
   145 
       
   146         delete d->flickData[webView];
       
   147         d->flickData.remove(webView);
       
   148 
       
   149         return;
       
   150     }
       
   151 }
       
   152 
       
   153 static QPoint scrollOffset(QObject *widget)
       
   154 {
       
   155     int x = 0, y = 0;
       
   156 
       
   157     QGraphicsWebView *gWebView = dynamic_cast<QGraphicsWebView*>(widget);
       
   158     if (gWebView) {
       
   159         QWebFrame *frame = gWebView->page()->mainFrame();
       
   160         x = frame->evaluateJavaScript("window.scrollX").toInt();
       
   161         y = frame->evaluateJavaScript("window.scrollY").toInt();
       
   162     }
       
   163 
       
   164     QAbstractScrollArea *scrollArea = dynamic_cast<QAbstractScrollArea*>(widget);
       
   165     if (scrollArea) {
       
   166         x = scrollArea->horizontalScrollBar()->value();
       
   167         y = scrollArea->verticalScrollBar()->value();
       
   168     }
       
   169 
       
   170     QWebView *webView = dynamic_cast<QWebView*>(widget);
       
   171     if (webView) {
       
   172         QWebFrame *frame = webView->page()->mainFrame();
       
   173         x = frame->evaluateJavaScript("window.scrollX").toInt();
       
   174         y = frame->evaluateJavaScript("window.scrollY").toInt();
       
   175     }
       
   176 
       
   177     return QPoint(x, y);
       
   178 }
       
   179 
       
   180 static void setScrollOffset(QObject *widget, const QPoint &p)
       
   181 {
       
   182     QGraphicsWebView *gWebView = dynamic_cast<QGraphicsWebView*>(widget);
       
   183     if (gWebView) {
       
   184         QWebFrame *frame = gWebView->page()->mainFrame();
       
   185         if (frame)
       
   186             frame->evaluateJavaScript(QString("window.scrollTo(%1,%2);").arg(p.x()).arg(p.y()));
       
   187         return;
       
   188     }
       
   189 
       
   190     QAbstractScrollArea *scrollArea = dynamic_cast<QAbstractScrollArea*>(widget);
       
   191     if (scrollArea) {
       
   192         scrollArea->horizontalScrollBar()->setValue(p.x());
       
   193         scrollArea->verticalScrollBar()->setValue(p.y());
       
   194         return;
       
   195     }
       
   196 
       
   197     QWebView *webView = dynamic_cast<QWebView*>(widget);
       
   198     QWebFrame *frame = webView ? webView->page()->mainFrame() : 0;
       
   199     if (frame)
       
   200         frame->evaluateJavaScript(QString("window.scrollTo(%1,%2);").arg(p.x()).arg(p.y()));
       
   201 }
       
   202 
       
   203 static QPoint deaccelerate(const QPoint &speed, int a = 1, int max = 64)
       
   204 {
       
   205     int x = qBound(-max, speed.x(), max);
       
   206     int y = qBound(-max, speed.y(), max);
       
   207     x = (x == 0) ? x : (x > 0) ? qMax(0, x - a) : qMin(0, x + a);
       
   208     y = (y == 0) ? y : (y > 0) ? qMax(0, y - a) : qMin(0, y + a);
       
   209     return QPoint(x, y);
       
   210 }
       
   211 
       
   212 bool FlickCharm::eventFilter(QObject *object, QEvent *event)
       
   213 {
       
   214 //    qDebug() << "FlickCharm::eventFilter: " << object << event;
       
   215 //    if (!object->isWidgetType())
       
   216 //        return false;
       
   217 
       
   218     QEvent::Type type = event->type();
       
   219     switch (type) {
       
   220         case QEvent::MouseButtonPress:
       
   221         case QEvent::MouseButtonRelease:
       
   222         case QEvent::MouseMove:
       
   223         case QEvent::GraphicsSceneMousePress:
       
   224         case QEvent::GraphicsSceneMouseRelease:
       
   225         case QEvent::GraphicsSceneMouseMove:
       
   226             break;
       
   227         default:
       
   228             return false;
       
   229     }
       
   230 
       
   231     QPoint eventPos;
       
   232     QEvent::Type eventType;
       
   233     Qt::MouseButtons eventButtons;
       
   234     QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event);
       
   235     if (mouseEvent) {
       
   236         if (mouseEvent->modifiers() != Qt::NoModifier)
       
   237             return false;
       
   238         eventPos = mouseEvent->pos();
       
   239         eventType = mouseEvent->type();
       
   240         eventButtons = mouseEvent->buttons();
       
   241     }
       
   242     else {
       
   243         QGraphicsSceneMouseEvent *mouseEvent = dynamic_cast<QGraphicsSceneMouseEvent*>(event);
       
   244         if (mouseEvent && mouseEvent->modifiers() == Qt::NoModifier) {
       
   245             eventPos = mouseEvent->pos().toPoint();
       
   246             eventType = mouseEvent->type();
       
   247             eventButtons = mouseEvent->buttons();
       
   248         }
       
   249         else
       
   250             return false;
       
   251     }
       
   252 
       
   253 //    QWidget *viewport = dynamic_cast<QWidget*>(object);
       
   254     FlickData *data = d->flickData.value(object);
       
   255     if (!data || data->ignored.removeAll(event)) {
       
   256         return false;
       
   257     }
       
   258 
       
   259     bool consumed = false;
       
   260     switch (data->state) {
       
   261 
       
   262     case FlickData::Steady:
       
   263         if (eventType == QEvent::MouseButtonPress ||
       
   264             eventType == QEvent::GraphicsSceneMousePress)
       
   265             if (eventButtons == Qt::LeftButton) {
       
   266                 consumed = true;
       
   267                 data->state = FlickData::Pressed;
       
   268                 data->pressPos = eventPos;
       
   269                 data->offset = scrollOffset(data->widget);
       
   270             }
       
   271         break;
       
   272 
       
   273     case FlickData::Pressed:
       
   274         if (eventType == QEvent::MouseButtonRelease) {
       
   275             consumed = true;
       
   276             data->state = FlickData::Steady;
       
   277 
       
   278             QMouseEvent *event1 = new QMouseEvent(QEvent::MouseButtonPress,
       
   279                                                   data->pressPos, Qt::LeftButton,
       
   280                                                   Qt::LeftButton, Qt::NoModifier);
       
   281             QMouseEvent *event2 = new QMouseEvent(*mouseEvent);
       
   282 
       
   283             data->ignored << event1;
       
   284             data->ignored << event2;
       
   285             QApplication::postEvent(object, event1);
       
   286             QApplication::postEvent(object, event2);
       
   287         }
       
   288         else if (eventType == QEvent::GraphicsSceneMouseRelease) {
       
   289             consumed = true;
       
   290             data->state = FlickData::Steady;
       
   291 
       
   292             QGraphicsSceneMouseEvent *origMouseEvent = static_cast<QGraphicsSceneMouseEvent*>(event);
       
   293             QGraphicsSceneMouseEvent *event1 =
       
   294                     new QGraphicsSceneMouseEvent(QEvent::GraphicsSceneMousePress);
       
   295             event1->setPos(origMouseEvent->pos());
       
   296             event1->setScenePos(origMouseEvent->scenePos());
       
   297             event1->setLastPos(origMouseEvent->lastPos());
       
   298             event1->setLastScenePos(origMouseEvent->lastScenePos());
       
   299             event1->setButtons(origMouseEvent->buttons());
       
   300             event1->setButton(origMouseEvent->button());
       
   301             event1->setModifiers(origMouseEvent->modifiers());
       
   302 
       
   303             QGraphicsSceneMouseEvent *event2 = new QGraphicsSceneMouseEvent(origMouseEvent->type());
       
   304             event2->setPos(origMouseEvent->pos());
       
   305             event2->setScenePos(origMouseEvent->scenePos());
       
   306             event2->setLastPos(origMouseEvent->lastPos());
       
   307             event2->setLastScenePos(origMouseEvent->lastScenePos());
       
   308             event2->setButtons(origMouseEvent->buttons());
       
   309             event2->setButton(origMouseEvent->button());
       
   310             event2->setModifiers(origMouseEvent->modifiers());
       
   311 
       
   312             data->ignored << event1;
       
   313             data->ignored << event2;
       
   314             QApplication::postEvent(object, event1);
       
   315             QApplication::postEvent(object, event2);
       
   316         }
       
   317         if (eventType == QEvent::MouseMove ||
       
   318             eventType == QEvent::GraphicsSceneMouseMove) {
       
   319             consumed = true;
       
   320             data->state = FlickData::ManualScroll;
       
   321             data->dragPos = QCursor::pos();
       
   322             if (!d->ticker.isActive())
       
   323                 d->ticker.start(20, this);
       
   324         }
       
   325         break;
       
   326 
       
   327     case FlickData::ManualScroll:
       
   328         if (eventType == QEvent::MouseMove ||
       
   329             eventType == QEvent::GraphicsSceneMouseMove) {
       
   330             consumed = true;
       
   331             QPoint delta = eventPos - data->pressPos;
       
   332             setScrollOffset(data->widget, data->offset - delta);
       
   333         }
       
   334         if (eventType == QEvent::MouseButtonRelease ||
       
   335             eventType == QEvent::GraphicsSceneMouseRelease) {
       
   336             consumed = true;
       
   337             data->state = FlickData::AutoScroll;
       
   338         }
       
   339         break;
       
   340 
       
   341     case FlickData::AutoScroll:
       
   342         if (eventType == QEvent::MouseButtonPress ||
       
   343             eventType == QEvent::GraphicsSceneMousePress) {
       
   344             consumed = true;
       
   345             data->state = FlickData::Stop;
       
   346             data->speed = QPoint(0, 0);
       
   347             data->pressPos = eventPos;
       
   348             data->offset = scrollOffset(data->widget);
       
   349         }
       
   350         if (eventType == QEvent::MouseButtonRelease ||
       
   351             eventType == QEvent::GraphicsSceneMouseRelease) {
       
   352             consumed = true;
       
   353             data->state = FlickData::Steady;
       
   354             data->speed = QPoint(0, 0);
       
   355         }
       
   356         break;
       
   357 
       
   358     case FlickData::Stop:
       
   359         if (eventType == QEvent::MouseButtonRelease ||
       
   360             eventType == QEvent::GraphicsSceneMouseRelease) {
       
   361             consumed = true;
       
   362             data->state = FlickData::Steady;
       
   363         }
       
   364         if (eventType == QEvent::MouseMove ||
       
   365             eventType == QEvent::GraphicsSceneMouseMove) {
       
   366             consumed = true;
       
   367             data->state = FlickData::ManualScroll;
       
   368             data->dragPos = QCursor::pos();
       
   369             if (!d->ticker.isActive())
       
   370                 d->ticker.start(20, this);
       
   371         }
       
   372         break;
       
   373 
       
   374     default:
       
   375         break;
       
   376     }
       
   377 
       
   378     return consumed;
       
   379 }
       
   380 
       
   381 void FlickCharm::timerEvent(QTimerEvent *event)
       
   382 {
       
   383     int count = 0;
       
   384     QHashIterator<QObject*, FlickData*> item(d->flickData);
       
   385     while (item.hasNext()) {
       
   386         item.next();
       
   387         FlickData *data = item.value();
       
   388 
       
   389         if (data->state == FlickData::ManualScroll) {
       
   390             count++;
       
   391             data->speed = QCursor::pos() - data->dragPos;
       
   392             data->dragPos = QCursor::pos();
       
   393         }
       
   394 
       
   395         if (data->state == FlickData::AutoScroll) {
       
   396             count++;
       
   397             data->speed = deaccelerate(data->speed);
       
   398             QPoint p = scrollOffset(data->widget);
       
   399             setScrollOffset(data->widget, p - data->speed);
       
   400             if (data->speed == QPoint(0, 0))
       
   401                 data->state = FlickData::Steady;
       
   402         }
       
   403     }
       
   404 
       
   405     if (!count)
       
   406         d->ticker.stop();
       
   407 
       
   408     QObject::timerEvent(event);
       
   409 }