src/gui/graphicsview/qgraphicsscene.cpp
changeset 3 41300fa6a67c
parent 0 1918ee327afb
child 4 3b1da2848fc7
--- a/src/gui/graphicsview/qgraphicsscene.cpp	Tue Jan 26 12:42:25 2010 +0200
+++ b/src/gui/graphicsview/qgraphicsscene.cpp	Tue Feb 02 00:43:10 2010 +0200
@@ -294,6 +294,7 @@
       needSortTopLevelItems(true),
       holesInTopLevelSiblingIndex(false),
       topLevelSequentialOrdering(true),
+      scenePosDescendantsUpdatePending(false),
       stickyFocus(false),
       hasFocus(false),
       focusItem(0),
@@ -488,6 +489,55 @@
 
 /*!
     \internal
+*/
+void QGraphicsScenePrivate::setScenePosItemEnabled(QGraphicsItem *item, bool enabled)
+{
+    QGraphicsItem *p = item->d_ptr->parent;
+    while (p) {
+        p->d_ptr->scenePosDescendants = enabled;
+        p = p->d_ptr->parent;
+    }
+    if (!enabled && !scenePosDescendantsUpdatePending) {
+        scenePosDescendantsUpdatePending = true;
+        QMetaObject::invokeMethod(q_func(), "_q_updateScenePosDescendants", Qt::QueuedConnection);
+    }
+}
+
+/*!
+    \internal
+*/
+void QGraphicsScenePrivate::registerScenePosItem(QGraphicsItem *item)
+{
+    scenePosItems.insert(item);
+    setScenePosItemEnabled(item, true);
+}
+
+/*!
+    \internal
+*/
+void QGraphicsScenePrivate::unregisterScenePosItem(QGraphicsItem *item)
+{
+    scenePosItems.remove(item);
+    setScenePosItemEnabled(item, false);
+}
+
+/*!
+    \internal
+*/
+void QGraphicsScenePrivate::_q_updateScenePosDescendants()
+{
+    foreach (QGraphicsItem *item, scenePosItems) {
+        QGraphicsItem *p = item->d_ptr->parent;
+        while (p) {
+            p->d_ptr->scenePosDescendants = 1;
+            p = p->d_ptr->parent;
+        }
+    }
+    scenePosDescendantsUpdatePending = false;
+}
+
+/*!
+    \internal
 
     Schedules an item for removal. This function leaves some stale indexes
     around in the BSP tree if called from the item's destructor; these will
@@ -505,7 +555,8 @@
     // Clear focus on the item to remove any reference in the focusWidget chain.
     item->clearFocus();
 
-    markDirty(item, QRectF(), false, false, false, false, /*removingItemFromScene=*/true);
+    markDirty(item, QRectF(), /*invalidateChildren=*/false, /*force=*/false,
+              /*ignoreOpacity=*/false, /*removingItemFromScene=*/true);
 
     if (item->d_ptr->inDestructor) {
         // The item is actually in its destructor, we call the special method in the index.
@@ -518,12 +569,24 @@
 
     item->d_ptr->clearSubFocus();
 
+    if (item->flags() & QGraphicsItem::ItemSendsScenePositionChanges)
+        unregisterScenePosItem(item);
+
+    QGraphicsScene *oldScene = item->d_func()->scene;
+    item->d_func()->scene = 0;
+
+    //We need to remove all children first because they might use their parent
+    //attributes (e.g. sceneTransform).
+    if (!item->d_ptr->inDestructor) {
+        // Remove all children recursively
+        for (int i = 0; i < item->d_ptr->children.size(); ++i)
+            q->removeItem(item->d_ptr->children.at(i));
+    }
+
     if (!item->d_ptr->inDestructor && item == tabFocusFirst) {
         QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
-        widget->d_func()->fixFocusChainBeforeReparenting(0, 0);
-    }
-
-    item->d_func()->scene = 0;
+        widget->d_func()->fixFocusChainBeforeReparenting(0, oldScene, 0);
+    }
 
     // Unregister focus proxy.
     item->d_ptr->resetFocusProxy();
@@ -551,6 +614,19 @@
     if (item == lastActivePanel)
         lastActivePanel = 0;
 
+    // Cancel active touches
+    {
+        QMap<int, QGraphicsItem *>::iterator it = itemForTouchPointId.begin();
+        while (it != itemForTouchPointId.end()) {
+            if (it.value() == item) {
+                sceneCurrentTouchPoints.remove(it.key());
+                it = itemForTouchPointId.erase(it);
+            } else {
+                ++it;
+            }
+        }
+    }
+
     // Disable selectionChanged() for individual items
     ++selectionChanging;
     int oldSelectedItemsSize = selectedItems.size();
@@ -571,12 +647,6 @@
             ++iterator;
     }
 
-    if (!item->d_ptr->inDestructor) {
-        // Remove all children recursively
-        for (int i = 0; i < item->d_ptr->children.size(); ++i)
-            q->removeItem(item->d_ptr->children.at(i));
-    }
-
     if (item->isPanel() && item->isVisible() && item->panelModality() != QGraphicsItem::NonModal)
         leaveModal(item);
 
@@ -691,6 +761,7 @@
         focusItem = 0;
         sendEvent(lastFocusItem, &event);
 
+#ifndef QT_NO_IM
         if (lastFocusItem
             && (lastFocusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod)) {
             // Reset any visible preedit text
@@ -706,6 +777,7 @@
                     views.at(i)->inputContext()->reset();
             }
         }
+#endif //QT_NO_IM
     }
 
     if (item) {
@@ -1060,9 +1132,8 @@
 bool QGraphicsScenePrivate::sendEvent(QGraphicsItem *item, QEvent *event)
 {
     if (QGraphicsObject *object = item->toGraphicsObject()) {
-        QApplicationPrivate *qAppPriv = QApplicationPrivate::instance();
-        if (qAppPriv->gestureManager) {
-            if (qAppPriv->gestureManager->filterEvent(object, event))
+        if (qt_gestureManager) {
+            if (qt_gestureManager->filterEvent(object, event))
                 return true;
         }
     }
@@ -2290,8 +2361,9 @@
     // NB! We have to clear the index before deleting items; otherwise the
     // index might try to access dangling item pointers.
     d->index->clear();
-    const QList<QGraphicsItem *> items = d->topLevelItems;
-    qDeleteAll(items);
+    // NB! QGraphicsScenePrivate::unregisterTopLevelItem() removes items
+    while (!d->topLevelItems.isEmpty())
+        delete d->topLevelItems.first();
     Q_ASSERT(d->topLevelItems.isEmpty());
     d->lastItemCount = 0;
     d->allItemsIgnoreHoverEvents = true;
@@ -2499,11 +2571,9 @@
     item->d_ptr->resolveFont(d->font.resolve());
     item->d_ptr->resolvePalette(d->palette.resolve());
 
-    if (!item->d_ptr->explicitlyHidden) {
-       if (d->unpolishedItems.isEmpty())
-           QMetaObject::invokeMethod(this, "_q_polishItems", Qt::QueuedConnection);
-       d->unpolishedItems.insert(item);
-    }
+   if (d->unpolishedItems.isEmpty())
+       QMetaObject::invokeMethod(this, "_q_polishItems", Qt::QueuedConnection);
+   d->unpolishedItems.insert(item);
 
     // Reenable selectionChanged() for individual items
     --d->selectionChanging;
@@ -2537,6 +2607,9 @@
         }
     }
 
+    if (item->flags() & QGraphicsItem::ItemSendsScenePositionChanges)
+        d->registerScenePosItem(item);
+
     // Ensure that newly added items that have subfocus set, gain
     // focus automatically if there isn't a focus item already.
     if (!d->focusItem && item != d->lastFocusItem && item->focusItem() == item)
@@ -2823,18 +2896,20 @@
 }
 
 /*!
-    Returns the scene's current focus item, or 0 if no item currently has
-    focus.
+    When the scene is active, this functions returns the scene's current focus
+    item, or 0 if no item currently has focus. When the scene is inactive, this
+    functions returns the item that will gain input focus when the scene becomes
+    active.
 
     The focus item receives keyboard input when the scene receives a
     key event.
 
-    \sa setFocusItem(), QGraphicsItem::hasFocus()
+    \sa setFocusItem(), QGraphicsItem::hasFocus(), isActive()
 */
 QGraphicsItem *QGraphicsScene::focusItem() const
 {
     Q_D(const QGraphicsScene);
-    return d->focusItem;
+    return isActive() ? d->focusItem : d->lastFocusItem;
 }
 
 /*!
@@ -4146,7 +4221,7 @@
     QRect br = pixmapExposed.boundingRect();
 
     // Don't use subpixmap if we get a full update.
-    if (pixmapExposed.isEmpty() || (pixmapExposed.numRects() == 1 && br.contains(pix->rect()))) {
+    if (pixmapExposed.isEmpty() || (pixmapExposed.rectCount() == 1 && br.contains(pix->rect()))) {
         pix->fill(Qt::transparent);
         pixmapPainter.begin(pix);
     } else {
@@ -4172,7 +4247,6 @@
     if (!subPix.isNull()) {
         // Blit the subpixmap into the main pixmap.
         pixmapPainter.begin(pix);
-        pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source);
         pixmapPainter.setClipRegion(pixmapExposed);
         pixmapPainter.drawPixmap(br.topLeft(), subPix);
         pixmapPainter.end();
@@ -4360,9 +4434,12 @@
         bool allowPartialCacheExposure = !viewRect.contains(deviceRect);
 #else
         // Only if deviceRect is 20% taller or wider than the desktop.
-        QRect desktopRect = QApplication::desktop()->availableGeometry(widget);
-        bool allowPartialCacheExposure = (desktopRect.width() * 1.2 < deviceRect.width()
-                                          || desktopRect.height() * 1.2 < deviceRect.height());
+        bool allowPartialCacheExposure = false;
+        if (widget) {
+            QRect desktopRect = QApplication::desktop()->availableGeometry(widget);
+            allowPartialCacheExposure = (desktopRect.width() * 1.2 < deviceRect.width()
+                                         || desktopRect.height() * 1.2 < deviceRect.height());
+        }
 #endif
         QRegion scrollExposure;
         if (deviceData->cacheIndent != QPoint() || allowPartialCacheExposure) {
@@ -4578,6 +4655,7 @@
     if (itemHasChildren && itemClipsChildrenToShape)
         ENSURE_TRANSFORM_PTR;
 
+#ifndef QT_NO_GRAPHICSEFFECT
     if (item->d_ptr->graphicsEffect && item->d_ptr->graphicsEffect->isEnabled()) {
         ENSURE_TRANSFORM_PTR;
         QGraphicsItemPaintInfo info(viewTransform, transformPtr, effectTransform, exposedRegion, widget, &styleOptionTmp,
@@ -4593,14 +4671,19 @@
             painter->setWorldTransform(*transformPtr);
         painter->setOpacity(opacity);
 
-        if (sourced->lastEffectTransform != painter->worldTransform()) {
+        if (sourced->currentCachedSystem() != Qt::LogicalCoordinates
+            && sourced->lastEffectTransform != painter->worldTransform())
+        {
             sourced->lastEffectTransform = painter->worldTransform();
-            sourced->invalidateCache();
+            sourced->invalidateCache(QGraphicsEffectSourcePrivate::TransformChanged);
         }
-        item->d_ptr->graphicsEffect->draw(painter, source);
+
+        item->d_ptr->graphicsEffect->draw(painter);
         painter->setWorldTransform(restoreTransform);
         sourced->info = 0;
-    } else {
+    } else
+#endif //QT_NO_GRAPHICSEFFECT
+    {
         draw(item, painter, viewTransform, transformPtr, exposedRegion, widget, opacity,
              effectTransform, wasDirtyParentSceneTransform, drawItem);
     }
@@ -4693,15 +4776,13 @@
 }
 
 void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, bool invalidateChildren,
-                                      bool maybeDirtyClipPath, bool force, bool ignoreOpacity,
-                                      bool removingItemFromScene)
+                                      bool force, bool ignoreOpacity, bool removingItemFromScene)
 {
     Q_ASSERT(item);
     if (updateAll)
         return;
 
-    if (item->d_ptr->discardUpdateRequest(/*ignoreClipping=*/maybeDirtyClipPath,
-                                          /*ignoreVisibleBit=*/force,
+    if (item->d_ptr->discardUpdateRequest(/*ignoreVisibleBit=*/force,
                                           /*ignoreDirtyBit=*/removingItemFromScene || invalidateChildren,
                                           /*ignoreOpacity=*/ignoreOpacity)) {
         if (item->d_ptr->dirty) {
@@ -4769,10 +4850,12 @@
     QGraphicsItem *p = item->d_ptr->parent;
     while (p) {
         p->d_ptr->dirtyChildren = 1;
+#ifndef QT_NO_GRAPHICSEFFECT
         if (p->d_ptr->graphicsEffect && p->d_ptr->graphicsEffect->isEnabled()) {
             p->d_ptr->dirty = 1;
             p->d_ptr->fullUpdatePending = 1;
         }
+#endif //QT_NO_GRAPHICSEFFECT
         p = p->d_ptr->parent;
     }
 }
@@ -4914,7 +4997,7 @@
                     continue;
                 }
 
-                if (item->d_ptr->paintedViewBoundingRectsNeedRepaint && !paintedViewBoundingRect.isEmpty()) {
+                if (item->d_ptr->paintedViewBoundingRectsNeedRepaint) {
                     paintedViewBoundingRect.translate(viewPrivate->dirtyScrollOffset);
                     if (!viewPrivate->updateRect(paintedViewBoundingRect))
                         paintedViewBoundingRect = QRect(-1, -1, -1, -1); // Outside viewport.
@@ -5613,17 +5696,22 @@
         touchEvent->setAccepted(acceptTouchEvents);
         res = acceptTouchEvents && sendEvent(item, touchEvent);
         eventAccepted = touchEvent->isAccepted();
-        item->d_ptr->acceptedTouchBeginEvent = (res && eventAccepted);
+        if (itemForTouchPointId.value(touchEvent->touchPoints().first().id()) == 0) {
+            // item was deleted
+            item = 0;
+        } else {
+            item->d_ptr->acceptedTouchBeginEvent = (res && eventAccepted);
+        }
         touchEvent->spont = false;
         if (res && eventAccepted) {
             // the first item to accept the TouchBegin gets an implicit grab.
             for (int i = 0; i < touchEvent->touchPoints().count(); ++i) {
                 const QTouchEvent::TouchPoint &touchPoint = touchEvent->touchPoints().at(i);
-                itemForTouchPointId[touchPoint.id()] = item;
+                itemForTouchPointId[touchPoint.id()] = item; // can be zero
             }
             break;
         }
-        if (item->isPanel())
+        if (item && item->isPanel())
             break;
     }
 
@@ -5769,7 +5857,7 @@
     QWidget *viewport = event->widget();
     if (!viewport)
         return;
-    QList<QGesture *> allGestures = event->allGestures();
+    QList<QGesture *> allGestures = event->gestures();
     DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
             << "Delivering gestures:" <<  allGestures;
 
@@ -5908,7 +5996,12 @@
 
         QGraphicsItemPrivate *gid = item->QGraphicsItem::d_func();
         foreach(QGesture *g, alreadyIgnoredGestures) {
-            if (gid->gestureContext.contains(g->gestureType()))
+            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;
         }
         if (gestures.isEmpty())
@@ -5921,8 +6014,12 @@
         sendEvent(item, &ev);
         QSet<QGesture *> ignoredGestures;
         foreach (QGesture *g, gestures) {
-            if (!ev.isAccepted() && !ev.isAccepted(g))
+            if (!ev.isAccepted() && !ev.isAccepted(g)) {
                 ignoredGestures.insert(g);
+            } else {
+                if (g->state() == Qt::GestureStarted)
+                    gestureTargets[g] = item;
+            }
         }
         if (!ignoredGestures.isEmpty()) {
             // get a list of items under the (current) hotspot of each ignored
@@ -5949,6 +6046,13 @@
             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());
+        }
+    }
 
     // forget about targets for gestures that have ended
     foreach (QGesture *g, allGestures) {
@@ -5963,6 +6067,88 @@
     }
 }
 
+void QGraphicsScenePrivate::cancelGesturesForChildren(QGesture *original, QWidget *viewport)
+{
+    Q_ASSERT(original);
+    QGraphicsItem *originalItem = gestureTargets.value(original);
+    Q_ASSERT(originalItem);
+
+    // iterate over all active gestures and for each find the owner
+    // if the owner is part of our sub-hierarchy, cancel it.
+
+    QSet<QGesture *> canceledGestures;
+    QHash<QGesture *, QGraphicsObject *>::Iterator iter = gestureTargets.begin();
+    while (iter != gestureTargets.end()) {
+        QGraphicsObject *item = iter.value();
+        // note that we don't touch the gestures for our originalItem
+        if (item != originalItem && originalItem->isAncestorOf(item)) {
+            DEBUG() << "  found a gesture to cancel" << iter.key();
+            iter.key()->d_func()->state = Qt::GestureCanceled;
+            canceledGestures << iter.key();
+        }
+        ++iter;
+    }
+
+    // sort them per target item by cherry picking from almostCanceledGestures and delivering
+    QSet<QGesture *> almostCanceledGestures = canceledGestures;
+    QSet<QGesture *>::Iterator setIter;
+    while (!almostCanceledGestures.isEmpty()) {
+        QGraphicsObject *target = 0;
+        QSet<QGesture*> gestures;
+        setIter = almostCanceledGestures.begin();
+        // sort per target item
+        while (setIter != almostCanceledGestures.end()) {
+            QGraphicsObject *item = gestureTargets.value(*setIter);
+            if (target == 0)
+                target = item;
+            if (target == item) {
+                gestures << *setIter;
+                setIter = almostCanceledGestures.erase(setIter);
+            } else {
+                ++setIter;
+            }
+        }
+        Q_ASSERT(target);
+
+        QList<QGesture *> list = gestures.toList();
+        QGestureEvent ev(list);
+        sendEvent(target, &ev);
+
+        foreach (QGesture *g, list) {
+            if (ev.isAccepted() || ev.isAccepted(g))
+                gestures.remove(g);
+        }
+
+        foreach (QGesture *g, gestures) {
+            if (!g->hasHotSpot())
+                continue;
+
+            QPoint screenPos = g->hotSpot().toPoint();
+            QList<QGraphicsItem *> items = itemsAtPosition(screenPos, QPointF(), viewport);
+            for (int j = 0; j < items.size(); ++j) {
+                QGraphicsObject *item = items.at(j)->toGraphicsObject();
+                if (!item)
+                    continue;
+                QGraphicsItemPrivate *d = item->QGraphicsItem::d_func();
+                if (d->gestureContext.contains(g->gestureType())) {
+                    QList<QGesture *> list;
+                    list << g;
+                    QGestureEvent ev(list);
+                    sendEvent(item, &ev);
+                    if (ev.isAccepted() || ev.isAccepted(g))
+                        break; // successfully delivered
+                }
+            }
+        }
+    }
+
+    Q_ASSERT(qt_gestureManager); // it would be very odd if we got called without a manager.
+    for (setIter = canceledGestures.begin(); setIter != canceledGestures.end(); ++setIter) {
+        qt_gestureManager->recycle(*setIter);
+        gestureTargets.remove(*setIter);
+    }
+}
+
 QT_END_NAMESPACE
 
 #include "moc_qgraphicsscene.cpp"