/**************************************************************************** ** ** 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" #ifdef Q_WS_MAC #include "qmacgesturerecognizer_mac_p.h" #endif #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(); #if defined(Q_WS_MAC) registerGestureRecognizer(new QMacSwipeGestureRecognizer); registerGestureRecognizer(new QMacPinchGestureRecognizer); #if defined(QT_MAC_USE_COCOA) registerGestureRecognizer(new QMacPanGestureRecognizer); #endif #else registerGestureRecognizer(new QPanGestureRecognizer); #endif } 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(object)->d_func()->data.in_destructor) return 0; } QWeakPointer 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 triggeredGestures; QSet finishedGestures; QSet newMaybeGestures; QSet canceledGestures; QSet notGestures; QGraphicsObject *graphicsObject = qobject_cast(receiver); if (receiver->isWidgetType() || graphicsObject) { QMap contexts; if (receiver->isWidgetType()) { QWidget *w = static_cast(receiver); if (!w->d_func()->gestureContext.isEmpty()) { typedef QMap::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::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::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::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::const_iterator ContextIterator; for (ContextIterator cit = contexts.begin(), ce = contexts.end(); cit != ce; ++cit) { Qt::GestureType gestureType = cit.value(); QMap::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(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 startedGestures = triggeredGestures - activeGestures; triggeredGestures &= activeGestures; // check if a running gesture switched back to maybe state QSet activeToMaybeGestures = activeGestures & newMaybeGestures; // check if a running gesture switched back to not gesture state, i.e. were canceled QSet 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 notMaybeGestures = (startedGestures | triggeredGestures | finishedGestures | canceledGestures | notGestures); foreach(QGesture *gesture, notMaybeGestures) { QMap::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 notStarted = finishedGestures - activeGestures; if (!notStarted.isEmpty()) { // there are some gestures that claim to be finished, but never started. // probably those are "singleshot" gestures so we'll fake the started state. foreach (QGesture *gesture, notStarted) gesture->d_func()->state = Qt::GestureStarted; deliverEvents(notStarted, receiver); } 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 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 &gestures, QObject *lastReceiver) { if (gestures.isEmpty()) return; // group gestures by widgets typedef QMap > GesturesPerReceiver; GesturesPerReceiver groupedGestures; // for conflicted gestures the key is always the innermost widget (i.e. the child) GesturesPerReceiver conflictedGestures; QMultiHash 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::const_iterator ObjectGesturesIterator; for (ObjectGesturesIterator it = objectGestures.begin(), e = objectGestures.end(); it != e; ++it) { QObject *object1 = it.key(); QWidget *widget1 = qobject_cast(object1); QGraphicsObject *item1 = qobject_cast(object1); QGesture *gesture1 = it.value(); ObjectGesturesIterator cit = it; for (++cit; cit != e; ++cit) { QObject *object2 = cit.key(); QWidget *widget2 = qobject_cast(object2); QGraphicsObject *item2 = qobject_cast(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::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"