src/gui/graphicsview/qgraphicsscene.cpp
changeset 30 5dc02b23752f
parent 25 e24348a560a6
child 33 3e2da88830cd
--- a/src/gui/graphicsview/qgraphicsscene.cpp	Wed Jun 23 19:07:03 2010 +0300
+++ b/src/gui/graphicsview/qgraphicsscene.cpp	Tue Jul 06 15:10:48 2010 +0300
@@ -50,7 +50,7 @@
     The class serves as a container for QGraphicsItems. It is used together
     with QGraphicsView for visualizing graphical items, such as lines,
     rectangles, text, or even custom items, on a 2D surface. QGraphicsScene is
-    part of \l{The Graphics View Framework}.
+    part of the \l{Graphics View Framework}.
 
     QGraphicsScene also provides functionality that lets you efficiently
     determine both the location of items, and for determining what items are
@@ -228,6 +228,7 @@
 #include <QtCore/qstack.h>
 #include <QtCore/qtimer.h>
 #include <QtCore/qvarlengtharray.h>
+#include <QtCore/QMetaMethod>
 #include <QtGui/qapplication.h>
 #include <QtGui/qdesktopwidget.h>
 #include <QtGui/qevent.h>
@@ -277,8 +278,6 @@
     hover->setAccepted(mouseEvent->isAccepted());
 }
 
-int QGraphicsScenePrivate::changedSignalIndex;
-
 /*!
     \internal
 */
@@ -291,13 +290,20 @@
       updateAll(false),
       calledEmitUpdated(false),
       processDirtyItemsEmitted(false),
-      selectionChanging(0),
       needSortTopLevelItems(true),
       holesInTopLevelSiblingIndex(false),
       topLevelSequentialOrdering(true),
       scenePosDescendantsUpdatePending(false),
       stickyFocus(false),
       hasFocus(false),
+      lastMouseGrabberItemHasImplicitMouseGrab(false),
+      allItemsIgnoreHoverEvents(true),
+      allItemsUseDefaultCursor(true),
+      painterStateProtection(true),
+      sortCacheEnabled(false),
+      allItemsIgnoreTouchEvents(true),
+      selectionChanging(0),
+      rectAdjust(2),
       focusItem(0),
       lastFocusItem(0),
       tabFocusFirst(0),
@@ -306,16 +312,10 @@
       activationRefCount(0),
       childExplicitActivation(0),
       lastMouseGrabberItem(0),
-      lastMouseGrabberItemHasImplicitMouseGrab(false),
       dragDropItem(0),
       enterWidget(0),
       lastDropAction(Qt::IgnoreAction),
-      allItemsIgnoreHoverEvents(true),
-      allItemsUseDefaultCursor(true),
-      painterStateProtection(true),
-      sortCacheEnabled(false),
-      style(0),
-      allItemsIgnoreTouchEvents(true)
+      style(0)
 {
 }
 
@@ -329,9 +329,10 @@
     index = new QGraphicsSceneBspTreeIndex(q);
 
     // Keep this index so we can check for connected slots later on.
-    if (!changedSignalIndex) {
-        changedSignalIndex = signalIndex("changed(QList<QRectF>)");
-    }
+    changedSignalIndex = signalIndex("changed(QList<QRectF>)");
+    processDirtyItemsIndex = q->metaObject()->indexOfSlot("_q_processDirtyItems()");
+    polishItemsIndex = q->metaObject()->indexOfSlot("_q_polishItems()");
+
     qApp->d_func()->scene_list.append(q);
     q->update();
 }
@@ -689,6 +690,10 @@
     if (item == lastMouseGrabberItem)
         lastMouseGrabberItem = 0;
 
+    // Reset the current drop item
+    if (item == dragDropItem)
+        dragDropItem = 0;
+
     // Reenable selectionChanged() for individual items
     --selectionChanging;
     if (!selectionChanging && selectedItems.size() != oldSelectedItemsSize)
@@ -701,6 +706,13 @@
         else
             ++it;
     }
+    QGraphicsObject *dummy = static_cast<QGraphicsObject *>(item);
+    cachedTargetItems.removeOne(dummy);
+    cachedItemGestures.remove(dummy);
+    cachedAlreadyDeliveredGestures.remove(dummy);
+
+    foreach (Qt::GestureType gesture, item->d_ptr->gestureContext.keys())
+        ungrabGesture(item, gesture);
 }
 
 /*!
@@ -1311,10 +1323,10 @@
             setFocus = true;
             break;
         }
-        if (item->isEnabled() && ((item->flags() & QGraphicsItem::ItemIsFocusable) && item->d_ptr->mouseSetsFocus)) {
+        if (item->isEnabled() && ((item->flags() & QGraphicsItem::ItemIsFocusable))) {
             if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) {
                 setFocus = true;
-                if (item != q->focusItem())
+                if (item != q->focusItem() && item->d_ptr->mouseSetsFocus)
                     q->setFocusItem(item, Qt::MouseFocusReason);
                 break;
             }
@@ -2533,10 +2545,16 @@
         return;
     }
 
-    if (d->unpolishedItems.isEmpty())
-        QMetaObject::invokeMethod(this, "_q_polishItems", Qt::QueuedConnection);
-    d->unpolishedItems.append(item);
-    item->d_ptr->pendingPolish = true;
+    // QDeclarativeItems do not rely on initial itemChanged message, as the componentComplete
+    // function allows far more opportunity for delayed-construction optimization.
+    if (!item->d_ptr->isDeclarativeItem) {
+        if (d->unpolishedItems.isEmpty()) {
+            QMetaMethod method = metaObject()->method(d->polishItemsIndex);
+            method.invoke(this, Qt::QueuedConnection);
+        }
+        d->unpolishedItems.append(item);
+        item->d_ptr->pendingPolish = true;
+    }
 
     // Detach this item from its parent if the parent's scene is different
     // from this scene.
@@ -2584,6 +2602,9 @@
         d->enableTouchEventsOnViews();
     }
 
+    foreach (Qt::GestureType gesture, item->d_ptr->gestureContext.keys())
+        d->grabGesture(item, gesture);
+
     // Update selection lists
     if (item->isSelected())
         d->selectedItems << item;
@@ -3211,7 +3232,10 @@
             // Update all views.
             for (int i = 0; i < d->views.size(); ++i) {
                 QGraphicsView *view = d->views.at(i);
-                view->d_func()->updateRegion(QRegion(view->mapFromScene(rect).boundingRect()));
+                if (view->isTransformed())
+                    view->d_func()->updateRectF(view->viewportTransform().mapRect(rect));
+                else
+                    view->d_func()->updateRectF(rect);
             }
         } else {
             d->updatedRects << rect;
@@ -4253,6 +4277,8 @@
         widgetItem->paintWindowFrame(painter, option, widget);
         if (painterStateProtection)
             painter->restore();
+    } else if (widgetItem->autoFillBackground()) {
+        painter->fillRect(option->exposedRect, widgetItem->palette().window());
     }
 
     widgetItem->paint(painter, option, widget);
@@ -4350,11 +4376,6 @@
     bool pixmapFound;
     QGraphicsItemCache *itemCache = itemd->extraItemCache();
     if (cacheMode == QGraphicsItem::ItemCoordinateCache) {
-        if (itemCache->boundingRect != brect.toRect()) {
-            itemCache->boundingRect = brect.toRect();
-            itemCache->allExposed = true;
-            itemCache->exposed.clear();
-        }
         pixmapKey = itemCache->key;
     } else {
         pixmapKey = itemCache->deviceData.value(widget).key;
@@ -4367,19 +4388,24 @@
     if (cacheMode == QGraphicsItem::ItemCoordinateCache) {
         QSize pixmapSize;
         bool fixedCacheSize = false;
-        QRectF brectAligned = brect.toAlignedRect();
+        QRect br = brect.toAlignedRect();
         if ((fixedCacheSize = itemCache->fixedSize.isValid())) {
             pixmapSize = itemCache->fixedSize;
         } else {
-            pixmapSize = brectAligned.size().toSize();
+            pixmapSize = br.size();
         }
 
         // Create or recreate the pixmap.
         int adjust = itemCache->fixedSize.isValid() ? 0 : 2;
         QSize adjustSize(adjust*2, adjust*2);
-        QRectF br = brectAligned.adjusted(-adjust, -adjust, adjust, adjust);
+        br.adjust(-adjust, -adjust, adjust, adjust);
         if (pix.isNull() || (!fixedCacheSize && (pixmapSize + adjustSize) != pix.size())) {
             pix = QPixmap(pixmapSize + adjustSize);
+            itemCache->boundingRect = br;
+            itemCache->exposed.clear();
+            itemCache->allExposed = true;
+        } else if (itemCache->boundingRect != br) {
+            itemCache->boundingRect = br;
             itemCache->exposed.clear();
             itemCache->allExposed = true;
         }
@@ -4433,10 +4459,10 @@
         // qpixmap-image-transform-pixmap roundtrip.
         if (newPainterOpacity != oldPainterOpacity) {
             painter->setOpacity(newPainterOpacity);
-            painter->drawPixmap(br, pix, QRectF(QPointF(), pix.size()));
+            painter->drawPixmap(br.topLeft(), pix);
             painter->setOpacity(oldPainterOpacity);
         } else {
-            painter->drawPixmap(br, pix, QRectF(QPointF(), pix.size()));
+            painter->drawPixmap(br.topLeft(), pix);
         }
         return;
     }
@@ -4693,11 +4719,11 @@
     if (drawItem) {
         const QRectF brect = adjustedItemEffectiveBoundingRect(item);
         ENSURE_TRANSFORM_PTR
-        QRect viewBoundingRect = translateOnlyTransform ? brect.translated(transformPtr->dx(), transformPtr->dy()).toRect()
-                                                        : transformPtr->mapRect(brect).toRect();
+        QRect viewBoundingRect = translateOnlyTransform ? brect.translated(transformPtr->dx(), transformPtr->dy()).toAlignedRect()
+                                                        : transformPtr->mapRect(brect).toAlignedRect();
+        viewBoundingRect.adjust(-rectAdjust, -rectAdjust, rectAdjust, rectAdjust);
         if (widget)
             item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect);
-        viewBoundingRect.adjust(-1, -1, 1, 1);
         drawItem = exposedRegion ? exposedRegion->intersects(viewBoundingRect)
                                  : !viewBoundingRect.normalized().isEmpty();
         if (!drawItem) {
@@ -4718,7 +4744,7 @@
     if (item->d_ptr->graphicsEffect && item->d_ptr->graphicsEffect->isEnabled()) {
         ENSURE_TRANSFORM_PTR;
         QGraphicsItemPaintInfo info(viewTransform, transformPtr, effectTransform, exposedRegion, widget, &styleOptionTmp,
-                                    painter, opacity, wasDirtyParentSceneTransform, drawItem);
+                                    painter, opacity, wasDirtyParentSceneTransform, itemHasContents && !itemIsFullyTransparent);
         QGraphicsEffectSource *source = item->d_ptr->graphicsEffect->d_func()->source;
         QGraphicsItemEffectSourcePrivate *sourced = static_cast<QGraphicsItemEffectSourcePrivate *>
                                                     (source->d_func());
@@ -4884,7 +4910,9 @@
         return;
 
     if (!processDirtyItemsEmitted) {
-        QMetaObject::invokeMethod(q_ptr, "_q_processDirtyItems", Qt::QueuedConnection);
+        QMetaMethod method = q_ptr->metaObject()->method(processDirtyItemsIndex);
+        method.invoke(q_ptr, Qt::QueuedConnection);
+//        QMetaObject::invokeMethod(q_ptr, "_q_processDirtyItems", Qt::QueuedConnection);
         processDirtyItemsEmitted = true;
     }
 
@@ -4944,34 +4972,29 @@
     if (itemIsUntransformable) {
         const QTransform xform = itemq->deviceTransform(viewq->viewportTransform());
         if (!item->hasBoundingRegionGranularity)
-            return view->updateRect(xform.mapRect(rect).toRect());
-        return view->updateRegion(xform.map(QRegion(rect.toRect())));
+            return view->updateRectF(xform.mapRect(rect));
+        return view->updateRegion(rect, xform);
     }
 
     if (item->sceneTransformTranslateOnly && view->identityMatrix) {
         const qreal dx = item->sceneTransform.dx();
         const qreal dy = item->sceneTransform.dy();
-        if (!item->hasBoundingRegionGranularity) {
-            QRectF r(rect);
-            r.translate(dx - view->horizontalScroll(), dy - view->verticalScroll());
-            return view->updateRect(r.toRect());
-        }
-        QRegion r(rect.toRect());
-        r.translate(qRound(dx) - view->horizontalScroll(), qRound(dy) - view->verticalScroll());
-        return view->updateRegion(r);
+        QRectF r(rect);
+        r.translate(dx - view->horizontalScroll(), dy - view->verticalScroll());
+        return view->updateRectF(r);
     }
 
     if (!viewq->isTransformed()) {
         if (!item->hasBoundingRegionGranularity)
-            return view->updateRect(item->sceneTransform.mapRect(rect).toRect());
-        return view->updateRegion(item->sceneTransform.map(QRegion(rect.toRect())));
+            return view->updateRectF(item->sceneTransform.mapRect(rect));
+        return view->updateRegion(rect, item->sceneTransform);
     }
 
     QTransform xform = item->sceneTransform;
     xform *= viewq->viewportTransform();
     if (!item->hasBoundingRegionGranularity)
-        return view->updateRect(xform.mapRect(rect).toRect());
-    return view->updateRegion(xform.map(QRegion(rect.toRect())));
+        return view->updateRectF(xform.mapRect(rect));
+    return view->updateRegion(rect, xform);
 }
 
 void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool dirtyAncestorContainsChildren,
@@ -5107,9 +5130,15 @@
 
     // Process children.
     if (itemHasChildren && item->d_ptr->dirtyChildren) {
+        const bool itemClipsChildrenToShape = item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape;
+        if (itemClipsChildrenToShape) {
+            // Make sure child updates are clipped to the item's bounding rect.
+            for (int i = 0; i < views.size(); ++i)
+                views.at(i)->d_func()->setUpdateClip(item);
+        }
         if (!dirtyAncestorContainsChildren) {
             dirtyAncestorContainsChildren = item->d_ptr->fullUpdatePending
-                                            && (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape);
+                                            && itemClipsChildrenToShape;
         }
         const bool allChildrenDirty = item->d_ptr->allChildrenDirty;
         const bool parentIgnoresVisible = item->d_ptr->ignoreVisible;
@@ -5132,6 +5161,12 @@
             }
             processDirtyItemsRecursive(child, dirtyAncestorContainsChildren, opacity);
         }
+
+        if (itemClipsChildrenToShape) {
+            // Reset updateClip.
+            for (int i = 0; i < views.size(); ++i)
+                views.at(i)->d_func()->setUpdateClip(0);
+        }
     } else if (wasDirtyParentSceneTransform) {
         item->d_ptr->invalidateChildrenSceneTransform();
     }
@@ -5187,8 +5222,14 @@
     // Determine view, expose and flags.
     QGraphicsView *view = widget ? qobject_cast<QGraphicsView *>(widget->parentWidget()) : 0;
     QRegion *expose = 0;
-    if (view)
+    const quint32 oldRectAdjust = d->rectAdjust;
+    if (view) {
         expose = &view->d_func()->exposedRegion;
+        if (view->d_func()->optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)
+            d->rectAdjust = 1;
+        else
+            d->rectAdjust = 2;
+    }
 
     // Find all toplevels, they are already sorted.
     QList<QGraphicsItem *> topLevelItems;
@@ -5201,6 +5242,7 @@
         }
     }
 
+    d->rectAdjust = oldRectAdjust;
     // Reset discovery bits.
     for (int i = 0; i < topLevelItems.size(); ++i)
         topLevelItems.at(i)->d_ptr->itemDiscovered = 0;
@@ -5577,6 +5619,8 @@
 void QGraphicsScenePrivate::addView(QGraphicsView *view)
 {
     views << view;
+    foreach (Qt::GestureType gesture, grabbedGestures.keys())
+        view->viewport()->grabGesture(gesture);
 }
 
 void QGraphicsScenePrivate::removeView(QGraphicsView *view)
@@ -5906,45 +5950,51 @@
     dispatchHoverEvent(&hoverEvent);
 }
 
-void QGraphicsScenePrivate::getGestureTargets(const QSet<QGesture *> &gestures,
-                                              QWidget *viewport,
-                                              QMap<Qt::GestureType, QGesture *> *conflictedGestures,
-                                              QList<QList<QGraphicsObject *> > *conflictedItems,
-                                              QHash<QGesture *, QGraphicsObject *> *normalGestures)
-{
+void QGraphicsScenePrivate::gestureTargetsAtHotSpots(const QSet<QGesture *> &gestures,
+                                              Qt::GestureFlag flag,
+                                              QHash<QGraphicsObject *, QSet<QGesture *> > *targets,
+                                              QSet<QGraphicsObject *> *itemsSet,
+                                              QSet<QGesture *> *normal,
+                                              QSet<QGesture *> *conflicts)
+{
+    QSet<QGesture *> normalGestures; // that are not in conflicted state.
     foreach (QGesture *gesture, gestures) {
-        Qt::GestureType gestureType = gesture->gestureType();
-        if (gesture->hasHotSpot()) {
-            QPoint screenPos = gesture->hotSpot().toPoint();
-            QList<QGraphicsItem *> items = itemsAtPosition(screenPos, QPointF(), viewport);
-            QList<QGraphicsObject *> result;
-            for (int j = 0; j < items.size(); ++j) {
-                QGraphicsItem *item = items.at(j);
-
-                // Check if the item is blocked by a modal panel and use it as
-                // a target instead of this item.
-                (void) item->isBlockedByModalPanel(&item);
-
-                if (QGraphicsObject *itemobj = item->toGraphicsObject()) {
-                    QGraphicsItemPrivate *d = item->d_func();
-                    if (d->gestureContext.contains(gestureType)) {
-                        result.append(itemobj);
+        if (!gesture->hasHotSpot())
+            continue;
+        const Qt::GestureType gestureType = gesture->gestureType();
+        QList<QGraphicsItem *> items = itemsAtPosition(QPoint(), gesture->d_func()->sceneHotSpot, 0);
+        for (int j = 0; j < items.size(); ++j) {
+            QGraphicsItem *item = items.at(j);
+
+            // Check if the item is blocked by a modal panel and use it as
+            // a target instead of this item.
+            (void) item->isBlockedByModalPanel(&item);
+
+            if (QGraphicsObject *itemobj = item->toGraphicsObject()) {
+                QGraphicsItemPrivate *d = item->QGraphicsItem::d_func();
+                QMap<Qt::GestureType, Qt::GestureFlags>::const_iterator it =
+                        d->gestureContext.find(gestureType);
+                if (it != d->gestureContext.end() && (!flag || (it.value() & flag))) {
+                    if (normalGestures.contains(gesture)) {
+                        normalGestures.remove(gesture);
+                        if (conflicts)
+                            conflicts->insert(gesture);
+                    } else {
+                        normalGestures.insert(gesture);
                     }
+                    if (targets)
+                        (*targets)[itemobj].insert(gesture);
+                    if (itemsSet)
+                        (*itemsSet).insert(itemobj);
                 }
-                // Don't propagate through panels.
-                if (item->isPanel())
-                    break;
             }
-            DEBUG() << "QGraphicsScenePrivate::getGestureTargets:"
-                    << gesture << result;
-            if (result.size() == 1) {
-                normalGestures->insert(gesture, result.first());
-            } else if (!result.isEmpty()) {
-                conflictedGestures->insert(gestureType, gesture);
-                conflictedItems->append(result);
-            }
+            // Don't propagate through panels.
+            if (item->isPanel())
+                break;
         }
     }
+    if (normal)
+        *normal = normalGestures;
 }
 
 void QGraphicsScenePrivate::gestureEventHandler(QGestureEvent *event)
@@ -5952,200 +6002,220 @@
     QWidget *viewport = event->widget();
     if (!viewport)
         return;
+    QGraphicsView *graphicsView = qobject_cast<QGraphicsView *>(viewport->parent());
+    if (!graphicsView)
+        return;
+
     QList<QGesture *> allGestures = event->gestures();
     DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
-            << "Delivering gestures:" <<  allGestures;
-
-    typedef QHash<QGraphicsObject *, QList<QGesture *> > GesturesPerItem;
-    GesturesPerItem gesturesPerItem;
+            << "Gestures:" <<  allGestures;
 
     QSet<QGesture *> startedGestures;
+    QPoint delta = viewport->mapFromGlobal(QPoint());
+    QTransform toScene = QTransform::fromTranslate(delta.x(), delta.y())
+                         * graphicsView->viewportTransform().inverted();
     foreach (QGesture *gesture, allGestures) {
+        // cache scene coordinates of the hot spot
+        if (gesture->hasHotSpot()) {
+            gesture->d_func()->sceneHotSpot = toScene.map(gesture->hotSpot());
+        } else {
+            gesture->d_func()->sceneHotSpot = QPointF();
+        }
+
         QGraphicsObject *target = gestureTargets.value(gesture, 0);
         if (!target) {
             // when we are not in started mode but don't have a target
             // then the only one interested in gesture is the view/scene
             if (gesture->state() == Qt::GestureStarted)
                 startedGestures.insert(gesture);
-        } else {
-            gesturesPerItem[target].append(gesture);
         }
     }
 
-    QMap<Qt::GestureType, QGesture *> conflictedGestures;
-    QList<QList<QGraphicsObject *> > conflictedItems;
-    QHash<QGesture *, QGraphicsObject *> normalGestures;
-    getGestureTargets(startedGestures, viewport, &conflictedGestures, &conflictedItems,
-                      &normalGestures);
-    DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
-            << "Conflicting gestures:" <<  conflictedGestures.values() << conflictedItems;
-    Q_ASSERT((conflictedGestures.isEmpty() && conflictedItems.isEmpty()) ||
-              (!conflictedGestures.isEmpty() && !conflictedItems.isEmpty()));
-
-    // gestures that were sent as override events, but no one accepted them
-    QHash<QGesture *, QGraphicsObject *> ignoredConflictedGestures;
-
-    // deliver conflicted gestures as override events first
-    while (!conflictedGestures.isEmpty() && !conflictedItems.isEmpty()) {
-        // get the topmost item to deliver the override event
-        Q_ASSERT(!conflictedItems.isEmpty());
-        Q_ASSERT(!conflictedItems.first().isEmpty());
-        QGraphicsObject *topmost = conflictedItems.first().first();
-        for (int i = 1; i < conflictedItems.size(); ++i) {
-            QGraphicsObject *item = conflictedItems.at(i).first();
-            if (qt_closestItemFirst(item, topmost)) {
-                topmost = item;
-            }
-        }
-        // get a list of gestures to send to the item
-        QList<Qt::GestureType> grabbedGestures =
-                topmost->QGraphicsItem::d_func()->gestureContext.keys();
-        QList<QGesture *> gestures;
-        for (int i = 0; i < grabbedGestures.size(); ++i) {
-            if (QGesture *g = conflictedGestures.value(grabbedGestures.at(i), 0)) {
-                gestures.append(g);
-                if (!ignoredConflictedGestures.contains(g))
-                    ignoredConflictedGestures.insert(g, topmost);
-            }
-        }
-
-        // send gesture override to the topmost item
-        QGestureEvent ev(gestures);
-        ev.t = QEvent::GestureOverride;
-        ev.setWidget(event->widget());
-        // mark event and individual gestures as ignored
-        ev.ignore();
-        foreach(QGesture *g, gestures)
-            ev.setAccepted(g, false);
+    if (!startedGestures.isEmpty()) {
+        QSet<QGesture *> normalGestures; // that have just one target
+        QSet<QGesture *> conflictedGestures; // that have multiple possible targets
+        gestureTargetsAtHotSpots(startedGestures, Qt::GestureFlag(0), &cachedItemGestures, 0,
+                                 &normalGestures, &conflictedGestures);
+        cachedTargetItems = cachedItemGestures.keys();
+        qSort(cachedTargetItems.begin(), cachedTargetItems.end(), qt_closestItemFirst);
         DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
-                << "delivering override to"
-                << topmost << gestures;
-        sendEvent(topmost, &ev);
-        // mark all accepted gestures to deliver them as normal gesture events
-        foreach (QGesture *g, gestures) {
-            if (ev.isAccepted() || ev.isAccepted(g)) {
-                conflictedGestures.remove(g->gestureType());
-                gestureTargets.remove(g);
-                // add the gesture to the list of normal delivered gestures
-                normalGestures.insert(g, topmost);
+                << "Normal gestures:" << normalGestures
+                << "Conflicting gestures:" << conflictedGestures;
+
+        // deliver conflicted gestures as override events AND remember
+        // initial gesture targets
+        if (!conflictedGestures.isEmpty()) {
+            for (int i = 0; i < cachedTargetItems.size(); ++i) {
+                QWeakPointer<QGraphicsObject> item = cachedTargetItems.at(i);
+
+                // get gestures to deliver to the current item
+                QSet<QGesture *> gestures = conflictedGestures & cachedItemGestures.value(item.data());
+                if (gestures.isEmpty())
+                    continue;
+
                 DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
-                        << "override was accepted:"
-                        << g << topmost;
-                ignoredConflictedGestures.remove(g);
+                        << "delivering override to"
+                        << item.data() << gestures;
+                // send gesture override
+                QGestureEvent ev(gestures.toList());
+                ev.t = QEvent::GestureOverride;
+                ev.setWidget(event->widget());
+                // mark event and individual gestures as ignored
+                ev.ignore();
+                foreach(QGesture *g, gestures)
+                    ev.setAccepted(g, false);
+                sendEvent(item.data(), &ev);
+                // mark all accepted gestures to deliver them as normal gesture events
+                foreach (QGesture *g, gestures) {
+                    if (ev.isAccepted() || ev.isAccepted(g)) {
+                        conflictedGestures.remove(g);
+                        // mark the item as a gesture target
+                        if (item)
+                            gestureTargets.insert(g, item.data());
+                        DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+                                << "override was accepted:"
+                                << g << item.data();
+                    }
+                    // remember the first item that received the override event
+                    // as it most likely become a target if noone else accepts
+                    // the override event
+                    if (!gestureTargets.contains(g) && item)
+                        gestureTargets.insert(g, item.data());
+
+                }
+                if (conflictedGestures.isEmpty())
+                    break;
             }
         }
-        // remove the item that we've already delivered from the list
-        for (int i = 0; i < conflictedItems.size(); ) {
-            QList<QGraphicsObject *> &items = conflictedItems[i];
-            if (items.first() == topmost) {
-                items.removeFirst();
-                if (items.isEmpty()) {
-                    conflictedItems.removeAt(i);
-                    continue;
+        // remember the initial target item for each gesture that was not in
+        // the conflicted state.
+        if (!normalGestures.isEmpty()) {
+            for (int i = 0; i < cachedTargetItems.size() && !normalGestures.isEmpty(); ++i) {
+                QGraphicsObject *item = cachedTargetItems.at(i);
+
+                // get gestures to deliver to the current item
+                foreach (QGesture *g, cachedItemGestures.value(item)) {
+                    if (!gestureTargets.contains(g)) {
+                        gestureTargets.insert(g, item);
+                        normalGestures.remove(g);
+                    }
                 }
             }
-            ++i;
         }
     }
 
-    // put back those started gestures that are not in the conflicted state
-    // and remember their targets
-    QHash<QGesture *, QGraphicsObject *>::const_iterator it = normalGestures.begin(),
-                                                          e = normalGestures.end();
-    for (; it != e; ++it) {
-        QGesture *g = it.key();
-        QGraphicsObject *receiver = it.value();
-        Q_ASSERT(!gestureTargets.contains(g));
-        gestureTargets.insert(g, receiver);
-        gesturesPerItem[receiver].append(g);
-    }
-    it = ignoredConflictedGestures.begin();
-    e = ignoredConflictedGestures.end();
-    for (; it != e; ++it) {
-        QGesture *g = it.key();
-        QGraphicsObject *receiver = it.value();
-        Q_ASSERT(!gestureTargets.contains(g));
-        gestureTargets.insert(g, receiver);
-        gesturesPerItem[receiver].append(g);
-    }
-
-    DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
-            << "Started gestures:" << normalGestures.keys()
-            << "All gestures:" << gesturesPerItem.values();
-
-    // deliver all events
-    QList<QGesture *> alreadyIgnoredGestures;
-    QHash<QGraphicsObject *, QSet<QGesture *> > itemIgnoredGestures;
-    QList<QGraphicsObject *> targetItems = gesturesPerItem.keys();
-    qSort(targetItems.begin(), targetItems.end(), qt_closestItemFirst);
-    for (int i = 0; i < targetItems.size(); ++i) {
-        QGraphicsObject *item = targetItems.at(i);
-        QList<QGesture *> gestures = gesturesPerItem.value(item);
-        // remove gestures that were already delivered once and were ignored
-        DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
-                << "already ignored gestures for item"
-                << item << ":" << itemIgnoredGestures.value(item);
-
-        if (itemIgnoredGestures.contains(item)) // don't deliver twice to the same item
-            continue;
-
-        QGraphicsItemPrivate *gid = item->QGraphicsItem::d_func();
-        foreach(QGesture *g, alreadyIgnoredGestures) {
-            QMap<Qt::GestureType, Qt::GestureFlags>::iterator contextit =
-                    gid->gestureContext.find(g->gestureType());
-            bool deliver = contextit != gid->gestureContext.end() &&
-                (g->state() == Qt::GestureStarted ||
-                 (contextit.value() & Qt::ReceivePartialGestures));
-            if (deliver)
-                gestures += g;
+
+    // deliver all gesture events
+    QSet<QGesture *> undeliveredGestures;
+    QSet<QGesture *> parentPropagatedGestures;
+    foreach (QGesture *gesture, allGestures) {
+        if (QGraphicsObject *target = gestureTargets.value(gesture, 0)) {
+            cachedItemGestures[target].insert(gesture);
+            cachedTargetItems.append(target);
+            undeliveredGestures.insert(gesture);
+            QGraphicsItemPrivate *d = target->QGraphicsItem::d_func();
+            const Qt::GestureFlags flags = d->gestureContext.value(gesture->gestureType());
+            if (flags & Qt::IgnoredGesturesPropagateToParent)
+                parentPropagatedGestures.insert(gesture);
+        } else {
+            DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+                    << "no target for" << gesture << "at"
+                    << gesture->hotSpot() << gesture->d_func()->sceneHotSpot;
         }
+    }
+    qSort(cachedTargetItems.begin(), cachedTargetItems.end(), qt_closestItemFirst);
+    for (int i = 0; i < cachedTargetItems.size(); ++i) {
+        QWeakPointer<QGraphicsObject> receiver = cachedTargetItems.at(i);
+        QSet<QGesture *> gestures =
+                undeliveredGestures & cachedItemGestures.value(receiver.data());
+        gestures -= cachedAlreadyDeliveredGestures.value(receiver.data());
+
         if (gestures.isEmpty())
             continue;
+
+        cachedAlreadyDeliveredGestures[receiver.data()] += gestures;
+        const bool isPanel = receiver.data()->isPanel();
+
         DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
                 << "delivering to"
-                << item << gestures;
-        QGestureEvent ev(gestures);
+                << receiver.data() << gestures;
+        QGestureEvent ev(gestures.toList());
         ev.setWidget(event->widget());
-        sendEvent(item, &ev);
+        sendEvent(receiver.data(), &ev);
         QSet<QGesture *> ignoredGestures;
         foreach (QGesture *g, gestures) {
             if (!ev.isAccepted() && !ev.isAccepted(g)) {
-                ignoredGestures.insert(g);
+                // if the gesture was ignored by its target, we will update the
+                // targetItems list with a possible target items (items that
+                // want to receive partial gestures).
+                // ### wont' work if the target was destroyed in the event
+                //     we will just stop delivering it.
+                if (receiver && receiver.data() == gestureTargets.value(g, 0))
+                    ignoredGestures.insert(g);
             } else {
-                if (g->state() == Qt::GestureStarted)
-                    gestureTargets[g] = item;
+                if (receiver && g->state() == Qt::GestureStarted) {
+                    // someone accepted the propagated initial GestureStarted
+                    // event, let it be the new target for all following events.
+                    gestureTargets[g] = receiver.data();
+                }
+                undeliveredGestures.remove(g);
             }
         }
-        if (!ignoredGestures.isEmpty()) {
-            // get a list of items under the (current) hotspot of each ignored
-            // gesture and start delivery again from the beginning
+        if (undeliveredGestures.isEmpty())
+            break;
+
+        // ignoredGestures list is only filled when delivering to the gesture
+        // target item, so it is safe to assume item == target.
+        if (!ignoredGestures.isEmpty() && !isPanel) {
+            // look for new potential targets for gestures that were ignored
+            // and should be propagated.
+
+            QSet<QGraphicsObject *> targetsSet = cachedTargetItems.toSet();
+
+            if (receiver) {
+                // first if the gesture should be propagated to parents only
+                for (QSet<QGesture *>::iterator it = ignoredGestures.begin();
+                     it != ignoredGestures.end();) {
+                    if (parentPropagatedGestures.contains(*it)) {
+                        QGesture *gesture = *it;
+                        const Qt::GestureType gestureType = gesture->gestureType();
+                        QGraphicsItem *item = receiver.data();
+                        while (item) {
+                            if (QGraphicsObject *obj = item->toGraphicsObject()) {
+                                if (item->d_func()->gestureContext.contains(gestureType)) {
+                                    targetsSet.insert(obj);
+                                    cachedItemGestures[obj].insert(gesture);
+                                }
+                            }
+                            if (item->isPanel())
+                                break;
+                            item = item->parentItem();
+                        }
+
+                        it = ignoredGestures.erase(it);
+                        continue;
+                    }
+                    ++it;
+                }
+            }
+
+            gestureTargetsAtHotSpots(ignoredGestures, Qt::ReceivePartialGestures,
+                                     &cachedItemGestures, &targetsSet, 0, 0);
+
+            cachedTargetItems = targetsSet.toList();
+            qSort(cachedTargetItems.begin(), cachedTargetItems.end(), qt_closestItemFirst);
             DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
-                    << "item has ignored the event, will propagate."
-                    << item << ignoredGestures;
-            itemIgnoredGestures[item] += ignoredGestures;
-            QMap<Qt::GestureType, QGesture *> conflictedGestures;
-            QList<QList<QGraphicsObject *> > itemsForConflictedGestures;
-            QHash<QGesture *, QGraphicsObject *> normalGestures;
-            getGestureTargets(ignoredGestures, viewport,
-                              &conflictedGestures, &itemsForConflictedGestures,
-                              &normalGestures);
-            QSet<QGraphicsObject *> itemsSet = targetItems.toSet();
-            for (int k = 0; k < itemsForConflictedGestures.size(); ++k)
-                itemsSet += itemsForConflictedGestures.at(k).toSet();
-            targetItems = itemsSet.toList();
-            qSort(targetItems.begin(), targetItems.end(), qt_closestItemFirst);
-            alreadyIgnoredGestures = conflictedGestures.values();
-            DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
-                    << "new targets:" << targetItems;
+                    << "new targets:" << cachedTargetItems;
             i = -1; // start delivery again
             continue;
         }
     }
+
     foreach (QGesture *g, startedGestures) {
         if (g->gestureCancelPolicy() == QGesture::CancelAllInContext) {
             DEBUG() << "lets try to cancel some";
             // find gestures in context in Qt::GestureStarted or Qt::GestureUpdated state and cancel them
-            cancelGesturesForChildren(g, event->widget());
+            cancelGesturesForChildren(g);
         }
     }
 
@@ -6160,9 +6230,13 @@
             break;
         }
     }
-}
-
-void QGraphicsScenePrivate::cancelGesturesForChildren(QGesture *original, QWidget *viewport)
+
+    cachedTargetItems.clear();
+    cachedItemGestures.clear();
+    cachedAlreadyDeliveredGestures.clear();
+}
+
+void QGraphicsScenePrivate::cancelGesturesForChildren(QGesture *original)
 {
     Q_ASSERT(original);
     QGraphicsItem *originalItem = gestureTargets.value(original);
@@ -6218,8 +6292,7 @@
             if (!g->hasHotSpot())
                 continue;
 
-            QPoint screenPos = g->hotSpot().toPoint();
-            QList<QGraphicsItem *> items = itemsAtPosition(screenPos, QPointF(), viewport);
+            QList<QGraphicsItem *> items = itemsAtPosition(QPoint(), g->d_func()->sceneHotSpot, 0);
             for (int j = 0; j < items.size(); ++j) {
                 QGraphicsObject *item = items.at(j)->toGraphicsObject();
                 if (!item)
@@ -6245,6 +6318,27 @@
     }
 }
 
+void QGraphicsScenePrivate::grabGesture(QGraphicsItem *, Qt::GestureType gesture)
+{
+    (void)QGestureManager::instance(); // create a gesture manager
+    if (!grabbedGestures[gesture]++) {
+        foreach (QGraphicsView *view, views)
+            view->viewport()->grabGesture(gesture);
+    }
+}
+
+void QGraphicsScenePrivate::ungrabGesture(QGraphicsItem *item, Qt::GestureType gesture)
+{
+    // we know this can only be an object
+    Q_ASSERT(item->d_ptr->isObject);
+    QGraphicsObject *obj = static_cast<QGraphicsObject *>(item);
+    QGestureManager::instance()->cleanupCachedGestures(obj, gesture);
+    if (!--grabbedGestures[gesture]) {
+        foreach (QGraphicsView *view, views)
+            view->viewport()->ungrabGesture(gesture);
+    }
+}
+
 QT_END_NAMESPACE
 
 #include "moc_qgraphicsscene.cpp"