src/gui/kernel/qgesturemanager.cpp
author Alex Gilkes <alex.gilkes@nokia.com>
Mon, 11 Jan 2010 14:00:40 +0000
changeset 0 1918ee327afb
child 3 41300fa6a67c
permissions -rw-r--r--
Revision: 200952

/****************************************************************************
**
** 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 "private/qevent_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<Qt::GestureState>();

#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 fails to create a gesture object, skipping registration.");
        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;
    } else if (QGesture *g = qobject_cast<QGesture *>(object)) {
        return g;
    } else {
        Q_ASSERT(qobject_cast<QGraphicsObject *>(object));
    }

    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->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->d_func()->gestureType = type;
#if defined(GESTURE_DEBUG)
                state->setObjectName(QString::number((int)type));
#endif
            }
            objectGestures.insert(QGestureManager::ObjectGesture(object, type), state);
            gestureToRecognizer[state] = recognizer;
            gestureOwners[state] = object;
        }
    }
    return state;
}

bool QGestureManager::filterEventThroughContexts(const QMap<QObject *,
                                                 Qt::GestureType> &contexts,
                                                 QEvent *event)
{
    QSet<QGesture *> triggeredGestures;
    QSet<QGesture *> finishedGestures;
    QSet<QGesture *> newMaybeGestures;
    QSet<QGesture *> canceledGestures;
    QSet<QGesture *> notGestures;

    // TODO: sort contexts by the gesture type and check if one of the contexts
    //       is already active.

    // 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
            }
        }
    }

    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.
        // probably those are "singleshot" gestures so we'll fake the started state.
        foreach (QGesture *gesture, notStarted)
            gesture->d_func()->state = Qt::GestureStarted;
        QSet<QGesture *> undeliveredGestures;
        deliverEvents(notStarted, &undeliveredGestures);
        finishedGestures -= undeliveredGestures;
    }

    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;
    }

    QSet<QGesture *> undeliveredGestures;
    deliverEvents(startedGestures+triggeredGestures+finishedGestures+canceledGestures,
                  &undeliveredGestures);

    activeGestures -= undeliveredGestures;

    // reset gestures that ended
    QSet<QGesture *> endedGestures =
            finishedGestures + canceledGestures + undeliveredGestures;
    foreach (QGesture *gesture, endedGestures) {
        if (QGestureRecognizer *recognizer = gestureToRecognizer.value(gesture, 0)) {
            recognizer->reset(gesture);
        }
        gestureTargets.remove(gesture);
    }
    return false;
}

bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event)
{
    QSet<Qt::GestureType> types;
    QMap<QObject *, Qt::GestureType> contexts;
    QWidget *w = receiver;
    typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator;
    if (!w->d_func()->gestureContext.isEmpty()) {
        for(ContextIterator it = w->d_func()->gestureContext.begin(),
            e = w->d_func()->gestureContext.end(); it != e; ++it) {
            types.insert(it.key());
            contexts.insertMulti(w, it.key());
        }
    }
    // find all gesture contexts for the widget tree
    w = w->isWindow() ? 0 : w->parentWidget();
    while (w)
    {
        for (ContextIterator it = w->d_func()->gestureContext.begin(),
             e = w->d_func()->gestureContext.end(); it != e; ++it) {
            if (it.value() == Qt::WidgetWithChildrenGesture) {
                if (!types.contains(it.key())) {
                    types.insert(it.key());
                    contexts.insertMulti(w, it.key());
                }
            }
        }
        if (w->isWindow())
            break;
        w = w->parentWidget();
    }
    return filterEventThroughContexts(contexts, event);
}

bool QGestureManager::filterEvent(QGraphicsObject *receiver, QEvent *event)
{
    QSet<Qt::GestureType> types;
    QMap<QObject *, Qt::GestureType> contexts;
    QGraphicsObject *item = receiver;
    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) {
            types.insert(it.key());
            contexts.insertMulti(item, it.key());
        }
    }
    // find all gesture contexts for the graphics object 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::ItemWithChildrenGesture) {
                if (!types.contains(it.key()))
                    contexts.insertMulti(item, it.key());
            }
        }
        item = item->parentObject();
    }
    return filterEventThroughContexts(contexts, event);
}

bool QGestureManager::filterEvent(QGesture *state, QEvent *event)
{
    QMap<QObject *, Qt::GestureType> contexts;
    contexts.insert(state, state->gestureType());
    return filterEventThroughContexts(contexts, event);
}

void QGestureManager::getGestureTargets(const QSet<QGesture*> &gestures,
    QMap<QWidget *, QList<QGesture *> > *conflicts,
    QMap<QWidget *, QList<QGesture *> > *normal)
{
    typedef QHash<Qt::GestureType, QHash<QWidget *, QGesture *> > GestureByTypes;
    GestureByTypes gestureByTypes;

    // sort gestures by types
    foreach (QGesture *gesture, gestures) {
        QWidget *receiver = gestureTargets.value(gesture, 0);
        Q_ASSERT(receiver);
        gestureByTypes[gesture->gestureType()].insert(receiver, gesture);
    }

    // for each gesture type
    foreach (Qt::GestureType type, gestureByTypes.keys()) {
        QHash<QWidget *, QGesture *> gestures = gestureByTypes.value(type);
        foreach (QWidget *widget, gestures.keys()) {
            QWidget *w = widget->parentWidget();
            while (w) {
                QMap<Qt::GestureType, Qt::GestureContext>::const_iterator it
                        = w->d_func()->gestureContext.find(type);
                if (it != w->d_func()->gestureContext.end()) {
                    // i.e. 'w' listens to gesture 'type'
                    Qt::GestureContext context = it.value();
                    if (context == Qt::WidgetWithChildrenGesture && w != widget) {
                        // conflicting gesture!
                        (*conflicts)[widget].append(gestures[widget]);
                        break;
                    }
                }
                if (w->isWindow()) {
                    w = 0;
                    break;
                }
                w = w->parentWidget();
            }
            if (!w)
                (*normal)[widget].append(gestures[widget]);
        }
    }
}

void QGestureManager::deliverEvents(const QSet<QGesture *> &gestures,
                                    QSet<QGesture *> *undeliveredGestures)
{
    if (gestures.isEmpty())
        return;

    typedef QMap<QWidget *, QList<QGesture *> > GesturesPerWidget;
    GesturesPerWidget conflictedGestures;
    GesturesPerWidget normalStartedGestures;

    QSet<QGesture *> startedGestures;
    // first figure out the initial receivers of gestures
    for (QSet<QGesture *>::const_iterator it = gestures.begin(),
         e = gestures.end(); it != e; ++it) {
        QGesture *gesture = *it;
        QWidget *target = gestureTargets.value(gesture, 0);
        if (!target) {
            // the gesture has just started and doesn't have a target yet.
            Q_ASSERT(gesture->state() == Qt::GestureStarted);
            if (gesture->hasHotSpot()) {
                // guess the target widget using the hotspot of the gesture
                QPoint pt = gesture->hotSpot().toPoint();
                if (QWidget *w = qApp->topLevelAt(pt)) {
                    target = w->childAt(w->mapFromGlobal(pt));
                }
            } else {
                // or use the context of the gesture
                QObject *context = gestureOwners.value(gesture, 0);
                if (context->isWidgetType())
                    target = static_cast<QWidget *>(context);
            }
            if (target)
                gestureTargets.insert(gesture, target);
        }

        Qt::GestureType gestureType = gesture->gestureType();
        Q_ASSERT(gestureType != Qt::CustomGesture);

        if (target) {
            if (gesture->state() == Qt::GestureStarted) {
                startedGestures.insert(gesture);
            } else {
                normalStartedGestures[target].append(gesture);
            }
        } else {
            DEBUG() << "QGestureManager::deliverEvent: could not find the target for gesture"
                    << gesture->gestureType();
            qWarning("QGestureManager::deliverEvent: could not find the target for gesture");
            undeliveredGestures->insert(gesture);
        }
    }

    getGestureTargets(startedGestures, &conflictedGestures, &normalStartedGestures);
    DEBUG() << "QGestureManager::deliverEvents:"
            << "\nstarted: " << startedGestures
            << "\nconflicted: " << conflictedGestures
            << "\nnormal: " << normalStartedGestures
            << "\n";

    // if there are conflicting gestures, send the GestureOverride event
    for (GesturesPerWidget::const_iterator it = conflictedGestures.begin(),
        e = conflictedGestures.end(); it != e; ++it) {
        QWidget *receiver = it.key();
        QList<QGesture *> gestures = it.value();
        DEBUG() << "QGestureManager::deliverEvents: sending GestureOverride to"
                << receiver
                << "gestures:" << gestures;
        QGestureEvent event(gestures);
        event.t = QEvent::GestureOverride;
        // mark event and individual gestures as ignored
        event.ignore();
        foreach(QGesture *g, gestures)
            event.setAccepted(g, false);

        QApplication::sendEvent(receiver, &event);
        bool eventAccepted = event.isAccepted();
        foreach(QGesture *gesture, event.allGestures()) {
            if (eventAccepted || event.isAccepted(gesture)) {
                QWidget *w = event.d_func()->targetWidgets.value(gesture->gestureType(), 0);
                Q_ASSERT(w);
                DEBUG() << "override event: gesture was accepted:" << gesture << w;
                QList<QGesture *> &gestures = normalStartedGestures[w];
                gestures.append(gesture);
                // override the target
                gestureTargets[gesture] = w;
            } else {
                DEBUG() << "override event: gesture wasn't accepted. putting back:" << gesture;
                QList<QGesture *> &gestures = normalStartedGestures[receiver];
                gestures.append(gesture);
            }
        }
    }

    // delivering gestures that are not in conflicted state
    for (GesturesPerWidget::const_iterator it = normalStartedGestures.begin(),
        e = normalStartedGestures.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"