src/gui/graphicsview/qgraphicsview.cpp
changeset 30 5dc02b23752f
parent 19 fcece45ef507
child 33 3e2da88830cd
--- a/src/gui/graphicsview/qgraphicsview.cpp	Wed Jun 23 19:07:03 2010 +0300
+++ b/src/gui/graphicsview/qgraphicsview.cpp	Tue Jul 06 15:10:48 2010 +0300
@@ -327,16 +327,21 @@
       dragMode(QGraphicsView::NoDrag),
       sceneInteractionAllowed(true), hasSceneRect(false),
       connectedToScene(false),
-      mousePressButton(Qt::NoButton),
+      useLastMouseEvent(false),
       identityMatrix(true),
       dirtyScroll(true),
       accelerateScrolling(true),
+      keepLastCenterPoint(true),
+      transforming(false),
+      handScrolling(false),
+      mustAllocateStyleOptions(false),
+      mustResizeBackgroundPixmap(true),
+      fullUpdatePending(true),
+      hasUpdateClip(false),
+      mousePressButton(Qt::NoButton),
       leftIndent(0), topIndent(0),
       lastMouseEvent(QEvent::None, QPoint(), Qt::NoButton, 0, 0),
-      useLastMouseEvent(false),
-      keepLastCenterPoint(true),
       alignment(Qt::AlignCenter),
-      transforming(false),
       transformationAnchor(QGraphicsView::AnchorViewCenter), resizeAnchor(QGraphicsView::NoAnchor),
       viewportUpdateMode(QGraphicsView::MinimalViewportUpdate),
       optimizationFlags(0),
@@ -345,14 +350,11 @@
       rubberBanding(false),
       rubberBandSelectionMode(Qt::IntersectsItemShape),
 #endif
-      handScrolling(false), handScrollMotions(0), cacheMode(0),
-      mustAllocateStyleOptions(false),
-      mustResizeBackgroundPixmap(true),
+      handScrollMotions(0), cacheMode(0),
 #ifndef QT_NO_CURSOR
       hasStoredOriginalCursor(false),
 #endif
       lastDragDropEvent(0),
-      fullUpdatePending(true),
       updateSceneSlotReimplementedChecked(false)
 {
     styleOptions.reserve(QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS);
@@ -733,11 +735,13 @@
     }
 
     // Restore the original viewport cursor.
-    hasStoredOriginalCursor = false;
-    if (dragMode == QGraphicsView::ScrollHandDrag)
-        viewport->setCursor(Qt::OpenHandCursor);
-    else
-        viewport->setCursor(originalCursor);
+    if (hasStoredOriginalCursor) {
+        hasStoredOriginalCursor = false;
+        if (dragMode == QGraphicsView::ScrollHandDrag)
+            viewport->setCursor(Qt::OpenHandCursor);
+        else
+            viewport->setCursor(originalCursor);
+    }
 }
 #endif
 
@@ -852,10 +856,7 @@
     if (fullUpdatePending) {
         viewport->update();
     } else if (viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate) {
-        if (optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)
-            viewport->update(dirtyBoundingRect.adjusted(-1, -1, 1, 1));
-        else
-            viewport->update(dirtyBoundingRect.adjusted(-2, -2, 2, 2));
+        viewport->update(dirtyBoundingRect);
     } else {
         viewport->update(dirtyRegion); // Already adjusted in updateRect/Region.
     }
@@ -880,46 +881,92 @@
     }
 }
 
-bool QGraphicsViewPrivate::updateRegion(const QRegion &r)
+/*
+   Calling this function results in update rects being clipped to the item's
+   bounding rect. Note that updates prior to this function call is not clipped.
+   The clip is removed by passing 0.
+*/
+void QGraphicsViewPrivate::setUpdateClip(QGraphicsItem *item)
 {
-    if (fullUpdatePending || viewportUpdateMode == QGraphicsView::NoViewportUpdate || r.isEmpty())
+    Q_Q(QGraphicsView);
+    // We simply ignore the request if the update mode is either FullViewportUpdate
+    // or NoViewportUpdate; in that case there's no point in clipping anything.
+    if (!item || viewportUpdateMode == QGraphicsView::NoViewportUpdate
+        || viewportUpdateMode == QGraphicsView::FullViewportUpdate) {
+        hasUpdateClip = false;
+        return;
+    }
+
+    // Calculate the clip (item's bounding rect in view coordinates).
+    // Optimized version of:
+    // QRect clip = item->deviceTransform(q->viewportTransform())
+    //              .mapRect(item->boundingRect()).toAlignedRect();
+    QRect clip;
+    if (item->d_ptr->itemIsUntransformable()) {
+        QTransform xform = item->deviceTransform(q->viewportTransform());
+        clip = xform.mapRect(item->boundingRect()).toAlignedRect();
+    } else if (item->d_ptr->sceneTransformTranslateOnly && identityMatrix) {
+        QRectF r(item->boundingRect());
+        r.translate(item->d_ptr->sceneTransform.dx() - horizontalScroll(),
+                    item->d_ptr->sceneTransform.dy() - verticalScroll());
+        clip = r.toAlignedRect();
+    } else if (!q->isTransformed()) {
+        clip = item->d_ptr->sceneTransform.mapRect(item->boundingRect()).toAlignedRect();
+    } else {
+        QTransform xform = item->d_ptr->sceneTransform;
+        xform *= q->viewportTransform();
+        clip = xform.mapRect(item->boundingRect()).toAlignedRect();
+    }
+
+    if (hasUpdateClip) {
+        // Intersect with old clip.
+        updateClip &= clip;
+    } else {
+        updateClip = clip;
+        hasUpdateClip = true;
+    }
+}
+
+bool QGraphicsViewPrivate::updateRegion(const QRectF &rect, const QTransform &xform)
+{
+    if (rect.isEmpty())
         return false;
 
-    const QRect boundingRect = r.boundingRect();
-    if (!intersectsViewport(boundingRect, viewport->width(), viewport->height()))
-        return false; // Update region outside viewport.
-
-    switch (viewportUpdateMode) {
-    case QGraphicsView::FullViewportUpdate:
-        fullUpdatePending = true;
-        viewport->update();
-        break;
-    case QGraphicsView::BoundingRectViewportUpdate:
-        QRect_unite(&dirtyBoundingRect, boundingRect);
-        if (containsViewport(dirtyBoundingRect, viewport->width(), viewport->height())) {
-            fullUpdatePending = true;
-            viewport->update();
-        }
-        break;
-    case QGraphicsView::SmartViewportUpdate: // ### DEPRECATE
-    case QGraphicsView::MinimalViewportUpdate:
-    {
-        const QVector<QRect> &rects = r.rects();
-        for (int i = 0; i < rects.size(); ++i) {
-            if (optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)
-                dirtyRegion += rects.at(i).adjusted(-1, -1, 1, 1);
-            else
-                dirtyRegion += rects.at(i).adjusted(-2, -2, 2, 2);
-        }
-        break;
+    if (viewportUpdateMode != QGraphicsView::MinimalViewportUpdate
+        && viewportUpdateMode != QGraphicsView::SmartViewportUpdate) {
+        // No point in updating with QRegion granularity; use the rect instead.
+        return updateRectF(xform.mapRect(rect));
     }
-    default:
-        break;
+
+    // Update mode is either Minimal or Smart, so we have to do a potentially slow operation,
+    // which is clearly documented here: QGraphicsItem::setBoundingRegionGranularity.
+    const QRegion region = xform.map(QRegion(rect.toAlignedRect()));
+    QRect viewRect = region.boundingRect();
+    const bool dontAdjustForAntialiasing = optimizationFlags & QGraphicsView::DontAdjustForAntialiasing;
+    if (dontAdjustForAntialiasing)
+        viewRect.adjust(-1, -1, 1, 1);
+    else
+        viewRect.adjust(-2, -2, 2, 2);
+    if (!intersectsViewport(viewRect, viewport->width(), viewport->height()))
+        return false; // Update region for sure outside viewport.
+
+    const QVector<QRect> &rects = region.rects();
+    for (int i = 0; i < rects.size(); ++i) {
+        viewRect = rects.at(i);
+        if (dontAdjustForAntialiasing)
+            viewRect.adjust(-1, -1, 1, 1);
+        else
+            viewRect.adjust(-2, -2, 2, 2);
+        if (hasUpdateClip)
+            viewRect &= updateClip;
+        dirtyRegion += viewRect;
     }
 
     return true;
 }
 
+// NB! Assumes the rect 'r' is already aligned and adjusted for antialiasing.
+// For QRectF use updateRectF(const QRectF &) to ensure proper adjustments.
 bool QGraphicsViewPrivate::updateRect(const QRect &r)
 {
     if (fullUpdatePending || viewportUpdateMode == QGraphicsView::NoViewportUpdate
@@ -933,7 +980,10 @@
         viewport->update();
         break;
     case QGraphicsView::BoundingRectViewportUpdate:
-        QRect_unite(&dirtyBoundingRect, r);
+        if (hasUpdateClip)
+            QRect_unite(&dirtyBoundingRect, r & updateClip);
+        else
+            QRect_unite(&dirtyBoundingRect, r);
         if (containsViewport(dirtyBoundingRect, viewport->width(), viewport->height())) {
             fullUpdatePending = true;
             viewport->update();
@@ -941,10 +991,10 @@
         break;
     case QGraphicsView::SmartViewportUpdate: // ### DEPRECATE
     case QGraphicsView::MinimalViewportUpdate:
-        if (optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)
-            dirtyRegion += r.adjusted(-1, -1, 1, 1);
+        if (hasUpdateClip)
+            dirtyRegion += r & updateClip;
         else
-            dirtyRegion += r.adjusted(-2, -2, 2, 2);
+            dirtyRegion += r;
         break;
     default:
         break;
@@ -1035,10 +1085,28 @@
 void QGraphicsViewPrivate::updateInputMethodSensitivity()
 {
     Q_Q(QGraphicsView);
-    bool enabled = scene && scene->focusItem()
-                   && (scene->focusItem()->flags() & QGraphicsItem::ItemAcceptsInputMethod);
+    QGraphicsItem *focusItem = 0;
+    bool enabled = scene && (focusItem = scene->focusItem())
+                   && (focusItem->d_ptr->flags & QGraphicsItem::ItemAcceptsInputMethod);
     q->setAttribute(Qt::WA_InputMethodEnabled, enabled);
     q->viewport()->setAttribute(Qt::WA_InputMethodEnabled, enabled);
+
+    if (!enabled) {
+        q->setInputMethodHints(0);
+        return;
+    }
+
+    QGraphicsProxyWidget *proxy = focusItem->d_ptr->isWidget && focusItem->d_ptr->isProxyWidget()
+                                    ? static_cast<QGraphicsProxyWidget *>(focusItem) : 0;
+    if (!proxy) {
+        q->setInputMethodHints(focusItem->inputMethodHints());
+    } else if (QWidget *widget = proxy->widget()) {
+    if (QWidget *fw = widget->focusWidget())
+        widget = fw;
+        q->setInputMethodHints(widget->inputMethodHints());
+    } else {
+        q->setInputMethodHints(0);
+    }
 }
 
 /*!
@@ -1807,8 +1875,6 @@
 void QGraphicsView::ensureVisible(const QRectF &rect, int xmargin, int ymargin)
 {
     Q_D(QGraphicsView);
-    Q_UNUSED(xmargin);
-    Q_UNUSED(ymargin);
     qreal width = viewport()->width();
     qreal height = viewport()->height();
     QRectF viewRect = d->matrix.mapRect(rect);
@@ -2622,6 +2688,11 @@
     if (d->scene && !d->scene->d_func()->allItemsIgnoreTouchEvents)
         widget->setAttribute(Qt::WA_AcceptTouchEvents);
 
+    if (d->scene) {
+        foreach (Qt::GestureType gesture, d->scene->d_func()->grabbedGestures.keys())
+            widget->grabGesture(gesture);
+    }
+
     widget->setAcceptDrops(acceptDrops());
 }
 
@@ -3383,8 +3454,14 @@
 
     // Items
     if (!(d->optimizationFlags & IndirectPainting)) {
+        const quint32 oldRectAdjust = d->scene->d_func()->rectAdjust;
+        if (d->optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)
+            d->scene->d_func()->rectAdjust = 1;
+        else
+            d->scene->d_func()->rectAdjust = 2;
         d->scene->d_func()->drawItems(&painter, viewTransformed ? &viewTransform : 0,
                                       &d->exposedRegion, viewport());
+        d->scene->d_func()->rectAdjust = oldRectAdjust;
         // Make sure the painter's world transform is restored correctly when
         // drawing without painter state protection (DontSavePainterState).
         // We only change the worldTransform() so there's no need to do a full-blown