summaryrefslogtreecommitdiffstats
path: root/src/gui/kernel/qgesturemanager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/kernel/qgesturemanager.cpp')
-rw-r--r--src/gui/kernel/qgesturemanager.cpp453
1 files changed, 453 insertions, 0 deletions
diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp
new file mode 100644
index 0000000..000f44f
--- /dev/null
+++ b/src/gui/kernel/qgesturemanager.cpp
@@ -0,0 +1,453 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qgesturemanager_p.h"
+#include "private/qstandardgestures_p.h"
+#include "private/qwidget_p.h"
+#include "private/qgesture_p.h"
+#include "private/qgraphicsitem_p.h"
+#include "qgesture.h"
+#include "qevent.h"
+#include "qgraphicsitem.h"
+
+#include "qdebug.h"
+
+// #define GESTURE_DEBUG
+#ifndef GESTURE_DEBUG
+# define DEBUG if (0) qDebug
+#else
+# define DEBUG qDebug
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QGestureManager::QGestureManager(QObject *parent)
+ : QObject(parent), state(NotGesture), lastCustomGestureId(0)
+{
+ qRegisterMetaType<Qt::GestureState>();
+
+ registerGestureRecognizer(new QPanGestureRecognizer);
+}
+
+QGestureManager::~QGestureManager()
+{
+
+}
+
+Qt::GestureType QGestureManager::registerGestureRecognizer(QGestureRecognizer *recognizer)
+{
+ QGesture *dummy = recognizer->createGesture(0);
+ if (!dummy) {
+ qWarning("QGestureManager::registerGestureRecognizer: the recognizer doesn't provide gesture object");
+ return Qt::GestureType(0);
+ }
+ Qt::GestureType type = dummy->gestureType();
+ if (type == Qt::CustomGesture) {
+ // generate a new custom gesture id
+ ++lastCustomGestureId;
+ type = Qt::GestureType(Qt::CustomGesture + lastCustomGestureId);
+ }
+ recognizers.insertMulti(type, recognizer);
+ delete dummy;
+ return type;
+}
+
+void QGestureManager::unregisterGestureRecognizer(Qt::GestureType)
+{
+
+}
+
+QGesture* QGestureManager::getState(QObject *object, Qt::GestureType type)
+{
+ // if the widget is being deleted we should be carefull and not to
+ // create a new state, as it will create QWeakPointer which doesnt work
+ // from the destructor.
+ if (object->isWidgetType()) {
+ if (static_cast<QWidget *>(object)->d_func()->data.in_destructor)
+ return 0;
+ }
+
+ QWeakPointer<QGesture> state = objectGestures.value(QGestureManager::ObjectGesture(object, type));
+ if (!state) {
+ QGestureRecognizer *recognizer = recognizers.value(type);
+ if (recognizer) {
+ state = recognizer->createGesture(object);
+ if (!state)
+ return 0;
+ if (state.data()->gestureType() == Qt::CustomGesture) {
+ // if the recognizer didn't fill in the gesture type, then this
+ // is a custom gesture with autogenerated it and we fill it.
+ state.data()->d_func()->gestureType = type;
+ }
+ objectGestures.insert(QGestureManager::ObjectGesture(object, type), state);
+ gestureToRecognizer[state.data()] = recognizer;
+ }
+ }
+ return state.data();
+}
+
+bool QGestureManager::filterEvent(QObject *receiver, QEvent *event)
+{
+ QSet<QGesture *> triggeredGestures;
+ QSet<QGesture *> finishedGestures;
+ QSet<QGesture *> newMaybeGestures;
+ QSet<QGesture *> canceledGestures;
+ QSet<QGesture *> notGestures;
+
+ QGraphicsObject *graphicsObject = qobject_cast<QGraphicsObject *>(receiver);
+ if (receiver->isWidgetType() || graphicsObject) {
+ QMap<QObject *, Qt::GestureType> contexts;
+ if (receiver->isWidgetType()) {
+ QWidget *w = static_cast<QWidget *>(receiver);
+ if (!w->d_func()->gestureContext.isEmpty()) {
+ typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator;
+ for(ContextIterator it = w->d_func()->gestureContext.begin(),
+ e = w->d_func()->gestureContext.end(); it != e; ++it) {
+ contexts.insertMulti(w, it.key());
+ }
+ }
+ // find all gesture contexts for the widget tree
+ w = w->parentWidget();
+ while (w)
+ {
+ typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator;
+ for (ContextIterator it = w->d_func()->gestureContext.begin(),
+ e = w->d_func()->gestureContext.end(); it != e; ++it) {
+ if (it.value() == Qt::WidgetWithChildrenGesture)
+ contexts.insertMulti(w, it.key());
+ }
+ w = w->parentWidget();
+ }
+ } else {
+ QGraphicsObject *item = graphicsObject;
+ if (!item->QGraphicsItem::d_func()->gestureContext.isEmpty()) {
+ typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator;
+ for(ContextIterator it = item->QGraphicsItem::d_func()->gestureContext.begin(),
+ e = item->QGraphicsItem::d_func()->gestureContext.end(); it != e; ++it) {
+ contexts.insertMulti(item, it.key());
+ }
+ }
+ // find all gesture contexts for the widget tree
+ item = item->parentObject();
+ while (item)
+ {
+ typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator;
+ for (ContextIterator it = item->QGraphicsItem::d_func()->gestureContext.begin(),
+ e = item->QGraphicsItem::d_func()->gestureContext.end(); it != e; ++it) {
+ if (it.value() == Qt::WidgetWithChildrenGesture)
+ contexts.insertMulti(item, it.key());
+ }
+ item = item->parentObject();
+ }
+ }
+ // filter the event through recognizers
+ typedef QMap<QObject *, Qt::GestureType>::const_iterator ContextIterator;
+ for (ContextIterator cit = contexts.begin(), ce = contexts.end(); cit != ce; ++cit) {
+ Qt::GestureType gestureType = cit.value();
+ QMap<Qt::GestureType, QGestureRecognizer *>::const_iterator
+ rit = recognizers.lowerBound(gestureType),
+ re = recognizers.upperBound(gestureType);
+ for (; rit != re; ++rit) {
+ QGestureRecognizer *recognizer = rit.value();
+ QObject *target = cit.key();
+ QGesture *state = getState(target, gestureType);
+ if (!state)
+ continue;
+ QGestureRecognizer::Result result = recognizer->filterEvent(state, target, event);
+ QGestureRecognizer::Result type = result & QGestureRecognizer::ResultState_Mask;
+ if (type == QGestureRecognizer::GestureTriggered) {
+ DEBUG() << "QGestureManager: gesture triggered: " << state;
+ triggeredGestures << state;
+ } else if (type == QGestureRecognizer::GestureFinished) {
+ DEBUG() << "QGestureManager: gesture finished: " << state;
+ finishedGestures << state;
+ } else if (type == QGestureRecognizer::MaybeGesture) {
+ DEBUG() << "QGestureManager: maybe gesture: " << state;
+ newMaybeGestures << state;
+ } else if (type == QGestureRecognizer::NotGesture) {
+ DEBUG() << "QGestureManager: not gesture: " << state;
+ notGestures << state;
+ } else if (type == QGestureRecognizer::Ignore) {
+ DEBUG() << "QGestureManager: gesture ignored the event: " << state;
+ } else {
+ DEBUG() << "QGestureManager: hm, lets assume the recognizer ignored the event: " << state;
+ }
+ if (result & QGestureRecognizer::ConsumeEventHint) {
+ DEBUG() << "QGestureManager: we were asked to consume the event: " << state;
+ //TODO: consume events if asked
+ }
+ }
+ }
+ } else if (QGesture *state = qobject_cast<QGesture*>(receiver)) {
+ if (QGestureRecognizer *recognizer = gestureToRecognizer.value(state)) {
+ QGestureRecognizer::Result result = recognizer->filterEvent(state, state, event);
+ QGestureRecognizer::Result type = result & QGestureRecognizer::ResultState_Mask;
+ if (type == QGestureRecognizer::GestureTriggered) {
+ DEBUG() << "QGestureManager: gesture triggered: " << state;
+ triggeredGestures << state;
+ } else if (type == QGestureRecognizer::GestureFinished) {
+ DEBUG() << "QGestureManager: gesture finished: " << state;
+ finishedGestures << state;
+ } else if (type == QGestureRecognizer::MaybeGesture) {
+ DEBUG() << "QGestureManager: maybe gesture: " << state;
+ newMaybeGestures << state;
+ } else if (type == QGestureRecognizer::NotGesture) {
+ DEBUG() << "QGestureManager: not gesture: " << state;
+ notGestures << state;
+ } else if (type == QGestureRecognizer::Ignore) {
+ DEBUG() << "QGestureManager: gesture ignored the event: " << state;
+ } else {
+ DEBUG() << "QGestureManager: hm, lets assume the recognizer ignored the event: " << state;
+ }
+ }
+ } else {
+ return false;
+ }
+
+ QSet<QGesture *> startedGestures = triggeredGestures - activeGestures;
+ triggeredGestures &= activeGestures;
+
+ // check if a running gesture switched back to maybe state
+ QSet<QGesture *> activeToMaybeGestures = activeGestures & newMaybeGestures;
+
+ // check if a running gesture switched back to not gesture state, i.e. were canceled
+ QSet<QGesture *> activeToCancelGestures = activeGestures & notGestures;
+ canceledGestures += activeToCancelGestures;
+
+ // start timers for new gestures in maybe state
+ foreach (QGesture *state, newMaybeGestures) {
+ QBasicTimer &timer = maybeGestures[state];
+ if (!timer.isActive())
+ timer.start(3000, this);
+ }
+ // kill timers for gestures that were in maybe state
+ QSet<QGesture *> notMaybeGestures = (startedGestures | triggeredGestures | finishedGestures | canceledGestures | notGestures);
+ foreach(QGesture *gesture, notMaybeGestures) {
+ QMap<QGesture *, QBasicTimer>::iterator it =
+ maybeGestures.find(gesture);
+ if (it != maybeGestures.end()) {
+ it.value().stop();
+ maybeGestures.erase(it);
+ }
+ }
+
+ Q_ASSERT((startedGestures & finishedGestures).isEmpty());
+ Q_ASSERT((startedGestures & newMaybeGestures).isEmpty());
+ Q_ASSERT((startedGestures & canceledGestures).isEmpty());
+ Q_ASSERT((finishedGestures & newMaybeGestures).isEmpty());
+ Q_ASSERT((finishedGestures & canceledGestures).isEmpty());
+ Q_ASSERT((canceledGestures & newMaybeGestures).isEmpty());
+
+ QSet<QGesture *> notStarted = finishedGestures - activeGestures;
+ if (!notStarted.isEmpty()) {
+ // there are some gestures that claim to be finished, but never started.
+ qWarning("QGestureManager::filterEvent: some gestures were finished even though they've never started");
+ finishedGestures -= notStarted;
+ }
+
+ activeGestures += startedGestures;
+ // sanity check: all triggered gestures should already be in active gestures list
+ Q_ASSERT((activeGestures & triggeredGestures).size() == triggeredGestures.size());
+ activeGestures -= finishedGestures;
+ activeGestures -= activeToMaybeGestures;
+ activeGestures -= canceledGestures;
+
+ // set the proper gesture state on each gesture
+ foreach (QGesture *gesture, startedGestures)
+ gesture->d_func()->state = Qt::GestureStarted;
+ foreach (QGesture *gesture, triggeredGestures)
+ gesture->d_func()->state = Qt::GestureUpdated;
+ foreach (QGesture *gesture, finishedGestures)
+ gesture->d_func()->state = Qt::GestureFinished;
+ foreach (QGesture *gesture, canceledGestures)
+ gesture->d_func()->state = Qt::GestureCanceled;
+ foreach (QGesture *gesture, activeToMaybeGestures)
+ gesture->d_func()->state = Qt::GestureFinished;
+
+ if (!activeGestures.isEmpty() || !maybeGestures.isEmpty() ||
+ !startedGestures.isEmpty() || !triggeredGestures.isEmpty() ||
+ !finishedGestures.isEmpty() || !canceledGestures.isEmpty()) {
+ DEBUG() << "QGestureManager::filterEvent:"
+ << "\n\tactiveGestures:" << activeGestures
+ << "\n\tmaybeGestures:" << maybeGestures.keys()
+ << "\n\tstarted:" << startedGestures
+ << "\n\ttriggered:" << triggeredGestures
+ << "\n\tfinished:" << finishedGestures
+ << "\n\tcanceled:" << canceledGestures;
+ }
+
+ deliverEvents(startedGestures+triggeredGestures+finishedGestures+canceledGestures, receiver);
+
+ // reset gestures that ended
+ QSet<QGesture *> endedGestures = finishedGestures + canceledGestures;
+ foreach (QGesture *gesture, endedGestures) {
+ if (QGestureRecognizer *recognizer = gestureToRecognizer.value(gesture, 0)) {
+ recognizer->reset(gesture);
+ }
+ gestureTargets.remove(gesture);
+ }
+ return false;
+}
+
+void QGestureManager::deliverEvents(const QSet<QGesture*> &gestures, QObject *lastReceiver)
+{
+ if (gestures.isEmpty())
+ return;
+
+ // group gestures by widgets
+ typedef QMap<QObject *, QList<QGesture *> > GesturesPerReceiver;
+ GesturesPerReceiver groupedGestures;
+ // for conflicted gestures the key is always the innermost widget (i.e. the child)
+ GesturesPerReceiver conflictedGestures;
+ QMultiHash<QObject *, QGesture *> objectGestures;
+
+ foreach (QGesture *gesture, gestures) {
+ QObject *target = gestureTargets.value(gesture, 0);
+ if (!target) {
+ Q_ASSERT(gesture->state() == Qt::GestureStarted);
+ if (gesture->hasHotSpot()) {
+ // guess the target using the hotspot of the gesture
+ QPoint pt = gesture->hotSpot().toPoint();
+ if (!pt.isNull()) {
+ if (QWidget *w = qApp->topLevelAt(pt))
+ target = w->childAt(w->mapFromGlobal(pt));
+ }
+ }
+ if (!target) {
+ target = gesture->targetObject();
+ if (!target)
+ target = lastReceiver;
+ }
+ }
+ if (target) {
+ gestureTargets.insert(gesture, target);
+ if (target->isWidgetType())
+ objectGestures.insert(target, gesture);
+ groupedGestures[target].append(gesture);
+ } else {
+ qWarning() << "QGestureManager::deliverEvent: could not find the target for gesture"
+ << gesture->gestureType();
+ }
+ }
+
+ typedef QMultiHash<QObject *, QGesture *>::const_iterator ObjectGesturesIterator;
+ for (ObjectGesturesIterator it = objectGestures.begin(), e = objectGestures.end(); it != e; ++it) {
+ QObject *object1 = it.key();
+ QWidget *widget1 = qobject_cast<QWidget *>(object1);
+ QGraphicsObject *item1 = qobject_cast<QGraphicsObject *>(object1);
+ QGesture *gesture1 = it.value();
+ ObjectGesturesIterator cit = it;
+ for (++cit; cit != e; ++cit) {
+ QObject *object2 = cit.key();
+ QWidget *widget2 = qobject_cast<QWidget *>(object2);
+ QGraphicsObject *item2 = qobject_cast<QGraphicsObject *>(object2);
+ QGesture *gesture2 = cit.value();
+ // TODO: ugly, rewrite this.
+ if ((widget1 && widget2 && widget2->isAncestorOf(widget1)) ||
+ (item1 && item2 && item2->isAncestorOf(item1))) {
+ groupedGestures[object2].removeOne(gesture2);
+ groupedGestures[object1].removeOne(gesture1);
+ conflictedGestures[object1].append(gesture1);
+ } else if ((widget1 && widget2 && widget1->isAncestorOf(widget2)) ||
+ (item1 && item2 && item1->isAncestorOf(item2))) {
+ groupedGestures[object2].removeOne(gesture2);
+ groupedGestures[object1].removeOne(gesture1);
+ conflictedGestures[object2].append(gesture2);
+ }
+ }
+ }
+
+ DEBUG() << "deliverEvents: conflicted =" << conflictedGestures.values()
+ << " grouped =" << groupedGestures.values();
+
+ // if there are conflicting gestures, send the GestureOverride event
+ for (GesturesPerReceiver::const_iterator it = conflictedGestures.begin(),
+ e = conflictedGestures.end(); it != e; ++it) {
+ DEBUG() << "QGestureManager::deliverEvents: sending GestureOverride to"
+ << it.key()
+ << " gestures:" << it.value();
+ QGestureEvent event(it.value());
+ event.t = QEvent::GestureOverride;
+ event.ignore();
+ QApplication::sendEvent(it.key(), &event);
+ if (!event.isAccepted()) {
+ // nobody accepted the GestureOverride, put it back to deliver to
+ // the closest context (i.e. to the inner-most widget).
+ DEBUG() <<" override was not accepted";
+ groupedGestures[it.key()].append(it.value());
+ }
+ }
+
+ for (GesturesPerReceiver::const_iterator it = groupedGestures.begin(),
+ e = groupedGestures.end(); it != e; ++it) {
+ if (!it.value().isEmpty()) {
+ DEBUG() << "QGestureManager::deliverEvents: sending to" << it.key()
+ << " gestures:" << it.value();
+ QGestureEvent event(it.value());
+ QApplication::sendEvent(it.key(), &event);
+ }
+ }
+}
+
+void QGestureManager::timerEvent(QTimerEvent *event)
+{
+ QMap<QGesture*, QBasicTimer>::iterator it = maybeGestures.begin(),
+ e = maybeGestures.end();
+ for (; it != e; ) {
+ QBasicTimer &timer = it.value();
+ Q_ASSERT(timer.isActive());
+ if (timer.timerId() == event->timerId()) {
+ timer.stop();
+ QGesture *gesture = it.key();
+ it = maybeGestures.erase(it);
+ DEBUG() << "QGestureManager::timerEvent: gesture stopped due to timeout:" << gesture;
+ QGestureRecognizer *recognizer = gestureToRecognizer.value(gesture, 0);
+ if (recognizer)
+ recognizer->reset(gesture);
+ } else {
+ ++it;
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qgesturemanager_p.cpp"