summaryrefslogtreecommitdiffstats
path: root/src/gui/graphicsview/qgraphicsitem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/graphicsview/qgraphicsitem.cpp')
-rw-r--r--src/gui/graphicsview/qgraphicsitem.cpp440
1 files changed, 277 insertions, 163 deletions
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp
index 7b885df..fc81f39 100644
--- a/src/gui/graphicsview/qgraphicsitem.cpp
+++ b/src/gui/graphicsview/qgraphicsitem.cpp
@@ -522,6 +522,11 @@
#include <private/qtextdocumentlayout_p.h>
#include <private/qtextengine_p.h>
+#ifdef Q_WS_X11
+#include <private/qt_x11_p.h>
+#include <private/qpixmap_x11_p.h>
+#endif
+
#include <math.h>
QT_BEGIN_NAMESPACE
@@ -562,29 +567,6 @@ Q_GLOBAL_STATIC(QGraphicsItemCustomDataStore, qt_dataStore)
/*!
\internal
- Removes the first instance of \a child from \a children. This is a
- heuristic approach that assumes that it's common to remove items from the
- start or end of the list.
-*/
-static void qt_graphicsitem_removeChild(QGraphicsItem *child, QList<QGraphicsItem *> *children)
-{
- const int n = children->size();
- for (int i = 0; i < (n + 1) / 2; ++i) {
- if (children->at(i) == child) {
- children->removeAt(i);
- return;
- }
- int j = n - i - 1;
- if (children->at(j) == child) {
- children->removeAt(j);
- return;
- }
- }
-}
-
-/*!
- \internal
-
Returns a QPainterPath of \a path when stroked with the \a pen.
Ignoring dash pattern.
*/
@@ -783,6 +765,131 @@ QVariant QGraphicsItemPrivate::inputMethodQueryHelper(Qt::InputMethodQuery query
/*!
\internal
+ If \a deleting is true, then this item is being deleted, and \a parent is
+ null. Make sure not to trigger any pure virtual function calls (e.g.,
+ prepareGeometryChange).
+*/
+void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent, bool deleting)
+{
+ Q_Q(QGraphicsItem);
+ if (newParent == q) {
+ qWarning("QGraphicsItem::setParentItem: cannot assign %p as a parent of itself", this);
+ return;
+ }
+ if (newParent == parent)
+ return;
+
+ QVariant variant;
+ qVariantSetValue<QGraphicsItem *>(variant, newParent);
+ newParent = qVariantValue<QGraphicsItem *>(q->itemChange(QGraphicsItem::ItemParentChange, variant));
+ if (newParent == parent)
+ return;
+
+ if (QGraphicsWidget *w = isWidget ? static_cast<QGraphicsWidget *>(q) : q->parentWidget()) {
+ // Update the child focus chain; when reparenting a widget that has a
+ // focus child, ensure that that focus child clears its focus child
+ // chain from our parents before it's reparented.
+ if (QGraphicsWidget *focusChild = w->focusWidget())
+ focusChild->clearFocus();
+ }
+
+ // We anticipate geometry changes. If the item is deleted, it will be
+ // removed from the index at a later stage, and the whole scene will be
+ // updated.
+ if (!deleting)
+ q_ptr->prepareGeometryChange();
+
+ if (parent) {
+ // Remove from current parent
+ parent->d_ptr->removeChild(q);
+ qVariantSetValue<QGraphicsItem *>(variant, q);
+ parent->itemChange(QGraphicsItem::ItemChildRemovedChange, variant);
+ }
+
+ // Update toplevelitem list. If this item is being deleted, its parent
+ // will be 0 but we don't want to register/unregister it in the TLI list.
+ if (scene && !deleting) {
+ if (parent && !newParent) {
+ scene->d_func()->registerTopLevelItem(q);
+ } else if (!parent && newParent) {
+ scene->d_func()->unregisterTopLevelItem(q);
+ }
+ }
+
+ if ((parent = newParent)) {
+ bool implicitUpdate = false;
+ if (parent->d_func()->scene && parent->d_func()->scene != scene) {
+ // Move this item to its new parent's scene
+ parent->d_func()->scene->addItem(q);
+ implicitUpdate = true;
+ } else if (!parent->d_func()->scene && scene) {
+ // Remove this item from its former scene
+ scene->removeItem(q);
+ }
+
+ parent->d_ptr->addChild(q);
+ qVariantSetValue<QGraphicsItem *>(variant, q);
+ parent->itemChange(QGraphicsItem::ItemChildAddedChange, variant);
+ if (!implicitUpdate)
+ updateHelper();
+
+ // Inherit ancestor flags from the new parent.
+ updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1));
+ updateAncestorFlag(QGraphicsItem::ItemClipsChildrenToShape);
+ updateAncestorFlag(QGraphicsItem::ItemIgnoresTransformations);
+
+ // Update item visible / enabled.
+ if (parent->isVisible() != visible) {
+ if (!parent->isVisible() || !explicitlyHidden)
+ setVisibleHelper(parent->isVisible(), /* explicit = */ false, /* update = */ !implicitUpdate);
+ }
+ if (parent->isEnabled() != enabled) {
+ if (!parent->isEnabled() || !explicitlyDisabled)
+ setEnabledHelper(parent->isEnabled(), /* explicit = */ false, /* update = */ !implicitUpdate);
+ }
+
+ } else {
+ // Inherit ancestor flags from the new parent.
+ updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1));
+ updateAncestorFlag(QGraphicsItem::ItemClipsChildrenToShape);
+ updateAncestorFlag(QGraphicsItem::ItemIgnoresTransformations);
+
+ // Update item visible / enabled.
+ if (!visible && !explicitlyHidden)
+ setVisibleHelper(true, /* explicit = */ false);
+ if (!enabled && !explicitlyDisabled)
+ setEnabledHelper(true, /* explicit = */ false);
+
+ // If the item is being deleted, the whole scene will be updated.
+ if (!deleting)
+ updateHelper();
+ }
+
+ if (scene) {
+ // Invalidate any sort caching; arrival of a new item means we need to
+ // resort.
+ scene->d_func()->invalidateSortCache();
+ }
+
+ // Resolve opacity.
+ if (parent)
+ resolveEffectiveOpacity(parent->effectiveOpacity());
+ else
+ resolveEffectiveOpacity(1.0);
+
+ // Resolve depth.
+ resolveDepth(parent ? parent->d_ptr->depth : -1);
+
+ // Invalidate transform cache.
+ invalidateSceneTransformCache();
+
+ // Deliver post-change notification
+ q->itemChange(QGraphicsItem::ItemParentHasChanged, qVariantFromValue<QGraphicsItem *>(parent));
+}
+
+/*!
+ \internal
+
Empty all cached pixmaps from the pixmap cache.
*/
void QGraphicsItemCache::purge()
@@ -855,24 +962,17 @@ QGraphicsItem::QGraphicsItem(QGraphicsItemPrivate &dd, QGraphicsItem *parent,
*/
QGraphicsItem::~QGraphicsItem()
{
+ if (d_ptr->scene && !d_ptr->parent)
+ d_ptr->scene->d_func()->unregisterTopLevelItem(this);
+
clearFocus();
- d_ptr->removeExtraItemCache();
- QVariant variant;
- foreach (QGraphicsItem *child, d_ptr->children) {
- if (QGraphicsItem *parent = child->parentItem()) {
- qVariantSetValue<QGraphicsItem *>(variant, child);
- parent->itemChange(ItemChildRemovedChange, variant);
- }
- delete child;
- }
- d_ptr->children.clear();
+ d_ptr->removeExtraItemCache();
+ QList<QGraphicsItem *> oldChildren = d_ptr->children;
+ qDeleteAll(oldChildren);
+ Q_ASSERT(d_ptr->children.isEmpty());
- if (QGraphicsItem *parent = parentItem()) {
- qVariantSetValue<QGraphicsItem *>(variant, this);
- parent->itemChange(ItemChildRemovedChange, variant);
- qt_graphicsitem_removeChild(this, &parent->d_func()->children);
- }
+ d_ptr->setParentItemHelper(0, /* deleting = */ true);
if (d_ptr->scene)
d_ptr->scene->d_func()->_q_removeItemLater(this);
@@ -1017,103 +1117,7 @@ QGraphicsWidget *QGraphicsItem::window() const
*/
void QGraphicsItem::setParentItem(QGraphicsItem *parent)
{
- if (parent == this) {
- qWarning("QGraphicsItem::setParentItem: cannot assign %p as a parent of itself", this);
- return;
- }
- if (parent == d_ptr->parent)
- return;
- QVariant variant;
- qVariantSetValue<QGraphicsItem *>(variant, parent);
- parent = qVariantValue<QGraphicsItem *>(itemChange(ItemParentChange, variant));
- if (parent == d_ptr->parent)
- return;
-
- if (QGraphicsWidget *w = d_ptr->isWidget ? static_cast<QGraphicsWidget *>(this) : parentWidget()) {
- // Update the child focus chain; when reparenting a widget that has a
- // focus child, ensure that that focus child clears its focus child
- // chain from our parents before it's reparented.
- if (QGraphicsWidget *focusChild = w->focusWidget())
- focusChild->clearFocus();
- }
-
- // We anticipate geometry changes
- prepareGeometryChange();
-
- if (d_ptr->parent) {
- // Remove from current parent
- qt_graphicsitem_removeChild(this, &d_ptr->parent->d_func()->children);
- qVariantSetValue<QGraphicsItem *>(variant, this);
- d_ptr->parent->itemChange(ItemChildRemovedChange, variant);
- }
-
- if ((d_ptr->parent = parent)) {
- bool implicitUpdate = false;
- if (parent->d_func()->scene && parent->d_func()->scene != d_ptr->scene) {
- // Move this item to its new parent's scene
- parent->d_func()->scene->addItem(this);
- implicitUpdate = true;
- } else if (!parent->d_func()->scene && d_ptr->scene) {
- // Remove this item from its former scene
- d_ptr->scene->removeItem(this);
- }
-
- d_ptr->parent->d_func()->children << this;
- qVariantSetValue<QGraphicsItem *>(variant, this);
- d_ptr->parent->itemChange(ItemChildAddedChange, variant);
- if (!implicitUpdate)
- d_ptr->updateHelper();
-
- // Inherit ancestor flags from the new parent.
- d_ptr->updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1));
- d_ptr->updateAncestorFlag(ItemClipsChildrenToShape);
- d_ptr->updateAncestorFlag(ItemIgnoresTransformations);
-
- // Update item visible / enabled.
- if (d_ptr->parent->isVisible() != d_ptr->visible) {
- if (!d_ptr->parent->isVisible() || !d_ptr->explicitlyHidden)
- d_ptr->setVisibleHelper(d_ptr->parent->isVisible(), /* explicit = */ false, /* update = */ !implicitUpdate);
- }
- if (d_ptr->parent->isEnabled() != d_ptr->enabled) {
- if (!d_ptr->parent->isEnabled() || !d_ptr->explicitlyDisabled)
- d_ptr->setEnabledHelper(d_ptr->parent->isEnabled(), /* explicit = */ false, /* update = */ !implicitUpdate);
- }
-
- } else {
- // Inherit ancestor flags from the new parent.
- d_ptr->updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1));
- d_ptr->updateAncestorFlag(ItemClipsChildrenToShape);
- d_ptr->updateAncestorFlag(ItemIgnoresTransformations);
-
- // Update item visible / enabled.
- if (!d_ptr->visible && !d_ptr->explicitlyHidden)
- d_ptr->setVisibleHelper(true, /* explicit = */ false);
- if (!d_ptr->enabled && !d_ptr->explicitlyDisabled)
- d_ptr->setEnabledHelper(true, /* explicit = */ false);
-
- d_ptr->updateHelper();
- }
-
- if (d_ptr->scene) {
- // Invalidate any sort caching; arrival of a new item means we need to
- // resort.
- d_ptr->scene->d_func()->invalidateSortCache();
- }
-
- // Resolve opacity.
- if (QGraphicsItem *p = d_ptr->parent)
- d_ptr->resolveEffectiveOpacity(p->effectiveOpacity());
- else
- d_ptr->resolveEffectiveOpacity(1.0);
-
- // Resolve depth.
- d_ptr->resolveDepth(parent ? parent->d_ptr->depth : -1);
-
- // Invalidate transform cache.
- d_ptr->invalidateSceneTransformCache();
-
- // Deliver post-change notification
- itemChange(QGraphicsItem::ItemParentHasChanged, qVariantFromValue<QGraphicsItem *>(parent));
+ d_ptr->setParentItemHelper(parent, /* deleting = */ false);
}
/*!
@@ -2930,11 +2934,11 @@ qreal QGraphicsItem::zValue() const
/*!
Sets the Z-value, or the elevation, of the item, to \a z. The elevation
decides the stacking order of sibling (neighboring) items. An item of high
- Z-value will be drawn on top of an item with a lower Z-value if they
- share the same parent item. In addition, children of an item will always be drawn
- on top of the parent, regardless of the child's Z-value. Sibling items
- that share the same Z-value will be drawn in an undefined order, although
- the order will stay the same for as long as the items live.
+ Z-value will be drawn on top of an item with a lower Z-value if they share
+ the same parent item. In addition, children of an item will always be
+ drawn on top of the parent, regardless of the child's Z-value. Sibling
+ items that share the same Z-value will be drawn in order of insertion; the
+ last inserted child is stacked above previous children.
\img graphicsview-zorder.png
@@ -2961,7 +2965,7 @@ void QGraphicsItem::setZValue(qreal z)
qreal newZ = qreal(itemChange(ItemZValueChange, double(z)).toDouble());
if (newZ == d_ptr->z)
return;
- d_ptr->z = z;
+ d_ptr->z = newZ;
d_ptr->fullUpdateHelper();
if (d_ptr->scene) {
@@ -3589,15 +3593,27 @@ void QGraphicsItem::setBoundingRegionGranularity(qreal granularity)
only case where the item's background should be marked as dirty even when
the item isn't visible.
*/
-void QGraphicsItemPrivate::updateHelper(const QRectF &rect, bool force)
+void QGraphicsItemPrivate::updateHelper(const QRectF &rect, bool force, bool updateCache)
{
// No scene, or if the scene is updating everything, means we have nothing
// to do. The only exception is if the scene tracks the growing scene rect.
- if (dirty)
+ QGraphicsItemCache *cache = maybeExtraItemCache();
+ if (dirty && (!updateCache || !cache || cache->allExposed))
return;
+
if (!scene || (scene && scene->d_func()->updateAll && scene->d_func()->hasSceneRect))
return;
- if (scene && (visible || force)) {
+
+ if (updateCache && QGraphicsItem::CacheMode(cacheMode) != QGraphicsItem::NoCache) {
+ if (rect.isNull()) {
+ cache->allExposed = true;
+ cache->exposed.clear();
+ } else {
+ cache->exposed.append(rect);
+ }
+ }
+
+ if (!dirty && scene && (visible || force)) {
if (rect.isNull())
dirty = 1;
scene->itemUpdated(q_ptr, rect);
@@ -3706,6 +3722,41 @@ void QGraphicsItemPrivate::invalidateSceneTransformCache()
children.at(i)->d_ptr->invalidateSceneTransformCache();
}
+/*!
+ \internal
+*/
+void QGraphicsItemPrivate::addChild(QGraphicsItem *child)
+{
+ child->d_ptr->siblingIndex = children.size();
+ children.append(child);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsItemPrivate::removeChild(QGraphicsItem *child)
+{
+ int idx = child->d_ptr->siblingIndex;
+ int size = children.size();
+ for (int i = idx; i < size - 1; ++i) {
+ QGraphicsItem *p = children[i + 1];
+ children[i] = p;
+ p->d_ptr->siblingIndex = i;
+ }
+ children.removeLast();
+}
+
+/*!
+ \internal
+*/
+QGraphicsItemCache *QGraphicsItemPrivate::maybeExtraItemCache() const
+{
+ return (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData));
+}
+
+/*!
+ \internal
+*/
QGraphicsItemCache *QGraphicsItemPrivate::extraItemCache() const
{
QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData));
@@ -3717,6 +3768,9 @@ QGraphicsItemCache *QGraphicsItemPrivate::extraItemCache() const
return c;
}
+/*!
+ \internal
+*/
void QGraphicsItemPrivate::removeExtraItemCache()
{
QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData));
@@ -3757,22 +3811,48 @@ bool QGraphicsItemPrivate::isProxyWidget() const
*/
void QGraphicsItem::update(const QRectF &rect)
{
- if (d_ptr->dirty)
- return;
- if (d_ptr->scene && isVisible()) {
- if (CacheMode(d_ptr->cacheMode) != NoCache) {
- QGraphicsItemCache *cache = d_ptr->extraItemCache();
- if (rect.isNull()) {
- cache->allExposed = true;
- cache->exposed.clear();
- } else {
- cache->exposed.append(rect);
- }
- }
- d_ptr->updateHelper(rect);
- }
+ d_ptr->updateHelper(rect, /* force = */ false, /* updateCache = */ true);
}
+/*!
+ \internal
+
+ Scrolls \a rect in \a pix by \a dx, \a dy.
+
+ ### This can be done much more efficiently by using XCopyArea on X11 with
+ the same dst and src, and through moving pixels in the raster engine. It
+ can probably also be done much better on the other paint engines.
+*/
+void _q_scrollPixmap(QPixmap *pix, const QRect &rect, int dx, int dy)
+{
+#if 0
+ QPainter painter(pix);
+ painter.setClipRect(rect);
+ painter.drawPixmap(rect.translated(dx, dy), *pix, rect);
+ painter.end();
+#elif defined Q_WS_X11
+ GC gc = XCreateGC(X11->display, pix->handle(), 0, 0);
+
+ XRectangle xrect;
+ xrect.x = rect.x();
+ xrect.y = rect.y();
+ xrect.width = rect.width();
+ xrect.height = rect.height();
+ XSetClipRectangles(X11->display, gc, 0, 0, &xrect, 1, YXBanded);
+
+ XCopyArea(X11->display, pix->handle(), pix->handle(), gc,
+ rect.x(), rect.y(), rect.width(), rect.height(),
+ rect.x()+dx, rect.y()+dy);
+ XFreeGC(X11->display, gc);
+#else
+ QPixmap newPix = *pix;
+ QPainter painter(&newPix);
+ painter.setClipRect(rect);
+ painter.drawPixmap(rect.translated(dx, dy), *pix, rect);
+ painter.end();
+ *pix = newPix;
+#endif
+}
/*!
\since 4.4
@@ -3801,11 +3881,45 @@ void QGraphicsItem::scroll(qreal dx, qreal dy, const QRectF &rect)
if (!d->scene)
return;
if (d->cacheMode != NoCache) {
- // ### This is very slow, and can be done much better. If the cache is
- // local and matches the below criteria for rotation and scaling, we
- // can easily scroll. And if the cache is in device coordinates, we
- // can scroll both the viewport and the cache.
- update(rect);
+ QGraphicsItemCache *c;
+ bool scrollCache = qFuzzyCompare(dx - int(dx), qreal(0.0)) && qFuzzyCompare(dy - int(dy), qreal(0.0))
+ && (c = (QGraphicsItemCache *)qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraCacheData)))
+ && (d->cacheMode == ItemCoordinateCache && !c->fixedSize.isValid());
+ if (scrollCache) {
+ QPixmap pix;
+ if (QPixmapCache::find(c->key, pix)) {
+ // Adjust with 2 pixel margin. Notice the loss of precision
+ // when converting to QRect.
+ int adjust = 2;
+ QRectF br = boundingRect().adjusted(-adjust, -adjust, adjust, adjust);
+ QRect irect = rect.toRect().translated(-br.x(), -br.y());
+
+ _q_scrollPixmap(&pix, irect, dx, dy);
+
+ QPixmapCache::insert(c->key, pix);
+
+ // Translate the existing expose.
+ foreach (QRectF exposedRect, c->exposed)
+ c->exposed += exposedRect.translated(dx, dy) & rect;
+
+ // Calculate exposure.
+ QRegion exposed;
+ QRect r = rect.toRect();
+ exposed += r;
+ exposed -= r.translated(dx, dy);
+ foreach (QRect rect, exposed.rects())
+ update(rect);
+ d_ptr->updateHelper();
+ } else {
+ update(rect);
+ }
+ } else {
+ // ### This is very slow, and can be done much better. If the cache is
+ // local and matches the below criteria for rotation and scaling, we
+ // can easily scroll. And if the cache is in device coordinates, we
+ // can scroll both the viewport and the cache.
+ update(rect);
+ }
return;
}