summaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/animation/qguivariantanimation.cpp24
-rw-r--r--src/gui/effects/effects.pri4
-rw-r--r--src/gui/effects/qgraphicseffect.cpp572
-rw-r--r--src/gui/effects/qgraphicseffect.h277
-rw-r--r--src/gui/effects/qgraphicseffect_p.h166
-rw-r--r--src/gui/egl/qegl.cpp2
-rw-r--r--src/gui/egl/qegl_p.h4
-rw-r--r--src/gui/egl/qeglproperties.cpp14
-rw-r--r--src/gui/egl/qeglproperties_p.h2
-rw-r--r--src/gui/embedded/qscreen_qws.cpp22
-rw-r--r--src/gui/embedded/qscreenlinuxfb_qws.cpp16
-rw-r--r--src/gui/graphicsview/graphicsview.pri12
-rw-r--r--src/gui/graphicsview/qgraph_p.h240
-rw-r--r--src/gui/graphicsview/qgraphicsanchorlayout.cpp454
-rw-r--r--src/gui/graphicsview/qgraphicsanchorlayout.h141
-rw-r--r--src/gui/graphicsview/qgraphicsanchorlayout_p.cpp2104
-rw-r--r--src/gui/graphicsview/qgraphicsanchorlayout_p.h477
-rw-r--r--src/gui/graphicsview/qgraphicsitem.cpp284
-rw-r--r--src/gui/graphicsview/qgraphicsitem.h6
-rw-r--r--src/gui/graphicsview/qgraphicsitem_p.h91
-rw-r--r--src/gui/graphicsview/qgraphicsscene.cpp74
-rw-r--r--src/gui/graphicsview/qgraphicsscene.h2
-rw-r--r--src/gui/graphicsview/qgraphicsscene_p.h25
-rw-r--r--src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp4
-rw-r--r--src/gui/graphicsview/qgraphicstransform.cpp216
-rw-r--r--src/gui/graphicsview/qgraphicstransform.h26
-rw-r--r--src/gui/graphicsview/qsimplex_p.cpp371
-rw-r--r--src/gui/graphicsview/qsimplex_p.h125
-rw-r--r--src/gui/gui.pro1
-rw-r--r--src/gui/image/qpixmap.cpp12
-rw-r--r--src/gui/image/qpixmap_mac.cpp14
-rw-r--r--src/gui/image/qpixmap_win.cpp2
-rw-r--r--src/gui/image/qpixmap_x11.cpp8
-rw-r--r--src/gui/image/qpixmapfilter.cpp271
-rw-r--r--src/gui/image/qpixmapfilter_p.h29
-rw-r--r--src/gui/kernel/qapplication_p.h12
-rw-r--r--src/gui/kernel/qapplication_win.cpp128
-rw-r--r--src/gui/kernel/qgesture.cpp37
-rw-r--r--src/gui/kernel/qgesture.h8
-rw-r--r--src/gui/kernel/qgesture_p.h5
-rw-r--r--src/gui/kernel/qstandardgestures.cpp126
-rw-r--r--src/gui/kernel/qstandardgestures.h4
-rw-r--r--src/gui/kernel/qstandardgestures_p.h5
-rw-r--r--src/gui/kernel/qwidget.cpp203
-rw-r--r--src/gui/kernel/qwidget.h4
-rw-r--r--src/gui/kernel/qwidget_mac.mm8
-rw-r--r--src/gui/kernel/qwidget_p.h60
-rw-r--r--src/gui/kernel/qwidget_qws.cpp23
-rw-r--r--src/gui/kernel/qwidget_win.cpp27
-rw-r--r--src/gui/kernel/qwidget_x11.cpp8
-rw-r--r--src/gui/math3d/qmatrix4x4.cpp62
-rw-r--r--src/gui/math3d/qmatrix4x4.h5
-rw-r--r--src/gui/math3d/qquaternion.cpp9
-rw-r--r--src/gui/math3d/qquaternion.h3
-rw-r--r--src/gui/math3d/qvector2d.cpp9
-rw-r--r--src/gui/math3d/qvector2d.h3
-rw-r--r--src/gui/math3d/qvector3d.cpp9
-rw-r--r--src/gui/math3d/qvector3d.h2
-rw-r--r--src/gui/math3d/qvector4d.cpp9
-rw-r--r--src/gui/math3d/qvector4d.h2
-rw-r--r--src/gui/painting/qbackingstore.cpp37
-rw-r--r--src/gui/painting/qbackingstore_p.h8
-rw-r--r--src/gui/painting/qblendfunctions.cpp4
-rw-r--r--src/gui/painting/qgrayraster.c5
-rw-r--r--src/gui/painting/qpaintdevice_mac.cpp2
-rw-r--r--src/gui/painting/qpaintengine.h1
-rw-r--r--src/gui/painting/qpaintengine_p.h4
-rw-r--r--src/gui/painting/qpaintengine_x11.cpp20
-rw-r--r--src/gui/painting/qpainter.cpp8
-rw-r--r--src/gui/painting/qwindowsurface_x11.cpp2
-rw-r--r--src/gui/text/qtextdocument_p.cpp52
-rw-r--r--src/gui/text/qtextdocument_p.h4
-rw-r--r--src/gui/widgets/qabstractscrollarea.cpp7
73 files changed, 6530 insertions, 487 deletions
diff --git a/src/gui/animation/qguivariantanimation.cpp b/src/gui/animation/qguivariantanimation.cpp
index 9badc82ee..de9363c4a 100644
--- a/src/gui/animation/qguivariantanimation.cpp
+++ b/src/gui/animation/qguivariantanimation.cpp
@@ -45,6 +45,10 @@
#include <private/qvariantanimation_p.h>
#include <QtGui/qcolor.h>
+#include <QtGui/qvector2d.h>
+#include <QtGui/qvector3d.h>
+#include <QtGui/qvector4d.h>
+#include <QtGui/qquaternion.h>
QT_BEGIN_NAMESPACE
@@ -56,17 +60,35 @@ template<> Q_INLINE_TEMPLATE QColor _q_interpolate(const QColor &f,const QColor
_q_interpolate(f.alpha(), t.alpha(), progress));
}
+template<> Q_INLINE_TEMPLATE QQuaternion _q_interpolate(const QQuaternion &f,const QQuaternion &t, qreal progress)
+{
+ return QQuaternion::slerp(f, t, progress);
+}
+
static int qRegisterGuiGetInterpolator()
{
qRegisterAnimationInterpolator<QColor>(_q_interpolateVariant<QColor>);
+ qRegisterAnimationInterpolator<QVector2D>(_q_interpolateVariant<QVector2D>);
+ qRegisterAnimationInterpolator<QVector3D>(_q_interpolateVariant<QVector3D>);
+ qRegisterAnimationInterpolator<QVector4D>(_q_interpolateVariant<QVector4D>);
+ qRegisterAnimationInterpolator<QQuaternion>(_q_interpolateVariant<QQuaternion>);
return 1;
}
Q_CONSTRUCTOR_FUNCTION(qRegisterGuiGetInterpolator)
static int qUnregisterGuiGetInterpolator()
{
+ // casts required by Sun CC 5.5
qRegisterAnimationInterpolator<QColor>(
- (QVariant (*)(const QColor &, const QColor &, qreal))0); // cast required by Sun CC 5.5
+ (QVariant (*)(const QColor &, const QColor &, qreal))0);
+ qRegisterAnimationInterpolator<QVector2D>(
+ (QVariant (*)(const QVector2D &, const QVector2D &, qreal))0);
+ qRegisterAnimationInterpolator<QVector3D>(
+ (QVariant (*)(const QVector3D &, const QVector3D &, qreal))0);
+ qRegisterAnimationInterpolator<QVector4D>(
+ (QVariant (*)(const QVector4D &, const QVector4D &, qreal))0);
+ qRegisterAnimationInterpolator<QQuaternion>(
+ (QVariant (*)(const QQuaternion &, const QQuaternion &, qreal))0);
return 1;
}
diff --git a/src/gui/effects/effects.pri b/src/gui/effects/effects.pri
new file mode 100644
index 000000000..0ebf96fcc
--- /dev/null
+++ b/src/gui/effects/effects.pri
@@ -0,0 +1,4 @@
+HEADERS += effects/qgraphicseffect.h \
+ effects/qgraphicseffect_p.h
+
+SOURCES += effects/qgraphicseffect.cpp
diff --git a/src/gui/effects/qgraphicseffect.cpp b/src/gui/effects/qgraphicseffect.cpp
new file mode 100644
index 000000000..949922a11
--- /dev/null
+++ b/src/gui/effects/qgraphicseffect.cpp
@@ -0,0 +1,572 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGraphicsEffect
+ \brief The QGraphicsEffect class is the base class for all graphics effects.
+ \since 4.6
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ Effects alter the appearance of elements by hooking into the rendering
+ pipeline and operating between the source (e.g., a QGraphicsPixmapItem),
+ and the destination device (e.g., QGraphicsView's viewport). Effects can be
+ disabled by calling setEnabled(); if the effect is disabled, the source is
+ rendered directly.
+
+ If you want to add a visual effect to a QGraphicsItem, you can either use
+ one of the standard effects, or create your own effect by making a subclass
+ of QGraphicsEffect.
+
+ Qt provides several standard effects, including:
+
+ \list
+ \o QGraphicsGrayScaleEffect - renders the item in shades of gray
+ \o QGraphicsColorizeEffect - renders the item in shades of any given color
+ \o QGraphicsPixelizeEffect - pixelizes the item with any pixel size
+ \o QGraphicsBlurEffect - blurs the item by a given radius
+ \o QGraphicsDropShadowEffect - renders a dropshadow behind the item
+ \endlist
+
+ If all you want is to add an effect to an item, you should visit the
+ documentation for the specific effect to learn more about how each effect
+ can be used.
+
+ If you want to create your own custom effect, you can start by creating a
+ subclass of QGraphicsEffect (or any of the existing effects), and
+ reimplement the virtual function draw(). This function is called whenever
+ the effect needs to redraw. draw() has two arguments: the painter, and a
+ pointer to the source (QGraphicsEffectSource). The source provides extra
+ context information, such as a pointer to the item that is rendering the
+ effect, any cached pixmap data, and the device rect bounds. See the draw()
+ documentation for more details. You can also get a pointer to the current
+ source by calling source().
+
+ If your effect changes, you can call update() to request a redraw. If your
+ custom effect changes the bounding rectangle of the source (e.g., a radial
+ glow effect may need to apply an extra margin), you can reimplement the
+ virtual boundingRectFor() function, and call updateBoundingRect() to notify
+ the framework whenever this rectangle changes. The virtual
+ sourceBoundingRectChanged() function is called to notify the effects that
+ the source's bounding rectangle has changed (e.g., if the source is a
+ QGraphicsRectItem, then if the rectangle parameters have changed).
+
+ \sa QGraphicsItem::setGraphicsEffect()
+*/
+
+#include "qgraphicseffect_p.h"
+
+#include <QtGui/qimage.h>
+#include <QtGui/qpainter.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+QGraphicsEffectSource::QGraphicsEffectSource(QGraphicsEffectSourcePrivate &dd, QObject *parent)
+ : QObject(dd, parent)
+{}
+
+QGraphicsEffectSource::~QGraphicsEffectSource()
+{}
+
+QRect QGraphicsEffectSource::deviceRect() const
+{
+ return d_func()->deviceRect();
+}
+
+QRectF QGraphicsEffectSource::boundingRect(Qt::CoordinateSystem system) const
+{
+ return d_func()->boundingRect(system);
+}
+
+const QGraphicsItem *QGraphicsEffectSource::graphicsItem() const
+{
+ return d_func()->graphicsItem();
+}
+
+const QWidget *QGraphicsEffectSource::widget() const
+{
+ return d_func()->widget();
+}
+
+const QStyleOption *QGraphicsEffectSource::styleOption() const
+{
+ return d_func()->styleOption();
+}
+
+void QGraphicsEffectSource::draw(QPainter *painter)
+{
+ d_func()->draw(painter);
+}
+
+void QGraphicsEffectSource::update()
+{
+ d_func()->update();
+}
+
+bool QGraphicsEffectSource::isPixmap() const
+{
+ return d_func()->isPixmap();
+}
+
+QPixmap QGraphicsEffectSource::pixmap(Qt::CoordinateSystem system, QPoint *offset) const
+{
+ return d_func()->pixmap(system, offset);
+}
+
+/*!
+ Constructs a new QGraphicsEffect instance.
+*/
+QGraphicsEffect::QGraphicsEffect(QObject *parent)
+ : QObject(*new QGraphicsEffectPrivate, parent)
+{
+}
+
+/*!
+ \internal
+*/
+QGraphicsEffect::QGraphicsEffect(QGraphicsEffectPrivate &dd, QObject *parent)
+ : QObject(dd, parent)
+{
+}
+
+/*!
+ Removes the effect from the source, and destroys the graphics effect.
+*/
+QGraphicsEffect::~QGraphicsEffect()
+{
+ Q_D(QGraphicsEffect);
+ d->setGraphicsEffectSource(0);
+}
+
+/*!
+ Returns the bounding rectangle for this effect (i.e., the bounding
+ rectangle of the source, adjusted by any margins applied by the effect
+ itself).
+
+ \sa boundingRectFor(), updateBoundingRect()
+*/
+QRectF QGraphicsEffect::boundingRect() const
+{
+ Q_D(const QGraphicsEffect);
+ if (d->source)
+ return boundingRectFor(d->source->boundingRect());
+ return QRectF();
+}
+
+/*!
+ Returns the bounding rectangle for this effect, given the provided source
+ \a rect. When writing you own custom effect, you must call
+ updateBoundingRect() whenever any parameters are changed that may cause
+ this this function to return a different value.
+
+ \sa boundingRect()
+*/
+QRectF QGraphicsEffect::boundingRectFor(const QRectF &rect) const
+{
+ return rect;
+}
+
+/*!
+ \property QGraphicsEffect::enabled
+ \brief whether the effect is enabled or not.
+
+ If an effect is disabled, the source will be rendered with as normal, with
+ no interference from the effect. If the effect is enabled (default), the
+ source will be rendered with the effect applied.
+
+ This property is provided so that you can disable certain effects on slow
+ platforms, in order to ensure that the user interface is responsive.
+*/
+bool QGraphicsEffect::isEnabled() const
+{
+ Q_D(const QGraphicsEffect);
+ return d->isEnabled;
+}
+
+void QGraphicsEffect::setEnabled(bool enable)
+{
+ Q_D(QGraphicsEffect);
+ if (d->isEnabled == enable)
+ return;
+
+ d->isEnabled = enable;
+ if (d->source)
+ d->source->update();
+ emit enabledChanged(enable);
+}
+
+/*!
+ Returns a pointer to the source, which provides extra context information
+ that can be useful for the effect.
+
+ \sa draw()
+*/
+QGraphicsEffectSource *QGraphicsEffect::source() const
+{
+ Q_D(const QGraphicsEffect);
+ return d->source;
+}
+
+/*!
+ This function notifies the effect framework that the effect's bounding
+ rectangle has changed. As a custom effect author, you must call this
+ function whenever you change any parameters that will cause the virtual
+ boundingRectFor() function to return a different value.
+
+ \sa boundingRectFor(), boundingRect()
+*/
+void QGraphicsEffect::updateBoundingRect()
+{
+ Q_D(QGraphicsEffect);
+ if (d->source)
+ d->source->update();
+}
+
+/*!
+ This virtual function is called by QGraphicsEffect to notify the effect
+ that the source has changed. If the effect applies any cache, then this
+ cache must be purged in order to reflect the new appearance of the source.
+
+ The \a flags describes what has changed.
+*/
+void QGraphicsEffect::sourceChanged(ChangeFlags flags)
+{
+ Q_UNUSED(flags);
+}
+
+QGraphicsGrayscaleEffect::QGraphicsGrayscaleEffect(QObject *parent)
+ : QGraphicsEffect(*new QGraphicsGrayscaleEffectPrivate, parent)
+{
+}
+
+QGraphicsGrayscaleEffect::~QGraphicsGrayscaleEffect()
+{
+}
+
+void QGraphicsGrayscaleEffect::draw(QPainter *painter, QGraphicsEffectSource *source)
+{
+ Q_D(QGraphicsGrayscaleEffect);
+ QPoint offset;
+ if (source->isPixmap()) {
+ // No point in drawing in device coordinates (pixmap will be scaled anyways).
+ const QPixmap pixmap = source->pixmap(Qt::LogicalCoordinates, &offset);
+ d->filter->draw(painter, offset, pixmap);
+ return;
+ }
+
+ // Draw pixmap in device coordinates to avoid pixmap scaling;
+ const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset);
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+ d->filter->draw(painter, offset, pixmap);
+ painter->setWorldTransform(restoreTransform);
+
+}
+
+QGraphicsColorizeEffect::QGraphicsColorizeEffect(QObject *parent)
+ : QGraphicsEffect(*new QGraphicsColorizeEffectPrivate, parent)
+{
+}
+
+QGraphicsColorizeEffect::~QGraphicsColorizeEffect()
+{
+}
+
+QColor QGraphicsColorizeEffect::color() const
+{
+ Q_D(const QGraphicsColorizeEffect);
+ return d->filter->color();
+}
+
+void QGraphicsColorizeEffect::setColor(const QColor &c)
+{
+ Q_D(QGraphicsColorizeEffect);
+ if (d->filter->color() == c)
+ return;
+
+ d->filter->setColor(c);
+ emit colorChanged(c);
+}
+
+void QGraphicsColorizeEffect::draw(QPainter *painter, QGraphicsEffectSource *source)
+{
+ Q_D(QGraphicsColorizeEffect);
+ QPoint offset;
+ if (source->isPixmap()) {
+ // No point in drawing in device coordinates (pixmap will be scaled anyways).
+ const QPixmap pixmap = source->pixmap(Qt::LogicalCoordinates, &offset);
+ d->filter->draw(painter, offset, pixmap);
+ return;
+ }
+
+ // Draw pixmap in deviceCoordinates to avoid pixmap scaling.
+ const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset);
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+ d->filter->draw(painter, offset, pixmap);
+ painter->setWorldTransform(restoreTransform);
+}
+
+QGraphicsPixelizeEffect::QGraphicsPixelizeEffect(QObject *parent)
+ : QGraphicsEffect(*new QGraphicsPixelizeEffectPrivate, parent)
+{
+}
+
+QGraphicsPixelizeEffect::~QGraphicsPixelizeEffect()
+{
+}
+
+int QGraphicsPixelizeEffect::pixelSize() const
+{
+ Q_D(const QGraphicsPixelizeEffect);
+ return d->pixelSize;
+}
+
+void QGraphicsPixelizeEffect::setPixelSize(int size)
+{
+ Q_D(QGraphicsPixelizeEffect);
+ if (d->pixelSize == size)
+ return;
+
+ d->pixelSize = size;
+ emit pixelSizeChanged(size);
+}
+
+static inline void pixelize(QImage *image, int pixelSize)
+{
+ Q_ASSERT(pixelSize > 0);
+ Q_ASSERT(image);
+ int width = image->width();
+ int height = image->height();
+ for (int y = 0; y < height; y += pixelSize) {
+ int ys = qMin(height - 1, y + pixelSize / 2);
+ QRgb *sbuf = reinterpret_cast<QRgb*>(image->scanLine(ys));
+ for (int x = 0; x < width; x += pixelSize) {
+ int xs = qMin(width - 1, x + pixelSize / 2);
+ QRgb color = sbuf[xs];
+ for (int yi = 0; yi < qMin(pixelSize, height - y); ++yi) {
+ QRgb *buf = reinterpret_cast<QRgb*>(image->scanLine(y + yi));
+ for (int xi = 0; xi < qMin(pixelSize, width - x); ++xi)
+ buf[x + xi] = color;
+ }
+ }
+ }
+}
+
+void QGraphicsPixelizeEffect::draw(QPainter *painter, QGraphicsEffectSource *source)
+{
+ Q_D(QGraphicsPixelizeEffect);
+ if (d->pixelSize <= 0) {
+ source->draw(painter);
+ return;
+ }
+
+ QPoint offset;
+ if (source->isPixmap()) {
+ const QPixmap pixmap = source->pixmap(Qt::LogicalCoordinates, &offset);
+ QImage image = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
+ pixelize(&image, d->pixelSize);
+ painter->drawImage(offset, image);
+ return;
+ }
+
+ // Draw pixmap in device coordinates to avoid pixmap scaling.
+ const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset);
+
+ // pixelize routine
+ QImage image = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
+ pixelize(&image, d->pixelSize);
+
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+ painter->drawImage(offset, image);
+ painter->setWorldTransform(restoreTransform);
+}
+
+QGraphicsBlurEffect::QGraphicsBlurEffect(QObject *parent)
+ : QGraphicsEffect(*new QGraphicsBlurEffectPrivate, parent)
+{
+}
+
+QGraphicsBlurEffect::~QGraphicsBlurEffect()
+{
+}
+
+int QGraphicsBlurEffect::blurRadius() const
+{
+ Q_D(const QGraphicsBlurEffect);
+ return d->filter->radius();
+}
+
+void QGraphicsBlurEffect::setBlurRadius(int radius)
+{
+ Q_D(QGraphicsBlurEffect);
+ if (d->filter->radius() == radius)
+ return;
+
+ d->filter->setRadius(radius);
+ updateBoundingRect();
+ emit blurRadiusChanged(radius);
+}
+
+QRectF QGraphicsBlurEffect::boundingRectFor(const QRectF &rect) const
+{
+ Q_D(const QGraphicsBlurEffect);
+ return d->filter->boundingRectFor(rect);
+}
+
+void QGraphicsBlurEffect::draw(QPainter *painter, QGraphicsEffectSource *source)
+{
+ Q_D(QGraphicsBlurEffect);
+ if (d->filter->radius() <= 0) {
+ source->draw(painter);
+ return;
+ }
+
+ QPoint offset;
+ if (source->isPixmap()) {
+ // No point in drawing in device coordinates (pixmap will be scaled anyways).
+ const QPixmap pixmap = source->pixmap(Qt::LogicalCoordinates, &offset);
+ d->filter->draw(painter, offset, pixmap);
+ return;
+ }
+
+ // Draw pixmap in device coordinates to avoid pixmap scaling.
+ const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset);
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+ d->filter->draw(painter, offset, pixmap);
+ painter->setWorldTransform(restoreTransform);
+}
+
+QGraphicsDropShadowEffect::QGraphicsDropShadowEffect(QObject *parent)
+ : QGraphicsEffect(*new QGraphicsDropShadowEffectPrivate, parent)
+{
+}
+
+QGraphicsDropShadowEffect::~QGraphicsDropShadowEffect()
+{
+}
+
+QPointF QGraphicsDropShadowEffect::offset() const
+{
+ Q_D(const QGraphicsDropShadowEffect);
+ return d->filter->offset();
+}
+
+void QGraphicsDropShadowEffect::setOffset(const QPointF &ofs)
+{
+ Q_D(QGraphicsDropShadowEffect);
+ if (d->filter->offset() == ofs)
+ return;
+
+ d->filter->setOffset(ofs);
+ updateBoundingRect();
+ emit offsetChanged(ofs);
+}
+
+int QGraphicsDropShadowEffect::blurRadius() const
+{
+ Q_D(const QGraphicsDropShadowEffect);
+ return d->filter->blurRadius();
+}
+
+void QGraphicsDropShadowEffect::setBlurRadius(int blurRadius)
+{
+ Q_D(QGraphicsDropShadowEffect);
+ if (d->filter->blurRadius() == blurRadius)
+ return;
+
+ d->filter->setBlurRadius(blurRadius);
+ updateBoundingRect();
+ emit blurRadiusChanged(blurRadius);
+}
+
+QColor QGraphicsDropShadowEffect::color() const
+{
+ Q_D(const QGraphicsDropShadowEffect);
+ return d->filter->color();
+}
+
+void QGraphicsDropShadowEffect::setColor(const QColor &color)
+{
+ Q_D(QGraphicsDropShadowEffect);
+ if (d->filter->color() == color)
+ return;
+
+ d->filter->setColor(color);
+ emit colorChanged(color);
+}
+
+QRectF QGraphicsDropShadowEffect::boundingRectFor(const QRectF &rect) const
+{
+ Q_D(const QGraphicsDropShadowEffect);
+ return d->filter->boundingRectFor(rect);
+}
+
+void QGraphicsDropShadowEffect::draw(QPainter *painter, QGraphicsEffectSource *source)
+{
+ Q_D(QGraphicsDropShadowEffect);
+ if (d->filter->blurRadius() <= 0 && d->filter->offset().isNull()) {
+ source->draw(painter);
+ return;
+ }
+
+ QPoint offset;
+ if (source->isPixmap()) {
+ // No point in drawing in device coordinates (pixmap will be scaled anyways).
+ const QPixmap pixmap = source->pixmap(Qt::LogicalCoordinates, &offset);
+ d->filter->draw(painter, offset, pixmap);
+ return;
+ }
+
+ // Draw pixmap in device coordinates to avoid pixmap scaling.
+ const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset);
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+ d->filter->draw(painter, offset, pixmap);
+ painter->setWorldTransform(restoreTransform);
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/gui/effects/qgraphicseffect.h b/src/gui/effects/qgraphicseffect.h
new file mode 100644
index 000000000..99d854892
--- /dev/null
+++ b/src/gui/effects/qgraphicseffect.h
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSEFFECT_H
+#define QGRAPHICSEFFECT_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qrect.h>
+#include <QtGui/qcolor.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QGraphicsItem;
+class QStyleOption;
+class QPainter;
+class QPixmap;
+
+class QGraphicsEffectSourcePrivate;
+class Q_GUI_EXPORT QGraphicsEffectSource : public QObject
+{
+ Q_OBJECT
+public:
+ ~QGraphicsEffectSource();
+ const QGraphicsItem *graphicsItem() const;
+ const QWidget *widget() const;
+ const QStyleOption *styleOption() const;
+
+ bool isPixmap() const;
+ void draw(QPainter *painter);
+ void update();
+
+ QRectF boundingRect(Qt::CoordinateSystem coordinateSystem = Qt::LogicalCoordinates) const;
+ QRect deviceRect() const;
+ QPixmap pixmap(Qt::CoordinateSystem system = Qt::LogicalCoordinates, QPoint *offset = 0) const;
+
+protected:
+ QGraphicsEffectSource(QGraphicsEffectSourcePrivate &dd, QObject *parent = 0);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsEffectSource);
+ Q_DISABLE_COPY(QGraphicsEffectSource);
+ friend class QGraphicsEffect;
+ friend class QGraphicsEffectPrivate;
+ friend class QGraphicsScenePrivate;
+ friend class QGraphicsItem;
+ friend class QWidget;
+ friend class QWidgetPrivate;
+};
+
+class QGraphicsEffectPrivate;
+class Q_GUI_EXPORT QGraphicsEffect : public QObject
+{
+ Q_OBJECT
+ Q_FLAGS(ChangeFlags)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
+public:
+ enum ChangeFlag {
+ SourceAttached = 0x1,
+ SourceDetached = 0x2,
+ SourceBoundingRectChanged = 0x4,
+ SourceInvalidated = 0x8
+ };
+ Q_DECLARE_FLAGS(ChangeFlags, ChangeFlag);
+
+ QGraphicsEffect(QObject *parent = 0);
+ virtual ~QGraphicsEffect();
+
+ virtual QRectF boundingRectFor(const QRectF &rect) const;
+ QRectF boundingRect() const;
+
+ QGraphicsEffectSource *source() const;
+
+ bool isEnabled() const;
+
+public Q_SLOTS:
+ void setEnabled(bool enable);
+ // ### add update() slot
+
+Q_SIGNALS:
+ void enabledChanged(bool enabled);
+
+protected:
+ QGraphicsEffect(QGraphicsEffectPrivate &d, QObject *parent = 0);
+ virtual void draw(QPainter *painter, QGraphicsEffectSource *source) = 0;
+ virtual void sourceChanged(ChangeFlags flags);
+ void updateBoundingRect();
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsEffect)
+ Q_DISABLE_COPY(QGraphicsEffect)
+ friend class QGraphicsItem;
+ friend class QGraphicsItemPrivate;
+ friend class QGraphicsScenePrivate;
+ friend class QWidget;
+ friend class QWidgetPrivate;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsEffect::ChangeFlags);
+
+class QGraphicsGrayscaleEffectPrivate;
+class Q_GUI_EXPORT QGraphicsGrayscaleEffect: public QGraphicsEffect
+{
+ Q_OBJECT
+public:
+ QGraphicsGrayscaleEffect(QObject *parent = 0);
+ ~QGraphicsGrayscaleEffect();
+
+protected:
+ void draw(QPainter *painter, QGraphicsEffectSource *source);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsGrayscaleEffect)
+ Q_DISABLE_COPY(QGraphicsGrayscaleEffect)
+};
+
+class QGraphicsColorizeEffectPrivate;
+class Q_GUI_EXPORT QGraphicsColorizeEffect: public QGraphicsEffect
+{
+ Q_OBJECT
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+public:
+ QGraphicsColorizeEffect(QObject *parent = 0);
+ ~QGraphicsColorizeEffect();
+
+ QColor color() const;
+
+public Q_SLOTS:
+ void setColor(const QColor &c);
+
+Q_SIGNALS:
+ void colorChanged(const QColor &color);
+
+protected:
+ void draw(QPainter *painter, QGraphicsEffectSource *source);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsColorizeEffect)
+ Q_DISABLE_COPY(QGraphicsColorizeEffect)
+};
+
+class QGraphicsPixelizeEffectPrivate;
+class Q_GUI_EXPORT QGraphicsPixelizeEffect: public QGraphicsEffect
+{
+ Q_OBJECT
+ Q_PROPERTY(int pixelSize READ pixelSize WRITE setPixelSize NOTIFY pixelSizeChanged)
+public:
+ QGraphicsPixelizeEffect(QObject *parent = 0);
+ ~QGraphicsPixelizeEffect();
+
+ int pixelSize() const;
+
+public Q_SLOTS:
+ void setPixelSize(int pixelSize);
+
+Q_SIGNALS:
+ void pixelSizeChanged(int pixelSize);
+
+protected:
+ void draw(QPainter *painter, QGraphicsEffectSource *source);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsPixelizeEffect)
+ Q_DISABLE_COPY(QGraphicsPixelizeEffect)
+};
+
+class QGraphicsBlurEffectPrivate;
+class Q_GUI_EXPORT QGraphicsBlurEffect: public QGraphicsEffect
+{
+ Q_OBJECT
+ Q_PROPERTY(int blurRadius READ blurRadius WRITE setBlurRadius NOTIFY blurRadiusChanged)
+public:
+ QGraphicsBlurEffect(QObject *parent = 0);
+ ~QGraphicsBlurEffect();
+
+ QRectF boundingRectFor(const QRectF &rect) const;
+ int blurRadius() const;
+
+public Q_SLOTS:
+ void setBlurRadius(int blurRadius);
+
+Q_SIGNALS:
+ void blurRadiusChanged(int blurRadius);
+
+protected:
+ void draw(QPainter *painter, QGraphicsEffectSource *source);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsBlurEffect)
+ Q_DISABLE_COPY(QGraphicsBlurEffect)
+};
+
+class QGraphicsDropShadowEffectPrivate;
+class Q_GUI_EXPORT QGraphicsDropShadowEffect: public QGraphicsEffect
+{
+ Q_OBJECT
+ Q_PROPERTY(QPointF offset READ offset WRITE setOffset NOTIFY offsetChanged)
+ Q_PROPERTY(int blurRadius READ blurRadius WRITE setBlurRadius NOTIFY blurRadiusChanged)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+public:
+ QGraphicsDropShadowEffect(QObject *parent = 0);
+ ~QGraphicsDropShadowEffect();
+
+ QRectF boundingRectFor(const QRectF &rect) const;
+ QPointF offset() const;
+ int blurRadius() const;
+ QColor color() const;
+
+public Q_SLOTS:
+ void setOffset(const QPointF &ofs);
+ inline void setOffset(qreal dx, qreal dy)
+ { setOffset(QPointF(dx, dy)); }
+ inline void setOffset(qreal d)
+ { setOffset(QPointF(d, d)); }
+ void setBlurRadius(int blurRadius);
+ void setColor(const QColor &color);
+
+Q_SIGNALS:
+ void offsetChanged(const QPointF &offset);
+ void blurRadiusChanged(int blurRadius);
+ void colorChanged(const QColor &color);
+
+protected:
+ void draw(QPainter *painter, QGraphicsEffectSource *source);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsDropShadowEffect)
+ Q_DISABLE_COPY(QGraphicsDropShadowEffect)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGRAPHICSEFFECT_H
+
diff --git a/src/gui/effects/qgraphicseffect_p.h b/src/gui/effects/qgraphicseffect_p.h
new file mode 100644
index 000000000..35f2d40cb
--- /dev/null
+++ b/src/gui/effects/qgraphicseffect_p.h
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSEFFECT_P_H
+#define QGRAPHICSEFFECT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgraphicseffect.h"
+
+#include <private/qobject_p.h>
+#include <private/qpixmapfilter_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsEffectSourcePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsEffectSource)
+public:
+ QGraphicsEffectSourcePrivate() : QObjectPrivate() {}
+ virtual ~QGraphicsEffectSourcePrivate() {}
+ virtual void detach() = 0;
+ virtual QRectF boundingRect(Qt::CoordinateSystem system) const = 0;
+ virtual QRect deviceRect() const = 0;
+ virtual const QGraphicsItem *graphicsItem() const = 0;
+ virtual const QWidget *widget() const = 0;
+ virtual const QStyleOption *styleOption() const = 0;
+ virtual void draw(QPainter *p) = 0;
+ virtual void update() = 0;
+ virtual bool isPixmap() const = 0;
+ virtual QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset = 0) const = 0;
+ friend class QGraphicsScenePrivate;
+ friend class QGraphicsItem;
+ friend class QGraphicsItemPrivate;
+};
+
+class Q_GUI_EXPORT QGraphicsEffectPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsEffect)
+public:
+ QGraphicsEffectPrivate() : source(0), isEnabled(1) {}
+
+ inline void setGraphicsEffectSource(QGraphicsEffectSource *newSource)
+ {
+ QGraphicsEffect::ChangeFlags flags;
+ if (source) {
+ flags |= QGraphicsEffect::SourceDetached;
+ source->d_func()->detach();
+ delete source;
+ }
+ source = newSource;
+ if (newSource)
+ flags |= QGraphicsEffect::SourceAttached;
+ q_func()->sourceChanged(flags);
+ }
+
+ QGraphicsEffectSource *source;
+ QRectF boundingRect;
+ quint32 isEnabled : 1;
+ quint32 padding : 31; // feel free to use
+};
+
+class QGraphicsGrayscaleEffectPrivate : public QGraphicsEffectPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsGrayscaleEffect)
+public:
+ QGraphicsGrayscaleEffectPrivate()
+ {
+ filter = new QPixmapColorizeFilter;
+ filter->setColor(Qt::black);
+ }
+ ~QGraphicsGrayscaleEffectPrivate() { delete filter; }
+
+ QPixmapColorizeFilter *filter;
+};
+
+class QGraphicsColorizeEffectPrivate : public QGraphicsEffectPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsColorizeEffect)
+public:
+ QGraphicsColorizeEffectPrivate() { filter = new QPixmapColorizeFilter; }
+ ~QGraphicsColorizeEffectPrivate() { delete filter; }
+
+ QPixmapColorizeFilter *filter;
+};
+
+class QGraphicsPixelizeEffectPrivate : public QGraphicsEffectPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsPixelizeEffect)
+public:
+ QGraphicsPixelizeEffectPrivate() : pixelSize(3) {}
+
+ int pixelSize;
+};
+
+class QGraphicsBlurEffectPrivate : public QGraphicsEffectPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsBlurEffect)
+public:
+ QGraphicsBlurEffectPrivate() : filter(new QPixmapBlurFilter) {}
+ ~QGraphicsBlurEffectPrivate() { delete filter; }
+
+ QPixmapBlurFilter *filter;
+};
+
+class QGraphicsDropShadowEffectPrivate : public QGraphicsEffectPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsDropShadowEffect)
+public:
+ QGraphicsDropShadowEffectPrivate() : filter(new QPixmapDropShadowFilter) {}
+ ~QGraphicsDropShadowEffectPrivate() { delete filter; }
+
+ QPixmapDropShadowFilter *filter;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGRAPHICSEFFECT_P_H
+
diff --git a/src/gui/egl/qegl.cpp b/src/gui/egl/qegl.cpp
index 9d0ead197..f0df0c984 100644
--- a/src/gui/egl/qegl.cpp
+++ b/src/gui/egl/qegl.cpp
@@ -402,7 +402,7 @@ void QEglContext::dumpAllConfigs()
QString QEglContext::extensions()
{
- const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
+ const char* exts = eglQueryString(QEglContext::defaultDisplay(0), EGL_EXTENSIONS);
return QString(QLatin1String(exts));
}
diff --git a/src/gui/egl/qegl_p.h b/src/gui/egl/qegl_p.h
index bba5e59c5..97730d826 100644
--- a/src/gui/egl/qegl_p.h
+++ b/src/gui/egl/qegl_p.h
@@ -122,8 +122,8 @@ public:
void dumpAllConfigs();
- QString extensions();
- bool hasExtension(const char* extensionName);
+ static QString extensions();
+ static bool hasExtension(const char* extensionName);
private:
QEgl::API apiType;
diff --git a/src/gui/egl/qeglproperties.cpp b/src/gui/egl/qeglproperties.cpp
index 22b55feeb..7b582c43a 100644
--- a/src/gui/egl/qeglproperties.cpp
+++ b/src/gui/egl/qeglproperties.cpp
@@ -257,6 +257,20 @@ static void addTag(QString& str, const QString& tag)
str += tag;
}
+void QEglProperties::dumpAllConfigs()
+{
+ EGLint count = 0;
+ eglGetConfigs(QEglContext::defaultDisplay(0), 0, 0, &count);
+ if (count < 1)
+ return;
+
+ EGLConfig *configs = new EGLConfig [count];
+ eglGetConfigs(QEglContext::defaultDisplay(0), configs, count, &count);
+ for (EGLint index = 0; index < count; ++index)
+ qWarning() << QEglProperties(configs[index]).toString();
+ delete [] configs;
+}
+
// Convert a property list to a string suitable for debug output.
QString QEglProperties::toString() const
{
diff --git a/src/gui/egl/qeglproperties_p.h b/src/gui/egl/qeglproperties_p.h
index 4ef381496..383a87300 100644
--- a/src/gui/egl/qeglproperties_p.h
+++ b/src/gui/egl/qeglproperties_p.h
@@ -130,6 +130,8 @@ public:
QString toString() const;
+ static void dumpAllConfigs();
+
private:
QVarLengthArray<int> props;
};
diff --git a/src/gui/embedded/qscreen_qws.cpp b/src/gui/embedded/qscreen_qws.cpp
index 1d49bd148..cee4a21c4 100644
--- a/src/gui/embedded/qscreen_qws.cpp
+++ b/src/gui/embedded/qscreen_qws.cpp
@@ -181,7 +181,27 @@ void QScreenCursor::set(const QImage &image, int hotx, int hoty)
const QRect r = boundingRect();
hotspot = QPoint(hotx, hoty);
- cursor = image;
+ // These are in almost all cases the fastest formats to blend
+ QImage::Format f;
+ switch (qt_screen->depth()) {
+ case 12:
+ f = QImage::Format_ARGB4444_Premultiplied;
+ break;
+ case 15:
+ f = QImage::Format_ARGB8555_Premultiplied;
+ break;
+ case 16:
+ f = QImage::Format_ARGB8565_Premultiplied;
+ break;
+ case 18:
+ f = QImage::Format_ARGB6666_Premultiplied;
+ break;
+ default:
+ f = QImage::Format_ARGB32_Premultiplied;
+ }
+
+ cursor = image.convertToFormat(f);
+
size = image.size();
if (enable && !hwaccel)
diff --git a/src/gui/embedded/qscreenlinuxfb_qws.cpp b/src/gui/embedded/qscreenlinuxfb_qws.cpp
index 34fc54cae..69e5808e6 100644
--- a/src/gui/embedded/qscreenlinuxfb_qws.cpp
+++ b/src/gui/embedded/qscreenlinuxfb_qws.cpp
@@ -271,15 +271,13 @@ bool QLinuxFbScreen::connect(const QString &displaySpec)
QScreen::setFrameBufferLittleEndian(true);
#endif
- // Check for explicitly specified device
- const int len = 8; // "/dev/fbx"
- int m = displaySpec.indexOf(QLatin1String("/dev/fb"));
-
- QString dev;
- if (m > 0)
- dev = displaySpec.mid(m, len);
- else
- dev = QLatin1String("/dev/fb0");
+ QString dev = QLatin1String("/dev/fb0");
+ foreach(QString d, args) {
+ if (d.startsWith(QLatin1Char('/'))) {
+ dev = d;
+ break;
+ }
+ }
if (access(dev.toLatin1().constData(), R_OK|W_OK) == 0)
d_ptr->fd = QT_OPEN(dev.toLatin1().constData(), O_RDWR);
diff --git a/src/gui/graphicsview/graphicsview.pri b/src/gui/graphicsview/graphicsview.pri
index 9d232e451..547d7ce7a 100644
--- a/src/gui/graphicsview/graphicsview.pri
+++ b/src/gui/graphicsview/graphicsview.pri
@@ -22,7 +22,12 @@ HEADERS += graphicsview/qgraphicsgridlayout.h \
graphicsview/qgraphicsview_p.h \
graphicsview/qgraphicswidget.h \
graphicsview/qgraphicswidget_p.h \
- graphicsview/qgridlayoutengine_p.h
+ graphicsview/qgridlayoutengine_p.h \
+ graphicsview/qgraph_p.h \
+ graphicsview/qsimplex_p.h \
+ graphicsview/qgraphicsanchorlayout_p.h \
+ graphicsview/qgraphicsanchorlayout.h
+
SOURCES += graphicsview/qgraphicsgridlayout.cpp \
graphicsview/qgraphicsitem.cpp \
graphicsview/qgraphicsitemanimation.cpp \
@@ -41,4 +46,7 @@ SOURCES += graphicsview/qgraphicsgridlayout.cpp \
graphicsview/qgraphicsview.cpp \
graphicsview/qgraphicswidget.cpp \
graphicsview/qgraphicswidget_p.cpp \
- graphicsview/qgridlayoutengine.cpp
+ graphicsview/qgridlayoutengine.cpp \
+ graphicsview/qsimplex_p.cpp \
+ graphicsview/qgraphicsanchorlayout_p.cpp \
+ graphicsview/qgraphicsanchorlayout.cpp
diff --git a/src/gui/graphicsview/qgraph_p.h b/src/gui/graphicsview/qgraph_p.h
new file mode 100644
index 000000000..7130003f3
--- /dev/null
+++ b/src/gui/graphicsview/qgraph_p.h
@@ -0,0 +1,240 @@
+#include <QtCore/QHash>
+#include <QtCore/QQueue>
+#include <QtCore/QString>
+#include <QtCore/QDebug>
+
+#include <float.h>
+
+QT_BEGIN_NAMESPACE
+
+template <typename Vertex, typename EdgeData>
+class Graph
+{
+public:
+ Graph() {}
+
+ class const_iterator {
+ public:
+ const_iterator(const Graph *graph, bool begin) : g(graph){
+ if (begin) {
+ row = g->m_graph.constBegin();
+ //test if the graph is empty
+ if (row != g->m_graph.constEnd())
+ {
+ column = (*row)->constBegin();
+ }
+ } else {
+ row = g->m_graph.constEnd();
+ }
+ }
+
+ inline Vertex *operator*() {
+ return column.key();
+ }
+
+ inline Vertex *from() const {
+ return row.key();
+ }
+
+ inline Vertex *to() const {
+ return column.key();
+ }
+
+ inline bool operator==(const const_iterator &o) const { return !(*this != o); }
+ inline bool operator!=(const const_iterator &o) const {
+ if (row == g->m_graph.end()) {
+ return row != o.row;
+ } else {
+ return row != o.row || column != o.column;
+ }
+ }
+ inline const_iterator& operator=(const const_iterator &o) const { row = o.row; column = o.column; return *this;}
+
+ // prefix
+ const_iterator &operator++() {
+ if (row != g->m_graph.constEnd()) {
+ ++column;
+ if (column == (*row)->constEnd()) {
+ ++row;
+ if (row != g->m_graph.constEnd()) {
+ column = (*row)->constBegin();
+ }
+ }
+ }
+ return *this;
+ }
+
+ private:
+ const Graph *g;
+ Q_TYPENAME QHash<Vertex *, QHash<Vertex *, EdgeData *> * >::const_iterator row;
+ Q_TYPENAME QHash<Vertex *, EdgeData *>::const_iterator column;
+ };
+
+ const_iterator constBegin() const {
+ return const_iterator(this,true);
+ }
+
+ const_iterator constEnd() const {
+ return const_iterator(this,false);
+ }
+
+ /*!
+ * \internal
+ *
+ * If there is an edge between \a first and \a second, it will return a structure
+ * containing the data associated with the edge, otherwise it will return 0.
+ *
+ */
+ EdgeData *edgeData(Vertex* first, Vertex* second) {
+ QHash<Vertex *, EdgeData *> *row = m_graph.value(first);
+ return row ? row->value(second) : 0;
+ }
+
+ void createEdge(Vertex *first, Vertex *second, EdgeData *data)
+ {
+ // Creates a bidirectional edge
+#if defined(QT_DEBUG) && 0
+ qDebug("Graph::createEdge(): %s",
+ qPrintable(QString::fromAscii("%1-%2")
+ .arg(first->toString()).arg(second->toString())));
+#endif
+ if (edgeData(first, second)) {
+#ifdef QT_DEBUG
+ qWarning(qPrintable(QString::fromAscii("%1-%2 already has an edge")
+ .arg(first->toString()).arg(second->toString())));
+#endif
+ }
+ createDirectedEdge(first, second, data);
+ createDirectedEdge(second, first, data);
+ }
+
+ void removeEdge(Vertex *first, Vertex *second)
+ {
+ // Removes a bidirectional edge
+#if defined(QT_DEBUG) && 0
+ qDebug("Graph::removeEdge(): %s",
+ qPrintable(QString::fromAscii("%1-%2")
+ .arg(first->toString()).arg(second->toString())));
+#endif
+ EdgeData *data = edgeData(first, second);
+ removeDirectedEdge(first, second);
+ removeDirectedEdge(second, first);
+ if (data) delete data;
+ }
+
+ EdgeData *takeEdge(Vertex* first, Vertex* second)
+ {
+#if defined(QT_DEBUG) && 0
+ qDebug("Graph::takeEdge(): %s",
+ qPrintable(QString::fromAscii("%1-%2")
+ .arg(first->toString()).arg(second->toString())));
+#endif
+ // Removes a bidirectional edge
+ EdgeData *data = edgeData(first, second);
+ if (data) {
+ removeDirectedEdge(first, second);
+ removeDirectedEdge(second, first);
+ }
+ return data;
+ }
+
+ QList<Vertex *> adjacentVertices(Vertex *vertex) const
+ {
+ QHash<Vertex *, EdgeData *> *row = m_graph.value(vertex);
+ QList<Vertex *> l;
+ if (row)
+ l = row->keys();
+ return l;
+ }
+
+ void setRootVertex(Vertex *vertex)
+ {
+ userVertex = vertex;
+ }
+
+ QSet<Vertex*> vertices() const {
+ QSet<Vertex *> setOfVertices;
+ for (const_iterator it = constBegin(); it != constEnd(); ++it) {
+ setOfVertices.insert(*it);
+ }
+ return setOfVertices;
+ }
+
+ QList<QPair<Vertex*, Vertex*> > connections() const {
+ QList<QPair<Vertex*, Vertex*> > conns;
+ for (const_iterator it = constBegin(); it != constEnd(); ++it) {
+ Vertex *from = it.from();
+ Vertex *to = it.to();
+ // do not return (from,to) *and* (to,from)
+ if (from < to) {
+ conns.append(qMakePair(from, to));
+ }
+ }
+ return conns;
+ }
+
+#if defined(QT_DEBUG)
+ QString serializeToDot() { // traversal
+ QString strVertices;
+ QString edges;
+
+ QSet<Vertex *> setOfVertices = vertices();
+ for (Q_TYPENAME QSet<Vertex*>::const_iterator it = setOfVertices.begin(); it != setOfVertices.end(); ++it) {
+ Vertex *v = *it;
+ QList<Vertex*> adjacents = adjacentVertices(v);
+ for (int i = 0; i < adjacents.count(); ++i) {
+ Vertex *v1 = adjacents.at(i);
+ EdgeData *data = edgeData(v, v1);
+ bool forward = data->from == v;
+ if (forward) {
+ edges += QString::fromAscii("%1->%2 [label=\"[%3,%4,%5]\" dir=both color=\"#000000:#a0a0a0\"] \n")
+ .arg(v->toString())
+ .arg(v1->toString())
+ .arg(data->minSize)
+ .arg(data->prefSize)
+ .arg(data->maxSize)
+ ;
+ }
+ }
+ strVertices += QString::fromAscii("%1 [label=\"%2\"]\n").arg(v->toString()).arg(v->toString());
+ }
+ return QString::fromAscii("%1\n%2\n").arg(strVertices).arg(edges);
+ }
+#endif
+
+ Vertex *rootVertex() const
+ {
+ return userVertex;
+ }
+
+protected:
+ void createDirectedEdge(Vertex *from, Vertex *to, EdgeData *data)
+ {
+ QHash<Vertex *, EdgeData *> *adjacentToFirst = m_graph.value(from);
+ if (!adjacentToFirst) {
+ adjacentToFirst = new QHash<Vertex *, EdgeData *>();
+ m_graph.insert(from, adjacentToFirst);
+ }
+ adjacentToFirst->insert(to, data);
+ }
+
+ void removeDirectedEdge(Vertex *from, Vertex *to)
+ {
+ QHash<Vertex *, EdgeData *> *adjacentToFirst = m_graph.value(from);
+ Q_ASSERT(adjacentToFirst);
+
+ adjacentToFirst->remove(to);
+ if (adjacentToFirst->isEmpty()) {
+ //nobody point to 'from' so we can remove it from the graph
+ m_graph.remove(from);
+ delete adjacentToFirst;
+ }
+ }
+
+private:
+ Vertex *userVertex;
+
+ QHash<Vertex *, QHash<Vertex *, EdgeData *> *> m_graph;
+};
+
+QT_END_NAMESPACE
diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp
new file mode 100644
index 000000000..3c2ea37ec
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp
@@ -0,0 +1,454 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGraphicsAnchorLayout
+ \brief The QGraphicsAnchorLayout class provides a layout where one can anchor widgets
+ together in Graphics View.
+ \since 4.6
+ \ingroup appearance
+ \ingroup geomanagement
+ \ingroup graphicsview-api
+
+ The anchor layout is a layout where one can specify how widgets should be placed relative to
+ each other. The specification is called an anchor, and it is set up by calling anchor().
+ Anchors are always set up between edges of an item, where the "center" is also considered to
+ be an edge. Considering this example:
+ \code
+ QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout;
+ QGraphicsWidget *a = new QGraphicsWidget;
+ QGraphicsWidget *b = new QGraphicsWidget;
+ l->anchor(a, Qt::AnchorRight, b, Qt::AnchorLeft);
+ \endcode
+
+ Here is the right edge of item A anchored to the left edge of item B, with the result that
+ item B will be placed to the right of item A, with a spacing between A and B. If the
+ spacing is negative, the items will overlap to some extent. Items that are anchored are
+ automatically added to the layout, and if items are removed, all their anchors will be
+ automatically removed
+
+ \section1 Size Hints and Size Policies in QGraphicsLinearLayout
+ QGraphicsLinearLayout respects each item's size hints and size policies. However it does
+ not respect stretch factors currently. This might change in the future, so please refrain
+ from using stretch factors in anchor layout to avoid any future regressions.
+
+ \section1 Spacing within QGraphicsAnchorLayout
+
+ Between the items, the layout can distribute some space. If the spacing has not been
+ explicitly specified, the actual amount of space will usually be 0, but if the first edge
+ is the "opposite" of the second edge (i.e. Right is anchored to Left or vice-versa), the
+ size of the anchor will be queried from the style through the pixelMetric
+ PM_LayoutHorizontalSpacing (or PM_LayoutVerticalSpacing for vertical anchors).
+*/
+
+#include "qgraphicsanchorlayout_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QGraphicsAnchorLayout::QGraphicsAnchorLayout(QGraphicsLayoutItem *parent)
+ : QGraphicsLayout(*new QGraphicsAnchorLayoutPrivate(), parent)
+{
+ Q_D(QGraphicsAnchorLayout);
+ d->createLayoutEdges();
+}
+
+QGraphicsAnchorLayout::~QGraphicsAnchorLayout()
+{
+ Q_D(QGraphicsAnchorLayout);
+
+ for (int i = count() - 1; i >= 0; --i) {
+ QGraphicsLayoutItem *item = d->items.at(i);
+ removeAt(i);
+ if (item) {
+ if (item->ownedByLayout())
+ delete item;
+ }
+ }
+
+ d->removeCenterConstraints(this, QGraphicsAnchorLayoutPrivate::Horizontal);
+ d->removeCenterConstraints(this, QGraphicsAnchorLayoutPrivate::Vertical);
+ d->deleteLayoutEdges();
+
+ Q_ASSERT(d->itemCenterConstraints[0].isEmpty());
+ Q_ASSERT(d->itemCenterConstraints[1].isEmpty());
+ Q_ASSERT(d->items.isEmpty());
+ Q_ASSERT(d->m_vertexList.isEmpty());
+}
+
+/*!
+ * Creates an anchor between the edge \a firstEdge of item \a firstItem and the edge \a secondEdge
+ * of item \a secondItem. The magnitude of the anchor is picked up from the style. Anchors
+ * between a layout edge and an item edge will have a size of 0.
+ * If there is already an anchor between the edges, the the new anchor will replace the old one.
+ *
+ * \a firstItem and \a secondItem are automatically added to the layout if they are not part
+ * of the layout. This means that count() can increase with up to 2.
+ *
+ * The spacing an anchor will get depends on the type of anchor. For instance, anchors from the
+ * Right edge of one item to the Left edge of another (or vice versa) will use the default
+ * horizontal spacing. The same behaviour applies to Bottom to Top anchors, (but they will use
+ * the default vertical spacing). For all other anchor combinations, the spacing will be 0.
+ * All anchoring functions will follow this rule.
+ *
+ * The spacing can also be set manually by using setAnchorSpacing() method.
+ *
+ * \sa removeAnchor(), addCornerAnchors(), addLeftAndRightAnchors(), addTopAndBottomAnchors(),
+ * addAllAnchors()
+ */
+void QGraphicsAnchorLayout::addAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge)
+{
+ Q_D(QGraphicsAnchorLayout);
+ d->anchor(firstItem, firstEdge, secondItem, secondEdge);
+ invalidate();
+}
+
+/*!
+ * Creates two anchors between \a firstItem and \a secondItem, where one is for the horizontal
+ * edge and another one for the vertical edge that the corners \a firstCorner and \a
+ * secondCorner specifies.
+ * The magnitude of the anchors is picked up from the style.
+ *
+ * This is a convenience function, since anchoring corners can be expressed as anchoring two edges.
+ * For instance,
+ * \code
+ * layout->addAnchor(layout, Qt::AnchorTop, b, Qt::AnchorTop);
+ * layout->addAnchor(layout, Qt::AnchorLeft, b, Qt::AnchorLeft);
+ * \endcode
+ *
+ * has the same effect as
+ *
+ * \code
+ * layout->addCornerAnchors(layout, Qt::TopLeft, b, Qt::TopLeft);
+ * \endcode
+ *
+ * If there is already an anchor between the edge pairs, it will be replaced by the anchors that
+ * this function specifies.
+ *
+ * \a firstItem and \a secondItem are automatically added to the layout if they are not part
+ * of the layout. This means that count() can increase with up to 2.
+ */
+void QGraphicsAnchorLayout::addCornerAnchors(QGraphicsLayoutItem *firstItem,
+ Qt::Corner firstCorner,
+ QGraphicsLayoutItem *secondItem,
+ Qt::Corner secondCorner)
+{
+ Q_D(QGraphicsAnchorLayout);
+
+ // Horizontal anchor
+ Qt::AnchorPoint firstEdge = (firstCorner & 1 ? Qt::AnchorRight: Qt::AnchorLeft);
+ Qt::AnchorPoint secondEdge = (secondCorner & 1 ? Qt::AnchorRight: Qt::AnchorLeft);
+ d->anchor(firstItem, firstEdge, secondItem, secondEdge);
+
+ // Vertical anchor
+ firstEdge = (firstCorner & 2 ? Qt::AnchorBottom: Qt::AnchorTop);
+ secondEdge = (secondCorner & 2 ? Qt::AnchorBottom: Qt::AnchorTop);
+ d->anchor(firstItem, firstEdge, secondItem, secondEdge);
+
+ invalidate();
+}
+
+/*!
+ \fn QGraphicsAnchorLayout::addLeftAndRightAnchors(QGraphicsLayoutItem *firstEdge, QGraphicsLayoutItem *secondEdge)
+
+ This convenience function is equivalent to calling
+ \code
+ l->addAnchor(firstEdge, Qt::AnchorLeft, secondEdge, Qt::AnchorLeft);
+ l->addAnchor(firstEdge, Qt::AnchorRight, secondEdge, Qt::AnchorRight);
+ \endcode
+*/
+
+/*!
+ \fn QGraphicsAnchorLayout::addTopAndBottomAnchors(QGraphicsLayoutItem *firstEdge, QGraphicsLayoutItem *secondEdge)
+
+ This convenience function is equivalent to calling
+ \code
+ l->addAnchor(firstEdge, Qt::AnchorTop, secondEdge, Qt::AnchorTop);
+ l->addAnchor(firstEdge, Qt::AnchorBottom, secondEdge, Qt::AnchorBottom);
+ \endcode
+*/
+
+/*!
+ \fn QGraphicsAnchorLayout::addAllAnchors(QGraphicsLayoutItem *firstEdge, QGraphicsLayoutItem *secondEdge)
+
+ This convenience function is equivalent to calling
+ \code
+ l->addLeftAndRightAnchors(firstEdge, secondEdge);
+ l->addTopAndBottomAnchors(firstEdge, secondEdge);
+ \endcode
+*/
+
+/*!
+ Set the spacing between the anchor point defined by \a firstItem and \a firstEdge and
+ \a secondItem and \a secondEdge to be \a spacing.
+*/
+void QGraphicsAnchorLayout::setAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
+ const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge,
+ qreal spacing)
+{
+ Q_D(QGraphicsAnchorLayout);
+
+ if (!d->setAnchorSize(firstItem, firstEdge, secondItem, secondEdge, &spacing)) {
+ qWarning("setAnchorSpacing: The anchor does not exist.");
+ }
+ invalidate();
+}
+
+/*!
+ Returns the spacing between the anchor point defined by \a firstItem and \a firstEdge and
+ \a secondItem and \a secondEdge. The anchor must exist.
+*/
+qreal QGraphicsAnchorLayout::anchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
+ const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) const
+{
+ Q_D(const QGraphicsAnchorLayout);
+ qreal size = 0;
+ if (!d->anchorSize(firstItem, firstEdge, secondItem, secondEdge, 0, &size)) {
+ qWarning("anchorSpacing: The anchor does not exist.");
+ }
+ return size;
+}
+
+/*!
+ Resets the spacing between the anchor point defined by \a firstItem and \a firstEdge and
+ \a secondItem and \a secondEdge to be the default spacing. Depending on the anchor type, the
+ default spacing is either 0 or a value returned from the style.
+
+ \sa setAnchorSpacing(), anchorSpacing(), addAnchor()
+*/
+void QGraphicsAnchorLayout::unsetAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
+ const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge)
+{
+ Q_D(QGraphicsAnchorLayout);
+
+ if (!d->setAnchorSize(firstItem, firstEdge, secondItem, secondEdge, 0)) {
+ qWarning("unsetAnchorSpacing: The anchor does not exist.");
+ }
+ invalidate();
+}
+
+/*!
+ Removes the anchor between the edge \a firstEdge of item \a firstItem and the edge \a secondEdge
+ of item \a secondItem. If such an anchor does not exist, the layout will be left unchanged.
+*/
+void QGraphicsAnchorLayout::removeAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge)
+{
+ Q_D(QGraphicsAnchorLayout);
+ if ((firstItem == 0) || (secondItem == 0)) {
+ qWarning("QGraphicsAnchorLayout::removeAnchor: "
+ "Cannot remove anchor between NULL items");
+ return;
+ }
+
+ if (firstItem == secondItem) {
+ qWarning("QGraphicsAnchorLayout::removeAnchor: "
+ "Cannot remove anchor from the item to itself");
+ return;
+ }
+
+ if (d->edgeOrientation(secondEdge) != d->edgeOrientation(firstEdge)) {
+ qWarning("QGraphicsAnchorLayout::removeAnchor: "
+ "Cannot remove anchor from edges of different orientations");
+ return;
+ }
+
+ d->removeAnchor(firstItem, firstEdge, secondItem, secondEdge);
+ invalidate();
+}
+
+/*!
+ Sets the default horizontal spacing for the anchor layout to \a spacing.
+
+ \sa horizontalSpacing(), setVerticalSpacing(), setSpacing()
+*/
+void QGraphicsAnchorLayout::setHorizontalSpacing(qreal spacing)
+{
+ Q_D(QGraphicsAnchorLayout);
+ d->spacings[0] = spacing;
+ invalidate();
+}
+
+/*!
+ Sets the default vertical spacing for the anchor layout to \a spacing.
+
+ \sa verticalSpacing(), setHorizontalSpacing(), setSpacing()
+*/
+void QGraphicsAnchorLayout::setVerticalSpacing(qreal spacing)
+{
+ Q_D(QGraphicsAnchorLayout);
+ d->spacings[1] = spacing;
+ invalidate();
+}
+
+/*!
+ Sets the default horizontal and the default vertical spacing for the anchor layout to \a spacing.
+
+ If an item is anchored with no spacing associated with the anchor, it will use the default
+ spacing.
+ \sa setHorizontalSpacing(), setVerticalSpacing()
+*/
+void QGraphicsAnchorLayout::setSpacing(qreal spacing)
+{
+ Q_D(QGraphicsAnchorLayout);
+ d->spacings[0] = d->spacings[1] = spacing;
+ invalidate();
+}
+
+/*!
+ Returns the default horizontal spacing for the anchor layout.
+
+ \sa verticalSpacing(), setHorizontalSpacing()
+*/
+qreal QGraphicsAnchorLayout::horizontalSpacing() const
+{
+ Q_D(const QGraphicsAnchorLayout);
+ return d->effectiveSpacing(QGraphicsAnchorLayoutPrivate::Horizontal);
+}
+
+/*!
+ Returns the default vertical spacing for the anchor layout.
+
+ \sa horizontalSpacing(), setVerticalSpacing()
+*/
+qreal QGraphicsAnchorLayout::verticalSpacing() const
+{
+ Q_D(const QGraphicsAnchorLayout);
+ return d->effectiveSpacing(QGraphicsAnchorLayoutPrivate::Vertical);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsAnchorLayout::setGeometry(const QRectF &geom)
+{
+ Q_D(QGraphicsAnchorLayout);
+
+ QGraphicsLayout::setGeometry(geom);
+ d->calculateVertexPositions(QGraphicsAnchorLayoutPrivate::Horizontal);
+ d->calculateVertexPositions(QGraphicsAnchorLayoutPrivate::Vertical);
+ d->setItemsGeometries();
+}
+
+/*!
+ Removing an item will also remove any of the anchors associated with it.
+*/
+void QGraphicsAnchorLayout::removeAt(int index)
+{
+ Q_D(QGraphicsAnchorLayout);
+ QGraphicsLayoutItem *item = d->items.value(index);
+
+ if (!item)
+ return;
+
+ // Removing an item affects both horizontal and vertical graphs
+ d->restoreSimplifiedGraph(QGraphicsAnchorLayoutPrivate::Horizontal);
+ d->restoreSimplifiedGraph(QGraphicsAnchorLayoutPrivate::Vertical);
+
+ d->removeCenterConstraints(item, QGraphicsAnchorLayoutPrivate::Horizontal);
+ d->removeCenterConstraints(item, QGraphicsAnchorLayoutPrivate::Vertical);
+ d->removeAnchors(item);
+ d->items.remove(index);
+
+ item->setParentLayoutItem(0);
+ invalidate();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsAnchorLayout::count() const
+{
+ Q_D(const QGraphicsAnchorLayout);
+ return d->items.size();
+}
+
+/*!
+ \reimp
+*/
+QGraphicsLayoutItem *QGraphicsAnchorLayout::itemAt(int index) const
+{
+ Q_D(const QGraphicsAnchorLayout);
+ return d->items.value(index);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsAnchorLayout::invalidate()
+{
+ Q_D(QGraphicsAnchorLayout);
+ QGraphicsLayout::invalidate();
+ d->calculateGraphCacheDirty = 1;
+}
+
+/*!
+ \reimp
+*/
+QSizeF QGraphicsAnchorLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ Q_UNUSED(which);
+ Q_UNUSED(constraint);
+ Q_D(const QGraphicsAnchorLayout);
+
+ // Some setup calculations are delayed until the information is
+ // actually needed, avoiding unnecessary recalculations when
+ // adding multiple anchors.
+
+ // sizeHint() / effectiveSizeHint() already have a cache
+ // mechanism, using invalidate() to force recalculation. However
+ // sizeHint() is called three times after invalidation (for max,
+ // min and pref), but we just need do our setup once.
+
+ const_cast<QGraphicsAnchorLayoutPrivate *>(d)->calculateGraphs();
+
+ // ### apply constraint!
+ QSizeF engineSizeHint(
+ d->sizeHints[QGraphicsAnchorLayoutPrivate::Horizontal][which],
+ d->sizeHints[QGraphicsAnchorLayoutPrivate::Vertical][which]);
+
+ qreal left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+
+ return engineSizeHint + QSizeF(left + right, top + bottom);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h
new file mode 100644
index 000000000..3de9ae5ac
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsanchorlayout.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSANCHORLAYOUT_H
+#define QGRAPHICSANCHORLAYOUT_H
+
+#include <QtGui/qgraphicsitem.h>
+#include <QtGui/qgraphicslayout.h>
+
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGraphicsAnchorLayoutPrivate;
+
+class Q_GUI_EXPORT QGraphicsAnchorLayout : public QGraphicsLayout
+{
+public:
+ QGraphicsAnchorLayout(QGraphicsLayoutItem *parent = 0);
+ virtual ~QGraphicsAnchorLayout();
+
+ void addAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge);
+
+ void addCornerAnchors(QGraphicsLayoutItem *firstItem, Qt::Corner firstCorner,
+ QGraphicsLayoutItem *secondItem, Qt::Corner secondCorner);
+
+ inline void addLeftAndRightAnchors(QGraphicsLayoutItem *firstItem,
+ QGraphicsLayoutItem *secondItem);
+
+ inline void addTopAndBottomAnchors(QGraphicsLayoutItem *firstItem,
+ QGraphicsLayoutItem *secondItem);
+
+ inline void addAllAnchors(QGraphicsLayoutItem *firstItem,
+ QGraphicsLayoutItem *secondItem);
+
+ void setAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
+ const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge,
+ qreal spacing);
+
+ qreal anchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
+ const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) const;
+
+ void unsetAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
+ const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge);
+
+ void removeAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge);
+
+ void setHorizontalSpacing(qreal spacing);
+ void setVerticalSpacing(qreal spacing);
+ void setSpacing(qreal spacing);
+ qreal horizontalSpacing() const;
+ qreal verticalSpacing() const;
+
+ void removeAt(int index);
+ void setGeometry(const QRectF &rect);
+ int count() const;
+ QGraphicsLayoutItem *itemAt(int index) const;
+
+ void invalidate();
+protected:
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsAnchorLayout)
+ Q_DECLARE_PRIVATE(QGraphicsAnchorLayout)
+};
+
+
+void QGraphicsAnchorLayout::addLeftAndRightAnchors(QGraphicsLayoutItem *firstItem,
+ QGraphicsLayoutItem *secondItem)
+{
+ addAnchor(secondItem, Qt::AnchorLeft, firstItem, Qt::AnchorLeft);
+ addAnchor(firstItem, Qt::AnchorRight, secondItem, Qt::AnchorRight);
+}
+
+void QGraphicsAnchorLayout::addTopAndBottomAnchors(QGraphicsLayoutItem *firstItem,
+ QGraphicsLayoutItem *secondItem)
+{
+ addAnchor(secondItem, Qt::AnchorTop, firstItem, Qt::AnchorTop);
+ addAnchor(firstItem, Qt::AnchorBottom, secondItem, Qt::AnchorBottom);
+}
+
+void QGraphicsAnchorLayout::addAllAnchors(QGraphicsLayoutItem *firstItem,
+ QGraphicsLayoutItem *secondItem)
+{
+ addLeftAndRightAnchors(firstItem, secondItem);
+ addTopAndBottomAnchors(firstItem, secondItem);
+}
+
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp
new file mode 100644
index 000000000..9a34ad5a2
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp
@@ -0,0 +1,2104 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/qwidget.h>
+#include <QtCore/qlinkedlist.h>
+#include <QtCore/qstack.h>
+
+#ifdef QT_DEBUG
+#include <QtCore/qfile.h>
+#endif
+
+#include "qgraphicsanchorlayout_p.h"
+
+QT_BEGIN_NAMESPACE
+
+void AnchorData::refreshSizeHints(qreal effectiveSpacing)
+{
+ if (!isLayoutAnchor && from->m_item == to->m_item) {
+ bool hasCenter = false;
+ QGraphicsLayoutItem *item = from->m_item;
+
+ if (QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge)
+ == QGraphicsAnchorLayoutPrivate::Horizontal) {
+ minSize = item->minimumWidth();
+ prefSize = item->preferredWidth();
+ maxSize = item->maximumWidth();
+ hasCenter = (from->m_edge == Qt::AnchorHorizontalCenter
+ || to->m_edge == Qt::AnchorHorizontalCenter);
+ } else {
+ minSize = item->minimumHeight();
+ prefSize = item->preferredHeight();
+ maxSize = item->maximumHeight();
+ hasCenter = (from->m_edge == Qt::AnchorVerticalCenter
+ || to->m_edge == Qt::AnchorVerticalCenter);
+ }
+
+ if (hasCenter) {
+ minSize /= 2;
+ prefSize /= 2;
+ maxSize /= 2;
+ }
+
+ // Set the anchor effective sizes to preferred.
+ //
+ // Note: The idea here is that all items should remain at their
+ // preferred size unless where that's impossible. In cases where
+ // the item is subject to restrictions (anchored to the layout
+ // edges, for instance), the simplex solver will be run to
+ // recalculate and override the values we set here.
+ sizeAtMinimum = prefSize;
+ sizeAtPreferred = prefSize;
+ sizeAtMaximum = prefSize;
+
+ } else if (!hasSize) {
+ // Anchor has no size defined, use given default information
+ minSize = effectiveSpacing;
+ prefSize = effectiveSpacing;
+ maxSize = effectiveSpacing;
+
+ sizeAtMinimum = prefSize;
+ sizeAtPreferred = prefSize;
+ sizeAtMaximum = prefSize;
+ }
+}
+
+void ParallelAnchorData::updateChildrenSizes()
+{
+ firstEdge->sizeAtMinimum = secondEdge->sizeAtMinimum = sizeAtMinimum;
+ firstEdge->sizeAtPreferred = secondEdge->sizeAtPreferred = sizeAtPreferred;
+ firstEdge->sizeAtMaximum = secondEdge->sizeAtMaximum = sizeAtMaximum;
+
+ firstEdge->updateChildrenSizes();
+ secondEdge->updateChildrenSizes();
+}
+
+void ParallelAnchorData::refreshSizeHints(qreal effectiveSpacing)
+{
+ // First refresh children information
+ firstEdge->refreshSizeHints(effectiveSpacing);
+ secondEdge->refreshSizeHints(effectiveSpacing);
+
+ // ### should we warn if the parallel connection is invalid?
+ // e.g. 1-2-3 with 10-20-30, the minimum of the latter is
+ // bigger than the maximum of the former.
+
+ minSize = qMax(firstEdge->minSize, secondEdge->minSize);
+ maxSize = qMin(firstEdge->maxSize, secondEdge->maxSize);
+
+ prefSize = qMax(firstEdge->prefSize, secondEdge->prefSize);
+ prefSize = qMin(prefSize, maxSize);
+
+ // See comment in AnchorData::refreshSizeHints() about sizeAt* values
+ sizeAtMinimum = prefSize;
+ sizeAtPreferred = prefSize;
+ sizeAtMaximum = prefSize;
+}
+
+/*!
+ \internal
+ returns the factor in the interval [-1, 1].
+ -1 is at Minimum
+ 0 is at Preferred
+ 1 is at Maximum
+*/
+static qreal getFactor(qreal value, qreal min, qreal pref, qreal max)
+{
+ // ### Maybe remove some of the assertions? (since outside is asserting us)
+ Q_ASSERT(value > min || qFuzzyCompare(value, min));
+ Q_ASSERT(value < max || qFuzzyCompare(value, max));
+
+ if (qFuzzyCompare(value, min)) {
+ return -1.0;
+ } else if (qFuzzyCompare(value, pref)) {
+ return 0.0;
+ } else if (qFuzzyCompare(value, max)) {
+ return 1.0;
+ } else if (value < pref) {
+ // Since value < pref and value != pref and min <= value,
+ // we can assert that min < pref.
+ Q_ASSERT(min < pref);
+ return (value - min) / (pref - min) - 1;
+ } else {
+ // Since value > pref and value != pref and max >= value,
+ // we can assert that max > pref.
+ Q_ASSERT(max > pref);
+ return (value - pref) / (max - pref);
+ }
+}
+
+void SequentialAnchorData::updateChildrenSizes()
+{
+ // ### REMOVE ME
+ // ### check whether we are guarantee to get those or we need to warn stuff at this
+ // point.
+ Q_ASSERT(sizeAtMinimum > minSize || qFuzzyCompare(sizeAtMinimum, minSize));
+ Q_ASSERT(sizeAtMinimum < maxSize || qFuzzyCompare(sizeAtMinimum, maxSize));
+ Q_ASSERT(sizeAtPreferred > minSize || qFuzzyCompare(sizeAtPreferred, minSize));
+ Q_ASSERT(sizeAtPreferred < maxSize || qFuzzyCompare(sizeAtPreferred, maxSize));
+ Q_ASSERT(sizeAtMaximum > minSize || qFuzzyCompare(sizeAtMaximum, minSize));
+ Q_ASSERT(sizeAtMaximum < maxSize || qFuzzyCompare(sizeAtMaximum, maxSize));
+
+ // Band here refers if the value is in the Minimum To Preferred
+ // band (the lower band) or the Preferred To Maximum (the upper band).
+
+ qreal minFactor = getFactor(sizeAtMinimum, minSize, prefSize, maxSize);
+ qreal prefFactor = getFactor(sizeAtPreferred, minSize, prefSize, maxSize);
+ qreal maxFactor = getFactor(sizeAtMaximum, minSize, prefSize, maxSize);
+
+ for (int i = 0; i < m_edges.count(); ++i) {
+ AnchorData *e = m_edges.at(i);
+
+ qreal bandSize = minFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize;
+ e->sizeAtMinimum = e->prefSize + bandSize * minFactor;
+
+ bandSize = prefFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize;
+ e->sizeAtPreferred = e->prefSize + bandSize * prefFactor;
+
+ bandSize = maxFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize;
+ e->sizeAtMaximum = e->prefSize + bandSize * maxFactor;
+
+ e->updateChildrenSizes();
+ }
+}
+
+void SequentialAnchorData::refreshSizeHints(qreal effectiveSpacing)
+{
+ minSize = 0;
+ prefSize = 0;
+ maxSize = 0;
+
+ for (int i = 0; i < m_edges.count(); ++i) {
+ AnchorData *edge = m_edges.at(i);
+
+ // First refresh children information
+ edge->refreshSizeHints(effectiveSpacing);
+
+ minSize += edge->minSize;
+ prefSize += edge->prefSize;
+ maxSize += edge->maxSize;
+ }
+
+ // See comment in AnchorData::refreshSizeHints() about sizeAt* values
+ sizeAtMinimum = prefSize;
+ sizeAtPreferred = prefSize;
+ sizeAtMaximum = prefSize;
+}
+
+#ifdef QT_DEBUG
+void AnchorData::dump(int indent) {
+ if (type == Parallel) {
+ qDebug("%*s type: parallel:", indent, "");
+ ParallelAnchorData *p = static_cast<ParallelAnchorData *>(this);
+ p->firstEdge->dump(indent+2);
+ p->secondEdge->dump(indent+2);
+ } else if (type == Sequential) {
+ SequentialAnchorData *s = static_cast<SequentialAnchorData *>(this);
+ int kids = s->m_edges.count();
+ qDebug("%*s type: sequential(%d):", indent, "", kids);
+ for (int i = 0; i < kids; ++i) {
+ s->m_edges.at(i)->dump(indent+2);
+ }
+ } else {
+ qDebug("%*s type: Normal:", indent, "");
+ }
+}
+
+#endif
+
+QSimplexConstraint *GraphPath::constraint(const GraphPath &path) const
+{
+ // Calculate
+ QSet<AnchorData *> cPositives;
+ QSet<AnchorData *> cNegatives;
+ QSet<AnchorData *> intersection;
+
+ cPositives = positives + path.negatives;
+ cNegatives = negatives + path.positives;
+
+ intersection = cPositives & cNegatives;
+
+ cPositives -= intersection;
+ cNegatives -= intersection;
+
+ // Fill
+ QSimplexConstraint *c = new QSimplexConstraint;
+ QSet<AnchorData *>::iterator i;
+ for (i = cPositives.begin(); i != cPositives.end(); ++i)
+ c->variables.insert(*i, 1.0);
+
+ for (i = cNegatives.begin(); i != cNegatives.end(); ++i)
+ c->variables.insert(*i, -1.0);
+
+ return c;
+}
+
+#ifdef QT_DEBUG
+QString GraphPath::toString() const
+{
+ QString string(QLatin1String("Path: "));
+ foreach(AnchorData *edge, positives)
+ string += QString::fromAscii(" (+++) %1").arg(edge->toString());
+
+ foreach(AnchorData *edge, negatives)
+ string += QString::fromAscii(" (---) %1").arg(edge->toString());
+
+ return string;
+}
+#endif
+
+QGraphicsAnchorLayoutPrivate::QGraphicsAnchorLayoutPrivate()
+ : calculateGraphCacheDirty(1)
+{
+ for (int i = 0; i < NOrientations; ++i) {
+ spacings[i] = -1;
+ graphSimplified[i] = false;
+ }
+}
+
+Qt::AnchorPoint QGraphicsAnchorLayoutPrivate::oppositeEdge(Qt::AnchorPoint edge)
+{
+ switch (edge) {
+ case Qt::AnchorLeft:
+ edge = Qt::AnchorRight;
+ break;
+ case Qt::AnchorRight:
+ edge = Qt::AnchorLeft;
+ break;
+ case Qt::AnchorTop:
+ edge = Qt::AnchorBottom;
+ break;
+ case Qt::AnchorBottom:
+ edge = Qt::AnchorTop;
+ break;
+ default:
+ break;
+ }
+ return edge;
+}
+
+
+/*!
+ * \internal
+ *
+ * helper function in order to avoid overflowing anchor sizes
+ * the returned size will never be larger than FLT_MAX
+ *
+ */
+inline static qreal checkAdd(qreal a, qreal b)
+{
+ if (FLT_MAX - b < a)
+ return FLT_MAX;
+ return a + b;
+}
+
+/*!
+ * \internal
+ *
+ * Takes the sequence of vertices described by (\a before, \a vertices, \a after) and replaces
+ * all anchors connected to the vertices in \a vertices with one simplified anchor between
+ * \a before and \a after. The simplified anchor will be a placeholder for all the previous
+ * anchors between \a before and \a after, and can be restored back to the anchors it is a
+ * placeholder for.
+ */
+static bool simplifySequentialChunk(Graph<AnchorVertex, AnchorData> *graph,
+ AnchorVertex *before,
+ const QVector<AnchorVertex*> &vertices,
+ AnchorVertex *after)
+{
+ int i;
+#if defined(QT_DEBUG) && 0
+ QString strVertices;
+ for (i = 0; i < vertices.count(); ++i)
+ strVertices += QString::fromAscii("%1 - ").arg(vertices.at(i)->toString());
+ QString strPath = QString::fromAscii("%1 - %2%3").arg(before->toString(), strVertices, after->toString());
+ qDebug("simplifying [%s] to [%s - %s]", qPrintable(strPath), qPrintable(before->toString()), qPrintable(after->toString()));
+#endif
+
+ qreal min = 0;
+ qreal pref = 0;
+ qreal max = 0;
+
+ SequentialAnchorData *sequence = new SequentialAnchorData;
+ AnchorVertex *prev = before;
+ AnchorData *data;
+ for (i = 0; i <= vertices.count(); ++i) {
+ AnchorVertex *next = (i < vertices.count()) ? vertices.at(i) : after;
+ data = graph->takeEdge(prev, next);
+ min += data->minSize;
+ pref += data->prefSize;
+ max = checkAdd(max, data->maxSize);
+ sequence->m_edges.append(data);
+ prev = next;
+ }
+
+ // insert new
+ sequence->minSize = min;
+ sequence->prefSize = pref;
+ sequence->maxSize = max;
+
+ // Unless these values are overhidden by the simplex solver later-on,
+ // anchors will keep their own preferred size.
+ sequence->sizeAtMinimum = pref;
+ sequence->sizeAtPreferred = pref;
+ sequence->sizeAtMaximum = pref;
+
+ sequence->setVertices(vertices);
+
+ sequence->from = before;
+ sequence->to = after;
+
+ // data here is the last edge in the sequence
+ // ### this seems to be here for supporting reverse order sequences,
+ // but doesnt seem to be used right now
+ if (data->from != vertices.last())
+ qSwap(sequence->from, sequence->to);
+
+ // Note that since layout 'edges' can't be simplified away from
+ // the graph, it's safe to assume that if there's a layout
+ // 'edge', it'll be in the boundaries of the sequence.
+ sequence->isLayoutAnchor = (sequence->m_edges.first()->isLayoutAnchor
+ || sequence->m_edges.last()->isLayoutAnchor);
+
+ AnchorData *newAnchor = sequence;
+ if (AnchorData *oldAnchor = graph->takeEdge(before, after)) {
+ newAnchor = new ParallelAnchorData(oldAnchor, sequence);
+
+ newAnchor->isLayoutAnchor = (oldAnchor->isLayoutAnchor
+ || sequence->isLayoutAnchor);
+
+ min = qMax(oldAnchor->minSize, sequence->minSize);
+ max = qMin(oldAnchor->maxSize, sequence->maxSize);
+
+ pref = qMax(oldAnchor->prefSize, sequence->prefSize);
+ pref = qMin(pref, max);
+
+ newAnchor->minSize = min;
+ newAnchor->prefSize = pref;
+ newAnchor->maxSize = max;
+
+ // Same as above, by default, keep preferred size.
+ newAnchor->sizeAtMinimum = pref;
+ newAnchor->sizeAtPreferred = pref;
+ newAnchor->sizeAtMaximum = pref;
+ }
+ graph->createEdge(before, after, newAnchor);
+
+ // True if we created a parallel anchor
+ return newAnchor != sequence;
+}
+
+/*!
+ \internal
+
+ The purpose of this function is to simplify the graph.
+ Simplification serves two purposes:
+ 1. Reduce the number of edges in the graph, (thus the number of variables to the equation
+ solver is reduced, and the solver performs better).
+ 2. Be able to do distribution of sequences of edges more intelligently (esp. with sequential
+ anchors)
+
+ It is essential that it must be possible to restore simplified anchors back to their "original"
+ form. This is done by restoreSimplifiedAnchor().
+
+ There are two types of simplification that can be done:
+ 1. Sequential simplification
+ Sequential simplification means that all sequences of anchors will be merged into one single
+ anchor. Only anhcors that points in the same direction will be merged.
+ 2. Parallel simplification
+ If a simplified sequential anchor is about to be inserted between two vertices in the graph
+ and there already exist an anchor between those two vertices, a parallel anchor will be
+ created that serves as a placeholder for the sequential anchor and the anchor that was
+ already between the two vertices.
+
+ The process of simplification can be described as:
+
+ 1. Simplify all sequences of anchors into one anchor.
+ If no further simplification was done, go to (3)
+ - If there already exist an anchor where the sequential anchor is supposed to be inserted,
+ take that anchor out of the graph
+ - Then create a parallel anchor that holds the sequential anchor and the anchor just taken
+ out of the graph.
+ 2. Go to (1)
+ 3. Done
+
+
+ * Gathering sequential anchors *
+ The algorithm walks the graph in depth-first order, and only collects vertices that has two
+ edges connected to it. If the vertex does not have two edges or if it is a layout edge,
+ it will take all the previously collected vertices and try to create a simplified sequential
+ anchor representing all the previously collected vertices.
+ Once the simplified anchor is inserted, the collected list is cleared in order to find the next
+ sequence to simplify.
+ Note that there are some catches to this that are not covered by the above explanation.
+*/
+void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation)
+{
+ static bool noSimplification = !qgetenv("QT_ANCHORLAYOUT_NO_SIMPLIFICATION").isEmpty();
+ if (noSimplification)
+ return;
+
+ if (graphSimplified[orientation])
+ return;
+ graphSimplified[orientation] = true;
+
+#if 0
+ qDebug("Simplifying Graph for %s",
+ orientation == Horizontal ? "Horizontal" : "Vertical");
+#endif
+
+ AnchorVertex *rootVertex = graph[orientation].rootVertex();
+
+ if (!rootVertex)
+ return;
+
+ bool dirty;
+ do {
+ dirty = simplifyGraphIteration(orientation);
+ } while (dirty);
+}
+
+bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutPrivate::Orientation orientation)
+{
+ Q_Q(QGraphicsAnchorLayout);
+ Graph<AnchorVertex, AnchorData> &g = graph[orientation];
+ AnchorVertex *v = g.rootVertex();
+
+ QSet<AnchorVertex *> visited;
+ QStack<AnchorVertex *> stack;
+ stack.push(v);
+ QVector<AnchorVertex*> candidates;
+
+ const Qt::AnchorPoint centerEdge = pickEdge(Qt::AnchorHorizontalCenter, orientation);
+ const Qt::AnchorPoint layoutEdge = oppositeEdge(v->m_edge);
+
+ bool dirty = false;
+
+ // walk depth-first.
+ while (!stack.isEmpty()) {
+ v = stack.pop();
+ QList<AnchorVertex *> vertices = g.adjacentVertices(v);
+ const int count = vertices.count();
+ bool endOfSequence = (v->m_item == q && v->m_edge == layoutEdge) || count != 2;
+ if (count == 2 && v->m_item != q) {
+ candidates.append(v);
+ if (visited.contains(vertices.first()) && visited.contains(vertices.last())) {
+ // in case of a cycle
+ endOfSequence = true;
+ }
+ }
+ if (endOfSequence && candidates.count() >= 2) {
+ int i;
+ AnchorVertex *afterSequence= 0;
+ QList<AnchorVertex *> adjacentOfSecondLastVertex = g.adjacentVertices(candidates.last());
+ Q_ASSERT(adjacentOfSecondLastVertex.count() == 2);
+ if (adjacentOfSecondLastVertex.first() == candidates.at(candidates.count() - 2))
+ afterSequence = adjacentOfSecondLastVertex.last();
+ else
+ afterSequence = adjacentOfSecondLastVertex.first();
+
+ AnchorVertex *beforeSequence = 0;
+ QList<AnchorVertex *> adjacentOfSecondVertex = g.adjacentVertices(candidates.first());
+ Q_ASSERT(adjacentOfSecondVertex.count() == 2);
+ if (adjacentOfSecondVertex.first() == candidates.at(1))
+ beforeSequence = adjacentOfSecondVertex.last();
+ else
+ beforeSequence = adjacentOfSecondVertex.first();
+ // The complete path of the sequence to simplify is: beforeSequence, <candidates>, afterSequence
+ // where beforeSequence and afterSequence are the endpoints where the anchor is inserted
+ // between.
+#if defined(QT_DEBUG) && 0
+ // ### DEBUG
+ QString strCandidates;
+ for (i = 0; i < candidates.count(); ++i)
+ strCandidates += QString::fromAscii("%1 - ").arg(candidates.at(i)->toString());
+ QString strPath = QString::fromAscii("%1 - %2%3").arg(beforeSequence->toString(), strCandidates, afterSequence->toString());
+ qDebug("candidate list for sequential simplification:\n[%s]", qPrintable(strPath));
+#endif
+
+ bool forward;
+ AnchorVertex *prev = beforeSequence;
+ int intervalFrom = 0;
+
+ // Check for directionality (from). We don't want to destroy that information,
+ // thus we only combine anchors with the same direction.
+
+ // "i" is the index *including* the beforeSequence and afterSequence vertices.
+ for (i = 1; i <= candidates.count() + 1; ++i) {
+ bool atVertexAfter = i > candidates.count();
+ AnchorVertex *v1 = atVertexAfter ? afterSequence : candidates.at(i - 1);
+ AnchorData *data = g.edgeData(prev, v1);
+ Q_ASSERT(data);
+ if (i == 1) {
+ forward = (prev == data->from ? true : false);
+ } else if (forward != (prev == data->from) || atVertexAfter) {
+ int intervalTo = i;
+ if (forward != (prev == data->from))
+ --intervalTo;
+
+ // intervalFrom and intervalTo should now be indices to the vertex before and
+ // after the sequential anchor.
+ if (intervalTo - intervalFrom >= 2) {
+ // simplify in the range [intervalFrom, intervalTo]
+
+ // Trim off internal center anchors (Left-Center/Center-Right) from the
+ // start and the end of the sequence. We never want to simplify internal
+ // center anchors where there is an external anchor connected to the center.
+ AnchorVertex *intervalVertexFrom = intervalFrom == 0 ? beforeSequence : candidates.at(intervalFrom - 1);
+ if (intervalVertexFrom->m_edge == centerEdge
+ && intervalVertexFrom->m_item == candidates.at(intervalFrom)->m_item) {
+ ++intervalFrom;
+ intervalVertexFrom = candidates.at(intervalFrom - 1);
+ }
+ AnchorVertex *intervalVertexTo = intervalTo <= candidates.count() ? candidates.at(intervalTo - 1) : afterSequence;
+ if (intervalVertexTo->m_edge == centerEdge
+ && intervalVertexTo->m_item == candidates.at(intervalTo - 2)->m_item) {
+ --intervalTo;
+ intervalVertexTo = candidates.at(intervalTo - 1);
+ }
+
+ QVector<AnchorVertex*> subCandidates;
+ if (forward) {
+ subCandidates = candidates.mid(intervalFrom, intervalTo - intervalFrom - 1);
+ } else {
+ // reverse the order of the candidates.
+ qSwap(intervalVertexFrom, intervalVertexTo);
+ do {
+ ++intervalFrom;
+ subCandidates.prepend(candidates.at(intervalFrom - 1));
+ } while (intervalFrom < intervalTo - 1);
+ }
+ if (simplifySequentialChunk(&g, intervalVertexFrom, subCandidates, intervalVertexTo)) {
+ dirty = true;
+ break;
+ }
+ // finished simplification of chunk with same direction
+ }
+ if (forward == (prev == data->from))
+ --intervalTo;
+ intervalFrom = intervalTo;
+
+ forward = !forward;
+ }
+ prev = v1;
+ }
+
+ if (dirty)
+ break;
+ }
+
+ if (endOfSequence)
+ candidates.clear();
+
+ for (int i = 0; i < count; ++i) {
+ AnchorVertex *next = vertices.at(i);
+ if (next->m_item == q && next->m_edge == centerEdge)
+ continue;
+ if (visited.contains(next))
+ continue;
+ stack.push(next);
+ }
+
+ visited.insert(v);
+ }
+
+ return dirty;
+}
+
+static void restoreSimplifiedAnchor(Graph<AnchorVertex, AnchorData> &g,
+ AnchorData *edge,
+ AnchorVertex *before,
+ AnchorVertex *after)
+{
+ Q_ASSERT(edge->type != AnchorData::Normal);
+#if 0
+ static const char *anchortypes[] = {"Normal",
+ "Sequential",
+ "Parallel"};
+ qDebug("Restoring %s edge.", anchortypes[int(edge->type)]);
+#endif
+ if (edge->type == AnchorData::Sequential) {
+ SequentialAnchorData* seqEdge = static_cast<SequentialAnchorData*>(edge);
+ // restore the sequential anchor
+ AnchorVertex *prev = before;
+ AnchorVertex *last = after;
+ if (edge->from != prev)
+ qSwap(last, prev);
+
+ for (int i = 0; i < seqEdge->m_edges.count(); ++i) {
+ AnchorVertex *v1 = (i < seqEdge->m_children.count()) ? seqEdge->m_children.at(i) : last;
+ AnchorData *data = seqEdge->m_edges.at(i);
+ if (data->type != AnchorData::Normal) {
+ restoreSimplifiedAnchor(g, data, prev, v1);
+ } else {
+ g.createEdge(prev, v1, data);
+ }
+ prev = v1;
+ }
+ } else if (edge->type == AnchorData::Parallel) {
+ ParallelAnchorData* parallelEdge = static_cast<ParallelAnchorData*>(edge);
+ AnchorData *parallelEdges[2] = {parallelEdge->firstEdge,
+ parallelEdge->secondEdge};
+ for (int i = 0; i < 2; ++i) {
+ AnchorData *data = parallelEdges[i];
+ if (data->type == AnchorData::Normal) {
+ g.createEdge(before, after, data);
+ } else {
+ restoreSimplifiedAnchor(g, data, before, after);
+ }
+ }
+ }
+}
+
+void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientation)
+{
+ if (!graphSimplified[orientation])
+ return;
+ graphSimplified[orientation] = false;
+
+#if 0
+ qDebug("Restoring Simplified Graph for %s",
+ orientation == Horizontal ? "Horizontal" : "Vertical");
+#endif
+
+ Graph<AnchorVertex, AnchorData> &g = graph[orientation];
+
+ QList<QPair<AnchorVertex*, AnchorVertex*> > connections = g.connections();
+ for (int i = 0; i < connections.count(); ++i) {
+ AnchorVertex *v1 = connections.at(i).first;
+ AnchorVertex *v2 = connections.at(i).second;
+ AnchorData *edge = g.edgeData(v1, v2);
+ if (edge->type != AnchorData::Normal) {
+ AnchorData *oldEdge = g.takeEdge(v1, v2);
+ restoreSimplifiedAnchor(g, edge, v1, v2);
+ delete oldEdge;
+ }
+ }
+}
+
+QGraphicsAnchorLayoutPrivate::Orientation
+QGraphicsAnchorLayoutPrivate::edgeOrientation(Qt::AnchorPoint edge)
+{
+ return edge > Qt::AnchorRight ? Vertical : Horizontal;
+}
+
+/*!
+ \internal
+
+ Create internal anchors to connect the layout edges (Left to Right and
+ Top to Bottom).
+
+ These anchors doesn't have size restrictions, that will be enforced by
+ other anchors and items in the layout.
+*/
+void QGraphicsAnchorLayoutPrivate::createLayoutEdges()
+{
+ Q_Q(QGraphicsAnchorLayout);
+ QGraphicsLayoutItem *layout = q;
+
+ // Horizontal
+ AnchorData *data = new AnchorData(0, 0, QWIDGETSIZE_MAX);
+ addAnchor(layout, Qt::AnchorLeft, layout,
+ Qt::AnchorRight, data);
+ data->skipInPreferred = 1;
+
+ // Set the Layout Left edge as the root of the horizontal graph.
+ AnchorVertex *v = internalVertex(layout, Qt::AnchorLeft);
+ graph[Horizontal].setRootVertex(v);
+
+ // Vertical
+ data = new AnchorData(0, 0, QWIDGETSIZE_MAX);
+ addAnchor(layout, Qt::AnchorTop, layout,
+ Qt::AnchorBottom, data);
+ data->skipInPreferred = 1;
+
+ // Set the Layout Top edge as the root of the vertical graph.
+ v = internalVertex(layout, Qt::AnchorTop);
+ graph[Vertical].setRootVertex(v);
+}
+
+void QGraphicsAnchorLayoutPrivate::deleteLayoutEdges()
+{
+ Q_Q(QGraphicsAnchorLayout);
+
+ Q_ASSERT(internalVertex(q, Qt::AnchorHorizontalCenter) == NULL);
+ Q_ASSERT(internalVertex(q, Qt::AnchorVerticalCenter) == NULL);
+
+ removeAnchor(q, Qt::AnchorLeft, q, Qt::AnchorRight);
+ removeAnchor(q, Qt::AnchorTop, q, Qt::AnchorBottom);
+}
+
+void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item)
+{
+ Q_ASSERT(!graphSimplified[Horizontal] && !graphSimplified[Vertical]);
+
+ items.append(item);
+
+ // Horizontal
+ int minimumSize = item->minimumWidth();
+ int preferredSize = item->preferredWidth();
+ int maximumSize = item->maximumWidth();
+
+ AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize);
+ addAnchor(item, Qt::AnchorLeft, item,
+ Qt::AnchorRight, data);
+
+ // Vertical
+ minimumSize = item->minimumHeight();
+ preferredSize = item->preferredHeight();
+ maximumSize = item->maximumHeight();
+
+ data = new AnchorData(minimumSize, preferredSize, maximumSize);
+ addAnchor(item, Qt::AnchorTop, item,
+ Qt::AnchorBottom, data);
+}
+
+/*!
+ \internal
+
+ By default, each item in the layout is represented internally as
+ a single anchor in each direction. For instance, from Left to Right.
+
+ However, to support anchorage of items to the center of items, we
+ must split this internal anchor into two half-anchors. From Left
+ to Center and then from Center to Right, with the restriction that
+ these anchors must have the same time at all times.
+*/
+void QGraphicsAnchorLayoutPrivate::createCenterAnchors(
+ QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge)
+{
+ Orientation orientation;
+ switch (centerEdge) {
+ case Qt::AnchorHorizontalCenter:
+ orientation = Horizontal;
+ break;
+ case Qt::AnchorVerticalCenter:
+ orientation = Vertical;
+ break;
+ default:
+ // Don't create center edges unless needed
+ return;
+ }
+
+ Q_ASSERT(!graphSimplified[orientation]);
+
+ // Check if vertex already exists
+ if (internalVertex(item, centerEdge))
+ return;
+
+ // Orientation code
+ Qt::AnchorPoint firstEdge;
+ Qt::AnchorPoint lastEdge;
+
+ if (orientation == Horizontal) {
+ firstEdge = Qt::AnchorLeft;
+ lastEdge = Qt::AnchorRight;
+ } else {
+ firstEdge = Qt::AnchorTop;
+ lastEdge = Qt::AnchorBottom;
+ }
+
+ AnchorVertex *first = internalVertex(item, firstEdge);
+ AnchorVertex *last = internalVertex(item, lastEdge);
+ Q_ASSERT(first && last);
+
+ // Create new anchors
+ AnchorData *oldData = graph[orientation].edgeData(first, last);
+
+ int minimumSize = oldData->minSize / 2;
+ int preferredSize = oldData->prefSize / 2;
+ int maximumSize = oldData->maxSize / 2;
+
+ QSimplexConstraint *c = new QSimplexConstraint;
+ AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize);
+ c->variables.insert(data, 1.0);
+ addAnchor(item, firstEdge, item, centerEdge, data);
+
+ data = new AnchorData(minimumSize, preferredSize, maximumSize);
+ c->variables.insert(data, -1.0);
+ addAnchor(item, centerEdge, item, lastEdge, data);
+
+ itemCenterConstraints[orientation].append(c);
+
+ // Remove old one
+ removeAnchor(item, firstEdge, item, lastEdge);
+}
+
+void QGraphicsAnchorLayoutPrivate::removeCenterAnchors(
+ QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge,
+ bool substitute)
+{
+ Orientation orientation;
+ switch (centerEdge) {
+ case Qt::AnchorHorizontalCenter:
+ orientation = Horizontal;
+ break;
+ case Qt::AnchorVerticalCenter:
+ orientation = Vertical;
+ break;
+ default:
+ // Don't remove edges that not the center ones
+ return;
+ }
+
+ Q_ASSERT(!graphSimplified[orientation]);
+
+ // Orientation code
+ Qt::AnchorPoint firstEdge;
+ Qt::AnchorPoint lastEdge;
+
+ if (orientation == Horizontal) {
+ firstEdge = Qt::AnchorLeft;
+ lastEdge = Qt::AnchorRight;
+ } else {
+ firstEdge = Qt::AnchorTop;
+ lastEdge = Qt::AnchorBottom;
+ }
+
+ AnchorVertex *center = internalVertex(item, centerEdge);
+ if (!center)
+ return;
+ AnchorVertex *first = internalVertex(item, firstEdge);
+
+ Q_ASSERT(first);
+ Q_ASSERT(center);
+
+ Graph<AnchorVertex, AnchorData> &g = graph[orientation];
+
+
+ AnchorData *oldData = g.edgeData(first, center);
+ // Remove center constraint
+ for (int i = itemCenterConstraints[orientation].count() - 1; i >= 0; --i) {
+ if (itemCenterConstraints[orientation][i]->variables.contains(oldData)) {
+ delete itemCenterConstraints[orientation].takeAt(i);
+ break;
+ }
+ }
+
+ if (substitute) {
+ // Create the new anchor that should substitute the left-center-right anchors.
+ AnchorData *oldData = g.edgeData(first, center);
+
+ int minimumSize = oldData->minSize * 2;
+ int preferredSize = oldData->prefSize * 2;
+ int maximumSize = oldData->maxSize * 2;
+
+ AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize);
+ addAnchor(item, firstEdge, item, lastEdge, data);
+
+ // Remove old anchors
+ removeAnchor(item, firstEdge, item, centerEdge);
+ removeAnchor(item, centerEdge, item, lastEdge);
+
+ } else {
+ // this is only called from removeAnchors()
+ // first, remove all non-internal anchors
+ QList<AnchorVertex*> adjacents = g.adjacentVertices(center);
+ for (int i = 0; i < adjacents.count(); ++i) {
+ AnchorVertex *v = adjacents.at(i);
+ if (v->m_item != item) {
+ removeAnchor(item, centerEdge, v->m_item, v->m_edge);
+ }
+ }
+ // when all non-internal anchors is removed it will automatically merge the
+ // center anchor into a left-right (or top-bottom) anchor. We must also delete that.
+ // by this time, the center vertex is deleted and merged into a non-centered internal anchor
+ removeAnchor(item, firstEdge, item, lastEdge);
+ }
+}
+
+
+void QGraphicsAnchorLayoutPrivate::removeCenterConstraints(QGraphicsLayoutItem *item,
+ Orientation orientation)
+{
+ Q_ASSERT(!graphSimplified[orientation]);
+
+ // Remove the item center constraints associated to this item
+ // ### This is a temporary solution. We should probably use a better
+ // data structure to hold items and/or their associated constraints
+ // so that we can remove those easily
+
+ AnchorVertex *first = internalVertex(item, orientation == Horizontal ?
+ Qt::AnchorLeft :
+ Qt::AnchorTop);
+ AnchorVertex *center = internalVertex(item, orientation == Horizontal ?
+ Qt::AnchorHorizontalCenter :
+ Qt::AnchorVerticalCenter);
+
+ // Skip if no center constraints exist
+ if (!center)
+ return;
+
+ Q_ASSERT(first);
+ AnchorData *internalAnchor = graph[orientation].edgeData(first, center);
+
+ // Look for our anchor in all item center constraints, then remove it
+ for (int i = 0; i < itemCenterConstraints[orientation].size(); ++i) {
+ if (itemCenterConstraints[orientation][i]->variables.contains(internalAnchor)) {
+ delete itemCenterConstraints[orientation].takeAt(i);
+ break;
+ }
+ }
+}
+
+/*!
+ * \internal
+ *
+ * Helper function that is called from the anchor functions in the public API.
+ * If \a spacing is 0, it will pick up the spacing defined by the style.
+ */
+void QGraphicsAnchorLayoutPrivate::anchor(QGraphicsLayoutItem *firstItem,
+ Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem,
+ Qt::AnchorPoint secondEdge,
+ qreal *spacing)
+{
+ Q_Q(QGraphicsAnchorLayout);
+ if ((firstItem == 0) || (secondItem == 0)) {
+ qWarning("QGraphicsAnchorLayout::addAnchor(): "
+ "Cannot anchor NULL items");
+ return;
+ }
+
+ if (firstItem == secondItem) {
+ qWarning("QGraphicsAnchorLayout::addAnchor(): "
+ "Cannot anchor the item to itself");
+ return;
+ }
+
+ if (edgeOrientation(secondEdge) != edgeOrientation(firstEdge)) {
+ qWarning("QGraphicsAnchorLayout::addAnchor(): "
+ "Cannot anchor edges of different orientations");
+ return;
+ }
+
+ // Guarantee that the graph is no simplified when adding this anchor,
+ // anchor manipulation always happen in the full graph
+ restoreSimplifiedGraph(edgeOrientation(firstEdge));
+
+ // In QGraphicsAnchorLayout, items are represented in its internal
+ // graph as four anchors that connect:
+ // - Left -> HCenter
+ // - HCenter-> Right
+ // - Top -> VCenter
+ // - VCenter -> Bottom
+
+ // Ensure that the internal anchors have been created for both items.
+ if (firstItem != q && !items.contains(firstItem)) {
+ restoreSimplifiedGraph(edgeOrientation(firstEdge) == Horizontal ? Vertical : Horizontal);
+ createItemEdges(firstItem);
+ addChildLayoutItem(firstItem);
+ }
+ if (secondItem != q && !items.contains(secondItem)) {
+ restoreSimplifiedGraph(edgeOrientation(firstEdge) == Horizontal ? Vertical : Horizontal);
+ createItemEdges(secondItem);
+ addChildLayoutItem(secondItem);
+ }
+
+ // Create center edges if needed
+ createCenterAnchors(firstItem, firstEdge);
+ createCenterAnchors(secondItem, secondEdge);
+
+ // Use heuristics to find out what the user meant with this anchor.
+ correctEdgeDirection(firstItem, firstEdge, secondItem, secondEdge);
+
+ AnchorData *data;
+ if (!spacing) {
+ // If firstItem or secondItem is the layout itself, the spacing will default to 0.
+ // Otherwise, the following matrix is used (questionmark means that the spacing
+ // is queried from the style):
+ // from
+ // to Left HCenter Right
+ // Left 0 0 ?
+ // HCenter 0 0 0
+ // Right ? 0 0
+ if (firstItem != q
+ && secondItem != q
+ && pickEdge(firstEdge, Horizontal) != Qt::AnchorHorizontalCenter
+ && oppositeEdge(firstEdge) == secondEdge) {
+ data = new AnchorData; // ask the style later
+ } else {
+ data = new AnchorData(0); // spacing should be 0
+ }
+ addAnchor(firstItem, firstEdge, secondItem, secondEdge, data);
+ } else if (*spacing >= 0) {
+ data = new AnchorData(*spacing);
+ addAnchor(firstItem, firstEdge, secondItem, secondEdge, data);
+ } else {
+ data = new AnchorData(-*spacing);
+ addAnchor(secondItem, secondEdge, firstItem, firstEdge, data);
+ }
+}
+
+void QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem,
+ Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem,
+ Qt::AnchorPoint secondEdge,
+ AnchorData *data)
+{
+ Q_Q(QGraphicsAnchorLayout);
+
+ // Guarantee that the graph is no simplified when adding this anchor,
+ // anchor manipulation always happen in the full graph
+ restoreSimplifiedGraph(edgeOrientation(firstEdge));
+
+ // Is the Vertex (firstItem, firstEdge) already represented in our
+ // internal structure?
+ AnchorVertex *v1 = addInternalVertex(firstItem, firstEdge);
+ AnchorVertex *v2 = addInternalVertex(secondItem, secondEdge);
+
+ // Remove previous anchor
+ // ### Could we update the existing edgeData rather than creating a new one?
+ if (graph[edgeOrientation(firstEdge)].edgeData(v1, v2))
+ removeAnchor(firstItem, firstEdge, secondItem, secondEdge);
+
+ // Create a bi-directional edge in the sense it can be transversed both
+ // from v1 or v2. "data" however is shared between the two references
+ // so we still know that the anchor direction is from 1 to 2.
+ data->from = v1;
+ data->to = v2;
+#ifdef QT_DEBUG
+ data->name = QString::fromAscii("%1 --to--> %2").arg(v1->toString()).arg(v2->toString());
+#endif
+ // Keep track of anchors that are connected to the layout 'edges'
+ data->isLayoutAnchor = (v1->m_item == q || v2->m_item == q);
+
+ graph[edgeOrientation(firstEdge)].createEdge(v1, v2, data);
+}
+
+void QGraphicsAnchorLayoutPrivate::removeAnchor(QGraphicsLayoutItem *firstItem,
+ Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem,
+ Qt::AnchorPoint secondEdge)
+{
+ // Guarantee that the graph is no simplified when adding this anchor,
+ // anchor manipulation always happen in the full graph
+ restoreSimplifiedGraph(edgeOrientation(firstEdge));
+
+ // Look for both vertices
+ AnchorVertex *v1 = internalVertex(firstItem, firstEdge);
+ AnchorVertex *v2 = internalVertex(secondItem, secondEdge);
+
+ Q_ASSERT(v1 && v2);
+
+ // Remove edge from graph
+ graph[edgeOrientation(firstEdge)].removeEdge(v1, v2);
+
+ // Decrease vertices reference count (may trigger a deletion)
+ removeInternalVertex(firstItem, firstEdge);
+ removeInternalVertex(secondItem, secondEdge);
+}
+
+bool QGraphicsAnchorLayoutPrivate::setAnchorSize(const QGraphicsLayoutItem *firstItem,
+ Qt::AnchorPoint firstEdge,
+ const QGraphicsLayoutItem *secondItem,
+ Qt::AnchorPoint secondEdge,
+ const qreal *anchorSize)
+{
+ // ### we can avoid restoration if we really want to, but we would have to
+ // search recursively through all composite anchors
+ restoreSimplifiedGraph(edgeOrientation(firstEdge));
+ AnchorVertex *v1 = internalVertex(firstItem, firstEdge);
+ AnchorVertex *v2 = internalVertex(secondItem, secondEdge);
+
+ AnchorData *data = graph[edgeOrientation(firstEdge)].edgeData(v1, v2);
+ if (data) {
+ if (anchorSize) {
+ data->setFixedSize(*anchorSize);
+ } else {
+ data->unsetSize();
+ }
+ }
+
+ return data;
+}
+
+bool QGraphicsAnchorLayoutPrivate::anchorSize(const QGraphicsLayoutItem *firstItem,
+ Qt::AnchorPoint firstEdge,
+ const QGraphicsLayoutItem *secondItem,
+ Qt::AnchorPoint secondEdge,
+ qreal *minSize,
+ qreal *prefSize,
+ qreal *maxSize) const
+{
+ Q_ASSERT(minSize || prefSize || maxSize);
+ QGraphicsAnchorLayoutPrivate *that = const_cast<QGraphicsAnchorLayoutPrivate *>(this);
+ that->restoreSimplifiedGraph(edgeOrientation(firstEdge));
+ AnchorVertex *v1 = internalVertex(firstItem, firstEdge);
+ AnchorVertex *v2 = internalVertex(secondItem, secondEdge);
+
+ AnchorData *data = that->graph[edgeOrientation(firstEdge)].edgeData(v1, v2);
+ if (data) {
+ if (minSize)
+ *minSize = data->minSize;
+ if (prefSize)
+ *prefSize = data->prefSize;
+ if (maxSize)
+ *maxSize = data->maxSize;
+ }
+ return data;
+}
+
+AnchorVertex *QGraphicsAnchorLayoutPrivate::addInternalVertex(QGraphicsLayoutItem *item,
+ Qt::AnchorPoint edge)
+{
+ QPair<QGraphicsLayoutItem *, Qt::AnchorPoint> pair(item, edge);
+ QPair<AnchorVertex *, int> v = m_vertexList.value(pair);
+
+ if (!v.first) {
+ Q_ASSERT(v.second == 0);
+ v.first = new AnchorVertex(item, edge);
+ }
+ v.second++;
+ m_vertexList.insert(pair, v);
+ return v.first;
+}
+
+/**
+ * \internal
+ *
+ * returns the AnchorVertex that was dereferenced, also when it was removed.
+ * returns 0 if it did not exist.
+ */
+void QGraphicsAnchorLayoutPrivate::removeInternalVertex(QGraphicsLayoutItem *item,
+ Qt::AnchorPoint edge)
+{
+ QPair<QGraphicsLayoutItem *, Qt::AnchorPoint> pair(item, edge);
+ QPair<AnchorVertex *, int> v = m_vertexList.value(pair);
+
+ if (!v.first) {
+ qWarning("This item with this edge is not in the graph");
+ return;
+ }
+
+ v.second--;
+ if (v.second == 0) {
+ // Remove reference and delete vertex
+ m_vertexList.remove(pair);
+ delete v.first;
+ } else {
+ // Update reference count
+ m_vertexList.insert(pair, v);
+
+ if ((v.second == 2) &&
+ ((edge == Qt::AnchorHorizontalCenter) ||
+ (edge == Qt::AnchorVerticalCenter))) {
+ removeCenterAnchors(item, edge, true);
+ }
+ }
+}
+
+void QGraphicsAnchorLayoutPrivate::removeVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge)
+{
+ if (AnchorVertex *v = internalVertex(item, edge)) {
+ Graph<AnchorVertex, AnchorData> &g = graph[edgeOrientation(edge)];
+ const QList<AnchorVertex *> allVertices = graph[edgeOrientation(edge)].adjacentVertices(v);
+ AnchorVertex *v2;
+ foreach (v2, allVertices) {
+ g.removeEdge(v, v2);
+ removeInternalVertex(item, edge);
+ removeInternalVertex(v2->m_item, v2->m_edge);
+ }
+ }
+}
+
+void QGraphicsAnchorLayoutPrivate::removeAnchors(QGraphicsLayoutItem *item)
+{
+ Q_ASSERT(!graphSimplified[Horizontal] && !graphSimplified[Vertical]);
+
+ // remove the center anchor first!!
+ removeCenterAnchors(item, Qt::AnchorHorizontalCenter, false);
+ removeVertex(item, Qt::AnchorLeft);
+ removeVertex(item, Qt::AnchorRight);
+
+ removeCenterAnchors(item, Qt::AnchorVerticalCenter, false);
+ removeVertex(item, Qt::AnchorTop);
+ removeVertex(item, Qt::AnchorBottom);
+}
+
+/*!
+ \internal
+
+ Use heuristics to determine the correct orientation of a given anchor.
+
+ After API discussions, we decided we would like expressions like
+ anchor(A, Left, B, Right) to mean the same as anchor(B, Right, A, Left).
+ The problem with this is that anchors could become ambiguous, for
+ instance, what does the anchor A, B of size X mean?
+
+ "pos(B) = pos(A) + X" or "pos(A) = pos(B) + X" ?
+
+ To keep the API user friendly and at the same time, keep our algorithm
+ deterministic, we use an heuristic to determine a direction for each
+ added anchor and then keep it. The heuristic is based on the fact
+ that people usually avoid overlapping items, therefore:
+
+ "A, RIGHT to B, LEFT" means that B is to the LEFT of A.
+ "B, LEFT to A, RIGHT" is corrected to the above anchor.
+
+ Special correction is also applied when one of the items is the
+ layout. We handle Layout Left as if it was another items's Right
+ and Layout Right as another item's Left.
+*/
+void QGraphicsAnchorLayoutPrivate::correctEdgeDirection(QGraphicsLayoutItem *&firstItem,
+ Qt::AnchorPoint &firstEdge,
+ QGraphicsLayoutItem *&secondItem,
+ Qt::AnchorPoint &secondEdge)
+{
+ Q_Q(QGraphicsAnchorLayout);
+
+ Qt::AnchorPoint effectiveFirst = firstEdge;
+ Qt::AnchorPoint effectiveSecond = secondEdge;
+
+ if (firstItem == q)
+ effectiveFirst = QGraphicsAnchorLayoutPrivate::oppositeEdge(firstEdge);
+ if (secondItem == q)
+ effectiveSecond = QGraphicsAnchorLayoutPrivate::oppositeEdge(secondEdge);
+
+ if (effectiveFirst < effectiveSecond) {
+
+ // ### DEBUG
+ /* printf("Swapping Anchor from %s %d --to--> %s %d\n",
+ firstItem->isLayout() ? "<layout>" :
+ qPrintable(static_cast<QGraphicsWidget *>(firstItem)->data(0).toString()),
+ firstEdge,
+ secondItem->isLayout() ? "<layout>" :
+ qPrintable(static_cast<QGraphicsWidget *>(secondItem)->data(0).toString()),
+ secondEdge);
+ */
+ qSwap(firstItem, secondItem);
+ qSwap(firstEdge, secondEdge);
+ }
+}
+
+qreal QGraphicsAnchorLayoutPrivate::effectiveSpacing(Orientation orientation) const
+{
+ Q_Q(const QGraphicsAnchorLayout);
+ qreal s = spacings[orientation];
+ if (s < 0) {
+ // ### make sure behaviour is the same as in QGraphicsGridLayout
+ QGraphicsLayoutItem *parent = q->parentLayoutItem();
+ while (parent && parent->isLayout()) {
+ parent = parent->parentLayoutItem();
+ }
+ if (parent) {
+ QGraphicsItem *parentItem = parent->graphicsItem();
+ if (parentItem && parentItem->isWidget()) {
+ QGraphicsWidget *w = static_cast<QGraphicsWidget*>(parentItem);
+ s = w->style()->pixelMetric(orientation == Horizontal
+ ? QStyle::PM_LayoutHorizontalSpacing
+ : QStyle::PM_LayoutVerticalSpacing);
+ }
+ }
+ }
+ return s;
+}
+
+/*!
+ \internal
+
+ Called on activation. Uses Linear Programming to define minimum, preferred
+ and maximum sizes for the layout. Also calculates the sizes that each item
+ should assume when the layout is in one of such situations.
+*/
+void QGraphicsAnchorLayoutPrivate::calculateGraphs()
+{
+ if (!calculateGraphCacheDirty)
+ return;
+
+ calculateGraphs(Horizontal);
+ calculateGraphs(Vertical);
+
+ calculateGraphCacheDirty = 0;
+}
+
+// ### remove me:
+QList<AnchorData *> getVariables(QList<QSimplexConstraint *> constraints)
+{
+ QSet<AnchorData *> variableSet;
+ for (int i = 0; i < constraints.count(); ++i) {
+ const QSimplexConstraint *c = constraints[i];
+ foreach (QSimplexVariable *var, c->variables.keys()) {
+ variableSet += static_cast<AnchorData *>(var);
+ }
+ }
+ return variableSet.toList();
+}
+
+/*!
+ \internal
+
+ Calculate graphs is the method that puts together all the helper routines
+ so that the AnchorLayout can calculate the sizes of each item.
+
+ In a nutshell it should do:
+
+ 1) Update anchor nominal sizes, that is, the size that each anchor would
+ have if no other restrictions applied. This is done by quering the
+ layout style and the sizeHints of the items belonging to the layout.
+
+ 2) Simplify the graph by grouping together parallel and sequential anchors
+ into "group anchors". These have equivalent minimum, preferred and maximum
+ sizeHints as the anchors they replace.
+
+ 3) Check if we got to a trivial case. In some cases, the whole graph can be
+ simplified into a single anchor. If so, use this information. If not,
+ then call the Simplex solver to calculate the anchors sizes.
+
+ 4) Once the root anchors had its sizes calculated, propagate that to the
+ anchors they represent.
+*/
+void QGraphicsAnchorLayoutPrivate::calculateGraphs(
+ QGraphicsAnchorLayoutPrivate::Orientation orientation)
+{
+ Q_Q(QGraphicsAnchorLayout);
+
+ // Simplify the graph
+ simplifyGraph(orientation);
+
+ // Reset the nominal sizes of each anchor based on the current item sizes
+ setAnchorSizeHintsFromItems(orientation);
+
+ // Traverse all graph edges and store the possible paths to each vertex
+ findPaths(orientation);
+
+ // From the paths calculated above, extract the constraints that the current
+ // anchor setup impose, to our Linear Programming problem.
+ constraintsFromPaths(orientation);
+
+ // Split the constraints and anchors into groups that should be fed to the
+ // simplex solver independently. Currently we find two groups:
+ //
+ // 1) The "trunk", that is, the set of anchors (items) that are connected
+ // to the two opposite sides of our layout, and thus need to stretch in
+ // order to fit in the current layout size.
+ //
+ // 2) The floating or semi-floating anchors (items) that are those which
+ // are connected to only one (or none) of the layout sides, thus are not
+ // influenced by the layout size.
+ QList<QList<QSimplexConstraint *> > parts;
+ parts = getGraphParts(orientation);
+
+ // Now run the simplex solver to calculate Minimum, Preferred and Maximum sizes
+ // of the "trunk" set of constraints and variables.
+ // ### does trunk always exist? empty = trunk is the layout left->center->right
+ QList<QSimplexConstraint *> trunkConstraints = parts[0];
+ QList<QSimplexConstraint *> sizeHintConstraints;
+ sizeHintConstraints = constraintsFromSizeHints(getVariables(trunkConstraints));
+ trunkConstraints += sizeHintConstraints;
+
+ // For minimum and maximum, use the path between the two layout sides as the
+ // objective function.
+
+ // Retrieve that path
+ AnchorVertex *v = internalVertex(q, pickEdge(Qt::AnchorRight, orientation));
+ GraphPath trunkPath = graphPaths[orientation].value(v);
+
+ if (!trunkConstraints.isEmpty()) {
+#if 0
+ qDebug("Simplex used for trunk of %s",
+ orientation == Horizontal ? "Horizontal" : "Vertical");
+#endif
+
+ // Solve min and max size hints for trunk
+ QPair<qreal, qreal> minMax = solveMinMax(trunkConstraints, trunkPath);
+ sizeHints[orientation][Qt::MinimumSize] = minMax.first;
+ sizeHints[orientation][Qt::MaximumSize] = minMax.second;
+
+ // Solve for preferred. The objective function is calculated from the constraints
+ // and variables internally.
+ solvePreferred(trunkConstraints);
+
+ // Propagate the new sizes down the simplified graph, ie. tell the
+ // group anchors to set their children anchors sizes.
+
+ // ### we calculated variables already a few times, can't we reuse that?
+ QList<AnchorData *> trunkVariables = getVariables(trunkConstraints);
+
+ for (int i = 0; i < trunkVariables.count(); ++i)
+ trunkVariables.at(i)->updateChildrenSizes();
+
+ // Calculate and set the preferred size for the layout from the edge sizes that
+ // were calculated above.
+ qreal pref(0.0);
+ foreach (const AnchorData *ad, trunkPath.positives) {
+ pref += ad->sizeAtPreferred;
+ }
+ foreach (const AnchorData *ad, trunkPath.negatives) {
+ pref -= ad->sizeAtPreferred;
+ }
+ sizeHints[orientation][Qt::PreferredSize] = pref;
+ } else {
+#if 0
+ qDebug("Simplex NOT used for trunk of %s",
+ orientation == Horizontal ? "Horizontal" : "Vertical");
+#endif
+
+ // No Simplex is necessary because the path was simplified all the way to a single
+ // anchor.
+ Q_ASSERT(trunkPath.positives.count() == 1);
+ Q_ASSERT(trunkPath.negatives.count() == 0);
+
+ AnchorData *ad = trunkPath.positives.toList()[0];
+ ad->sizeAtMinimum = ad->minSize;
+ ad->sizeAtPreferred = ad->prefSize;
+ ad->sizeAtMaximum = ad->maxSize;
+
+ // Propagate
+ ad->updateChildrenSizes();
+
+ sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum;
+ sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred;
+ sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum;
+ }
+
+ // Delete the constraints, we won't use them anymore.
+ qDeleteAll(sizeHintConstraints);
+ sizeHintConstraints.clear();
+
+ // For the other parts that not the trunk, solve only for the preferred size
+ // that is the size they will remain at, since they are not stretched by the
+ // layout.
+
+ // Solve the other only for preferred, skip trunk
+ for (int i = 1; i < parts.count(); ++i) {
+ QList<QSimplexConstraint *> partConstraints = parts[i];
+ QList<AnchorData *> partVariables = getVariables(partConstraints);
+ Q_ASSERT(!partVariables.isEmpty());
+
+ sizeHintConstraints = constraintsFromSizeHints(partVariables);
+ partConstraints += sizeHintConstraints;
+ solvePreferred(partConstraints);
+
+ // Propagate size at preferred to other sizes. Semi-floats
+ // always will be in their sizeAtPreferred.
+ for (int j = 0; j < partVariables.count(); ++j) {
+ AnchorData *ad = partVariables[j];
+ Q_ASSERT(ad);
+ ad->sizeAtMinimum = ad->sizeAtPreferred;
+ ad->sizeAtMaximum = ad->sizeAtPreferred;
+ ad->updateChildrenSizes();
+ }
+
+ // Delete the constraints, we won't use them anymore.
+ qDeleteAll(sizeHintConstraints);
+ sizeHintConstraints.clear();
+ }
+
+ // Clean up our data structures. They are not needed anymore since
+ // distribution uses just interpolation.
+ qDeleteAll(constraints[orientation]);
+ constraints[orientation].clear();
+ graphPaths[orientation].clear(); // ###
+}
+
+/*!
+ \internal
+
+ For graph edges ("anchors") that represent items, this method updates their
+ intrinsic size restrictions, based on the item size hints.
+*/
+void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orientation)
+{
+ Graph<AnchorVertex, AnchorData> &g = graph[orientation];
+ QList<QPair<AnchorVertex *, AnchorVertex *> > vertices = g.connections();
+
+ qreal spacing = effectiveSpacing(orientation);
+
+ for (int i = 0; i < vertices.count(); ++i) {
+ AnchorData *data = g.edgeData(vertices.at(i).first, vertices.at(i).second);;
+ Q_ASSERT(data->from && data->to);
+ data->refreshSizeHints(spacing);
+ }
+}
+
+/*!
+ \internal
+
+ This method walks the graph using a breadth-first search to find paths
+ between the root vertex and each vertex on the graph. The edges
+ directions in each path are considered and they are stored as a
+ positive edge (left-to-right) or negative edge (right-to-left).
+
+ The list of paths is used later to generate a list of constraints.
+ */
+void QGraphicsAnchorLayoutPrivate::findPaths(Orientation orientation)
+{
+ QQueue<QPair<AnchorVertex *, AnchorVertex *> > queue;
+
+ QSet<AnchorData *> visited;
+
+ AnchorVertex *root = graph[orientation].rootVertex();
+
+ graphPaths[orientation].insert(root, GraphPath());
+
+ foreach (AnchorVertex *v, graph[orientation].adjacentVertices(root)) {
+ queue.enqueue(qMakePair(root, v));
+ }
+
+ while(!queue.isEmpty()) {
+ QPair<AnchorVertex *, AnchorVertex *> pair = queue.dequeue();
+ AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second);
+
+ if (visited.contains(edge))
+ continue;
+
+ visited.insert(edge);
+ GraphPath current = graphPaths[orientation].value(pair.first);
+
+ if (edge->from == pair.first)
+ current.positives.insert(edge);
+ else
+ current.negatives.insert(edge);
+
+ graphPaths[orientation].insert(pair.second, current);
+
+ foreach (AnchorVertex *v,
+ graph[orientation].adjacentVertices(pair.second)) {
+ queue.enqueue(qMakePair(pair.second, v));
+ }
+ }
+}
+
+/*!
+ \internal
+
+ Each vertex on the graph that has more than one path to it
+ represents a contra int to the sizes of the items in these paths.
+
+ This method walks the list of paths to each vertex, generate
+ the constraints and store them in a list so they can be used later
+ by the Simplex solver.
+*/
+void QGraphicsAnchorLayoutPrivate::constraintsFromPaths(Orientation orientation)
+{
+ foreach (AnchorVertex *vertex, graphPaths[orientation].uniqueKeys())
+ {
+ int valueCount = graphPaths[orientation].count(vertex);
+ if (valueCount == 1)
+ continue;
+
+ QList<GraphPath> pathsToVertex = graphPaths[orientation].values(vertex);
+ for (int i = 1; i < valueCount; ++i) {
+ constraints[orientation] += \
+ pathsToVertex[0].constraint(pathsToVertex[i]);
+ }
+ }
+}
+
+/*!
+ \internal
+
+ Create LP constraints for each anchor based on its minimum and maximum
+ sizes, as specified in its size hints
+*/
+QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHints(
+ const QList<AnchorData *> &anchors)
+{
+ QList<QSimplexConstraint *> anchorConstraints;
+ for (int i = 0; i < anchors.size(); ++i) {
+ QSimplexConstraint *c = new QSimplexConstraint;
+ c->variables.insert(anchors[i], 1.0);
+ c->constant = anchors[i]->minSize;
+ c->ratio = QSimplexConstraint::MoreOrEqual;
+ anchorConstraints += c;
+
+ c = new QSimplexConstraint;
+ c->variables.insert(anchors[i], 1.0);
+ c->constant = anchors[i]->maxSize;
+ c->ratio = QSimplexConstraint::LessOrEqual;
+ anchorConstraints += c;
+ }
+
+ return anchorConstraints;
+}
+
+/*!
+ \internal
+*/
+QList< QList<QSimplexConstraint *> >
+QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation)
+{
+ Q_Q(QGraphicsAnchorLayout);
+
+ // Find layout vertices and edges for the current orientation.
+ AnchorVertex *layoutFirstVertex = \
+ internalVertex(q, pickEdge(Qt::AnchorLeft, orientation));
+
+ AnchorVertex *layoutCentralVertex = \
+ internalVertex(q, pickEdge(Qt::AnchorHorizontalCenter, orientation));
+
+ AnchorVertex *layoutLastVertex = \
+ internalVertex(q, pickEdge(Qt::AnchorRight, orientation));
+
+ Q_ASSERT(layoutFirstVertex && layoutLastVertex);
+
+ AnchorData *edgeL1 = NULL;
+ AnchorData *edgeL2 = NULL;
+
+ // The layout may have a single anchor between Left and Right or two half anchors
+ // passing through the center
+ if (layoutCentralVertex) {
+ edgeL1 = graph[orientation].edgeData(layoutFirstVertex, layoutCentralVertex);
+ edgeL2 = graph[orientation].edgeData(layoutCentralVertex, layoutLastVertex);
+ } else {
+ edgeL1 = graph[orientation].edgeData(layoutFirstVertex, layoutLastVertex);
+ }
+
+ QLinkedList<QSimplexConstraint *> remainingConstraints;
+ for (int i = 0; i < constraints[orientation].count(); ++i) {
+ remainingConstraints += constraints[orientation][i];
+ }
+ for (int i = 0; i < itemCenterConstraints[orientation].count(); ++i) {
+ remainingConstraints += itemCenterConstraints[orientation][i];
+ }
+
+ QList<QSimplexConstraint *> trunkConstraints;
+ QSet<QSimplexVariable *> trunkVariables;
+
+ trunkVariables += edgeL1;
+ if (edgeL2)
+ trunkVariables += edgeL2;
+
+ bool dirty;
+ do {
+ dirty = false;
+
+ QLinkedList<QSimplexConstraint *>::iterator it = remainingConstraints.begin();
+ while (it != remainingConstraints.end()) {
+ QSimplexConstraint *c = *it;
+ bool match = false;
+
+ // Check if this constraint have some overlap with current
+ // trunk variables...
+ foreach (QSimplexVariable *ad, trunkVariables) {
+ if (c->variables.contains(ad)) {
+ match = true;
+ break;
+ }
+ }
+
+ // If so, we add it to trunk, and erase it from the
+ // remaining constraints.
+ if (match) {
+ trunkConstraints += c;
+ trunkVariables += QSet<QSimplexVariable *>::fromList(c->variables.keys());
+ it = remainingConstraints.erase(it);
+ dirty = true;
+ } else {
+ // Note that we don't erase the constraint if it's not
+ // a match, since in a next iteration of a do-while we
+ // can pass on it again and it will be a match.
+ //
+ // For example: if trunk share a variable with
+ // remainingConstraints[1] and it shares with
+ // remainingConstraints[0], we need a second iteration
+ // of the do-while loop to match both.
+ ++it;
+ }
+ }
+ } while (dirty);
+
+ QList< QList<QSimplexConstraint *> > result;
+ result += trunkConstraints;
+
+ if (!remainingConstraints.isEmpty()) {
+ QList<QSimplexConstraint *> nonTrunkConstraints;
+ QLinkedList<QSimplexConstraint *>::iterator it = remainingConstraints.begin();
+ while (it != remainingConstraints.end()) {
+ nonTrunkConstraints += *it;
+ ++it;
+ }
+ result += nonTrunkConstraints;
+ }
+
+ return result;
+}
+
+/*!
+ \internal
+
+ Use the current vertices distance to calculate and set the geometry of
+ each item.
+*/
+void QGraphicsAnchorLayoutPrivate::setItemsGeometries()
+{
+ AnchorVertex *firstH, *secondH, *firstV, *secondV;
+
+ foreach (QGraphicsLayoutItem *item, items) {
+ firstH = internalVertex(item, Qt::AnchorLeft);
+ secondH = internalVertex(item, Qt::AnchorRight);
+ firstV = internalVertex(item, Qt::AnchorTop);
+ secondV = internalVertex(item, Qt::AnchorBottom);
+
+ QPointF topLeft(firstH->distance, firstV->distance);
+ QPointF bottomRight(secondH->distance, secondV->distance);
+
+ item->setGeometry(QRectF(topLeft, bottomRight));
+ }
+}
+
+/*!
+ \internal
+
+ Calculate the position of each vertex based on the paths to each of
+ them as well as the current edges sizes.
+*/
+void QGraphicsAnchorLayoutPrivate::calculateVertexPositions(
+ QGraphicsAnchorLayoutPrivate::Orientation orientation)
+{
+ Q_Q(QGraphicsAnchorLayout);
+ QQueue<QPair<AnchorVertex *, AnchorVertex *> > queue;
+ QSet<AnchorVertex *> visited;
+
+ // Get root vertex
+ AnchorVertex *root = graph[orientation].rootVertex();
+
+ qreal widgetMargin;
+ qreal layoutMargin;
+
+ // Initialize the first vertex
+ if (orientation == Horizontal) {
+ widgetMargin = q->geometry().x();
+ q->getContentsMargins(&layoutMargin, 0, 0, 0);
+ } else {
+ // Root position is equal to the top margin
+ widgetMargin = q->geometry().y();
+ q->getContentsMargins(0, &layoutMargin, 0, 0);
+ }
+ root->distance = widgetMargin + layoutMargin;
+ visited.insert(root);
+
+ // Add initial edges to the queue
+ foreach (AnchorVertex *v, graph[orientation].adjacentVertices(root)) {
+ queue.enqueue(qMakePair(root, v));
+ }
+
+ // Do initial calculation required by "interpolateEdge()"
+ setupEdgesInterpolation(orientation);
+
+ // Traverse the graph and calculate vertex positions, we need to
+ // visit all pairs since each of them could have a sequential
+ // anchor inside, which hides more vertices.
+ while (!queue.isEmpty()) {
+ QPair<AnchorVertex *, AnchorVertex *> pair = queue.dequeue();
+ AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second);
+
+ // Both vertices were interpolated, and the anchor itself can't have other
+ // anchors inside (it's not a complex anchor).
+ if (edge->type == AnchorData::Normal && visited.contains(pair.second))
+ continue;
+
+ visited.insert(pair.second);
+ interpolateEdge(pair.first, edge, orientation);
+
+ QList<AnchorVertex *> adjacents = graph[orientation].adjacentVertices(pair.second);
+ for (int i = 0; i < adjacents.count(); ++i) {
+ if (!visited.contains(adjacents.at(i)))
+ queue.enqueue(qMakePair(pair.second, adjacents.at(i)));
+ }
+ }
+}
+
+/*!
+ \internal
+
+ Calculate interpolation parameters based on current Layout Size.
+ Must once before calling "interpolateEdgeSize()" for each edge.
+*/
+void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation(
+ Orientation orientation)
+{
+ Q_Q(QGraphicsAnchorLayout);
+ qreal lower, upper, current;
+
+ if (orientation == Horizontal) {
+ current = q->contentsRect().width();
+ } else {
+ current = q->contentsRect().height();
+ }
+
+ if (current < sizeHints[orientation][Qt::PreferredSize]) {
+ interpolationInterval[orientation] = MinToPreferred;
+ lower = sizeHints[orientation][Qt::MinimumSize];
+ upper = sizeHints[orientation][Qt::PreferredSize];
+ } else {
+ interpolationInterval[orientation] = PreferredToMax;
+ lower = sizeHints[orientation][Qt::PreferredSize];
+ upper = sizeHints[orientation][Qt::MaximumSize];
+ }
+
+ if (upper == lower) {
+ interpolationProgress[orientation] = 0;
+ } else {
+ interpolationProgress[orientation] = (current - lower) / (upper - lower);
+ }
+}
+
+/*!
+ \internal
+
+ Calculate the current Edge size based on the current Layout size and the
+ size the edge is supposed to have when:
+
+ - the layout is at its minimum size.
+ - the layout is at its preferred size.
+ - the layout is at its maximum size.
+
+ These three key values are calculated in advance using linear
+ programming (more expensive) or the simplification algorithm, then
+ subsequential resizes of the parent layout require a simple
+ interpolation.
+
+ If the edge is sequential or parallel, it's possible to have more
+ vertices to be initalized, so it calls specialized functions that
+ will recurse back to interpolateEdge().
+ */
+void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base,
+ AnchorData *edge,
+ Orientation orientation)
+{
+ qreal lower, upper;
+
+ if (interpolationInterval[orientation] == MinToPreferred) {
+ lower = edge->sizeAtMinimum;
+ upper = edge->sizeAtPreferred;
+ } else {
+ lower = edge->sizeAtPreferred;
+ upper = edge->sizeAtMaximum;
+ }
+
+ qreal edgeDistance = (interpolationProgress[orientation] * (upper - lower)) + lower;
+
+ Q_ASSERT(edge->from == base || edge->to == base);
+
+ if (edge->from == base)
+ edge->to->distance = base->distance + edgeDistance;
+ else
+ edge->from->distance = base->distance - edgeDistance;
+
+ // Process child anchors
+ if (edge->type == AnchorData::Sequential)
+ interpolateSequentialEdges(edge->from,
+ static_cast<SequentialAnchorData *>(edge),
+ orientation);
+ else if (edge->type == AnchorData::Parallel)
+ interpolateParallelEdges(edge->from,
+ static_cast<ParallelAnchorData *>(edge),
+ orientation);
+}
+
+void QGraphicsAnchorLayoutPrivate::interpolateParallelEdges(
+ AnchorVertex *base, ParallelAnchorData *data, Orientation orientation)
+{
+ // In parallels the boundary vertices are already calculate, we
+ // just need to look for sequential groups inside, because only
+ // them may have new vertices associated.
+
+ // First edge
+ if (data->firstEdge->type == AnchorData::Sequential)
+ interpolateSequentialEdges(base,
+ static_cast<SequentialAnchorData *>(data->firstEdge),
+ orientation);
+ else if (data->firstEdge->type == AnchorData::Parallel)
+ interpolateParallelEdges(base,
+ static_cast<ParallelAnchorData *>(data->firstEdge),
+ orientation);
+
+ // Second edge
+ if (data->secondEdge->type == AnchorData::Sequential)
+ interpolateSequentialEdges(base,
+ static_cast<SequentialAnchorData *>(data->secondEdge),
+ orientation);
+ else if (data->secondEdge->type == AnchorData::Parallel)
+ interpolateParallelEdges(base,
+ static_cast<ParallelAnchorData *>(data->secondEdge),
+ orientation);
+}
+
+void QGraphicsAnchorLayoutPrivate::interpolateSequentialEdges(
+ AnchorVertex *base, SequentialAnchorData *data, Orientation orientation)
+{
+ AnchorVertex *prev = base;
+
+ // ### I'm not sure whether this assumption is safe. If not,
+ // consider that m_edges.last() could be used instead (so
+ // at(0) would be the one to be treated specially).
+ Q_ASSERT(base == data->m_edges.at(0)->to || base == data->m_edges.at(0)->from);
+
+ // Skip the last
+ for (int i = 0; i < data->m_edges.count() - 1; ++i) {
+ AnchorData *child = data->m_edges.at(i);
+ interpolateEdge(prev, child, orientation);
+ prev = child->to;
+ }
+
+ // Treat the last specially, since we already calculated it's end
+ // vertex, so it's only interesting if it's a complex one
+ if (data->m_edges.last()->type != AnchorData::Normal)
+ interpolateEdge(prev, data->m_edges.last(), orientation);
+}
+
+QPair<qreal, qreal>
+QGraphicsAnchorLayoutPrivate::solveMinMax(QList<QSimplexConstraint *> constraints,
+ GraphPath path)
+{
+ QSimplex simplex;
+ simplex.setConstraints(constraints);
+
+ // Obtain the objective constraint
+ QSimplexConstraint objective;
+ QSet<AnchorData *>::const_iterator iter;
+ for (iter = path.positives.constBegin(); iter != path.positives.constEnd(); ++iter)
+ objective.variables.insert(*iter, 1.0);
+
+ for (iter = path.negatives.constBegin(); iter != path.negatives.constEnd(); ++iter)
+ objective.variables.insert(*iter, -1.0);
+
+ simplex.setObjective(&objective);
+
+ // Calculate minimum values
+ qreal min = simplex.solveMin();
+
+ // Save sizeAtMinimum results
+ QList<QSimplexVariable *> variables = simplex.constraintsVariables();
+ for (int i = 0; i < variables.size(); ++i) {
+ AnchorData *ad = static_cast<AnchorData *>(variables[i]);
+ ad->sizeAtMinimum = ad->result;
+ }
+
+ // Calculate maximum values
+ qreal max = simplex.solveMax();
+
+ // Save sizeAtMaximum results
+ for (int i = 0; i < variables.size(); ++i) {
+ AnchorData *ad = static_cast<AnchorData *>(variables[i]);
+ ad->sizeAtMaximum = ad->result;
+ }
+
+ return qMakePair<qreal, qreal>(min, max);
+}
+
+void QGraphicsAnchorLayoutPrivate::solvePreferred(QList<QSimplexConstraint *> constraints)
+{
+ QList<AnchorData *> variables = getVariables(constraints);
+ QList<QSimplexConstraint *> preferredConstraints;
+ QList<QSimplexVariable *> preferredVariables;
+ QSimplexConstraint objective;
+
+ // Fill the objective coefficients for this variable. In the
+ // end the objective function will be
+ //
+ // z = n * (A_shrink + B_shrink + ...) + (A_grower + B_grower + ...)
+ //
+ // where n is the number of variables that have
+ // slacks. Note that here we use the number of variables
+ // as coefficient, this is to mark the "shrinker slack
+ // variable" less likely to get value than the "grower
+ // slack variable".
+
+ // This will fill the values for the structural constraints
+ // and we now fill the values for the slack constraints (one per variable),
+ // which have this form (the constant A_pref was set when creating the slacks):
+ //
+ // A + A_shrinker - A_grower = A_pref
+ //
+ for (int i = 0; i < variables.size(); ++i) {
+ AnchorData *ad = static_cast<AnchorData *>(variables[i]);
+ if (ad->skipInPreferred)
+ continue;
+
+ QSimplexVariable *grower = new QSimplexVariable;
+ QSimplexVariable *shrinker = new QSimplexVariable;
+ QSimplexConstraint *c = new QSimplexConstraint;
+ c->variables.insert(ad, 1.0);
+ c->variables.insert(shrinker, 1.0);
+ c->variables.insert(grower, -1.0);
+ c->constant = ad->prefSize;
+
+ preferredConstraints += c;
+ preferredVariables += grower;
+ preferredVariables += shrinker;
+
+ objective.variables.insert(grower, 1.0);
+ objective.variables.insert(shrinker, variables.size());
+ }
+
+
+ QSimplex *simplex = new QSimplex;
+ simplex->setConstraints(constraints + preferredConstraints);
+ simplex->setObjective(&objective);
+
+ // Calculate minimum values
+ simplex->solveMin();
+
+ // Save sizeAtPreferred results
+ for (int i = 0; i < variables.size(); ++i) {
+ AnchorData *ad = static_cast<AnchorData *>(variables[i]);
+ ad->sizeAtPreferred = ad->result;
+ }
+
+ // Make sure we delete the simplex solver -before- we delete the
+ // constraints used by it.
+ delete simplex;
+
+ // Delete constraints and variables we created.
+ qDeleteAll(preferredConstraints);
+ qDeleteAll(preferredVariables);
+}
+
+#ifdef QT_DEBUG
+void QGraphicsAnchorLayoutPrivate::dumpGraph()
+{
+ QFile file(QString::fromAscii("anchorlayout.dot"));
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
+ qWarning("Could not write to %s", file.fileName().toLocal8Bit().constData());
+
+ QString str = QString::fromAscii("digraph anchorlayout {\nnode [shape=\"rect\"]\n%1}");
+ QString dotContents = graph[0].serializeToDot();
+ dotContents += graph[1].serializeToDot();
+ file.write(str.arg(dotContents).toLocal8Bit());
+
+ file.close();
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h
new file mode 100644
index 000000000..15a1b44b8
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h
@@ -0,0 +1,477 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGraphicsWidget>
+
+#include "qgraphicslayout_p.h"
+#include "qgraphicsanchorlayout.h"
+#include "qgraph_p.h"
+#include "qsimplex_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*
+ The public QGraphicsAnchorLayout interface represents an anchorage point
+ as a pair of a <QGraphicsLayoutItem *> and a <Qt::AnchorPoint>.
+
+ Internally though, it has a graph of anchorage points (vertices) and
+ anchors (edges), represented by the AnchorVertex and AnchorData structs
+ respectively.
+*/
+
+/*!
+ \internal
+
+ Represents a vertex (anchorage point) in the internal graph
+*/
+struct AnchorVertex {
+ AnchorVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge)
+ : m_item(item), m_edge(edge) {}
+
+ AnchorVertex()
+ : m_item(0), m_edge(Qt::AnchorPoint(0)) {}
+
+#ifdef QT_DEBUG
+ inline QString toString() const;
+#endif
+ QGraphicsLayoutItem *m_item;
+ Qt::AnchorPoint m_edge;
+
+ // Current distance from this vertex to the layout edge (Left or Top)
+ // Value is calculated from the current anchors sizes.
+ qreal distance;
+};
+
+#ifdef QT_DEBUG
+inline QString AnchorVertex::toString() const
+{
+ if (!this || !m_item) {
+ return QLatin1String("NULL");
+ }
+ QString edge;
+ switch (m_edge) {
+ case Qt::AnchorLeft:
+ edge = QLatin1String("Left");
+ break;
+ case Qt::AnchorHorizontalCenter:
+ edge = QLatin1String("HorizontalCenter");
+ break;
+ case Qt::AnchorRight:
+ edge = QLatin1String("Right");
+ break;
+ case Qt::AnchorTop:
+ edge = QLatin1String("Top");
+ break;
+ case Qt::AnchorVerticalCenter:
+ edge = QLatin1String("VerticalCenter");
+ break;
+ case Qt::AnchorBottom:
+ edge = QLatin1String("Bottom");
+ break;
+ default:
+ edge = QLatin1String("None");
+ break;
+ }
+ QString itemName;
+ if (m_item->isLayout()) {
+ itemName = QLatin1String("layout");
+ } else {
+ if (QGraphicsItem *item = m_item->graphicsItem()) {
+ itemName = item->data(0).toString();
+ }
+ }
+ edge.insert(0, QLatin1String("%1_"));
+ return edge.arg(itemName);
+}
+#endif
+
+/*!
+ \internal
+
+ Represents an edge (anchor) in the internal graph.
+*/
+struct AnchorData : public QSimplexVariable {
+ enum Type {
+ Normal = 0,
+ Sequential,
+ Parallel
+ };
+ AnchorData(qreal minimumSize, qreal preferredSize, qreal maximumSize)
+ : QSimplexVariable(), from(0), to(0),
+ minSize(minimumSize), prefSize(preferredSize),
+ maxSize(maximumSize), sizeAtMinimum(preferredSize),
+ sizeAtPreferred(preferredSize), sizeAtMaximum(preferredSize),
+ skipInPreferred(0), type(Normal), hasSize(true),
+ isLayoutAnchor(false) {}
+
+ AnchorData(qreal size)
+ : QSimplexVariable(), from(0), to(0),
+ minSize(size), prefSize(size), maxSize(size),
+ sizeAtMinimum(size), sizeAtPreferred(size), sizeAtMaximum(size),
+ skipInPreferred(0), type(Normal), hasSize(true),
+ isLayoutAnchor(false) {}
+
+ AnchorData()
+ : QSimplexVariable(), from(0), to(0),
+ minSize(0), prefSize(0), maxSize(0),
+ sizeAtMinimum(0), sizeAtPreferred(0), sizeAtMaximum(0),
+ skipInPreferred(0), type(Normal), hasSize(false),
+ isLayoutAnchor(false) {}
+
+ virtual void updateChildrenSizes() { };
+ virtual void refreshSizeHints(qreal effectiveSpacing);
+
+ virtual ~AnchorData() {}
+
+#ifdef QT_DEBUG
+ void dump(int indent = 2);
+ inline QString toString() const;
+ QString name;
+#endif
+
+ inline void setFixedSize(qreal size)
+ {
+ minSize = size;
+ prefSize = size;
+ maxSize = size;
+ sizeAtMinimum = size;
+ sizeAtPreferred = size;
+ sizeAtMaximum = size;
+ hasSize = true;
+ }
+
+ inline void unsetSize()
+ {
+ hasSize = false;
+ }
+
+ // Anchor is semantically directed
+ AnchorVertex *from;
+ AnchorVertex *to;
+
+ // Size restrictions of this edge. For anchors internal to items, these
+ // values are derived from the respective item size hints. For anchors
+ // that were added by users, these values are equal to the specified anchor
+ // size.
+ qreal minSize;
+ qreal prefSize;
+ qreal maxSize;
+
+ // These attributes define which sizes should that anchor be in when the
+ // layout is at its minimum, preferred or maximum sizes. Values are
+ // calculated by the Simplex solver based on the current layout setup.
+ qreal sizeAtMinimum;
+ qreal sizeAtPreferred;
+ qreal sizeAtMaximum;
+
+ uint skipInPreferred : 1;
+ uint type : 2; // either Normal, Sequential or Parallel
+ uint hasSize : 1; // if false, get size from style.
+ uint isLayoutAnchor : 1; // if this anchor is connected to a layout 'edge'
+protected:
+ AnchorData(Type type, qreal size = 0)
+ : QSimplexVariable(), from(0), to(0),
+ minSize(size), prefSize(size),
+ maxSize(size), sizeAtMinimum(size),
+ sizeAtPreferred(size), sizeAtMaximum(size),
+ skipInPreferred(0), type(type), hasSize(true),
+ isLayoutAnchor(false) {}
+};
+
+#ifdef QT_DEBUG
+inline QString AnchorData::toString() const
+{
+ return QString::fromAscii("Anchor(%1)").arg(name);
+}
+#endif
+
+struct SequentialAnchorData : public AnchorData
+{
+ SequentialAnchorData() : AnchorData(AnchorData::Sequential)
+ {
+#ifdef QT_DEBUG
+ name = QLatin1String("SequentialAnchorData");
+#endif
+ }
+
+ virtual void updateChildrenSizes();
+ virtual void refreshSizeHints(qreal effectiveSpacing);
+
+ void setVertices(const QVector<AnchorVertex*> &vertices)
+ {
+ m_children = vertices;
+#ifdef QT_DEBUG
+ name = QString::fromAscii("%1 -- %2").arg(vertices.first()->toString(), vertices.last()->toString());
+#endif
+ }
+
+ QVector<AnchorVertex*> m_children; // list of vertices in the sequence
+ QVector<AnchorData*> m_edges; // keep the list of edges too.
+};
+
+struct ParallelAnchorData : public AnchorData
+{
+ ParallelAnchorData(AnchorData *first, AnchorData *second)
+ : AnchorData(AnchorData::Parallel),
+ firstEdge(first), secondEdge(second)
+ {
+ // ### Those asserts force that both child anchors have the same direction,
+ // but can't we simplify a pair of anchors in opposite directions?
+ Q_ASSERT(first->from == second->from);
+ Q_ASSERT(first->to == second->to);
+ from = first->from;
+ to = first->to;
+#ifdef QT_DEBUG
+ name = QString::fromAscii("%1 | %2").arg(first->toString(), second->toString());
+#endif
+ }
+
+ virtual void updateChildrenSizes();
+ virtual void refreshSizeHints(qreal effectiveSpacing);
+
+ AnchorData* firstEdge;
+ AnchorData* secondEdge;
+};
+
+/*!
+ \internal
+
+ Representation of a valid path for a given vertex in the graph.
+ In this struct, "positives" is the set of anchors that have been
+ traversed in the forward direction, while "negatives" is the set
+ with the ones walked backwards.
+
+ This paths are compared against each other to produce LP Constraints,
+ the exact order in which the anchors were traversed is not relevant.
+*/
+class GraphPath
+{
+public:
+ GraphPath() {};
+
+ QSimplexConstraint *constraint(const GraphPath &path) const;
+#ifdef QT_DEBUG
+ QString toString() const;
+#endif
+ QSet<AnchorData *> positives;
+ QSet<AnchorData *> negatives;
+};
+
+
+/*!
+ \internal
+
+ QGraphicsAnchorLayout private methods and attributes.
+*/
+class QGraphicsAnchorLayoutPrivate : public QGraphicsLayoutPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsAnchorLayout)
+
+public:
+ // When the layout geometry is different from its Minimum, Preferred
+ // or Maximum values, interpolation is used to calculate the geometries
+ // of the items.
+ //
+ // Interval represents which interpolation interval are we operating in.
+ enum Interval {
+ MinToPreferred = 0,
+ PreferredToMax
+ };
+
+ // Several structures internal to the layout are duplicated to handle
+ // both Horizontal and Vertical restrictions.
+ //
+ // Orientation is used to reference the right structure in each context
+ enum Orientation {
+ Horizontal = 0,
+ Vertical,
+ NOrientations
+ };
+
+ QGraphicsAnchorLayoutPrivate();
+
+ static Qt::AnchorPoint oppositeEdge(
+ Qt::AnchorPoint edge);
+
+ static Orientation edgeOrientation(Qt::AnchorPoint edge);
+
+ static Qt::AnchorPoint pickEdge(Qt::AnchorPoint edge, Orientation orientation)
+ {
+ if (orientation == Vertical && int(edge) <= 2)
+ return (Qt::AnchorPoint)(edge + 3);
+ else if (orientation == Horizontal && int(edge) >= 3) {
+ return (Qt::AnchorPoint)(edge - 3);
+ }
+ return edge;
+ }
+
+ // Init methods
+ void createLayoutEdges();
+ void deleteLayoutEdges();
+ void createItemEdges(QGraphicsLayoutItem *item);
+ void createCenterAnchors(QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge);
+ void removeCenterAnchors(QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge, bool substitute = true);
+ void removeCenterConstraints(QGraphicsLayoutItem *item, Orientation orientation);
+
+ // helper function used by the 4 API functions
+ void anchor(QGraphicsLayoutItem *firstItem,
+ Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem,
+ Qt::AnchorPoint secondEdge,
+ qreal *spacing = 0);
+
+ // Anchor Manipulation methods
+ void addAnchor(QGraphicsLayoutItem *firstItem,
+ Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem,
+ Qt::AnchorPoint secondEdge,
+ AnchorData *data);
+
+ void removeAnchor(QGraphicsLayoutItem *firstItem,
+ Qt::AnchorPoint firstEdge,
+ QGraphicsLayoutItem *secondItem,
+ Qt::AnchorPoint secondEdge);
+
+ bool setAnchorSize(const QGraphicsLayoutItem *firstItem,
+ Qt::AnchorPoint firstEdge,
+ const QGraphicsLayoutItem *secondItem,
+ Qt::AnchorPoint secondEdge,
+ const qreal *anchorSize);
+
+ bool anchorSize(const QGraphicsLayoutItem *firstItem,
+ Qt::AnchorPoint firstEdge,
+ const QGraphicsLayoutItem *secondItem,
+ Qt::AnchorPoint secondEdge,
+ qreal *minSize = 0,
+ qreal *prefSize = 0,
+ qreal *maxSize = 0) const;
+
+ void removeAnchors(QGraphicsLayoutItem *item);
+
+ void removeVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge);
+
+ void correctEdgeDirection(QGraphicsLayoutItem *&firstItem,
+ Qt::AnchorPoint &firstEdge,
+ QGraphicsLayoutItem *&secondItem,
+ Qt::AnchorPoint &secondEdge);
+ // for getting the actual spacing (will query the style if the
+ // spacing is not explicitly set).
+ qreal effectiveSpacing(Orientation orientation) const;
+
+ // Activation methods
+ void simplifyGraph(Orientation orientation);
+ bool simplifyGraphIteration(Orientation orientation);
+ void restoreSimplifiedGraph(Orientation orientation);
+
+ void calculateGraphs();
+ void calculateGraphs(Orientation orientation);
+ void setAnchorSizeHintsFromItems(Orientation orientation);
+ void findPaths(Orientation orientation);
+ void constraintsFromPaths(Orientation orientation);
+ QList<QSimplexConstraint *> constraintsFromSizeHints(const QList<AnchorData *> &anchors);
+ QList<QList<QSimplexConstraint *> > getGraphParts(Orientation orientation);
+
+ inline AnchorVertex *internalVertex(const QPair<QGraphicsLayoutItem*, Qt::AnchorPoint> &itemEdge) const
+ {
+ return m_vertexList.value(itemEdge).first;
+ }
+
+ inline AnchorVertex *internalVertex(const QGraphicsLayoutItem *item, Qt::AnchorPoint edge) const
+ {
+ return internalVertex(qMakePair(const_cast<QGraphicsLayoutItem *>(item), edge));
+ }
+
+ AnchorVertex *addInternalVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge);
+ void removeInternalVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge);
+
+ // Geometry interpolation methods
+ void setItemsGeometries();
+
+ void calculateVertexPositions(Orientation orientation);
+ void setupEdgesInterpolation(Orientation orientation);
+ void interpolateEdge(AnchorVertex *base, AnchorData *edge, Orientation orientation);
+ void interpolateSequentialEdges(AnchorVertex *base, SequentialAnchorData *edge,
+ Orientation orientation);
+ void interpolateParallelEdges(AnchorVertex *base, ParallelAnchorData *edge,
+ Orientation orientation);
+
+ // Linear Programming solver methods
+ QPair<qreal, qreal> solveMinMax(QList<QSimplexConstraint *> constraints,
+ GraphPath path);
+ void solvePreferred(QList<QSimplexConstraint *> constraints);
+
+#ifdef QT_DEBUG
+ void dumpGraph();
+#endif
+
+
+ qreal spacings[NOrientations];
+ // Size hints from simplex engine
+ qreal sizeHints[2][3];
+
+ // Items
+ QVector<QGraphicsLayoutItem *> items;
+
+ // Mapping between high level anchorage points (Item, Edge) to low level
+ // ones (Graph Vertices)
+
+ QHash<QPair<QGraphicsLayoutItem*, Qt::AnchorPoint>, QPair<AnchorVertex *, int> > m_vertexList;
+
+ // Internal graph of anchorage points and anchors, for both orientations
+ Graph<AnchorVertex, AnchorData> graph[2];
+
+ // Graph paths and constraints, for both orientations
+ QMultiHash<AnchorVertex *, GraphPath> graphPaths[2];
+ QList<QSimplexConstraint *> constraints[2];
+ QList<QSimplexConstraint *> itemCenterConstraints[2];
+
+ // The interpolation interval and progress based on the current size
+ // as well as the key values (minimum, preferred and maximum)
+ Interval interpolationInterval[2];
+ qreal interpolationProgress[2];
+
+ // ###
+ bool graphSimplified[2];
+
+ uint calculateGraphCacheDirty : 1;
+};
+
+QT_END_NAMESPACE
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp
index 88fac1470..620f6f482 100644
--- a/src/gui/graphicsview/qgraphicsitem.cpp
+++ b/src/gui/graphicsview/qgraphicsitem.cpp
@@ -582,6 +582,7 @@
#include <QtGui/qstyleoption.h>
#include <QtGui/qevent.h>
#include <QtGui/qinputcontext.h>
+#include <QtGui/qgraphicseffect.h>
#include <private/qgraphicsitem_p.h>
#include <private/qgraphicswidget_p.h>
@@ -1055,11 +1056,6 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent)
*/
void QGraphicsItemPrivate::childrenBoundingRectHelper(QTransform *x, QRectF *rect)
{
- if (!dirtyChildrenBoundingRect) {
- *rect |= x->mapRect(childrenBoundingRect);
- return;
- }
-
for (int i = 0; i < children.size(); ++i) {
QGraphicsItem *child = children.at(i);
QGraphicsItemPrivate *childd = child->d_ptr.data();
@@ -1067,19 +1063,20 @@ void QGraphicsItemPrivate::childrenBoundingRectHelper(QTransform *x, QRectF *rec
if (hasPos || childd->transformData) {
// COMBINE
QTransform matrix = childd->transformToParent();
- matrix *= *x;
+ if (x)
+ matrix *= *x;
*rect |= matrix.mapRect(child->boundingRect());
if (!childd->children.isEmpty())
childd->childrenBoundingRectHelper(&matrix, rect);
} else {
- *rect |= x->mapRect(child->boundingRect());
+ if (x)
+ *rect |= x->mapRect(child->boundingRect());
+ else
+ *rect |= child->boundingRect();
if (!childd->children.isEmpty())
childd->childrenBoundingRectHelper(x, rect);
}
}
-
- childrenBoundingRect = *rect;
- dirtyChildrenBoundingRect = 0;
}
void QGraphicsItemPrivate::initStyleOption(QStyleOptionGraphicsItem *option, const QTransform &worldTransform,
@@ -1223,6 +1220,7 @@ QGraphicsItem::~QGraphicsItem()
d_ptr->setParentItemHelper(0);
}
+ delete d_ptr->graphicsEffect;
if (d_ptr->transformData) {
for(int i = 0; i < d_ptr->transformData->graphicsTransforms.size(); ++i) {
QGraphicsTransform *t = d_ptr->transformData->graphicsTransforms.at(i);
@@ -2030,8 +2028,8 @@ void QGraphicsItemPrivate::setEnabledHelper(bool newEnabled, bool explicitly, bo
enabled = newEnabledVariant.toBool();
// Schedule redraw.
- if (update && scene)
- scene->d_func()->markDirty(q_ptr);
+ if (update)
+ q_ptr->update();
foreach (QGraphicsItem *child, children) {
if (!newEnabled || !child->d_ptr->explicitlyDisabled)
@@ -2135,8 +2133,8 @@ void QGraphicsItem::setSelected(bool selected)
return;
d_ptr->selected = newSelected;
+ update();
if (d_ptr->scene) {
- d_ptr->scene->d_func()->markDirty(this);
QGraphicsScenePrivate *sceneD = d_ptr->scene->d_func();
if (selected) {
sceneD->selectedItems << this;
@@ -2243,6 +2241,118 @@ void QGraphicsItem::setOpacity(qreal opacity)
}
/*!
+ Returns a pointer to this item's effect if it has one; otherwise 0.
+
+ \since 4.6
+*/
+QGraphicsEffect *QGraphicsItem::graphicsEffect() const
+{
+ return d_ptr->graphicsEffect;
+}
+
+/*!
+ Sets \a effect as the item's effect. If there already is an effect installed
+ on this item, QGraphicsItem will delete the existing effect before installing
+ the new \a effect.
+
+ If \a effect is the installed on a different item, setGraphicsEffect() will remove
+ the effect from the item and install it on this item.
+
+ \note This function will apply the effect on itself and all its children.
+
+ \since 4.6
+*/
+void QGraphicsItem::setGraphicsEffect(QGraphicsEffect *effect)
+{
+ if (d_ptr->graphicsEffect == effect)
+ return;
+
+ if (d_ptr->graphicsEffect && effect) {
+ delete d_ptr->graphicsEffect;
+ d_ptr->graphicsEffect = 0;
+ }
+
+ if (!effect) {
+ // Unset current effect.
+ QGraphicsEffectPrivate *oldEffectPrivate = d_ptr->graphicsEffect->d_func();
+ d_ptr->graphicsEffect = 0;
+ if (oldEffectPrivate) {
+ oldEffectPrivate->setGraphicsEffectSource(0); // deletes the current source.
+ if (d_ptr->scene) // Update the views directly.
+ d_ptr->scene->d_func()->markDirty(this, QRectF(), false, false, false, false, true);
+ }
+ } else {
+ // Set new effect.
+ QGraphicsEffectSourcePrivate *sourced = new QGraphicsItemEffectSourcePrivate(this);
+ QGraphicsEffectSource *source = new QGraphicsEffectSource(*sourced);
+ d_ptr->graphicsEffect = effect;
+ effect->d_func()->setGraphicsEffectSource(source);
+ }
+
+ prepareGeometryChange();
+}
+
+/*!
+ \internal
+ \since 4.6
+ Returns the effective bounding rect of the item.
+ If the item has no effect, this is the same as the item's bounding rect.
+ If the item has an effect, the effective rect can be larger than the item's
+ bouding rect, depending on the effect.
+
+ \sa boundingRect()
+*/
+QRectF QGraphicsItemPrivate::effectiveBoundingRect() const
+{
+ QGraphicsEffect *effect = graphicsEffect;
+ QRectF brect = effect && effect->isEnabled() ? effect->boundingRect() : q_ptr->boundingRect();
+ if (ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)
+ return brect;
+
+ const QGraphicsItem *effectParent = parent;
+ while (effectParent) {
+ effect = effectParent->d_ptr->graphicsEffect;
+ if (effect && effect->isEnabled())
+ brect = effect->boundingRectFor(brect);
+ if (effectParent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)
+ return brect;
+ effectParent = effectParent->d_ptr->parent;
+ }
+
+ return brect;
+}
+
+/*!
+ \internal
+ \since 4.6
+ Returns the effective bounding rect of this item in scene coordinates,
+ by combining sceneTransform() with boundingRect(), taking into account
+ the effect that the item might have.
+
+ If the item has no effect, this is the same as sceneBoundingRect().
+
+ \sa effectiveBoundingRect(), sceneBoundingRect()
+*/
+QRectF QGraphicsItemPrivate::sceneEffectiveBoundingRect() const
+{
+ // Find translate-only offset
+ // COMBINE
+ QPointF offset;
+ const QGraphicsItem *parentItem = q_ptr;
+ const QGraphicsItemPrivate *itemd;
+ do {
+ itemd = parentItem->d_ptr.data();
+ if (itemd->transformData)
+ break;
+ offset += itemd->pos;
+ } while ((parentItem = itemd->parent));
+
+ QRectF br = effectiveBoundingRect();
+ br.translate(offset);
+ return !parentItem ? br : parentItem->sceneTransform().mapRect(br);
+}
+
+/*!
Returns true if this item can accept drag and drop events; otherwise,
returns false. By default, items do not accept drag and drop events; items
are transparent to drag and drop.
@@ -3769,10 +3879,10 @@ QRectF QGraphicsItem::childrenBoundingRect() const
if (!d_ptr->dirtyChildrenBoundingRect)
return d_ptr->childrenBoundingRect;
- QRectF childRect;
- QTransform x;
- d_ptr->childrenBoundingRectHelper(&x, &childRect);
- return childRect;
+ d_ptr->childrenBoundingRect = QRectF();
+ d_ptr->childrenBoundingRectHelper(0, &d_ptr->childrenBoundingRect);
+ d_ptr->dirtyChildrenBoundingRect = 0;
+ return d_ptr->childrenBoundingRect;
}
/*!
@@ -4723,6 +4833,13 @@ void QGraphicsItem::update(const QRectF &rect)
if (rect.isEmpty() && !rect.isNull())
return;
+ // Make sure we notify effects about invalidated source.
+ QGraphicsItem *item = this;
+ do {
+ if (item->d_ptr->graphicsEffect)
+ item->d_ptr->notifyInvalidated = 1;
+ } while ((item = item->d_ptr->parent));
+
if (CacheMode(d_ptr->cacheMode) != NoCache) {
QGraphicsItemCache *cache = d_ptr->extraItemCache();
if (d_ptr->discardUpdateRequest(/* ignoreVisibleBit = */ false,
@@ -6062,6 +6179,7 @@ void QGraphicsItem::dropEvent(QGraphicsSceneDragDropEvent *event)
void QGraphicsItem::focusInEvent(QFocusEvent *event)
{
Q_UNUSED(event);
+ update();
}
/*!
@@ -6073,6 +6191,7 @@ void QGraphicsItem::focusInEvent(QFocusEvent *event)
void QGraphicsItem::focusOutEvent(QFocusEvent *event)
{
Q_UNUSED(event);
+ update();
}
/*!
@@ -6087,8 +6206,7 @@ void QGraphicsItem::focusOutEvent(QFocusEvent *event)
void QGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
Q_UNUSED(event);
- if (d_ptr->scene)
- d_ptr->scene->d_func()->markDirty(this);
+ update();
}
/*!
@@ -6116,8 +6234,7 @@ void QGraphicsItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
void QGraphicsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
Q_UNUSED(event);
- if (d_ptr->scene)
- d_ptr->scene->d_func()->markDirty(this);
+ update();
}
/*!
@@ -6618,6 +6735,7 @@ void QGraphicsItem::prepareGeometryChange()
d_ptr->scene->d_func()->dirtyGrowingItemsBoundingRect = true;
d_ptr->geometryChanged = 1;
d_ptr->paintedViewBoundingRectsNeedRepaint = 1;
+ d_ptr->notifyBoundingRectChanged = !d_ptr->inSetPosHelper;
QGraphicsScenePrivate *scenePrivate = d_ptr->scene->d_func();
scenePrivate->index->prepareBoundingRectChange(this);
@@ -6641,8 +6759,11 @@ void QGraphicsItem::prepareGeometryChange()
}
QGraphicsItem *parent = this;
- while ((parent = parent->d_ptr->parent))
+ while ((parent = parent->d_ptr->parent)) {
parent->d_ptr->dirtyChildrenBoundingRect = 1;
+ // ### Only do this if the parent's effect applies to the entire subtree.
+ parent->d_ptr->notifyBoundingRectChanged = 1;
+ }
if (d_ptr->inSetPosHelper)
return;
@@ -10045,6 +10166,125 @@ int QGraphicsItemGroup::type() const
return Type;
}
+QRectF QGraphicsItemEffectSourcePrivate::boundingRect(Qt::CoordinateSystem system) const
+{
+ const bool deviceCoordinates = (system == Qt::DeviceCoordinates);
+ if (!info && deviceCoordinates) {
+ // Device coordinates without info not yet supported.
+ qWarning("QGraphicsEffectSource::boundingRect: Not yet implemented, lacking device context");
+ return QRectF();
+ }
+
+ QRectF rect = item->boundingRect();
+ if (!item->d_ptr->children.isEmpty())
+ rect |= item->childrenBoundingRect();
+
+ if (deviceCoordinates) {
+ Q_ASSERT(info->painter);
+ rect = info->painter->worldTransform().mapRect(rect);
+ }
+
+ return rect;
+}
+
+void QGraphicsItemEffectSourcePrivate::draw(QPainter *painter)
+{
+ if (!info) {
+ qWarning("QGraphicsEffectSource::draw: Can only begin as a result of QGraphicsEffect::draw");
+ return;
+ }
+
+ Q_ASSERT(item->d_ptr->scene);
+ QGraphicsScenePrivate *scened = item->d_ptr->scene->d_func();
+ if (painter == info->painter) {
+ scened->draw(item, painter, info->viewTransform, info->transformPtr, info->exposedRegion,
+ info->widget, info->opacity, info->effectTransform, info->wasDirtySceneTransform,
+ info->drawItem);
+ } else {
+ QTransform effectTransform = painter->worldTransform();
+ effectTransform *= info->painter->worldTransform().inverted();
+ if (info->effectTransform)
+ effectTransform *= *info->effectTransform;
+ scened->draw(item, painter, info->viewTransform, info->transformPtr, info->exposedRegion,
+ info->widget, info->opacity, &effectTransform, info->wasDirtySceneTransform,
+ info->drawItem);
+ }
+}
+
+QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *offset) const
+{
+ const bool deviceCoordinates = (system == Qt::DeviceCoordinates);
+ if (!info && deviceCoordinates) {
+ // Device coordinates without info not yet supported.
+ qWarning("QGraphicsEffectSource::pixmap: Not yet implemented, lacking device context");
+ return QPixmap();
+ }
+
+ if (!item->d_ptr->scene)
+ return QPixmap();
+ QGraphicsScenePrivate *scened = item->d_ptr->scene->d_func();
+
+ const QRectF sourceRect = boundingRect(system);
+ QRect effectRect = item->graphicsEffect()->boundingRectFor(sourceRect).toAlignedRect();
+ if (offset)
+ *offset = effectRect.topLeft();
+
+ if (deviceCoordinates) {
+ // Clip to viewport rect.
+ int left, top, right, bottom;
+ effectRect.getCoords(&left, &top, &right, &bottom);
+ if (left < 0) {
+ if (offset)
+ offset->rx() += -left;
+ effectRect.setX(0);
+ }
+ if (top < 0) {
+ if (offset)
+ offset->ry() += -top;
+ effectRect.setY(0);
+ }
+ // NB! We use +-1 for historical reasons (see QRect documentation).
+ if (right + 1 > info->widget->width())
+ effectRect.setRight(info->widget->width() - 1);
+ if (bottom + 1 > info->widget->height())
+ effectRect.setBottom(info->widget->height() -1);
+ }
+
+ if (effectRect.isEmpty())
+ return QPixmap();
+
+ QPixmap pixmap(effectRect.size());
+ pixmap.fill(Qt::transparent);
+ QPainter pixmapPainter(&pixmap);
+ pixmapPainter.setRenderHints(info ? info->painter->renderHints() : QPainter::TextAntialiasing);
+
+ QTransform effectTransform = QTransform::fromTranslate(-effectRect.x(), -effectRect.y());
+ if (deviceCoordinates && info->effectTransform)
+ effectTransform *= *info->effectTransform;
+
+ if (!info) {
+ // Logical coordinates without info.
+ QTransform sceneTransform = item->sceneTransform();
+ QTransform newEffectTransform = sceneTransform.inverted();
+ newEffectTransform *= effectTransform;
+ scened->draw(item, &pixmapPainter, 0, &sceneTransform, 0, 0, qreal(1.0),
+ &newEffectTransform, false, true);
+ } else if (deviceCoordinates) {
+ // Device coordinates with info.
+ scened->draw(item, &pixmapPainter, info->viewTransform, info->transformPtr, info->exposedRegion,
+ info->widget, info->opacity, &effectTransform, info->wasDirtySceneTransform,
+ info->drawItem);
+ } else {
+ // Item coordinates with info.
+ QTransform newEffectTransform = info->transformPtr->inverted();
+ newEffectTransform *= effectTransform;
+ scened->draw(item, &pixmapPainter, info->viewTransform, info->transformPtr, info->exposedRegion,
+ info->widget, info->opacity, &newEffectTransform, info->wasDirtySceneTransform,
+ info->drawItem);
+ }
+ return pixmap;
+}
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, QGraphicsItem *item)
{
diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h
index 176d7199e..7af7c2f2b 100644
--- a/src/gui/graphicsview/qgraphicsitem.h
+++ b/src/gui/graphicsview/qgraphicsitem.h
@@ -63,6 +63,7 @@ QT_MODULE(Gui)
class QBrush;
class QCursor;
class QFocusEvent;
+class QGraphicsEffect;
class QGraphicsItemGroup;
class QGraphicsObject;
class QGraphicsSceneContextMenuEvent;
@@ -210,6 +211,10 @@ public:
qreal effectiveOpacity() const;
void setOpacity(qreal opacity);
+ // Effect
+ QGraphicsEffect *graphicsEffect() const;
+ void setGraphicsEffect(QGraphicsEffect *effect);
+
Qt::MouseButtons acceptedMouseButtons() const;
void setAcceptedMouseButtons(Qt::MouseButtons buttons);
@@ -447,6 +452,7 @@ private:
friend class QGraphicsSceneIndexPrivate;
friend class QGraphicsSceneBspTreeIndex;
friend class QGraphicsSceneBspTreeIndexPrivate;
+ friend class QGraphicsItemEffectSourcePrivate;
friend class QGraphicsTransformPrivate;
friend class ::tst_QGraphicsItem;
friend bool qt_closestLeaf(const QGraphicsItem *, const QGraphicsItem *);
diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h
index 6456ae7fc..11f6f536c 100644
--- a/src/gui/graphicsview/qgraphicsitem_p.h
+++ b/src/gui/graphicsview/qgraphicsitem_p.h
@@ -60,6 +60,8 @@
#include "qgraphicstransform.h"
#include <private/qgraphicstransform_p.h>
+#include <private/qgraphicseffect_p.h>
+
#include <QtCore/qpoint.h>
#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
@@ -121,6 +123,7 @@ public:
scene(0),
parent(0),
transformData(0),
+ graphicsEffect(0),
index(-1),
siblingIndex(-1),
itemDepth(-1),
@@ -165,6 +168,8 @@ public:
acceptedTouchBeginEvent(0),
filtersDescendantEvents(0),
sceneTransformTranslateOnly(0),
+ notifyBoundingRectChanged(0),
+ notifyInvalidated(0),
mouseSetsFocus(1),
globalStackingOrder(-1),
q_ptr(0)
@@ -218,6 +223,8 @@ public:
void childrenBoundingRectHelper(QTransform *x, QRectF *rect);
void initStyleOption(QStyleOptionGraphicsItem *option, const QTransform &worldTransform,
const QRegion &exposedRegion, bool allItems = false) const;
+ QRectF effectiveBoundingRect() const;
+ QRectF sceneEffectiveBoundingRect() const;
virtual void resolveFont(uint inheritedMask)
{
@@ -418,6 +425,7 @@ public:
QList<QGraphicsItem *> children;
struct TransformData;
TransformData *transformData;
+ QGraphicsEffect *graphicsEffect;
QTransform sceneTransform;
int index;
int siblingIndex;
@@ -468,8 +476,10 @@ public:
quint32 acceptedTouchBeginEvent : 1;
quint32 filtersDescendantEvents : 1;
quint32 sceneTransformTranslateOnly : 1;
+ quint32 notifyBoundingRectChanged : 1;
+ quint32 notifyInvalidated : 1;
quint32 mouseSetsFocus : 1;
- quint32 unused : 3; // feel free to use
+ quint32 unused : 1; // feel free to use
// Optional stacking order
int globalStackingOrder;
@@ -502,19 +512,88 @@ struct QGraphicsItemPrivate::TransformData
return transform * *postmultiplyTransform;
}
- QTransform x(transform);
+ QMatrix4x4 x(transform);
for (int i = 0; i < graphicsTransforms.size(); ++i)
graphicsTransforms.at(i)->applyTo(&x);
x.translate(xOrigin, yOrigin);
- x.rotate(rotation, Qt::ZAxis);
- x.scale(scale, scale);
+ x.rotate(rotation, 0, 0, 1);
+ x.scale(scale);
x.translate(-xOrigin, -yOrigin);
+ QTransform t = x.toTransform(); // project the 3D matrix back to 2D.
if (postmultiplyTransform)
- x *= *postmultiplyTransform;
- return x;
+ t *= *postmultiplyTransform;
+ return t;
+ }
+};
+
+struct QGraphicsItemPaintInfo
+{
+ inline QGraphicsItemPaintInfo(const QTransform *const xform1, const QTransform *const xform2,
+ const QTransform *const xform3,
+ QRegion *r, QWidget *w, QStyleOptionGraphicsItem *opt,
+ QPainter *p, qreal o, bool b1, bool b2)
+ : viewTransform(xform1), transformPtr(xform2), effectTransform(xform3), exposedRegion(r), widget(w),
+ option(opt), painter(p), opacity(o), wasDirtySceneTransform(b1), drawItem(b2)
+ {}
+
+ const QTransform *viewTransform;
+ const QTransform *transformPtr;
+ const QTransform *effectTransform;
+ QRegion *exposedRegion;
+ QWidget *widget;
+ QStyleOptionGraphicsItem *option;
+ QPainter *painter;
+ qreal opacity;
+ quint32 wasDirtySceneTransform : 1;
+ quint32 drawItem : 1;
+};
+
+class QGraphicsItemEffectSourcePrivate : public QGraphicsEffectSourcePrivate
+{
+public:
+ QGraphicsItemEffectSourcePrivate(QGraphicsItem *i)
+ : QGraphicsEffectSourcePrivate(), item(i), info(0)
+ {}
+
+ inline void detach()
+ { item->setGraphicsEffect(0); }
+
+ inline const QGraphicsItem *graphicsItem() const
+ { return item; }
+
+ inline const QWidget *widget() const
+ { return 0; }
+
+ inline void update()
+ { item->update(); }
+
+ inline bool isPixmap() const
+ {
+ return (item->type() == QGraphicsPixmapItem::Type);
+ //|| (item->d_ptr->isObject && qobject_cast<QFxImage *>(q_func()));
+ }
+
+ inline const QStyleOption *styleOption() const
+ { return info ? info->option : 0; }
+
+ inline QRect deviceRect() const
+ {
+ if (!info || !info->widget) {
+ qWarning("QGraphicsEffectSource::deviceRect: Not yet implemented, lacking device context");
+ return QRect();
+ }
+ return info->widget->rect();
}
+
+ QRectF boundingRect(Qt::CoordinateSystem system) const;
+ void draw(QPainter *);
+ QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset) const;
+
+ QGraphicsItem *item;
+ QGraphicsItemPaintInfo *info;
};
+
/*!
\internal
*/
diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp
index a8db74d59..a819822d3 100644
--- a/src/gui/graphicsview/qgraphicsscene.cpp
+++ b/src/gui/graphicsview/qgraphicsscene.cpp
@@ -244,11 +244,13 @@
#include <QtGui/qtransform.h>
#include <QtGui/qgesture.h>
#include <QtGui/qinputcontext.h>
+#include <QtGui/qgraphicseffect.h>
#include <private/qapplication_p.h>
#include <private/qobject_p.h>
#ifdef Q_WS_X11
#include <private/qt_x11_p.h>
#endif
+#include <private/qgraphicseffect_p.h>
QT_BEGIN_NAMESPACE
@@ -4302,7 +4304,7 @@ void QGraphicsScenePrivate::drawItems(QPainter *painter, const QTransform *const
void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter *painter,
const QTransform *const viewTransform,
QRegion *exposedRegion, QWidget *widget,
- qreal parentOpacity)
+ qreal parentOpacity, const QTransform *const effectTransform)
{
Q_ASSERT(item);
@@ -4351,11 +4353,12 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter *
const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape);
bool drawItem = itemHasContents && !itemIsFullyTransparent;
if (drawItem) {
- const QRectF brect = adjustedItemBoundingRect(item);
+ const QRectF brect = adjustedItemEffectiveBoundingRect(item);
ENSURE_TRANSFORM_PTR
QRect viewBoundingRect = translateOnlyTransform ? brect.translated(transformPtr->dx(), transformPtr->dy()).toRect()
: transformPtr->mapRect(brect).toRect();
- item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect);
+ if (widget)
+ item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect);
viewBoundingRect.adjust(-1, -1, 1, 1);
drawItem = exposedRegion ? exposedRegion->intersects(viewBoundingRect) : !viewBoundingRect.isEmpty();
if (!drawItem) {
@@ -4369,14 +4372,51 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter *
}
} // else we know for sure this item has children we must process.
+ if (itemHasChildren && itemClipsChildrenToShape)
+ ENSURE_TRANSFORM_PTR;
+
+ 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);
+ QGraphicsEffectSource *source = item->d_ptr->graphicsEffect->d_func()->source;
+ QGraphicsItemEffectSourcePrivate *sourced = static_cast<QGraphicsItemEffectSourcePrivate *>
+ (source->d_func());
+ sourced->info = &info;
+ const QTransform restoreTransform = painter->worldTransform();
+ if (effectTransform)
+ painter->setWorldTransform(*transformPtr * *effectTransform);
+ else
+ painter->setWorldTransform(*transformPtr);
+ item->d_ptr->graphicsEffect->draw(painter, source);
+ painter->setWorldTransform(restoreTransform);
+ sourced->info = 0;
+ } else {
+ draw(item, painter, viewTransform, transformPtr, exposedRegion, widget, opacity,
+ effectTransform, wasDirtyParentSceneTransform, drawItem);
+ }
+}
+
+void QGraphicsScenePrivate::draw(QGraphicsItem *item, QPainter *painter, const QTransform *const viewTransform,
+ const QTransform *const transformPtr, QRegion *exposedRegion, QWidget *widget,
+ qreal opacity, const QTransform *effectTransform,
+ bool wasDirtyParentSceneTransform, bool drawItem)
+{
+ const bool itemIsFullyTransparent = (opacity < 0.0001);
+ const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape);
+ const bool itemHasChildren = !item->d_ptr->children.isEmpty();
+
int i = 0;
if (itemHasChildren) {
item->d_ptr->ensureSortedChildren();
if (itemClipsChildrenToShape) {
painter->save();
- ENSURE_TRANSFORM_PTR
- painter->setWorldTransform(*transformPtr);
+ Q_ASSERT(transformPtr);
+ if (effectTransform)
+ painter->setWorldTransform(*transformPtr * *effectTransform);
+ else
+ painter->setWorldTransform(*transformPtr);
painter->setClipPath(item->shape(), Qt::IntersectClip);
}
@@ -4389,15 +4429,15 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter *
break;
if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity))
continue;
- drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity);
+ drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity, effectTransform);
}
}
// Draw item
if (drawItem) {
Q_ASSERT(!itemIsFullyTransparent);
- Q_ASSERT(itemHasContents);
- ENSURE_TRANSFORM_PTR
+ Q_ASSERT(!(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents));
+ Q_ASSERT(transformPtr);
item->d_ptr->initStyleOption(&styleOptionTmp, *transformPtr, exposedRegion
? *exposedRegion : QRegion(), exposedRegion == 0);
@@ -4406,8 +4446,12 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter *
if (savePainter)
painter->save();
- if (!itemHasChildren || !itemClipsChildrenToShape)
- painter->setWorldTransform(*transformPtr);
+ if (!itemHasChildren || !itemClipsChildrenToShape) {
+ if (effectTransform)
+ painter->setWorldTransform(*transformPtr * *effectTransform);
+ else
+ painter->setWorldTransform(*transformPtr);
+ }
if (itemClipsToShape)
painter->setClipPath(item->shape(), Qt::IntersectClip);
@@ -4430,7 +4474,7 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter *
child->d_ptr->dirtySceneTransform = 1;
if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity))
continue;
- drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity);
+ drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity, effectTransform);
}
}
@@ -4513,8 +4557,12 @@ void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, b
item->d_ptr->ignoreOpacity = 1;
QGraphicsItem *p = item->d_ptr->parent;
- while (p && !p->d_ptr->dirtyChildren) {
+ while (p) {
p->d_ptr->dirtyChildren = 1;
+ if (p->d_ptr->graphicsEffect && p->d_ptr->graphicsEffect->isEnabled()) {
+ p->d_ptr->dirty = 1;
+ p->d_ptr->fullUpdatePending = 1;
+ }
p = p->d_ptr->parent;
}
}
@@ -4623,7 +4671,7 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool
// Process item.
if (item->d_ptr->dirty || item->d_ptr->paintedViewBoundingRectsNeedRepaint) {
const bool useCompatUpdate = views.isEmpty() || isSignalConnected(changedSignalIndex);
- const QRectF itemBoundingRect = adjustedItemBoundingRect(item);
+ const QRectF itemBoundingRect = adjustedItemEffectiveBoundingRect(item);
if (useCompatUpdate && !itemIsUntransformable && qFuzzyIsNull(item->boundingRegionGranularity())) {
// This block of code is kept for compatibility. Since 4.5, by default
diff --git a/src/gui/graphicsview/qgraphicsscene.h b/src/gui/graphicsview/qgraphicsscene.h
index 0ef9f04e5..26cb1c2e3 100644
--- a/src/gui/graphicsview/qgraphicsscene.h
+++ b/src/gui/graphicsview/qgraphicsscene.h
@@ -302,10 +302,12 @@ private:
friend class QGraphicsViewPrivate;
friend class QGraphicsWidget;
friend class QGraphicsWidgetPrivate;
+ friend class QGraphicsEffect;
friend class QGraphicsSceneIndex;
friend class QGraphicsSceneIndexPrivate;
friend class QGraphicsSceneBspTreeIndex;
friend class QGraphicsSceneBspTreeIndexPrivate;
+ friend class QGraphicsItemEffectSourcePrivate;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsScene::SceneLayers)
diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h
index f1ddb5acd..4b8791ef7 100644
--- a/src/gui/graphicsview/qgraphicsscene_p.h
+++ b/src/gui/graphicsview/qgraphicsscene_p.h
@@ -202,7 +202,11 @@ public:
QRegion *exposedRegion, QWidget *widget);
void drawSubtreeRecursive(QGraphicsItem *item, QPainter *painter, const QTransform *const,
- QRegion *exposedRegion, QWidget *widget, qreal parentOpacity = qreal(1.0));
+ QRegion *exposedRegion, QWidget *widget, qreal parentOpacity = qreal(1.0),
+ const QTransform *const effectTransform = 0);
+ void draw(QGraphicsItem *, QPainter *, const QTransform *const, const QTransform *const,
+ QRegion *, QWidget *, qreal, const QTransform *const, bool, bool);
+
void markDirty(QGraphicsItem *item, const QRectF &rect = QRectF(), bool invalidateChildren = false,
bool maybeDirtyClipPath = false, bool force = false, bool ignoreOpacity = false,
bool removingItemFromScene = false);
@@ -223,10 +227,21 @@ public:
item->d_ptr->fullUpdatePending = 0;
item->d_ptr->ignoreVisible = 0;
item->d_ptr->ignoreOpacity = 0;
+ QGraphicsEffect::ChangeFlags flags;
+ if (item->d_ptr->notifyBoundingRectChanged) {
+ flags |= QGraphicsEffect::SourceBoundingRectChanged;
+ item->d_ptr->notifyBoundingRectChanged = 0;
+ }
+ if (item->d_ptr->notifyInvalidated) {
+ flags |= QGraphicsEffect::SourceInvalidated;
+ item->d_ptr->notifyInvalidated = 0;
+ }
if (recursive) {
for (int i = 0; i < item->d_ptr->children.size(); ++i)
resetDirtyItem(item->d_ptr->children.at(i), recursive);
}
+ if (flags && item->d_ptr->graphicsEffect)
+ item->d_ptr->graphicsEffect->sourceChanged(flags);
}
inline void ensureSortedTopLevelItems()
@@ -280,6 +295,14 @@ static inline QRectF adjustedItemBoundingRect(const QGraphicsItem *item)
return boundingRect;
}
+static inline QRectF adjustedItemEffectiveBoundingRect(const QGraphicsItem *item)
+{
+ Q_ASSERT(item);
+ QRectF boundingRect(QGraphicsItemPrivate::get(item)->effectiveBoundingRect());
+ _q_adjustRect(&boundingRect);
+ return boundingRect;
+}
+
QT_END_NAMESPACE
#endif // QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp
index 7483bc166..b9c4ed8eb 100644
--- a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp
+++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp
@@ -172,7 +172,7 @@ void QGraphicsSceneBspTreeIndexPrivate::_q_updateIndex()
if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)
continue;
- bsp.insertItem(item, item->sceneBoundingRect());
+ bsp.insertItem(item, item->d_ptr->sceneEffectiveBoundingRect());
}
}
unindexedItems.clear();
@@ -352,7 +352,7 @@ void QGraphicsSceneBspTreeIndexPrivate::removeItem(QGraphicsItem *item, bool rec
purgePending = true;
removedItems << item;
} else if (!(item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) {
- bsp.removeItem(item, item->sceneBoundingRect());
+ bsp.removeItem(item, item->d_ptr->sceneEffectiveBoundingRect());
}
} else {
unindexedItems.removeOne(item);
diff --git a/src/gui/graphicsview/qgraphicstransform.cpp b/src/gui/graphicsview/qgraphicstransform.cpp
index edfcf8aee..c5653d731 100644
--- a/src/gui/graphicsview/qgraphicstransform.cpp
+++ b/src/gui/graphicsview/qgraphicstransform.cpp
@@ -63,10 +63,17 @@
independent transformation. The resulting operation is then combined into a
single transform which is applied to QGraphicsItem.
+ Transformations are computed in true 3D space using QMatrix4x4.
+ When the transformation is applied to a QGraphicsItem, it will be
+ projected back to a 2D QTransform. When multiple QGraphicsTransform
+ objects are applied to a QGraphicsItem, all of the transformations
+ are computed in true 3D space, with the projection back to 2D
+ only occurring after the last QGraphicsTransform is applied.
+
If you want to create your own configurable transformation, you can create
a subclass of QGraphicsTransform (or any or the existing subclasses), and
reimplement the pure virtual applyTo() function, which takes a pointer to a
- QTransform. Each operation you would like to apply should be exposed as
+ QMatrix4x4. Each operation you would like to apply should be exposed as
properties (e.g., customTransform->setVerticalShear(2.5)). Inside you
reimplementation of applyTo(), you can modify the provided transform
respectively.
@@ -136,28 +143,13 @@ QGraphicsTransform::QGraphicsTransform(QGraphicsTransformPrivate &p, QObject *pa
}
/*!
- Applies this transformation to an identity transform, and returns the
- resulting transform.
-
- This is equivalent to passing an identity transform to applyTo().
-
- \sa applyTo()
-*/
-QTransform QGraphicsTransform::transform() const
-{
- QTransform t;
- applyTo(&t);
- return t;
-}
-
-/*!
- \fn void QGraphicsTransform::applyTo(QTransform *transform) const
+ \fn void QGraphicsTransform::applyTo(QMatrix4x4 *matrix) const
This pure virtual method has to be reimplemented in derived classes.
- It applies this transformation to \a transform.
+ It applies this transformation to \a matrix.
- \sa QGraphicsItem::transform()
+ \sa QGraphicsItem::transform(), QMatrix4x4::toTransform()
*/
/*!
@@ -189,11 +181,12 @@ void QGraphicsTransform::update()
relative to the parent as the rest of the item grows). By default the
origin is QPointF(0, 0).
- The two parameters xScale and yScale describe the scale factors to apply in
- horizontal and vertical direction. They can take on any value, including 0
- (to collapse the item to a point) or negativate value. A negative xScale
- value will mirror the item horizontally. A negative yScale value will flip
- the item vertically.
+ The parameters xScale, yScale, and zScale describe the scale factors to
+ apply in horizontal, vertical, and depth directions. They can take on any
+ value, including 0 (to collapse the item to a point) or negative value.
+ A negative xScale value will mirror the item horizontally. A negative yScale
+ value will flip the item vertically. A negative zScale will flip the
+ item end for end.
\sa QGraphicsTransform, QGraphicsItem::setScale(), QTransform::scale()
*/
@@ -202,10 +195,11 @@ class QGraphicsScalePrivate : public QGraphicsTransformPrivate
{
public:
QGraphicsScalePrivate()
- : xScale(1), yScale(1) {}
- QPointF origin;
+ : xScale(1), yScale(1), zScale(1) {}
+ QVector3D origin;
qreal xScale;
qreal yScale;
+ qreal zScale;
};
/*!
@@ -225,21 +219,23 @@ QGraphicsScale::~QGraphicsScale()
/*!
\property QGraphicsScale::origin
- \brief The QGraphicsScene class provides the origin of the scale.
+ \brief the origin of the scale in 3D space.
All scaling will be done relative to this point (i.e., this point
will stay fixed, relative to the parent, when the item is scaled).
- \sa xScale, yScale
+ \sa xScale, yScale, zScale
*/
-QPointF QGraphicsScale::origin() const
+QVector3D QGraphicsScale::origin() const
{
Q_D(const QGraphicsScale);
return d->origin;
}
-void QGraphicsScale::setOrigin(const QPointF &point)
+void QGraphicsScale::setOrigin(const QVector3D &point)
{
Q_D(QGraphicsScale);
+ if (d->origin == point)
+ return;
d->origin = point;
update();
emit originChanged();
@@ -254,7 +250,7 @@ void QGraphicsScale::setOrigin(const QPointF &point)
provide a negative value, the item will be mirrored horizontally around its
origin.
- \sa yScale, origin
+ \sa yScale, zScale, origin
*/
qreal QGraphicsScale::xScale() const
{
@@ -280,7 +276,7 @@ void QGraphicsScale::setXScale(qreal scale)
provide a negative value, the item will be flipped vertically around its
origin.
- \sa xScale, origin
+ \sa xScale, zScale, origin
*/
qreal QGraphicsScale::yScale() const
{
@@ -298,14 +294,40 @@ void QGraphicsScale::setYScale(qreal scale)
}
/*!
+ \property QGraphicsScale::zScale
+ \brief the depth scale factor.
+
+ The scale factor can be any real number; the default value is 1.0. If you
+ set the factor to 0.0, the item will be collapsed to a single point. If you
+ provide a negative value, the item will be flipped end for end around its
+ origin.
+
+ \sa xScale, yScale, origin
+*/
+qreal QGraphicsScale::zScale() const
+{
+ Q_D(const QGraphicsScale);
+ return d->zScale;
+}
+void QGraphicsScale::setZScale(qreal scale)
+{
+ Q_D(QGraphicsScale);
+ if (d->zScale == scale)
+ return;
+ d->zScale = scale;
+ update();
+ emit scaleChanged();
+}
+
+/*!
\reimp
*/
-void QGraphicsScale::applyTo(QTransform *transform) const
+void QGraphicsScale::applyTo(QMatrix4x4 *matrix) const
{
Q_D(const QGraphicsScale);
- transform->translate(d->origin.x(), d->origin.y());
- transform->scale(d->xScale, d->yScale);
- transform->translate(-d->origin.x(), -d->origin.y());
+ matrix->translate(d->origin);
+ matrix->scale(d->xScale, d->yScale, d->zScale);
+ matrix->translate(-d->origin);
}
/*!
@@ -319,10 +341,11 @@ void QGraphicsScale::applyTo(QTransform *transform) const
/*!
\fn QGraphicsScale::scaleChanged()
- This signal is emitted whenever the xScale or yScale of the object
- changes.
+ This signal is emitted whenever the xScale, yScale, or zScale
+ of the object changes.
\sa QGraphicsScale::xScale, QGraphicsScale::yScale
+ \sa QGraphicsScale::zScale
*/
/*!
@@ -359,20 +382,14 @@ void QGraphicsScale::applyTo(QTransform *transform) const
\sa QGraphicsTransform, QGraphicsItem::setRotation(), QTransform::rotate()
*/
-#define VECTOR_FOR_AXIS_X QVector3D(1, 0, 0)
-#define VECTOR_FOR_AXIS_Y QVector3D(0, 1, 0)
-#define VECTOR_FOR_AXIS_Z QVector3D(0, 0, 1)
-
-
class QGraphicsRotationPrivate : public QGraphicsTransformPrivate
{
public:
QGraphicsRotationPrivate()
- : angle(0), axis(VECTOR_FOR_AXIS_Z), simpleAxis(Qt::ZAxis) {}
- QPointF origin;
+ : angle(0), axis(0, 0, 1) {}
+ QVector3D origin;
qreal angle;
QVector3D axis;
- int simpleAxis;
};
/*!
@@ -392,21 +409,23 @@ QGraphicsRotation::~QGraphicsRotation()
/*!
\property QGraphicsRotation::origin
- \brief the origin of the rotation.
+ \brief the origin of the rotation in 3D space.
All rotations will be done relative to this point (i.e., this point
will stay fixed, relative to the parent, when the item is rotated).
\sa angle
*/
-QPointF QGraphicsRotation::origin() const
+QVector3D QGraphicsRotation::origin() const
{
Q_D(const QGraphicsRotation);
return d->origin;
}
-void QGraphicsRotation::setOrigin(const QPointF &point)
+void QGraphicsRotation::setOrigin(const QVector3D &point)
{
Q_D(QGraphicsRotation);
+ if (d->origin == point)
+ return;
d->origin = point;
update();
emit originChanged();
@@ -448,11 +467,11 @@ void QGraphicsRotation::setAngle(qreal angle)
*/
/*!
- \fn void QGraphicsRotation::angleChanged()
+ \fn void QGraphicsRotation::angleChanged()
- This signal is emitted whenever the angle has changed.
+ This signal is emitted whenever the angle has changed.
- \sa QGraphicsRotation::angle
+ \sa QGraphicsRotation::angle
*/
/*!
@@ -475,18 +494,9 @@ QVector3D QGraphicsRotation::axis() const
void QGraphicsRotation::setAxis(const QVector3D &axis)
{
Q_D(QGraphicsRotation);
- if (d->axis == axis)
+ if (d->axis == axis)
return;
- d->axis = axis;
- if (axis == VECTOR_FOR_AXIS_X) {
- d->simpleAxis = Qt::XAxis;
- } else if (axis == VECTOR_FOR_AXIS_Y) {
- d->simpleAxis = Qt::YAxis;
- } else if (axis == VECTOR_FOR_AXIS_Z) {
- d->simpleAxis = Qt::ZAxis;
- } else {
- d->simpleAxis = -1; // no predefined axis
- }
+ d->axis = axis;
update();
emit axisChanged();
}
@@ -495,90 +505,58 @@ void QGraphicsRotation::setAxis(const QVector3D &axis)
\fn void QGraphicsRotation::setAxis(Qt::Axis axis)
Convenience function to set the axis to \a axis.
-*/
+ Note: the Qt::YAxis rotation for QTransform is inverted from the
+ correct mathematical rotation in 3D space. The QGraphicsRotation
+ class implements a correct mathematical rotation. The following
+ two sequences of code will perform the same transformation:
+
+ \code
+ QTransform t;
+ t.rotate(45, Qt::YAxis);
+
+ QGraphicsRotation r;
+ r.setAxis(Qt::YAxis);
+ r.setAngle(-45);
+ \endcode
+*/
void QGraphicsRotation::setAxis(Qt::Axis axis)
{
switch (axis)
{
case Qt::XAxis:
- setAxis(VECTOR_FOR_AXIS_X);
+ setAxis(QVector3D(1, 0, 0));
break;
case Qt::YAxis:
- setAxis(VECTOR_FOR_AXIS_Y);
+ setAxis(QVector3D(0, 1, 0));
break;
case Qt::ZAxis:
- setAxis(VECTOR_FOR_AXIS_Z);
+ setAxis(QVector3D(0, 0, 1));
break;
}
}
-
-const qreal deg2rad = qreal(0.017453292519943295769); // pi/180
-static const qreal inv_dist_to_plane = 1. / 1024.;
-
/*!
\reimp
*/
-void QGraphicsRotation::applyTo(QTransform *t) const
+void QGraphicsRotation::applyTo(QMatrix4x4 *matrix) const
{
Q_D(const QGraphicsRotation);
- qreal a = d->angle;
-
- if (a == 0.)
- return;
-
- if (d->simpleAxis != -1) {
- //that's an optimization for simple axis
- t->translate(d->origin.x(), d->origin.y());
- t->rotate(a, Qt::Axis(d->simpleAxis));
- t->translate(-d->origin.x(), -d->origin.y());
- return;
- }
-
- qreal x = d->axis.x();
- qreal y = d->axis.y();
- qreal z = d->axis.z();
-
- if (x == 0. && y == 0 && z == 0)
+ if (d->angle == 0. || d->axis.isNull())
return;
- qreal c, s;
- if (a == 90. || a == -270.) {
- s = 1.;
- c = 0.;
- } else if (a == 270. || a == -90.) {
- s = -1.;
- c = 0.;
- } else if (a == 180.) {
- s = 0.;
- c = -1.;
- } else {
- qreal b = deg2rad*a;
- s = qSin(b);
- c = qCos(b);
- }
-
- qreal len = x * x + y * y + z * z;
- if (len != 1.) {
- len = 1. / qSqrt(len);
- x *= len;
- y *= len;
- z *= len;
- }
-
- t->translate(d->origin.x(), d->origin.y());
- *t = QTransform(x*x*(1-c)+c, x*y*(1-c)+z*s, x*z*(1-c)-y*s*inv_dist_to_plane,
- y*x*(1-c)-z*s, y*y*(1-c)+c, y*z*(1-c)-x*s*inv_dist_to_plane,
- 0, 0, 1) * *t;
- t->translate(-d->origin.x(), -d->origin.y());
+ matrix->translate(d->origin);
+ matrix->rotate(d->angle, d->axis.x(), d->axis.y(), d->axis.z());
+ matrix->translate(-d->origin);
}
/*!
\fn void QGraphicsRotation::axisChanged()
This signal is emitted whenever the axis of the object changes.
+
+ \sa QGraphicsRotation::axis
*/
#include "moc_qgraphicstransform.cpp"
diff --git a/src/gui/graphicsview/qgraphicstransform.h b/src/gui/graphicsview/qgraphicstransform.h
index 8ccc25854..d6d5b79f1 100644
--- a/src/gui/graphicsview/qgraphicstransform.h
+++ b/src/gui/graphicsview/qgraphicstransform.h
@@ -43,8 +43,9 @@
#define QGRAPHICSTRANSFORM_H
#include <QtCore/QObject>
-#include <QtGui/QTransform>
#include <QtGui/QVector3D>
+#include <QtGui/QTransform>
+#include <QtGui/QMatrix4x4>
QT_BEGIN_HEADER
@@ -62,8 +63,7 @@ public:
QGraphicsTransform(QObject *parent = 0);
~QGraphicsTransform();
- QTransform transform() const;
- virtual void applyTo(QTransform *transform) const = 0;
+ virtual void applyTo(QMatrix4x4 *matrix) const = 0;
protected Q_SLOTS:
void update();
@@ -83,15 +83,16 @@ class Q_GUI_EXPORT QGraphicsScale : public QGraphicsTransform
{
Q_OBJECT
- Q_PROPERTY(QPointF origin READ origin WRITE setOrigin NOTIFY originChanged)
+ Q_PROPERTY(QVector3D origin READ origin WRITE setOrigin NOTIFY originChanged)
Q_PROPERTY(qreal xScale READ xScale WRITE setXScale NOTIFY scaleChanged)
Q_PROPERTY(qreal yScale READ yScale WRITE setYScale NOTIFY scaleChanged)
+ Q_PROPERTY(qreal zScale READ zScale WRITE setZScale NOTIFY scaleChanged)
public:
QGraphicsScale(QObject *parent = 0);
~QGraphicsScale();
- QPointF origin() const;
- void setOrigin(const QPointF &point);
+ QVector3D origin() const;
+ void setOrigin(const QVector3D &point);
qreal xScale() const;
void setXScale(qreal);
@@ -99,7 +100,10 @@ public:
qreal yScale() const;
void setYScale(qreal);
- void applyTo(QTransform *transform) const;
+ qreal zScale() const;
+ void setZScale(qreal);
+
+ void applyTo(QMatrix4x4 *matrix) const;
Q_SIGNALS:
void originChanged();
@@ -115,15 +119,15 @@ class Q_GUI_EXPORT QGraphicsRotation : public QGraphicsTransform
{
Q_OBJECT
- Q_PROPERTY(QPointF origin READ origin WRITE setOrigin NOTIFY originChanged)
+ Q_PROPERTY(QVector3D origin READ origin WRITE setOrigin NOTIFY originChanged)
Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged)
Q_PROPERTY(QVector3D axis READ axis WRITE setAxis NOTIFY axisChanged)
public:
QGraphicsRotation(QObject *parent = 0);
~QGraphicsRotation();
- QPointF origin() const;
- void setOrigin(const QPointF &point);
+ QVector3D origin() const;
+ void setOrigin(const QVector3D &point);
qreal angle() const;
void setAngle(qreal);
@@ -132,7 +136,7 @@ public:
void setAxis(const QVector3D &axis);
void setAxis(Qt::Axis axis);
- void applyTo(QTransform *transform) const;
+ void applyTo(QMatrix4x4 *matrix) const;
Q_SIGNALS:
void originChanged();
diff --git a/src/gui/graphicsview/qsimplex_p.cpp b/src/gui/graphicsview/qsimplex_p.cpp
new file mode 100644
index 000000000..7fa5ab025
--- /dev/null
+++ b/src/gui/graphicsview/qsimplex_p.cpp
@@ -0,0 +1,371 @@
+#include "qsimplex_p.h"
+
+#include <QtCore/qset.h>
+#include <QtCore/qdebug.h>
+
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+QSimplex::QSimplex() : objective(0), rows(0), columns(0), firstArtificial(0), matrix(0)
+{
+}
+
+QSimplex::~QSimplex()
+{
+ clearDataStructures();
+}
+
+void QSimplex::clearDataStructures()
+{
+ if (matrix == 0)
+ return;
+
+ // Matrix
+ rows = 0;
+ columns = 0;
+ firstArtificial = 0;
+ free(matrix);
+ matrix = 0;
+
+ // Constraints
+ for (int i = 0; i < constraints.size(); ++i) {
+ delete constraints[i]->helper.first;
+ constraints[i]->helper.first = 0;
+ constraints[i]->helper.second = 0.0;
+ delete constraints[i]->artificial;
+ constraints[i]->artificial = 0;
+ }
+ constraints.clear();
+
+ // Other
+ variables.clear();
+ objective = 0;
+}
+
+void QSimplex::setConstraints(const QList<QSimplexConstraint *> newConstraints)
+{
+ clearDataStructures();
+
+ if (newConstraints.isEmpty())
+ return;
+ constraints = newConstraints;
+
+ // Set Variables direct mapping
+ QSet<QSimplexVariable *> variablesSet;
+ for (int i = 0; i < constraints.size(); ++i)
+ variablesSet += \
+ QSet<QSimplexVariable *>::fromList(constraints[i]->variables.keys());
+ variables = variablesSet.toList();
+
+ // Set Variables reverse mapping
+ for (int i = 0; i < variables.size(); ++i) {
+ // The variable "0" goes at the column "1", etc...
+ variables[i]->index = i + 1;
+ }
+
+ // Normalize Constraints
+ int variableIndex = variables.size();
+ QList <QSimplexVariable *> artificialList;
+
+ for (int i = 0; i < constraints.size(); ++i) {
+ QSimplexVariable *slack;
+ QSimplexVariable *surplus;
+ QSimplexVariable *artificial;
+
+ Q_ASSERT(constraints[i]->helper.first == 0);
+ Q_ASSERT(constraints[i]->artificial == 0);
+
+ switch(constraints[i]->ratio) {
+ case QSimplexConstraint::LessOrEqual:
+ slack = new QSimplexVariable;
+ slack->index = ++variableIndex;
+ constraints[i]->helper.first = slack;
+ constraints[i]->helper.second = 1.0;
+ break;
+ case QSimplexConstraint::MoreOrEqual:
+ surplus = new QSimplexVariable;
+ surplus->index = ++variableIndex;
+ constraints[i]->helper.first = surplus;
+ constraints[i]->helper.second = -1.0;
+ // fall through
+ case QSimplexConstraint::Equal:
+ artificial = new QSimplexVariable;
+ constraints[i]->artificial = artificial;
+ artificialList += constraints[i]->artificial;
+ break;
+ }
+ }
+
+ firstArtificial = variableIndex + 1;
+ for (int i = 0; i < artificialList.size(); ++i)
+ artificialList[i]->index = ++variableIndex;
+ artificialList.clear();
+
+ // Matrix
+
+ // One for each variable plus the Basic and BFS columns (first and last)
+ columns = variableIndex + 2;
+ // One for each constraint plus the objective function
+ rows = constraints.size() + 1;
+
+ matrix = (qreal *)malloc(sizeof(qreal) * columns * rows);
+ if (!matrix) {
+ qWarning() << "QSimplex: Unable to allocate memory!";
+ return;
+ }
+ for (int i = columns * rows - 1; i >= 0; --i)
+ matrix[i] = 0.0;
+
+ // Fill Matrix
+ for (int i = 1; i <= constraints.size(); ++i) {
+ QSimplexConstraint *c = constraints[i - 1];
+
+ if (c->artificial) {
+ // Will use artificial basic variable
+ setValueAt(i, 0, c->artificial->index);
+ setValueAt(i, c->artificial->index, 1.0);
+
+ if (c->helper.second != 0.0) {
+ // Surplus variable
+ setValueAt(i, c->helper.first->index, c->helper.second);
+ }
+ } else {
+ // Slack is used as the basic variable
+ Q_ASSERT(c->helper.second == 1.0);
+ setValueAt(i, 0, c->helper.first->index);
+ setValueAt(i, c->helper.first->index, 1.0);
+ }
+
+ QHash<QSimplexVariable *, qreal>::const_iterator iter;
+ for (iter = c->variables.constBegin();
+ iter != c->variables.constEnd();
+ ++iter) {
+ setValueAt(i, iter.key()->index, iter.value());
+ }
+
+ setValueAt(i, columns - 1, c->constant);
+ }
+
+ // Set temporary objective: -1 * sum_of_artificial_vars
+ for (int j = firstArtificial; j < columns - 1; ++j)
+ setValueAt(0, j, 1.0);
+
+ // Maximize our objective (artificial vars go to zero)
+ solveMaxHelper();
+
+ if (valueAt(0, columns - 1) != 0.0) {
+ qWarning() << "QSimplex: No feasible solution!";
+ clearDataStructures();
+ return;
+ }
+
+ // Remove artificial variables
+ clearColumns(firstArtificial, columns - 2);
+}
+
+void QSimplex::solveMaxHelper()
+{
+ reducedRowEchelon();
+ while (iterate()) ;
+}
+
+void QSimplex::setObjective(QSimplexConstraint *newObjective)
+{
+ objective = newObjective;
+}
+
+void QSimplex::clearRow(int rowIndex)
+{
+ qreal *item = matrix + rowIndex * columns;
+ for (int i = 0; i < columns; ++i)
+ item[i] = 0.0;
+}
+
+void QSimplex::clearColumns(int first, int last)
+{
+ for (int i = 0; i < rows; ++i) {
+ qreal *row = matrix + i * columns;
+ for (int j = first; j <= last; ++j)
+ row[j] = 0.0;
+ }
+}
+
+void QSimplex::dumpMatrix()
+{
+ printf("---- Simplex Matrix ----\n");
+
+ printf(" ");
+ for (int j = 0; j < columns; ++j)
+ printf(" <% 2d >", j);
+ printf("\n");
+
+ for (int i = 0; i < rows; ++i) {
+ printf("Row %2d:", i);
+
+ qreal *row = matrix + i * columns;
+ for (int j = 0; j < columns; ++j) {
+ printf(" % 2.2f", row[j]);
+ }
+ printf("\n");
+ }
+ printf("------------------------\n\n");
+}
+
+void QSimplex::combineRows(int toIndex, int fromIndex, qreal factor)
+{
+ if (!factor)
+ return;
+
+ qreal *from = matrix + fromIndex * columns;
+ qreal *to = matrix + toIndex * columns;
+
+ for (int j = 1; j < columns; ++j) {
+ qreal value = from[j];
+
+ // skip to[j] = to[j] + factor*0.0
+ if (value == 0.0)
+ continue;
+
+ to[j] += factor * value;
+
+ // ### Avoid Numerical errors
+ if (qAbs(to[j]) < 0.0000000001)
+ to[j] = 0.0;
+ }
+}
+
+int QSimplex::findPivotColumn()
+{
+ qreal min = 0;
+ int minIndex = -1;
+
+ for (int j = 0; j < columns-1; ++j) {
+ if (valueAt(0, j) < min) {
+ min = valueAt(0, j);
+ minIndex = j;
+ }
+ }
+
+ return minIndex;
+}
+
+int QSimplex::pivotRowForColumn(int column)
+{
+ qreal min = 999999999999.0; // ###
+ int minIndex = -1;
+
+ for (int i = 1; i < rows; ++i) {
+ qreal divisor = valueAt(i, column);
+ if (divisor <= 0)
+ continue;
+
+ qreal quotient = valueAt(i, columns - 1) / divisor;
+ if (quotient < min) {
+ min = quotient;
+ minIndex = i;
+ }
+ }
+
+ return minIndex;
+}
+
+void QSimplex::reducedRowEchelon()
+{
+ for (int i = 1; i < rows; ++i) {
+ int factorInObjectiveRow = valueAt(i, 0);
+ combineRows(0, i, -1 * valueAt(0, factorInObjectiveRow));
+ }
+}
+
+bool QSimplex::iterate()
+{
+ // Find Pivot column
+ int pivotColumn = findPivotColumn();
+ if (pivotColumn == -1)
+ return false;
+
+ // Find Pivot row for column
+ int pivotRow = pivotRowForColumn(pivotColumn);
+ if (pivotRow == -1) {
+ qWarning() << "QSimplex: Unbounded problem!";
+ return false;
+ }
+
+ // Normalize Pivot Row
+ qreal pivot = valueAt(pivotRow, pivotColumn);
+ if (pivot != 1.0)
+ combineRows(pivotRow, pivotRow, (1.0 - pivot) / pivot);
+
+ // Update other rows
+ for (int row=0; row < rows; ++row) {
+ if (row == pivotRow)
+ continue;
+
+ combineRows(row, pivotRow, -1 * valueAt(row, pivotColumn));
+ }
+
+ // Update first column
+ setValueAt(pivotRow, 0, pivotColumn);
+
+ // dumpMatrix();
+ // printf("------------ end of iteration --------------\n");
+ return true;
+}
+
+/*!
+ \internal
+
+ Both solveMin and solveMax are interfaces to this method.
+
+ The enum solverFactor admits 2 values: Minimum (-1) and Maximum (+1).
+ */
+qreal QSimplex::solver(solverFactor factor)
+{
+ // Remove old objective
+ clearRow(0);
+
+ // Set new objective
+ QHash<QSimplexVariable *, qreal>::const_iterator iter;
+ for (iter = objective->variables.constBegin();
+ iter != objective->variables.constEnd();
+ ++iter) {
+ setValueAt(0, iter.key()->index, -1 * factor * iter.value());
+ }
+
+ solveMaxHelper();
+ collectResults();
+
+ return factor * valueAt(0, columns - 1);
+}
+
+qreal QSimplex::solveMin()
+{
+ return solver(Minimum);
+}
+
+qreal QSimplex::solveMax()
+{
+ return solver(Maximum);
+}
+
+void QSimplex::collectResults()
+{
+ // All variables are zero unless overridden below.
+
+ // ### Is this really needed? Is there any chance that an
+ // important variable remains as non-basic at the end of simplex?
+ for (int i = 0; i < variables.size(); ++i)
+ variables[i]->result = 0;
+
+ // Basic variables
+ // Update the variable indicated in the first column with the value
+ // in the last column.
+ for (int i = 1; i < rows; ++i) {
+ int index = valueAt(i, 0) - 1;
+ if (index < variables.size())
+ variables[index]->result = valueAt(i, columns - 1);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/graphicsview/qsimplex_p.h b/src/gui/graphicsview/qsimplex_p.h
new file mode 100644
index 000000000..e3629dfa7
--- /dev/null
+++ b/src/gui/graphicsview/qsimplex_p.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 1992-$THISYEAR$ $TROLLTECH$. All rights reserved.
+**
+** This file is part of the $MODULE$ of the Qt Toolkit.
+**
+** $TROLLTECH_DUAL_LICENSE$
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#ifndef QSIMPLEX_P_H
+#define QSIMPLEX_P_H
+
+#include <QtCore/qhash.h>
+#include <QtCore/qpair.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QSimplexVariable
+{
+ QSimplexVariable() : result(0), index(0) {};
+
+ qreal result;
+ uint index;
+};
+
+
+/*!
+ \internal
+
+ Representation of a LP constraint like:
+
+ (c1 * X1) + (c2 * X2) + ... = K
+ or <= K
+ or >= K
+
+ Where (ci, Xi) are the pairs in "variables" and K the real "constant".
+*/
+struct QSimplexConstraint
+{
+ QSimplexConstraint() : constant(0), ratio(Equal), artificial(0) {};
+
+ enum Ratio {
+ LessOrEqual = 0,
+ Equal,
+ MoreOrEqual
+ };
+
+ QHash<QSimplexVariable *, qreal> variables;
+ qreal constant;
+ Ratio ratio;
+
+ QPair<QSimplexVariable *, qreal> helper;
+ QSimplexVariable * artificial;
+};
+
+
+class QSimplex
+{
+public:
+ QSimplex();
+ virtual ~QSimplex();
+
+ qreal solveMin();
+ qreal solveMax();
+ QList<QSimplexVariable *> constraintsVariables();
+
+ void setConstraints(const QList<QSimplexConstraint *> constraints);
+ void setObjective(QSimplexConstraint *objective);
+
+ void dumpMatrix();
+
+private:
+ // Matrix handling
+ qreal valueAt(int row, int column);
+ void setValueAt(int row, int column, qreal value);
+ void clearRow(int rowIndex);
+ void clearColumns(int first, int last);
+ void combineRows(int toIndex, int fromIndex, qreal factor);
+
+ // Simplex
+ int findPivotColumn();
+ int pivotRowForColumn(int column);
+ void reducedRowEchelon();
+ bool iterate();
+
+ // Helpers
+ void clearDataStructures();
+ void solveMaxHelper();
+ enum solverFactor { Minimum = -1, Maximum = 1 };
+ qreal solver(solverFactor factor);
+ void collectResults();
+
+ QList<QSimplexConstraint *> constraints;
+ QList<QSimplexVariable *> variables;
+ QSimplexConstraint *objective;
+
+ int rows;
+ int columns;
+ int firstArtificial;
+
+ qreal *matrix;
+};
+
+inline QList<QSimplexVariable *> QSimplex::constraintsVariables()
+{
+ return variables;
+}
+
+inline qreal QSimplex::valueAt(int rowIndex, int columnIndex)
+{
+ return matrix[rowIndex * columns + columnIndex];
+}
+
+inline void QSimplex::setValueAt(int rowIndex, int columnIndex, qreal value)
+{
+ matrix[rowIndex * columns + columnIndex] = value;
+}
+
+QT_END_NAMESPACE
+
+#endif // QSIMPLEX_P_H
diff --git a/src/gui/gui.pro b/src/gui/gui.pro
index ad74e47e6..eb3a33f37 100644
--- a/src/gui/gui.pro
+++ b/src/gui/gui.pro
@@ -35,6 +35,7 @@ include(graphicsview/graphicsview.pri)
include(util/util.pri)
include(statemachine/statemachine.pri)
include(math3d/math3d.pri)
+include(effects/effects.pri)
contains(QT_CONFIG, egl): include(egl/egl.pri)
diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp
index 73288530f..00e78ad05 100644
--- a/src/gui/image/qpixmap.cpp
+++ b/src/gui/image/qpixmap.cpp
@@ -643,13 +643,13 @@ void QPixmap::resize_helper(const QSize &s)
QPixmap pm(QSize(w, h), data->type);
bool uninit = false;
#if defined(Q_WS_X11)
- QX11PixmapData *x11Data = data->classId() == QPixmapData::X11Class ? static_cast<QX11PixmapData*>(data) : 0;
+ QX11PixmapData *x11Data = data->classId() == QPixmapData::X11Class ? static_cast<QX11PixmapData*>(data.data()) : 0;
if (x11Data) {
pm.x11SetScreen(x11Data->xinfo.screen());
uninit = x11Data->flags & QX11PixmapData::Uninitialized;
}
#elif defined(Q_WS_MAC)
- QMacPixmapData *macData = data->classId() == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(data) : 0;
+ QMacPixmapData *macData = data->classId() == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(data.data()) : 0;
if (macData)
uninit = macData->uninit;
#endif
@@ -663,7 +663,7 @@ void QPixmap::resize_helper(const QSize &s)
#if defined(Q_WS_X11)
if (x11Data && x11Data->x11_mask) {
- QX11PixmapData *pmData = static_cast<QX11PixmapData*>(pm.data);
+ QX11PixmapData *pmData = static_cast<QX11PixmapData*>(pm.data.data());
pmData->x11_mask = (Qt::HANDLE)XCreatePixmap(X11->display,
RootWindow(x11Data->xinfo.display(),
x11Data->xinfo.screen()),
@@ -1158,7 +1158,7 @@ Qt::HANDLE QPixmap::handle() const
{
#if defined(Q_WS_X11)
if (data->classId() == QPixmapData::X11Class)
- return static_cast<QX11PixmapData*>(data)->handle();
+ return static_cast<const QX11PixmapData*>(data.constData())->handle();
#endif
return 0;
}
@@ -1917,7 +1917,7 @@ void QPixmap::detach()
QImagePixmapCleanupHooks::executePixmapHooks(this);
#if defined(Q_WS_MAC)
- QMacPixmapData *macData = id == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(data) : 0;
+ QMacPixmapData *macData = id == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(data.data()) : 0;
if (macData) {
if (macData->cg_mask) {
CGImageRelease(macData->cg_mask);
@@ -1933,7 +1933,7 @@ void QPixmap::detach()
#if defined(Q_WS_X11)
if (data->classId() == QPixmapData::X11Class) {
- QX11PixmapData *d = static_cast<QX11PixmapData*>(data);
+ QX11PixmapData *d = static_cast<QX11PixmapData*>(data.data());
d->flags &= ~QX11PixmapData::Uninitialized;
// reset the cache data
diff --git a/src/gui/image/qpixmap_mac.cpp b/src/gui/image/qpixmap_mac.cpp
index 5959da121..4aa74f7ff 100644
--- a/src/gui/image/qpixmap_mac.cpp
+++ b/src/gui/image/qpixmap_mac.cpp
@@ -73,12 +73,12 @@ static int qt_pixmap_serial = 0;
Q_GUI_EXPORT quint32 *qt_mac_pixmap_get_base(const QPixmap *pix)
{
- return static_cast<QMacPixmapData*>(pix->data)->pixels;
+ return static_cast<QMacPixmapData*>(pix->data.data())->pixels;
}
Q_GUI_EXPORT int qt_mac_pixmap_get_bytes_per_line(const QPixmap *pix)
{
- return static_cast<QMacPixmapData*>(pix->data)->bytesPerRow;
+ return static_cast<QMacPixmapData*>(pix->data.data())->bytesPerRow;
}
void qt_mac_cgimage_data_free(void *info, const void *memoryToFree, size_t)
@@ -421,7 +421,7 @@ QPixmap QMacPixmapData::alphaChannel() const
void QMacPixmapData::setAlphaChannel(const QPixmap &alpha)
{
has_mask = true;
- QMacPixmapData *alphaData = static_cast<QMacPixmapData*>(alpha.data);
+ QMacPixmapData *alphaData = static_cast<QMacPixmapData*>(alpha.data.data());
macSetAlphaChannel(alphaData, false);
}
@@ -449,7 +449,7 @@ void QMacPixmapData::setMask(const QBitmap &mask)
has_alpha = false;
has_mask = true;
- QMacPixmapData *maskData = static_cast<QMacPixmapData*>(mask.data);
+ QMacPixmapData *maskData = static_cast<QMacPixmapData*>(mask.data.data());
macSetAlphaChannel(maskData, true);
}
@@ -961,14 +961,14 @@ Qt::HANDLE QPixmap::macQDAlphaHandle() const
Qt::HANDLE QPixmap::macCGHandle() const
{
if (data->classId() == QPixmapData::MacClass) {
- QMacPixmapData *d = static_cast<QMacPixmapData *>(data);
+ QMacPixmapData *d = static_cast<QMacPixmapData *>(data.data());
if (!d->cg_data)
d->macCreateCGImageRef();
CGImageRef ret = d->cg_data;
CGImageRetain(ret);
return ret;
} else if (data->classId() == QPixmapData::RasterClass) {
- return qt_mac_image_to_cgimage(static_cast<QRasterPixmapData *>(data)->image);
+ return qt_mac_image_to_cgimage(static_cast<QRasterPixmapData *>(data.data())->image);
}
return 0;
}
@@ -980,7 +980,7 @@ bool QMacPixmapData::hasAlphaChannel() const
CGImageRef qt_mac_create_imagemask(const QPixmap &pixmap, const QRectF &sr)
{
- QMacPixmapData *px = static_cast<QMacPixmapData*>(pixmap.data);
+ QMacPixmapData *px = static_cast<QMacPixmapData*>(pixmap.data.data());
if (px->cg_mask) {
if (px->cg_mask_rect == sr) {
CGImageRetain(px->cg_mask); //reference for the caller
diff --git a/src/gui/image/qpixmap_win.cpp b/src/gui/image/qpixmap_win.cpp
index b0f17ba7f..f12088d47 100644
--- a/src/gui/image/qpixmap_win.cpp
+++ b/src/gui/image/qpixmap_win.cpp
@@ -123,7 +123,7 @@ HBITMAP QPixmap::toWinHBITMAP(HBitmapFormat format) const
{
HBITMAP bitmap = 0;
if (data->classId() == QPixmapData::RasterClass) {
- QRasterPixmapData* d = static_cast<QRasterPixmapData*>(data);
+ QRasterPixmapData* d = static_cast<QRasterPixmapData*>(data.data());
int w = d->image.width();
int h = d->image.height();
diff --git a/src/gui/image/qpixmap_x11.cpp b/src/gui/image/qpixmap_x11.cpp
index 047bd332e..a75cadc6a 100644
--- a/src/gui/image/qpixmap_x11.cpp
+++ b/src/gui/image/qpixmap_x11.cpp
@@ -1875,7 +1875,7 @@ QPixmap QX11PixmapData::transformed(const QTransform &transform,
return bm;
} else { // color pixmap
QPixmap pm;
- QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(pm.data);
+ QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(pm.data.data());
x11Data->flags &= ~QX11PixmapData::Uninitialized;
x11Data->xinfo = xinfo;
x11Data->d = d;
@@ -1940,7 +1940,7 @@ void QPixmap::x11SetScreen(int screen)
if (screen < 0)
screen = QX11Info::appScreen();
- QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(data);
+ QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(data.data());
if (screen == x11Data->xinfo.screen())
return; // nothing to do
@@ -2037,7 +2037,7 @@ bool QX11PixmapData::hasAlphaChannel() const
const QX11Info &QPixmap::x11Info() const
{
if (data->classId() == QPixmapData::X11Class)
- return static_cast<QX11PixmapData*>(data)->xinfo;
+ return static_cast<QX11PixmapData*>(data.data())->xinfo;
else {
static QX11Info nullX11Info;
return nullX11Info;
@@ -2094,7 +2094,7 @@ Qt::HANDLE QPixmap::x11PictureHandle() const
{
#ifndef QT_NO_XRENDER
if (data->classId() == QPixmapData::X11Class)
- return static_cast<QX11PixmapData*>(data)->picture;
+ return static_cast<const QX11PixmapData*>(data.data())->picture;
else
return 0;
#else
diff --git a/src/gui/image/qpixmapfilter.cpp b/src/gui/image/qpixmapfilter.cpp
index 968ecd777..bc14b5e26 100644
--- a/src/gui/image/qpixmapfilter.cpp
+++ b/src/gui/image/qpixmapfilter.cpp
@@ -93,6 +93,9 @@ public:
\value DropShadowFilter A filter that is used to add a drop shadow to an
image. See QPixmapDropShadowFilter for more
information.
+ \value BlurFilter A filter that is used to blur an image using
+ a simple blur radius. See QPixmapBlurFilter
+ for more information.
\value UserFilter The first filter type that can be used for
application-specific purposes.
@@ -475,6 +478,227 @@ void QPixmapConvolutionFilter::draw(QPainter *painter, const QPointF &p, const Q
}
}
+/*!
+ \class QPixmapBlurFilter
+ \since 4.6
+ \ingroup multimedia
+
+ \brief The QPixmapBlurFilter class provides blur filtering
+ for pixmaps.
+
+ QPixmapBlurFilter implements a blur pixmap filter,
+ which is applied when \l{QPixmapFilter::}{draw()} is called.
+
+ The filter lets you specialize the radius of the blur as well
+ as the quality.
+
+ By default, the blur effect is produced by applying an exponential
+ filter generated from the specified blurRadius(). Paint engines
+ may override this with a custom blur that is faster on the
+ underlying hardware.
+
+ \sa {Pixmap Filters Example}, QPixmapConvolutionFilter, QPixmapDropShadowFilter
+
+ \internal
+*/
+
+class QPixmapBlurFilterPrivate : public QPixmapFilterPrivate
+{
+public:
+ QPixmapBlurFilterPrivate() : radius(5), quality(Qt::FastTransformation) {}
+
+ int radius;
+ Qt::TransformationMode quality;
+};
+
+
+/*!
+ Constructs a pixmap blur filter.
+
+ \internal
+*/
+QPixmapBlurFilter::QPixmapBlurFilter(QObject *parent)
+ : QPixmapFilter(*new QPixmapBlurFilterPrivate, BlurFilter, parent)
+{
+}
+
+/*!
+ Destructor of pixmap blur filter.
+
+ \internal
+*/
+QPixmapBlurFilter::~QPixmapBlurFilter()
+{
+}
+
+/*!
+ Sets the radius of the blur filter. Higher radius produces increased blurriness.
+
+ \internal
+*/
+void QPixmapBlurFilter::setRadius(int radius)
+{
+ Q_D(QPixmapBlurFilter);
+ d->radius = radius;
+}
+
+/*!
+ Gets the radius of the blur filter.
+
+ \internal
+*/
+int QPixmapBlurFilter::radius() const
+{
+ Q_D(const QPixmapBlurFilter);
+ return d->radius;
+}
+
+/*!
+ Setting the quality to FastTransformation causes the implementation
+ to trade off visual quality to blur the image faster. Setting the
+ quality to SmoothTransformation causes the implementation to improve
+ visual quality at the expense of speed. The implementation is free
+ to ignore this value if it only has a single blur algorithm.
+
+ \internal
+*/
+void QPixmapBlurFilter::setQuality(Qt::TransformationMode quality)
+{
+ Q_D(QPixmapBlurFilter);
+ d->quality = quality;
+}
+
+/*!
+ Gets the quality of the blur filter.
+
+ \internal
+*/
+Qt::TransformationMode QPixmapBlurFilter::quality() const
+{
+ Q_D(const QPixmapBlurFilter);
+ return d->quality;
+}
+
+/*!
+ \reimp
+
+ \internal
+*/
+QRectF QPixmapBlurFilter::boundingRectFor(const QRectF &rect) const
+{
+ Q_D(const QPixmapBlurFilter);
+ const qreal delta = d->radius * 2;
+ return rect.adjusted(-delta, -delta, delta, delta);
+}
+
+// Blur the image according to the blur radius
+// Based on exponential blur algorithm by Jani Huhtanen
+// (maximum radius is set to 16)
+static QImage blurred(const QImage& image, const QRect& rect, int radius)
+{
+ int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
+ int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius-1];
+
+ QImage result = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ int r1 = rect.top();
+ int r2 = rect.bottom();
+ int c1 = rect.left();
+ int c2 = rect.right();
+
+ int bpl = result.bytesPerLine();
+ int rgba[4];
+ unsigned char* p;
+
+ for (int col = c1; col <= c2; col++) {
+ p = result.scanLine(r1) + col * 4;
+ for (int i = 0; i < 4; i++)
+ rgba[i] = p[i] << 4;
+
+ p += bpl;
+ for (int j = r1; j < r2; j++, p += bpl)
+ for (int i = 0; i < 4; i++)
+ p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
+ }
+
+ for (int row = r1; row <= r2; row++) {
+ p = result.scanLine(row) + c1 * 4;
+ for (int i = 0; i < 4; i++)
+ rgba[i] = p[i] << 4;
+
+ p += 4;
+ for (int j = c1; j < c2; j++, p += 4)
+ for (int i = 0; i < 4; i++)
+ p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
+ }
+
+ for (int col = c1; col <= c2; col++) {
+ p = result.scanLine(r2) + col * 4;
+ for (int i = 0; i < 4; i++)
+ rgba[i] = p[i] << 4;
+
+ p -= bpl;
+ for (int j = r1; j < r2; j++, p -= bpl)
+ for (int i = 0; i < 4; i++)
+ p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
+ }
+
+ for (int row = r1; row <= r2; row++) {
+ p = result.scanLine(row) + c2 * 4;
+ for (int i = 0; i < 4; i++)
+ rgba[i] = p[i] << 4;
+
+ p -= 4;
+ for (int j = c1; j < c2; j++, p -= 4)
+ for (int i = 0; i < 4; i++)
+ p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
+ }
+
+ return result;
+}
+
+/*!
+ \reimp
+
+ \internal
+*/
+void QPixmapBlurFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF &srcRect) const
+{
+ Q_D(const QPixmapBlurFilter);
+ if (!painter->isActive())
+ return;
+
+ if (d->radius == 0) {
+ painter->drawPixmap(srcRect.translated(p), src, srcRect);
+ return;
+ }
+
+ QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ?
+ static_cast<QPaintEngineEx *>(painter->paintEngine())->createPixmapFilter(type()) : 0;
+ QPixmapBlurFilter *blurFilter = static_cast<QPixmapBlurFilter*>(filter);
+ if (blurFilter) {
+ blurFilter->setRadius(d->radius);
+ blurFilter->setQuality(d->quality);
+ blurFilter->draw(painter, p, src, srcRect);
+ delete blurFilter;
+ return;
+ }
+
+ QImage srcImage;
+ QImage destImage;
+
+ if (srcRect.isNull()) {
+ srcImage = src.toImage();
+ destImage = blurred(srcImage, srcImage.rect(), d->radius);
+ } else {
+ QRect rect = srcRect.toAlignedRect().intersected(src.rect());
+
+ srcImage = src.copy(rect).toImage();
+ destImage = blurred(srcImage, srcImage.rect(), d->radius);
+ }
+
+ painter->drawImage(p, destImage);
+}
+
// grayscales the image to dest (could be same). If rect isn't defined
// destination image size is used to determine the dimension of grayscaling
// process.
@@ -626,16 +850,11 @@ class QPixmapDropShadowFilterPrivate : public QPixmapFilterPrivate
{
public:
QPixmapDropShadowFilterPrivate()
- : offset(8, 8),
- radius(1),
- color(63, 63, 63, 255) {
- }
+ : offset(8, 8), color(63, 63, 63, 180), blurFilter(new QPixmapBlurFilter) {}
QPointF offset;
- qreal radius;
QColor color;
-
- QPixmapConvolutionFilter *convolution;
+ QPixmapBlurFilter *blurFilter;
};
/*!
@@ -680,9 +899,7 @@ QPixmapDropShadowFilter::QPixmapDropShadowFilter(QObject *parent)
: QPixmapFilter(*new QPixmapDropShadowFilterPrivate, DropShadowFilter, parent)
{
Q_D(QPixmapDropShadowFilter);
- d->convolution = new QPixmapConvolutionFilter;
-
- setBlurRadius(1);
+ d->blurFilter->setRadius(1);
}
/*!
@@ -693,7 +910,7 @@ QPixmapDropShadowFilter::QPixmapDropShadowFilter(QObject *parent)
QPixmapDropShadowFilter::~QPixmapDropShadowFilter()
{
Q_D(QPixmapDropShadowFilter);
- delete d->convolution;
+ delete d->blurFilter;
}
/*!
@@ -705,10 +922,10 @@ QPixmapDropShadowFilter::~QPixmapDropShadowFilter()
\internal
*/
-qreal QPixmapDropShadowFilter::blurRadius() const
+int QPixmapDropShadowFilter::blurRadius() const
{
Q_D(const QPixmapDropShadowFilter);
- return d->radius;
+ return d->blurFilter->radius();
}
/*!
@@ -720,18 +937,10 @@ qreal QPixmapDropShadowFilter::blurRadius() const
\internal
*/
-void QPixmapDropShadowFilter::setBlurRadius(qreal radius)
+void QPixmapDropShadowFilter::setBlurRadius(int radius)
{
Q_D(QPixmapDropShadowFilter);
-
- d->radius = radius;
-
- int dim = 2 * qRound(radius) + 1;
- QVarLengthArray<qreal> arr(dim * dim);
- qreal f = 1 / qreal(dim * dim);
- for (int i = 0; i < dim * dim; ++i)
- arr[i] = f;
- d->convolution->setConvolutionKernel(arr.data(), dim, dim);
+ d->blurFilter->setRadius(radius);
}
/*!
@@ -805,10 +1014,11 @@ QRectF QPixmapDropShadowFilter::boundingRectFor(const QRectF &rect) const
{
Q_D(const QPixmapDropShadowFilter);
- qreal x1 = qMin(rect.left(), rect.left() + d->offset.x() - d->radius);
- qreal y1 = qMin(rect.top(), rect.top() + d->offset.y() - d->radius);
- qreal x2 = qMax(rect.right(), rect.right() + d->offset.x() + d->radius);
- qreal y2 = qMax(rect.bottom(), rect.bottom() + d->offset.y() + d->radius);
+ const qreal delta = qreal(d->blurFilter->radius() * 2);
+ qreal x1 = qMin(rect.left(), rect.left() + d->offset.x() - delta);
+ qreal y1 = qMin(rect.top(), rect.top() + d->offset.y() - delta);
+ qreal x2 = qMax(rect.right(), rect.right() + d->offset.x() + delta);
+ qreal y2 = qMax(rect.bottom(), rect.bottom() + d->offset.y() + delta);
return QRectF(x1, y1, x2 - x1, y2 - y1);
}
@@ -823,7 +1033,7 @@ void QPixmapDropShadowFilter::draw(QPainter *p,
{
Q_D(const QPixmapDropShadowFilter);
- QPixmap tmp = src.isNull() ? px : px.copy(src.toRect());
+ QImage tmp = src.isNull() ? px.toImage() : px.copy(src.toRect()).toImage();
QPainter tmpPainter(&tmp);
// blacken the image...
@@ -831,10 +1041,13 @@ void QPixmapDropShadowFilter::draw(QPainter *p,
tmpPainter.fillRect(0, 0, tmp.width(), tmp.height(), d->color);
tmpPainter.end();
+ const QPixmap pixTmp = QPixmap::fromImage(tmp);
+
// draw the blurred drop shadow...
- d->convolution->draw(p, pos + d->offset, tmp);
+ d->blurFilter->draw(p, pos + d->offset, pixTmp);
// Draw the actual pixmap...
p->drawPixmap(pos, px, src);
}
+
QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmapfilter_p.h b/src/gui/image/qpixmapfilter_p.h
index b8588bd52..29d919ddd 100644
--- a/src/gui/image/qpixmapfilter_p.h
+++ b/src/gui/image/qpixmapfilter_p.h
@@ -78,6 +78,7 @@ public:
ConvolutionFilter,
ColorizeFilter,
DropShadowFilter,
+ BlurFilter,
UserFilter = 1024
};
@@ -117,6 +118,30 @@ private:
int columns() const;
};
+class QPixmapBlurFilterPrivate;
+
+class Q_GUI_EXPORT QPixmapBlurFilter : public QPixmapFilter
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPixmapBlurFilter)
+
+public:
+ QPixmapBlurFilter(QObject *parent = 0);
+ ~QPixmapBlurFilter();
+
+ void setRadius(int radius);
+ void setQuality(Qt::TransformationMode mode);
+
+ int radius() const;
+ Qt::TransformationMode quality() const;
+
+ QRectF boundingRectFor(const QRectF &rect) const;
+ void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect = QRectF()) const;
+
+private:
+ friend class QGLPixmapBlurFilter;
+};
+
class QPixmapColorizeFilterPrivate;
class Q_GUI_EXPORT QPixmapColorizeFilter : public QPixmapFilter
@@ -147,8 +172,8 @@ public:
QRectF boundingRectFor(const QRectF &rect) const;
void draw(QPainter *p, const QPointF &pos, const QPixmap &px, const QRectF &src = QRectF()) const;
- qreal blurRadius() const;
- void setBlurRadius(qreal radius);
+ int blurRadius() const;
+ void setBlurRadius(int radius);
QColor color() const;
void setColor(const QColor &color);
diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h
index 7a2775670..e839617a4 100644
--- a/src/gui/kernel/qapplication_p.h
+++ b/src/gui/kernel/qapplication_p.h
@@ -249,6 +249,17 @@ typedef struct tagGESTURECONFIG
#endif // WM_GESTURE
+#if defined(Q_WS_WINCE_WM) && defined(QT_WINCE_GESTURES)
+#undef GID_ZOOM
+#define GID_ZOOM 0xf000
+#undef GID_ROTATE
+#define GID_ROTATE 0xf001
+#undef GID_TWOFINGERTAP
+#define GID_TWOFINGERTAP 0xf002
+#undef GID_ROLLOVER
+#define GID_ROLLOVER 0xf003
+#endif
+
#endif // Q_WS_WIN
class QPanGesture;
@@ -536,6 +547,7 @@ public:
PtrBeginPanningFeedback BeginPanningFeedback;
PtrUpdatePanningFeedback UpdatePanningFeedback;
PtrEndPanningFeedback EndPanningFeedback;
+ QWidget *gestureWidget;
#endif
#ifdef QT_RX71_MULTITOUCH
diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp
index 7d9e6ea22..12cd87981 100644
--- a/src/gui/kernel/qapplication_win.cpp
+++ b/src/gui/kernel/qapplication_win.cpp
@@ -452,7 +452,7 @@ public:
bool translateConfigEvent(const MSG &msg);
bool translateCloseEvent(const MSG &msg);
bool translateTabletEvent(const MSG &msg, PACKET *localPacketBuf, int numPackets);
- bool translateGestureEvent(const MSG &msg);
+ bool translateGestureEvent(const MSG &msg, const GESTUREINFO &gi);
void repolishStyle(QStyle &style);
inline void showChildren(bool spontaneous) { d_func()->showChildren(spontaneous); }
inline void hideChildren(bool spontaneous) { d_func()->hideChildren(spontaneous); }
@@ -848,6 +848,7 @@ void qt_init(QApplicationPrivate *priv, int)
(PtrEndPanningFeedback)QLibrary::resolve(QLatin1String("uxtheme"),
"EndPanningFeedback");
#endif
+ priv->gestureWidget = 0;
}
/*****************************************************************************
@@ -2513,10 +2514,38 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam
}
result = false;
break;
- case WM_GESTURE:
- widget->translateGestureEvent(msg);
+ case WM_GESTURE: {
+ GESTUREINFO gi;
+ memset(&gi, 0, sizeof(GESTUREINFO));
+ gi.cbSize = sizeof(GESTUREINFO);
+
+ QApplicationPrivate *qAppPriv = QApplicationPrivate::instance();
+ BOOL bResult = false;
+ if (qAppPriv->GetGestureInfo)
+ bResult = qAppPriv->GetGestureInfo((HANDLE)msg.lParam, &gi);
+ if (bResult) {
+ if (gi.dwID == GID_BEGIN) {
+ // find the alien widget for the gesture position.
+ // This might not be accurate as the position is the center
+ // point of two fingers for multi-finger gestures.
+ QPoint pt(gi.ptsLocation.x, gi.ptsLocation.y);
+ QWidget *w = widget->childAt(widget->mapFromGlobal(pt));
+ qAppPriv->gestureWidget = w ? w : widget;
+ }
+ if (qAppPriv->gestureWidget)
+ static_cast<QETWidget*>(qAppPriv->gestureWidget)->translateGestureEvent(msg, gi);
+ if (qAppPriv->CloseGestureInfoHandle)
+ qAppPriv->CloseGestureInfoHandle((HANDLE)msg.lParam);
+ if (gi.dwID == GID_END)
+ qAppPriv->gestureWidget = 0;
+ } else {
+ DWORD dwErr = GetLastError();
+ if (dwErr > 0)
+ qWarning() << "translateGestureEvent: error = " << dwErr;
+ }
result = true;
break;
+ }
default:
result = false; // event was not processed
break;
@@ -3725,69 +3754,42 @@ bool QETWidget::translateCloseEvent(const MSG &)
return d_func()->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent);
}
-bool QETWidget::translateGestureEvent(const MSG &msg)
+bool QETWidget::translateGestureEvent(const MSG &, const GESTUREINFO &gi)
{
- GESTUREINFO gi;
- memset(&gi, 0, sizeof(GESTUREINFO));
- gi.cbSize = sizeof(GESTUREINFO);
+ const QPoint widgetPos = QPoint(gi.ptsLocation.x, gi.ptsLocation.y);
+ QWidget *alienWidget = !internalWinId() ? this : childAt(widgetPos);
+ if (alienWidget && alienWidget->internalWinId())
+ alienWidget = 0;
+ QWidget *widget = alienWidget ? alienWidget : this;
- QApplicationPrivate *qAppPriv = QApplicationPrivate::instance();
-#if defined(Q_WS_WINCE_WM) && defined(QT_WINCE_GESTURES)
-#undef GID_ZOOM
-#define GID_ZOOM 0xf000
-#undef GID_ROTATE
-#define GID_ROTATE 0xf001
-#undef GID_TWOFINGERTAP
-#define GID_TWOFINGERTAP 0xf002
-#undef GID_ROLLOVER
-#define GID_ROLLOVER 0xf003
-#endif
- BOOL bResult = false;
- if (qAppPriv->GetGestureInfo)
- bResult = qAppPriv->GetGestureInfo((HANDLE)msg.lParam, &gi);
-
- if (bResult) {
- const QPoint widgetPos = QPoint(gi.ptsLocation.x, gi.ptsLocation.y);
- QWidget *alienWidget = !internalWinId() ? this : childAt(widgetPos);
- if (alienWidget && alienWidget->internalWinId())
- alienWidget = 0;
- QWidget *widget = alienWidget ? alienWidget : this;
-
- QNativeGestureEvent event;
- event.sequenceId = gi.dwSequenceID;
- event.position = QPoint(gi.ptsLocation.x, gi.ptsLocation.y);
- event.argument = gi.ullArguments;
-
- switch (gi.dwID) {
- case GID_BEGIN:
- event.gestureType = QNativeGestureEvent::GestureBegin;
- break;
- case GID_END:
- event.gestureType = QNativeGestureEvent::GestureEnd;
- break;
- case GID_ZOOM:
- event.gestureType = QNativeGestureEvent::Zoom;
- break;
- case GID_PAN:
- event.gestureType = QNativeGestureEvent::Pan;
- break;
- case GID_ROTATE:
- event.gestureType = QNativeGestureEvent::Rotate;
- break;
- case GID_TWOFINGERTAP:
- case GID_ROLLOVER:
- default:
- break;
- }
- if (qAppPriv->CloseGestureInfoHandle)
- qAppPriv->CloseGestureInfoHandle((HANDLE)msg.lParam);
- if (event.gestureType != QNativeGestureEvent::None)
- qt_sendSpontaneousEvent(widget, &event);
- } else {
- DWORD dwErr = GetLastError();
- if (dwErr > 0)
- qWarning() << "translateGestureEvent: error = " << dwErr;
+ QNativeGestureEvent event;
+ event.sequenceId = gi.dwSequenceID;
+ event.position = QPoint(gi.ptsLocation.x, gi.ptsLocation.y);
+ event.argument = gi.ullArguments;
+
+ switch (gi.dwID) {
+ case GID_BEGIN:
+ event.gestureType = QNativeGestureEvent::GestureBegin;
+ break;
+ case GID_END:
+ event.gestureType = QNativeGestureEvent::GestureEnd;
+ break;
+ case GID_ZOOM:
+ event.gestureType = QNativeGestureEvent::Zoom;
+ break;
+ case GID_PAN:
+ event.gestureType = QNativeGestureEvent::Pan;
+ break;
+ case GID_ROTATE:
+ event.gestureType = QNativeGestureEvent::Rotate;
+ break;
+ case GID_TWOFINGERTAP:
+ case GID_ROLLOVER:
+ default:
+ break;
}
+ if (event.gestureType != QNativeGestureEvent::None)
+ qt_sendSpontaneousEvent(widget, &event);
return true;
}
diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp
index 79dcae1c6..eeb65280a 100644
--- a/src/gui/kernel/qgesture.cpp
+++ b/src/gui/kernel/qgesture.cpp
@@ -149,20 +149,18 @@ private:
\sa setGraphicsItem()
*/
-QGesture::QGesture(QObject *parent)
+QGesture::QGesture(QObject *gestureTarget, QObject *parent)
: QObject(*new QGesturePrivate, parent)
{
- if (parent)
- parent->installEventFilter(this);
+ setGestureTarget(gestureTarget);
}
/*! \internal
*/
-QGesture::QGesture(QGesturePrivate &dd, QObject *parent)
+QGesture::QGesture(QGesturePrivate &dd, QObject *gestureTarget, QObject *parent)
: QObject(dd, parent)
{
- if (parent)
- parent->installEventFilter(this);
+ setGestureTarget(gestureTarget);
}
/*!
@@ -172,6 +170,33 @@ QGesture::~QGesture()
{
}
+/*!
+ \property QGesture::gestureTarget
+
+ Gesture target is the object that the gesture will observe for events.
+ Typically this means that the gesture installs an event filter on the
+ target object.
+*/
+void QGesture::setGestureTarget(QObject *object)
+{
+ d_func()->setupGestureTarget(object);
+}
+
+QObject* QGesture::gestureTarget() const
+{
+ return d_func()->gestureTarget;
+}
+
+void QGesturePrivate::setupGestureTarget(QObject *object)
+{
+ Q_Q(QGesture);
+ if (gestureTarget)
+ gestureTarget->removeEventFilter(q);
+ if (object)
+ object->installEventFilter(q);
+ gestureTarget = object;
+}
+
/*! \internal
*/
bool QGesture::eventFilter(QObject *receiver, QEvent *event)
diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h
index ee28ea4ac..1d2fa6db1 100644
--- a/src/gui/kernel/qgesture.h
+++ b/src/gui/kernel/qgesture.h
@@ -63,20 +63,24 @@ class Q_GUI_EXPORT QGesture : public QObject
Q_DECLARE_PRIVATE(QGesture)
Q_PROPERTY(Qt::GestureState state READ state)
+ Q_PROPERTY(QObject* gestureTarget READ gestureTarget WRITE setGestureTarget)
public:
- explicit QGesture(QObject *parent = 0);
+ explicit QGesture(QObject *gestureTarget = 0, QObject *parent = 0);
~QGesture();
virtual bool filterEvent(QEvent *event) = 0;
+ void setGestureTarget(QObject *object);
+ QObject* gestureTarget() const;
+
void setGraphicsItem(QGraphicsItem *);
QGraphicsItem *graphicsItem() const;
Qt::GestureState state() const;
protected:
- QGesture(QGesturePrivate &dd, QObject *parent);
+ QGesture(QGesturePrivate &dd, QObject *gestureTarget, QObject *parent);
bool eventFilter(QObject*, QEvent*);
virtual void reset();
diff --git a/src/gui/kernel/qgesture_p.h b/src/gui/kernel/qgesture_p.h
index 37f314639..f5847132d 100644
--- a/src/gui/kernel/qgesture_p.h
+++ b/src/gui/kernel/qgesture_p.h
@@ -69,11 +69,14 @@ class QGesturePrivate : public QObjectPrivate
public:
QGesturePrivate()
- : graphicsItem(0), eventFilterProxyGraphicsItem(0), state(Qt::NoGesture)
+ : gestureTarget(0), graphicsItem(0), eventFilterProxyGraphicsItem(0),
+ state(Qt::NoGesture)
{
}
+ virtual void setupGestureTarget(QObject *o);
+ QPointer<QObject> gestureTarget;
QGraphicsItem *graphicsItem;
QGraphicsItem *eventFilterProxyGraphicsItem;
diff --git a/src/gui/kernel/qstandardgestures.cpp b/src/gui/kernel/qstandardgestures.cpp
index 10689ba45..d798d32af 100644
--- a/src/gui/kernel/qstandardgestures.cpp
+++ b/src/gui/kernel/qstandardgestures.cpp
@@ -69,42 +69,35 @@ QWidgetPrivate *qt_widget_private(QWidget *widget);
On some platform like Windows it's necessary to provide a non-null widget
as \a parent to get native gesture support.
*/
-QPanGesture::QPanGesture(QWidget *parent)
- : QGesture(*new QPanGesturePrivate, parent)
+QPanGesture::QPanGesture(QWidget *gestureTarget, QObject *parent)
+ : QGesture(*new QPanGesturePrivate, gestureTarget, parent)
{
- if (parent) {
- QApplicationPrivate *qAppPriv = QApplicationPrivate::instance();
- qAppPriv->widgetGestures[parent].pan = this;
-#ifdef Q_WS_WIN
- qt_widget_private(parent)->winSetupGestures();
-#endif
- }
}
-/*! \internal */
-bool QPanGesture::event(QEvent *event)
+void QPanGesturePrivate::setupGestureTarget(QObject *newGestureTarget)
{
- switch (event->type()) {
- case QEvent::ParentAboutToChange:
- if (QWidget *w = qobject_cast<QWidget*>(parent())) {
- QApplicationPrivate::instance()->widgetGestures[w].pan = 0;
+ Q_Q(QPanGesture);
+ if (gestureTarget && gestureTarget->isWidgetType()) {
+ QWidget *w = static_cast<QWidget*>(gestureTarget.data());
+ QApplicationPrivate::instance()->widgetGestures[w].pan = 0;
#ifdef Q_WS_WIN
- qt_widget_private(w)->winSetupGestures();
+ qt_widget_private(w)->winSetupGestures();
#endif
- }
- break;
- case QEvent::ParentChange:
- if (QWidget *w = qobject_cast<QWidget*>(parent())) {
- QApplicationPrivate::instance()->widgetGestures[w].pan = this;
+ }
+
+ if (newGestureTarget && newGestureTarget->isWidgetType()) {
+ QWidget *w = static_cast<QWidget*>(newGestureTarget);
+ QApplicationPrivate::instance()->widgetGestures[w].pan = q;
#ifdef Q_WS_WIN
- qt_widget_private(w)->winSetupGestures();
+ qt_widget_private(w)->winSetupGestures();
#endif
- }
- break;
- default:
- break;
}
+ QGesturePrivate::setupGestureTarget(newGestureTarget);
+}
+/*! \internal */
+bool QPanGesture::event(QEvent *event)
+{
#if defined(Q_OS_MAC) && !defined(QT_MAC_USE_COCOA)
Q_D(QPanGesture);
if (event->type() == QEvent::Timer) {
@@ -179,27 +172,29 @@ bool QPanGesture::filterEvent(QEvent *event)
d->lastOffset = d->totalOffset = QSize();
} else if (event->type() == QEvent::TouchEnd) {
if (state() != Qt::NoGesture) {
- if (!ev->touchPoints().isEmpty()) {
- QTouchEvent::TouchPoint p = ev->touchPoints().at(0);
- const QPoint pos = p.pos().toPoint();
- const QPoint lastPos = p.lastPos().toPoint();
- const QPoint startPos = p.startPos().toPoint();
- d->lastOffset = QSize(pos.x() - lastPos.x(), pos.y() - lastPos.y());
- d->totalOffset = QSize(pos.x() - startPos.x(), pos.y() - startPos.y());
+ if (ev->touchPoints().size() == 2) {
+ QTouchEvent::TouchPoint p1 = ev->touchPoints().at(0);
+ QTouchEvent::TouchPoint p2 = ev->touchPoints().at(1);
+ d->lastOffset =
+ QSize(p1.pos().x() - p1.lastPos().x() + p2.pos().x() - p2.lastPos().x(),
+ p1.pos().y() - p1.lastPos().y() + p2.pos().y() - p2.lastPos().y()) / 2;
+ d->totalOffset += d->lastOffset;
}
updateState(Qt::GestureFinished);
}
reset();
} else if (event->type() == QEvent::TouchUpdate) {
- QTouchEvent::TouchPoint p = ev->touchPoints().at(0);
- const QPoint pos = p.pos().toPoint();
- const QPoint lastPos = p.lastPos().toPoint();
- const QPoint startPos = p.startPos().toPoint();
- d->lastOffset = QSize(pos.x() - lastPos.x(), pos.y() - lastPos.y());
- d->totalOffset = QSize(pos.x() - startPos.x(), pos.y() - startPos.y());
- if (d->totalOffset.width() > 10 || d->totalOffset.height() > 10 ||
- d->totalOffset.width() < -10 || d->totalOffset.height() < -10) {
- updateState(Qt::GestureUpdated);
+ if (ev->touchPoints().size() == 2) {
+ QTouchEvent::TouchPoint p1 = ev->touchPoints().at(0);
+ QTouchEvent::TouchPoint p2 = ev->touchPoints().at(1);
+ d->lastOffset =
+ QSize(p1.pos().x() - p1.lastPos().x() + p2.pos().x() - p2.lastPos().x(),
+ p1.pos().y() - p1.lastPos().y() + p2.pos().y() - p2.lastPos().y()) / 2;
+ d->totalOffset += d->lastOffset;
+ if (d->totalOffset.width() > 10 || d->totalOffset.height() > 10 ||
+ d->totalOffset.width() < -10 || d->totalOffset.height() < -10) {
+ updateState(Qt::GestureUpdated);
+ }
}
}
#ifdef Q_OS_MAC
@@ -287,41 +282,35 @@ QSize QPanGesture::lastOffset() const
On some platform like Windows it's necessary to provide a non-null widget
as \a parent to get native gesture support.
*/
-QPinchGesture::QPinchGesture(QWidget *parent)
- : QGesture(*new QPinchGesturePrivate, parent)
+QPinchGesture::QPinchGesture(QWidget *gestureTarget, QObject *parent)
+ : QGesture(*new QPinchGesturePrivate, gestureTarget, parent)
{
- if (parent) {
- QApplicationPrivate *qAppPriv = QApplicationPrivate::instance();
- qAppPriv->widgetGestures[parent].pinch = this;
-#ifdef Q_WS_WIN
- qt_widget_private(parent)->winSetupGestures();
-#endif
- }
}
-/*! \internal */
-bool QPinchGesture::event(QEvent *event)
+void QPinchGesturePrivate::setupGestureTarget(QObject *newGestureTarget)
{
- switch (event->type()) {
- case QEvent::ParentAboutToChange:
- if (QWidget *w = qobject_cast<QWidget*>(parent())) {
- QApplicationPrivate::instance()->widgetGestures[w].pinch = 0;
+ Q_Q(QPinchGesture);
+ if (gestureTarget && gestureTarget->isWidgetType()) {
+ QWidget *w = static_cast<QWidget*>(gestureTarget.data());
+ QApplicationPrivate::instance()->widgetGestures[w].pinch = 0;
#ifdef Q_WS_WIN
- qt_widget_private(w)->winSetupGestures();
+ qt_widget_private(w)->winSetupGestures();
#endif
- }
- break;
- case QEvent::ParentChange:
- if (QWidget *w = qobject_cast<QWidget*>(parent())) {
- QApplicationPrivate::instance()->widgetGestures[w].pinch = this;
+ }
+
+ if (newGestureTarget && newGestureTarget->isWidgetType()) {
+ QWidget *w = static_cast<QWidget*>(newGestureTarget);
+ QApplicationPrivate::instance()->widgetGestures[w].pinch = q;
#ifdef Q_WS_WIN
- qt_widget_private(w)->winSetupGestures();
+ qt_widget_private(w)->winSetupGestures();
#endif
- }
- break;
- default:
- break;
}
+ QGesturePrivate::setupGestureTarget(newGestureTarget);
+}
+
+/*! \internal */
+bool QPinchGesture::event(QEvent *event)
+{
return QObject::event(event);
}
@@ -399,6 +388,7 @@ bool QPinchGesture::eventFilter(QObject *receiver, QEvent *event)
return QGesture::eventFilter(receiver, event);
}
+
/*! \internal */
bool QPinchGesture::filterEvent(QEvent *event)
{
diff --git a/src/gui/kernel/qstandardgestures.h b/src/gui/kernel/qstandardgestures.h
index 8b5421b66..0eb9d9239 100644
--- a/src/gui/kernel/qstandardgestures.h
+++ b/src/gui/kernel/qstandardgestures.h
@@ -63,7 +63,7 @@ class Q_GUI_EXPORT QPanGesture : public QGesture
Q_PROPERTY(QSize lastOffset READ lastOffset)
public:
- QPanGesture(QWidget *parent);
+ QPanGesture(QWidget *gestureTarget, QObject *parent = 0);
bool filterEvent(QEvent *event);
@@ -97,7 +97,7 @@ class Q_GUI_EXPORT QPinchGesture : public QGesture
Q_PROPERTY(QPoint centerPoint READ centerPoint)
public:
- QPinchGesture(QWidget *parent);
+ QPinchGesture(QWidget *gestureTarget, QObject *parent = 0);
bool filterEvent(QEvent *event);
void reset();
diff --git a/src/gui/kernel/qstandardgestures_p.h b/src/gui/kernel/qstandardgestures_p.h
index c52d16bb2..5fbcc5d1b 100644
--- a/src/gui/kernel/qstandardgestures_p.h
+++ b/src/gui/kernel/qstandardgestures_p.h
@@ -76,6 +76,8 @@ public:
#endif
}
+ void setupGestureTarget(QObject *o);
+
QSize totalOffset;
QSize lastOffset;
QPoint lastPosition;
@@ -98,6 +100,9 @@ public:
#endif
{
}
+
+ void setupGestureTarget(QObject *o);
+
qreal scaleFactor;
qreal lastScaleFactor;
qreal rotationAngle;
diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp
index 4de08fd6f..bbda469a7 100644
--- a/src/gui/kernel/qwidget.cpp
+++ b/src/gui/kernel/qwidget.cpp
@@ -186,6 +186,7 @@ QWidgetPrivate::QWidgetPrivate(int version)
, widgetItem(0)
, extraPaintEngine(0)
, polished(0)
+ , graphicsEffect(0)
, inheritedFontResolveMask(0)
, inheritedPaletteResolveMask(0)
, leftmargin(0)
@@ -242,6 +243,8 @@ QWidgetPrivate::~QWidgetPrivate()
if (extra)
deleteExtra();
+
+ delete graphicsEffect;
}
QWindowSurface *QWidgetPrivate::createDefaultWindowSurface()
@@ -1628,9 +1631,9 @@ bool QWidgetPrivate::isOverlapped(const QRect &rect) const
continue;
}
- if (qRectIntersects(sibling->data->crect, r)) {
+ if (qRectIntersects(sibling->d_func()->effectiveRectFor(sibling->data->crect), r)) {
const QWExtra *siblingExtra = sibling->d_func()->extra;
- if (siblingExtra && siblingExtra->hasMask
+ if (siblingExtra && siblingExtra->hasMask && !sibling->d_func()->graphicsEffect
&& !siblingExtra->mask.translated(sibling->data->crect.topLeft()).intersects(r)) {
continue;
}
@@ -1729,7 +1732,7 @@ QRect QWidgetPrivate::clipRect() const
const QWidget * w = q;
if (!w->isVisible())
return QRect();
- QRect r = q->rect();
+ QRect r = effectiveRectFor(q->rect());
int ox = 0;
int oy = 0;
while (w
@@ -1871,12 +1874,14 @@ void QWidgetPrivate::subtractOpaqueSiblings(QRegion &sourceRegion, bool *hasDirt
break;
QWidgetPrivate *pd = w->parentWidget()->d_func();
const int myIndex = pd->children.indexOf(const_cast<QWidget *>(w));
+ const QRect widgetGeometry = w->d_func()->effectiveRectFor(w->data->crect);
for (int i = myIndex + 1; i < pd->children.size(); ++i) {
QWidget *sibling = qobject_cast<QWidget *>(pd->children.at(i));
if (!sibling || !sibling->isVisible() || sibling->isWindow())
continue;
- if (!qRectIntersects(sibling->data->crect, w->data->crect))
+ const QRect siblingGeometry = sibling->d_func()->effectiveRectFor(sibling->data->crect);
+ if (!qRectIntersects(siblingGeometry, widgetGeometry))
continue;
if (dirtyClipBoundingRect) {
@@ -1884,7 +1889,7 @@ void QWidgetPrivate::subtractOpaqueSiblings(QRegion &sourceRegion, bool *hasDirt
dirtyClipBoundingRect = false;
}
- if (!qRectIntersects(sibling->data->crect, clipBoundingRect.translated(parentOffset)))
+ if (!qRectIntersects(siblingGeometry, clipBoundingRect.translated(parentOffset)))
continue;
if (dirtyParentClip) {
@@ -1896,7 +1901,8 @@ void QWidgetPrivate::subtractOpaqueSiblings(QRegion &sourceRegion, bool *hasDirt
const QRect siblingClipRect(sibling->d_func()->clipRect());
QRegion siblingDirty(parentClip);
siblingDirty &= (siblingClipRect.translated(siblingPos));
- const bool hasMask = sibling->d_func()->extra && sibling->d_func()->extra->hasMask;
+ const bool hasMask = sibling->d_func()->extra && sibling->d_func()->extra->hasMask
+ && !sibling->d_func()->graphicsEffect;
if (hasMask)
siblingDirty &= sibling->d_func()->extra->mask.translated(siblingPos);
if (siblingDirty.isEmpty())
@@ -1907,7 +1913,7 @@ void QWidgetPrivate::subtractOpaqueSiblings(QRegion &sourceRegion, bool *hasDirt
siblingDirty.translate(-parentOffset);
sourceRegion -= siblingDirty;
} else {
- sourceRegion -= sibling->data->crect.translated(-parentOffset);
+ sourceRegion -= siblingGeometry.translated(-parentOffset);
}
} else {
if (hasDirtySiblingsAbove)
@@ -1938,6 +1944,11 @@ void QWidgetPrivate::clipToEffectiveMask(QRegion &region) const
const QWidget *w = q;
QPoint offset;
+ if (graphicsEffect) {
+ w = q->parentWidget();
+ offset -= data.crect.topLeft();
+ }
+
while (w) {
const QWidgetPrivate *wd = w->d_func();
if (wd->extra && wd->extra->hasMask)
@@ -1971,6 +1982,12 @@ void QWidgetPrivate::updateIsOpaque()
// hw: todo: only needed if opacity actually changed
setDirtyOpaqueRegion();
+ if (graphicsEffect) {
+ // ### We should probably add QGraphicsEffect::isOpaque at some point.
+ setOpaque(false);
+ return;
+ }
+
Q_Q(QWidget);
#ifdef Q_WS_X11
if (q->testAttribute(Qt::WA_X11OpenGLOverlay)) {
@@ -4948,6 +4965,54 @@ void QWidgetPrivate::setSoftKeys_sys(const QList<QAction*> &softkeys)
}
#endif // !defined(Q_OS_SYMBIAN)
+QGraphicsEffect *QWidget::graphicsEffect() const
+{
+ Q_D(const QWidget);
+ return d->graphicsEffect;
+}
+
+/*!
+ Sets \a effect as the widget's effect. If there already is an effect installed
+ on this widget, QWidget will delete the existing effect before installing
+ the new \a effect.
+
+ If \a effect is the installed on a different widget, setGraphicsEffect() will remove
+ the effect from the widget and install it on this widget.
+
+ \note This function will apply the effect on itself and all its children.
+
+ \since 4.6
+*/
+void QWidget::setGraphicsEffect(QGraphicsEffect *effect)
+{
+ Q_D(QWidget);
+ if (d->graphicsEffect == effect)
+ return;
+
+ if (d->graphicsEffect && effect) {
+ delete d->graphicsEffect;
+ d->graphicsEffect = 0;
+ }
+
+ if (!effect) {
+ // Unset current effect.
+ QGraphicsEffectPrivate *oldEffectPrivate = d->graphicsEffect->d_func();
+ d->graphicsEffect = 0;
+ if (oldEffectPrivate) {
+ oldEffectPrivate->setGraphicsEffectSource(0); // deletes the current source.
+ }
+ } else {
+ // Set new effect.
+ QGraphicsEffectSourcePrivate *sourced = new QWidgetEffectSourcePrivate(this);
+ QGraphicsEffectSource *source = new QGraphicsEffectSource(*sourced);
+ d->graphicsEffect = effect;
+ effect->d_func()->setGraphicsEffectSource(source);
+ }
+
+ d->updateIsOpaque();
+ update();
+}
+
bool QWidgetPrivate::isAboutToShow() const
{
if (data.in_show)
@@ -5092,6 +5157,33 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP
return;
Q_Q(QWidget);
+ if (graphicsEffect && graphicsEffect->isEnabled()) {
+ QGraphicsEffectSource *source = graphicsEffect->d_func()->source;
+ QWidgetEffectSourcePrivate *sourced = static_cast<QWidgetEffectSourcePrivate *>
+ (source->d_func());
+ if (!sourced->context) {
+ QWidgetPaintContext context(pdev, rgn, offset, flags, sharedPainter, backingStore);
+ sourced->context = &context;
+ if (!sharedPainter) {
+ QPaintEngine *paintEngine = pdev->paintEngine();
+ paintEngine->d_func()->systemClip = rgn.translated(offset);
+ QPainter p(pdev);
+ p.translate(offset);
+ context.painter = &p;
+ graphicsEffect->draw(&p, source);
+ paintEngine->d_func()->systemClip = QRegion();
+ } else {
+ context.painter = sharedPainter;
+ sharedPainter->save();
+ sharedPainter->translate(offset);
+ graphicsEffect->draw(sharedPainter, source);
+ sharedPainter->restore();
+ }
+ sourced->context = 0;
+ return;
+ }
+ }
+
const bool asRoot = flags & DrawAsRoot;
const bool alsoOnScreen = flags & DrawPaintOnScreen;
const bool recursive = flags & DrawRecursive;
@@ -5124,7 +5216,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP
if (sharedPainter)
paintEngine->d_func()->systemClip = toBePainted;
else
- paintEngine->setSystemRect(q->data->crect);
+ paintEngine->d_func()->systemRect = q->data->crect;
//paint the background
if ((asRoot || q->autoFillBackground() || onScreen || q->testAttribute(Qt::WA_StyledBackground))
@@ -5163,7 +5255,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP
if (paintEngine) {
restoreRedirected();
if (!sharedPainter)
- paintEngine->setSystemRect(QRect());
+ paintEngine->d_func()->systemRect = QRect();
else
paintEngine->d_func()->currentClipWidget = 0;
paintEngine->d_func()->systemClip = QRegion();
@@ -5226,7 +5318,7 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis
dirtyBoundingRect = false;
}
- if (qRectIntersects(boundingRect, x->data->crect)) {
+ if (qRectIntersects(boundingRect, x->d_func()->effectiveRectFor(x->data->crect))) {
#ifdef Q_BACKINGSTORE_SUBSURFACES
if (x->windowSurface() == currentSurface)
#endif
@@ -5244,7 +5336,7 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis
QWidgetPrivate *wd = w->d_func();
const QPoint widgetPos(w->data->crect.topLeft());
- const bool hasMask = wd->extra && wd->extra->hasMask;
+ const bool hasMask = wd->extra && wd->extra->hasMask && !wd->graphicsEffect;
if (index > 0) {
QRegion wr(rgn);
@@ -5259,7 +5351,7 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis
if (w->updatesEnabled() && (!w->d_func()->extra || !w->d_func()->extra->proxyWidget)) {
QRegion wRegion(rgn);
- wRegion &= w->data->crect;
+ wRegion &= wd->effectiveRectFor(w->data->crect);
wRegion.translate(-widgetPos);
if (hasMask)
wRegion &= wd->extra->mask;
@@ -5267,6 +5359,93 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis
}
}
+QRectF QWidgetEffectSourcePrivate::boundingRect(Qt::CoordinateSystem system) const
+{
+ if (system != Qt::DeviceCoordinates)
+ return m_widget->rect();
+
+ if (!context) {
+ // Device coordinates without context not yet supported.
+ qWarning("QGraphicsEffectSource::boundingRect: Not yet implemented, lacking device context");
+ return QRectF();
+ }
+
+ return context->painter->worldTransform().mapRect(m_widget->rect());
+}
+
+void QWidgetEffectSourcePrivate::draw(QPainter *painter)
+{
+ if (!context || context->painter != painter) {
+ m_widget->render(painter);
+ return;
+ }
+
+ // The region saved in the context is neither clipped to the rect
+ // nor the mask, so we have to clip it here before calling drawWidget.
+ QRegion toBePainted = context->rgn;
+ toBePainted &= m_widget->rect();
+ QWidgetPrivate *wd = qt_widget_private(m_widget);
+ if (wd->extra && wd->extra->hasMask)
+ toBePainted &= wd->extra->mask;
+
+ wd->drawWidget(context->pdev, toBePainted, context->offset, context->flags,
+ context->sharedPainter, context->backingStore);
+}
+
+QPixmap QWidgetEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *offset) const
+{
+ const bool deviceCoordinates = (system == Qt::DeviceCoordinates);
+ if (!context && deviceCoordinates) {
+ // Device coordinates without context not yet supported.
+ qWarning("QGraphicsEffectSource::pixmap: Not yet implemented, lacking device context");
+ return QPixmap();
+ }
+
+ QPoint pixmapOffset;
+ QRectF sourceRect = m_widget->rect();
+
+ if (deviceCoordinates) {
+ const QTransform &painterTransform = context->painter->worldTransform();
+ sourceRect = painterTransform.mapRect(sourceRect);
+ pixmapOffset = painterTransform.map(pixmapOffset);
+ }
+
+ QRect effectRect = m_widget->graphicsEffect()->boundingRectFor(sourceRect).toAlignedRect();
+ if (offset)
+ *offset = effectRect.topLeft();
+
+ if (deviceCoordinates) {
+ // Clip to device rect.
+ int left, top, right, bottom;
+ effectRect.getCoords(&left, &top, &right, &bottom);
+ if (left < 0) {
+ if (offset)
+ offset->rx() += -left;
+ effectRect.setX(0);
+ }
+ if (top < 0) {
+ if (offset)
+ offset->ry() += -top;
+ effectRect.setY(0);
+ }
+ // NB! We use +-1 for historical reasons (see QRect documentation).
+ QPaintDevice *device = context->painter->device();
+ const int deviceWidth = device->width();
+ const int deviceHeight = device->height();
+ if (right + 1 > deviceWidth)
+ effectRect.setRight(deviceWidth - 1);
+ if (bottom + 1 > deviceHeight)
+ effectRect.setBottom(deviceHeight -1);
+ }
+
+ pixmapOffset -= effectRect.topLeft();
+
+ QPixmap pixmap(effectRect.size());
+ pixmap.fill(Qt::transparent);
+ m_widget->render(&pixmap, pixmapOffset);
+ return pixmap;
+}
+
/*!
\internal
diff --git a/src/gui/kernel/qwidget.h b/src/gui/kernel/qwidget.h
index a40b52d5c..2484ba616 100644
--- a/src/gui/kernel/qwidget.h
+++ b/src/gui/kernel/qwidget.h
@@ -96,6 +96,7 @@ class QWindowSurface;
class QLocale;
class QGraphicsProxyWidget;
class QGestureManager;
+class QGraphicsEffect;
#if defined(Q_WS_X11)
class QX11Info;
#endif
@@ -353,6 +354,9 @@ public:
const QRegion &sourceRegion = QRegion(),
RenderFlags renderFlags = RenderFlags(DrawWindowBackground | DrawChildren));
+ QGraphicsEffect *graphicsEffect() const;
+ void setGraphicsEffect(QGraphicsEffect *effect);
+
public Q_SLOTS:
void setWindowTitle(const QString &);
#ifndef QT_NO_STYLE_STYLESHEET
diff --git a/src/gui/kernel/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm
index 4bd72220e..ea5a53ea3 100644
--- a/src/gui/kernel/qwidget_mac.mm
+++ b/src/gui/kernel/qwidget_mac.mm
@@ -2567,7 +2567,7 @@ void QWidget::destroy(bool destroyWindow, bool destroySubWindows)
{
Q_D(QWidget);
if (!isWindow() && parentWidget())
- parentWidget()->d_func()->invalidateBuffer(geometry());
+ parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry()));
d->deactivateWidgetCleanup();
qt_mac_event_release(this);
if(testAttribute(Qt::WA_WState_Created)) {
@@ -2673,7 +2673,7 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f)
OSViewRef old_id = 0;
if (q->isVisible() && q->parentWidget() && parent != q->parentWidget())
- q->parentWidget()->d_func()->invalidateBuffer(q->geometry());
+ q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(q->geometry()));
// Maintain the glWidgets list on parent change: remove "our" gl widgets
// from the list on the old parent and grandparents.
@@ -4169,12 +4169,12 @@ void QWidgetPrivate::setGeometry_sys_helper(int x, int y, int w, int h, bool isM
setWSGeometry(false, oldRect);
if (isResize && QApplicationPrivate::graphicsSystem()) {
invalidateBuffer(q->rect());
- if (extra && !extra->mask.isEmpty()) {
+ if (extra && !graphicsEffect && !extra->mask.isEmpty()) {
QRegion oldRegion(extra->mask.translated(oldp));
oldRegion &= oldRect;
q->parentWidget()->d_func()->invalidateBuffer(oldRegion);
} else {
- q->parentWidget()->d_func()->invalidateBuffer(oldRect);
+ q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(oldRect));
}
}
}
diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h
index 8e58498e0..ed0abce5f 100644
--- a/src/gui/kernel/qwidget_p.h
+++ b/src/gui/kernel/qwidget_p.h
@@ -62,6 +62,7 @@
#include "QtGui/qsizepolicy.h"
#include "QtGui/qstyle.h"
#include "QtGui/qapplication.h"
+#include <private/qgraphicseffect_p.h>
#ifdef Q_WS_WIN
#include "QtCore/qt_windows.h"
@@ -460,6 +461,13 @@ public:
return extra ? extra->nativeChildrenForced : false;
}
+ inline QRect effectiveRectFor(const QRect &rect) const
+ {
+ if (graphicsEffect && graphicsEffect->isEnabled())
+ return graphicsEffect->boundingRectFor(rect).toAlignedRect();
+ return rect;
+ }
+
QSize adjustedSize() const;
inline void handleSoftwareInputPanel(Qt::MouseButton button, bool clickCausedFocus)
@@ -504,6 +512,7 @@ public:
QWidgetItemV2 *widgetItem;
QPaintEngine *extraPaintEngine;
mutable const QMetaObject *polished;
+ QGraphicsEffect *graphicsEffect;
// All widgets are added into the allWidgets set. Once
// they receive a window id they are also added to the mapper.
// This should just ensure that all widgets are deleted by QApplication
@@ -681,6 +690,57 @@ public:
};
+struct QWidgetPaintContext
+{
+ inline QWidgetPaintContext(QPaintDevice *d, const QRegion &r, const QPoint &o, int f,
+ QPainter *p, QWidgetBackingStore *b)
+ : pdev(d), rgn(r), offset(o), flags(f), sharedPainter(p), backingStore(b), painter(0) {}
+
+ QPaintDevice *pdev;
+ QRegion rgn;
+ QPoint offset;
+ int flags;
+ QPainter *sharedPainter;
+ QWidgetBackingStore *backingStore;
+ QPainter *painter;
+};
+
+class QWidgetEffectSourcePrivate : public QGraphicsEffectSourcePrivate
+{
+public:
+ QWidgetEffectSourcePrivate(QWidget *widget)
+ : QGraphicsEffectSourcePrivate(), m_widget(widget), context(0)
+ {}
+
+ inline void detach()
+ { m_widget->setGraphicsEffect(0); }
+
+ inline const QGraphicsItem *graphicsItem() const
+ { return 0; }
+
+ inline const QWidget *widget() const
+ { return m_widget; }
+
+ inline void update()
+ { m_widget->update(); }
+
+ inline bool isPixmap() const
+ { return false; }
+
+ inline const QStyleOption *styleOption() const
+ { return 0; }
+
+ inline QRect deviceRect() const
+ { return m_widget->window()->rect(); }
+
+ QRectF boundingRect(Qt::CoordinateSystem system) const;
+ void draw(QPainter *p);
+ QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset) const;
+
+ QWidget *m_widget;
+ QWidgetPaintContext *context;
+};
+
inline QWExtra *QWidgetPrivate::extraData() const
{
return extra;
diff --git a/src/gui/kernel/qwidget_qws.cpp b/src/gui/kernel/qwidget_qws.cpp
index 66c047a9b..f8ac25282 100644
--- a/src/gui/kernel/qwidget_qws.cpp
+++ b/src/gui/kernel/qwidget_qws.cpp
@@ -258,7 +258,7 @@ void QWidget::destroy(bool destroyWindow, bool destroySubWindows)
Q_D(QWidget);
if (!isWindow() && parentWidget())
- parentWidget()->d_func()->invalidateBuffer(geometry());
+ parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry()));
d->deactivateWidgetCleanup();
if (testAttribute(Qt::WA_WState_Created)) {
@@ -317,7 +317,7 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f)
Q_Q(QWidget);
bool wasCreated = q->testAttribute(Qt::WA_WState_Created);
if (q->isVisible() && q->parentWidget() && parent != q->parentWidget())
- q->parentWidget()->d_func()->invalidateBuffer(q->geometry());
+ q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(q->geometry()));
#ifndef QT_NO_CURSOR
QCursor oldcurs;
bool setcurs=q->testAttribute(Qt::WA_SetCursor);
@@ -581,6 +581,15 @@ void QWidgetPrivate::show_sys()
if (q->isWindow()) {
+
+ if (!q->testAttribute(Qt::WA_ShowWithoutActivating)
+ && q->windowType() != Qt::Popup
+ && q->windowType() != Qt::Tool
+ && q->windowType() != Qt::ToolTip) {
+ QWidget::qwsDisplay()->requestFocus(data.winid,true);
+ }
+
+
if (QWindowSurface *surface = q->windowSurface()) {
const QRect frameRect = q->frameGeometry();
if (surface->geometry() != frameRect)
@@ -597,12 +606,6 @@ void QWidgetPrivate::show_sys()
#endif
data.fstrut_dirty = true;
invalidateBuffer(r);
- if (!q->testAttribute(Qt::WA_ShowWithoutActivating)
- && q->windowType() != Qt::Popup
- && q->windowType() != Qt::Tool
- && q->windowType() != Qt::ToolTip) {
- QWidget::qwsDisplay()->requestFocus(data.winid,true);
- }
bool staysontop =
(q->windowFlags() & Qt::WindowStaysOnTopHint)
|| q->windowType() == Qt::Popup;
@@ -811,7 +814,7 @@ void QWidgetPrivate::lower_sys()
QWSChangeAltitudeCommand::Lower);
} else if (QWidget *p = q->parentWidget()) {
setDirtyOpaqueRegion();
- p->d_func()->invalidateBuffer(q->geometry());
+ p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry()));
}
}
@@ -820,7 +823,7 @@ void QWidgetPrivate::stackUnder_sys(QWidget*)
Q_Q(QWidget);
if (QWidget *p = q->parentWidget()) {
setDirtyOpaqueRegion();
- p->d_func()->invalidateBuffer(q->geometry());
+ p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry()));
}
}
diff --git a/src/gui/kernel/qwidget_win.cpp b/src/gui/kernel/qwidget_win.cpp
index 3d618fe9a..40e3fa24b 100644
--- a/src/gui/kernel/qwidget_win.cpp
+++ b/src/gui/kernel/qwidget_win.cpp
@@ -535,7 +535,7 @@ void QWidget::destroy(bool destroyWindow, bool destroySubWindows)
{
Q_D(QWidget);
if (!isWindow() && parentWidget())
- parentWidget()->d_func()->invalidateBuffer(geometry());
+ parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry()));
d->deactivateWidgetCleanup();
if (testAttribute(Qt::WA_WState_Created)) {
setAttribute(Qt::WA_WState_Created, false);
@@ -601,7 +601,7 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f)
Q_Q(QWidget);
bool wasCreated = q->testAttribute(Qt::WA_WState_Created);
if (q->isVisible() && q->parentWidget() && parent != q->parentWidget())
- q->parentWidget()->d_func()->invalidateBuffer(q->geometry());
+ q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(q->geometry()));
WId old_winid = data.winid;
// hide and reparent our own window away. Otherwise we might get
@@ -2070,11 +2070,21 @@ void QWidgetPrivate::winSetupGestures()
bool needh = false;
bool needv = false;
bool singleFingerPanEnabled = false;
- QStandardGestures gestures = qAppPriv->widgetGestures[q];
+ QApplicationPrivate::WidgetStandardGesturesMap::const_iterator it =
+ qAppPriv->widgetGestures.find(q);
+ if (it == qAppPriv->widgetGestures.end())
+ return;
+ const QStandardGestures &gestures = it.value();
WId winid = 0;
if (QAbstractScrollArea *asa = qobject_cast<QAbstractScrollArea*>(q)) {
- winid = asa->viewport()->winId();
+ winid = asa->viewport()->internalWinId();
+ if (!winid) {
+ QWidget *nativeParent = asa->viewport()->nativeParentWidget();
+ if (!nativeParent)
+ return;
+ winid = nativeParent->internalWinId();
+ }
QScrollBar *hbar = asa->horizontalScrollBar();
QScrollBar *vbar = asa->verticalScrollBar();
Qt::ScrollBarPolicy hbarpolicy = asa->horizontalScrollBarPolicy();
@@ -2085,9 +2095,13 @@ void QWidgetPrivate::winSetupGestures()
(vbarpolicy == Qt::ScrollBarAsNeeded && vbar->minimum() < vbar->maximum()));
singleFingerPanEnabled = asa->d_func()->singleFingerPanEnabled;
} else {
- winid = q->winId();
+ winid = q->internalWinId();
+ if (!winid) {
+ if (QWidget *nativeParent = q->nativeParentWidget())
+ winid = nativeParent->internalWinId();
+ }
}
- if (qAppPriv->SetGestureConfig) {
+ if (winid && qAppPriv->SetGestureConfig) {
GESTURECONFIG gc[3];
memset(gc, 0, sizeof(gc));
gc[0].dwID = GID_PAN;
@@ -2116,7 +2130,6 @@ void QWidgetPrivate::winSetupGestures()
else
gc[2].dwBlock = GC_ROTATE;
- Q_ASSERT(winid);
qAppPriv->SetGestureConfig(winid, 0, sizeof(gc)/sizeof(gc[0]), gc, sizeof(gc[0]));
}
}
diff --git a/src/gui/kernel/qwidget_x11.cpp b/src/gui/kernel/qwidget_x11.cpp
index 8baebe84a..62aba45f5 100644
--- a/src/gui/kernel/qwidget_x11.cpp
+++ b/src/gui/kernel/qwidget_x11.cpp
@@ -971,7 +971,7 @@ void QWidget::destroy(bool destroyWindow, bool destroySubWindows)
{
Q_D(QWidget);
if (!isWindow() && parentWidget())
- parentWidget()->d_func()->invalidateBuffer(geometry());
+ parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry()));
d->deactivateWidgetCleanup();
if (testAttribute(Qt::WA_WState_Created)) {
setAttribute(Qt::WA_WState_Created, false);
@@ -1054,7 +1054,7 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f)
QTLWExtra *topData = maybeTopData();
bool wasCreated = q->testAttribute(Qt::WA_WState_Created);
if (q->isVisible() && q->parentWidget() && parent != q->parentWidget())
- q->parentWidget()->d_func()->invalidateBuffer(q->geometry());
+ q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(q->geometry()));
extern void qPRCreate(const QWidget *, Window);
#ifndef QT_NO_CURSOR
QCursor oldcurs;
@@ -1280,7 +1280,7 @@ void QWidgetPrivate::updateSystemBackground()
else if (brush.style() == Qt::TexturePattern) {
extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap); // qpixmap_x11.cpp
XSetWindowBackgroundPixmap(X11->display, q->internalWinId(),
- static_cast<QX11PixmapData*>(qt_toX11Pixmap(brush.texture()).data)->x11ConvertToDefaultDepth());
+ static_cast<QX11PixmapData*>(qt_toX11Pixmap(brush.texture()).data.data())->x11ConvertToDefaultDepth());
} else
XSetWindowBackground(X11->display, q->internalWinId(),
QColormap::instance(xinfo.screen()).pixel(brush.color()));
@@ -1426,7 +1426,7 @@ void QWidgetPrivate::setWindowIcon_sys(bool forceReset)
// violates the ICCCM), since this works on all DEs known to Qt
if (!forceReset || !topData->iconPixmap)
topData->iconPixmap = new QPixmap(qt_toX11Pixmap(icon.pixmap(QSize(64,64))));
- pixmap_handle = static_cast<QX11PixmapData*>(topData->iconPixmap->data)->x11ConvertToDefaultDepth();
+ pixmap_handle = static_cast<QX11PixmapData*>(topData->iconPixmap->data.data())->x11ConvertToDefaultDepth();
}
}
}
diff --git a/src/gui/math3d/qmatrix4x4.cpp b/src/gui/math3d/qmatrix4x4.cpp
index 8fc439be6..36ffcbe3e 100644
--- a/src/gui/math3d/qmatrix4x4.cpp
+++ b/src/gui/math3d/qmatrix4x4.cpp
@@ -41,6 +41,7 @@
#include "qmatrix4x4.h"
#include <QtCore/qmath.h>
+#include <QtCore/qvariant.h>
#include <QtGui/qmatrix.h>
#include <QtGui/qtransform.h>
@@ -1438,18 +1439,59 @@ QMatrix QMatrix4x4::toAffine() const
qreal(m[3][0]), qreal(m[3][1]));
}
+static const qreal inv_dist_to_plane = 1. / 1024.;
+
/*!
Returns the conventional Qt 2D transformation matrix that
- corresponds to this matrix. It is assumed that this matrix
- only contains 2D transformation elements.
+ corresponds to this matrix.
+
+ If \a distanceToPlane is non-zero, it indicates a projection
+ factor to use to adjust for the z co-ordinate. The default
+ value of 1024 corresponds to the projection factor used
+ by QTransform::rotate() for the x and y axes.
+
+ If \a distToPlane is zero, then the returned QTransform
+ is formed by simply dropping the third row and third column
+ of the QMatrix4x4. This is suitable for implementing
+ orthographic projections where the z co-ordinate should
+ be dropped rather than projected.
\sa toAffine()
*/
-QTransform QMatrix4x4::toTransform() const
+QTransform QMatrix4x4::toTransform(qreal distanceToPlane) const
{
- return QTransform(qreal(m[0][0]), qreal(m[0][1]), qreal(m[0][3]),
- qreal(m[1][0]), qreal(m[1][1]), qreal(m[1][3]),
- qreal(m[3][0]), qreal(m[3][1]), qreal(m[3][3]));
+ if (distanceToPlane == 1024.0f) {
+ // Optimize the common case with constants.
+ return QTransform(qreal(m[0][0]), qreal(m[0][1]),
+ qreal(m[0][3]) - qreal(m[0][2]) *
+ inv_dist_to_plane,
+ qreal(m[1][0]), qreal(m[1][1]),
+ qreal(m[1][3]) - qreal(m[1][2]) *
+ inv_dist_to_plane,
+ qreal(m[3][0]), qreal(m[3][1]),
+ qreal(m[3][3]) - qreal(m[3][2]) *
+ inv_dist_to_plane);
+ } else if (distanceToPlane != 0.0f) {
+ // The following projection matrix is pre-multiplied with "matrix":
+ // | 1 0 0 0 |
+ // | 0 1 0 0 |
+ // | 0 0 1 0 |
+ // | 0 0 d 1 |
+ // where d = -1 / distanceToPlane. After projection, row 3 and
+ // column 3 are dropped to form the final QTransform.
+ qreal d = 1.0f / distanceToPlane;
+ return QTransform(qreal(m[0][0]), qreal(m[0][1]),
+ qreal(m[0][3]) - qreal(m[0][2]) * d,
+ qreal(m[1][0]), qreal(m[1][1]),
+ qreal(m[1][3]) - qreal(m[1][2]) * d,
+ qreal(m[3][0]), qreal(m[3][1]),
+ qreal(m[3][3]) - qreal(m[3][2]) * d);
+ } else {
+ // Orthographic projection: drop row 3 and column 3.
+ return QTransform(qreal(m[0][0]), qreal(m[0][1]), qreal(m[0][3]),
+ qreal(m[1][0]), qreal(m[1][1]), qreal(m[1][3]),
+ qreal(m[3][0]), qreal(m[3][1]), qreal(m[3][3]));
+ }
}
/*!
@@ -1776,6 +1818,14 @@ void QMatrix4x4::inferSpecialType()
flagBits = Scale;
}
+/*!
+ Returns the matrix as a QVariant.
+*/
+QMatrix4x4::operator QVariant() const
+{
+ return QVariant(QVariant::Matrix4x4, this);
+}
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QMatrix4x4 &m)
diff --git a/src/gui/math3d/qmatrix4x4.h b/src/gui/math3d/qmatrix4x4.h
index b02608d40..10e628b5e 100644
--- a/src/gui/math3d/qmatrix4x4.h
+++ b/src/gui/math3d/qmatrix4x4.h
@@ -58,6 +58,7 @@ QT_MODULE(Gui)
class QMatrix;
class QTransform;
+class QVariant;
class Q_GUI_EXPORT QMatrix4x4
{
@@ -158,7 +159,7 @@ public:
void toValueArray(qreal *values) const;
QMatrix toAffine() const;
- QTransform toTransform() const;
+ QTransform toTransform(qreal distanceToPlane = 1024.0f) const;
QPoint map(const QPoint& point) const;
QPointF map(const QPointF& point) const;
@@ -182,6 +183,8 @@ public:
void inferSpecialType();
+ operator QVariant() const;
+
#ifndef QT_NO_DEBUG_STREAM
friend Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QMatrix4x4 &m);
#endif
diff --git a/src/gui/math3d/qquaternion.cpp b/src/gui/math3d/qquaternion.cpp
index b2c598faf..f2793882b 100644
--- a/src/gui/math3d/qquaternion.cpp
+++ b/src/gui/math3d/qquaternion.cpp
@@ -41,6 +41,7 @@
#include "qquaternion.h"
#include <QtCore/qmath.h>
+#include <QtCore/qvariant.h>
#include <QtCore/qdebug.h>
QT_BEGIN_NAMESPACE
@@ -560,6 +561,14 @@ QQuaternion QQuaternion::nlerp
return (q1 * (1.0f - t) + q2b * t).normalized();
}
+/*!
+ Returns the quaternion as a QVariant.
+*/
+QQuaternion::operator QVariant() const
+{
+ return QVariant(QVariant::Quaternion, this);
+}
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QQuaternion &q)
diff --git a/src/gui/math3d/qquaternion.h b/src/gui/math3d/qquaternion.h
index a2fb29ff6..af60b614b 100644
--- a/src/gui/math3d/qquaternion.h
+++ b/src/gui/math3d/qquaternion.h
@@ -54,6 +54,7 @@ QT_MODULE(Gui)
#ifndef QT_NO_QUATERNION
class QMatrix4x4;
+class QVariant;
class Q_GUI_EXPORT QQuaternion
{
@@ -118,6 +119,8 @@ public:
QVector4D toVector4D() const;
#endif
+ operator QVariant() const;
+
#ifndef QT_NO_VECTOR3D
static QQuaternion fromAxisAndAngle(const QVector3D& axis, qreal angle);
#endif
diff --git a/src/gui/math3d/qvector2d.cpp b/src/gui/math3d/qvector2d.cpp
index 166202029..35c86a184 100644
--- a/src/gui/math3d/qvector2d.cpp
+++ b/src/gui/math3d/qvector2d.cpp
@@ -43,6 +43,7 @@
#include "qvector3d.h"
#include "qvector4d.h"
#include <QtCore/qdebug.h>
+#include <QtCore/qvariant.h>
#include <QtCore/qmath.h>
QT_BEGIN_NAMESPACE
@@ -402,6 +403,14 @@ QVector4D QVector2D::toVector4D() const
\sa toPoint(), toVector3D()
*/
+/*!
+ Returns the 2D vector as a QVariant.
+*/
+QVector2D::operator QVariant() const
+{
+ return QVariant(QVariant::Vector2D, this);
+}
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QVector2D &vector)
diff --git a/src/gui/math3d/qvector2d.h b/src/gui/math3d/qvector2d.h
index 93052f605..d9727e24f 100644
--- a/src/gui/math3d/qvector2d.h
+++ b/src/gui/math3d/qvector2d.h
@@ -53,6 +53,7 @@ QT_MODULE(Gui)
class QVector3D;
class QVector4D;
+class QVariant;
#ifndef QT_NO_VECTOR2D
@@ -114,6 +115,8 @@ public:
QPoint toPoint() const;
QPointF toPointF() const;
+ operator QVariant() const;
+
private:
float xp, yp;
diff --git a/src/gui/math3d/qvector3d.cpp b/src/gui/math3d/qvector3d.cpp
index 0e3f4e195..5c969792f 100644
--- a/src/gui/math3d/qvector3d.cpp
+++ b/src/gui/math3d/qvector3d.cpp
@@ -43,6 +43,7 @@
#include "qvector2d.h"
#include "qvector4d.h"
#include <QtCore/qmath.h>
+#include <QtCore/qvariant.h>
#include <QtCore/qdebug.h>
QT_BEGIN_NAMESPACE
@@ -530,6 +531,14 @@ QVector4D QVector3D::toVector4D() const
*/
/*!
+ Returns the 3D vector as a QVariant.
+*/
+QVector3D::operator QVariant() const
+{
+ return QVariant(QVariant::Vector3D, this);
+}
+
+/*!
Returns the length of the vector from the origin.
\sa lengthSquared(), normalized()
diff --git a/src/gui/math3d/qvector3d.h b/src/gui/math3d/qvector3d.h
index 36292d24f..d1370fbb9 100644
--- a/src/gui/math3d/qvector3d.h
+++ b/src/gui/math3d/qvector3d.h
@@ -127,6 +127,8 @@ public:
QPoint toPoint() const;
QPointF toPointF() const;
+ operator QVariant() const;
+
private:
float xp, yp, zp;
diff --git a/src/gui/math3d/qvector4d.cpp b/src/gui/math3d/qvector4d.cpp
index a2efff775..11eb4c4ac 100644
--- a/src/gui/math3d/qvector4d.cpp
+++ b/src/gui/math3d/qvector4d.cpp
@@ -43,6 +43,7 @@
#include "qvector3d.h"
#include "qvector2d.h"
#include <QtCore/qdebug.h>
+#include <QtCore/qvariant.h>
#include <QtCore/qmath.h>
QT_BEGIN_NAMESPACE
@@ -500,6 +501,14 @@ QVector3D QVector4D::toVector3DAffine() const
\sa toPoint(), toVector2D()
*/
+/*!
+ Returns the 4D vector as a QVariant.
+*/
+QVector4D::operator QVariant() const
+{
+ return QVariant(QVariant::Vector4D, this);
+}
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QVector4D &vector)
diff --git a/src/gui/math3d/qvector4d.h b/src/gui/math3d/qvector4d.h
index 42db45be9..69ccd3028 100644
--- a/src/gui/math3d/qvector4d.h
+++ b/src/gui/math3d/qvector4d.h
@@ -124,6 +124,8 @@ public:
QPoint toPoint() const;
QPointF toPointF() const;
+ operator QVariant() const;
+
private:
float xp, yp, zp, wp;
diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp
index 5321ce245..0fffaefc9 100644
--- a/src/gui/painting/qbackingstore.cpp
+++ b/src/gui/painting/qbackingstore.cpp
@@ -559,7 +559,8 @@ void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool up
}
const QPoint offset = widget->mapTo(tlw, QPoint());
- if (qt_region_strictContains(dirty, widget->rect().translated(offset))) {
+ const QRect widgetRect = widget->d_func()->effectiveRectFor(widget->rect());
+ if (qt_region_strictContains(dirty, widgetRect.translated(offset))) {
if (updateImmediately)
sendUpdateRequest(tlw, updateImmediately);
return; // Already dirty.
@@ -567,7 +568,10 @@ void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool up
if (invalidateBuffer) {
const bool eventAlreadyPosted = !dirty.isEmpty();
- dirty += rgn.translated(offset);
+ if (widget->d_func()->graphicsEffect)
+ dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect()).translated(offset);
+ else
+ dirty += rgn.translated(offset);
if (!eventAlreadyPosted || updateImmediately)
sendUpdateRequest(tlw, updateImmediately);
return;
@@ -580,8 +584,12 @@ void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool up
}
if (widget->d_func()->inDirtyList) {
- if (!qt_region_strictContains(widget->d_func()->dirty, widget->rect()))
- widget->d_func()->dirty += rgn;
+ if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect)) {
+ if (widget->d_func()->graphicsEffect)
+ widget->d_func()->dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect());
+ else
+ widget->d_func()->dirty += rgn;
+ }
} else {
addDirtyWidget(widget, rgn);
}
@@ -625,7 +633,8 @@ void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool upd
return;
}
- const QRect translatedRect(rect.translated(widget->mapTo(tlw, QPoint())));
+ const QRect widgetRect = widget->d_func()->effectiveRectFor(rect);
+ const QRect translatedRect(widgetRect.translated(widget->mapTo(tlw, QPoint())));
if (qt_region_strictContains(dirty, translatedRect)) {
if (updateImmediately)
sendUpdateRequest(tlw, updateImmediately);
@@ -647,8 +656,8 @@ void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool upd
}
if (widget->d_func()->inDirtyList) {
- if (!qt_region_strictContains(widget->d_func()->dirty, rect))
- widget->d_func()->dirty += rect;
+ if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect))
+ widget->d_func()->dirty += widgetRect;
} else {
addDirtyWidget(widget, rect);
}
@@ -884,7 +893,7 @@ void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
&& !isOverlapped(sourceRect) && !isOverlapped(destRect);
if (!accelerateMove) {
- QRegion parentR(parentRect);
+ QRegion parentR(effectiveRectFor(parentRect));
if (!extra || !extra->hasMask) {
parentR -= newRect;
} else {
@@ -1388,7 +1397,7 @@ void QWidgetPrivate::invalidateBuffer_resizeHelper(const QPoint &oldPos, const Q
const QRect newWidgetRect(q->rect());
const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height());
- if (!staticContents) {
+ if (!staticContents || graphicsEffect) {
QRegion staticChildren;
QWidgetBackingStore *bs = 0;
if (offset.isNull() && (bs = maybeBackingStore()))
@@ -1408,19 +1417,19 @@ void QWidgetPrivate::invalidateBuffer_resizeHelper(const QPoint &oldPos, const Q
return;
// Invalidate newly exposed area of the parent.
- if (extra && extra->hasMask) {
+ if (!graphicsEffect && extra && extra->hasMask) {
QRegion parentExpose(extra->mask.translated(oldPos));
parentExpose &= QRect(oldPos, oldSize);
if (hasStaticChildren)
parentExpose -= data.crect; // Offset is unchanged, safe to do this.
q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
} else {
- if (hasStaticChildren) {
+ if (hasStaticChildren && !graphicsEffect) {
QRegion parentExpose(QRect(oldPos, oldSize));
parentExpose -= data.crect; // Offset is unchanged, safe to do this.
q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
} else {
- q->parentWidget()->d_func()->invalidateBuffer(QRect(oldPos, oldSize));
+ q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(QRect(oldPos, oldSize)));
}
}
return;
@@ -1478,7 +1487,7 @@ void QWidgetPrivate::invalidateBuffer(const QRegion &rgn)
QRegion wrgn(rgn);
wrgn &= clipRect();
- if (extra && extra->hasMask)
+ if (!graphicsEffect && extra && extra->hasMask)
wrgn &= extra->mask;
if (wrgn.isEmpty())
return;
@@ -1506,7 +1515,7 @@ void QWidgetPrivate::invalidateBuffer(const QRect &rect)
if (wRect.isEmpty())
return;
- if (!extra || !extra->hasMask) {
+ if (graphicsEffect || !extra || !extra->hasMask) {
tlwExtra->backingStore->markDirty(wRect, q, false, true);
return;
}
diff --git a/src/gui/painting/qbackingstore_p.h b/src/gui/painting/qbackingstore_p.h
index ddc0a5966..b21d504f7 100644
--- a/src/gui/painting/qbackingstore_p.h
+++ b/src/gui/painting/qbackingstore_p.h
@@ -144,9 +144,13 @@ private:
inline void addDirtyWidget(QWidget *widget, const QRegion &rgn)
{
if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) {
- widget->d_func()->dirty = rgn;
+ QWidgetPrivate *widgetPrivate = widget->d_func();
+ if (widgetPrivate->graphicsEffect)
+ widgetPrivate->dirty = widgetPrivate->effectiveRectFor(rgn.boundingRect());
+ else
+ widgetPrivate->dirty = rgn;
dirtyWidgets.append(widget);
- widget->d_func()->inDirtyList = true;
+ widgetPrivate->inDirtyList = true;
}
}
diff --git a/src/gui/painting/qblendfunctions.cpp b/src/gui/painting/qblendfunctions.cpp
index e447301a9..cd78de02b 100644
--- a/src/gui/painting/qblendfunctions.cpp
+++ b/src/gui/painting/qblendfunctions.cpp
@@ -374,9 +374,9 @@ template <typename T> void qt_blend_argb24_on_rgb16(uchar *destPixels, int dbpl,
const uchar *src = srcPixels + y * sbpl;
const uchar *srcEnd = src + srcOffset;
while (src < srcEnd) {
-#if defined(QT_ARCH_ARM) || defined(QT_ARCH_POWERPC) || defined(QT_ARCH_SH) || (defined(QT_ARCH_WINDOWSCE) && !defined(_X86_))
+#if defined(QT_ARCH_ARM) || defined(QT_ARCH_POWERPC) || defined(QT_ARCH_SH) || defined(QT_ARCH_AVR32) || (defined(QT_ARCH_WINDOWSCE) && !defined(_X86_))
// non-16-bit aligned memory access is not possible on PowerPC,
- // ARM <v6 (QT_ARCH_ARMV6) & SH
+ // ARM <v6 (QT_ARCH_ARMV6) & SH & AVR32
quint16 spix = (quint16(src[2])<<8) + src[1];
#else
quint16 spix = *(quint16 *) (src + 1);
diff --git a/src/gui/painting/qgrayraster.c b/src/gui/painting/qgrayraster.c
index 7c960df1c..3d1b80644 100644
--- a/src/gui/painting/qgrayraster.c
+++ b/src/gui/painting/qgrayraster.c
@@ -160,12 +160,7 @@
#include <private/qrasterdefs_p.h>
#include <private/qgrayraster_p.h>
-// Bug in stdlib.h, see more information from fixed_stdlib.h
-#if (defined __SYMBIAN32__ && !defined __cplusplus)
-#include <fixed_stdlib.h>
-#else
#include <stdlib.h>
-#endif // defined __SYMBIAN32__ && !defined __cplusplus
#include <stdio.h>
/* This macro is used to indicate that a function parameter is unused. */
diff --git a/src/gui/painting/qpaintdevice_mac.cpp b/src/gui/painting/qpaintdevice_mac.cpp
index aa7c2acdd..51d52a28a 100644
--- a/src/gui/painting/qpaintdevice_mac.cpp
+++ b/src/gui/painting/qpaintdevice_mac.cpp
@@ -123,7 +123,7 @@ Q_GUI_EXPORT CGContextRef qt_mac_cg_context(const QPaintDevice *pdev)
// It would make sense to put this into a mac #ifdef'ed
// virtual function in the QPixmapData at some point
if (pm->data->classId() == QPixmapData::MacClass) {
- const QMacPixmapData *pmData = static_cast<const QMacPixmapData*>(pm->data);
+ const QMacPixmapData *pmData = static_cast<const QMacPixmapData*>(pm->data.data());
ret = CGBitmapContextCreate(pmData->pixels, pmData->w, pmData->h,
8, pmData->bytesPerRow, colorspace,
flags);
diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h
index f1e83dceb..bed2ab391 100644
--- a/src/gui/painting/qpaintengine.h
+++ b/src/gui/painting/qpaintengine.h
@@ -211,6 +211,7 @@ public:
Direct3D,
Pdf,
OpenVG,
+ OpenGL2,
User = 50, // first user type id
MaxUser = 100 // last user type id
diff --git a/src/gui/painting/qpaintengine_p.h b/src/gui/painting/qpaintengine_p.h
index 7ea68d24e..d3cd6f941 100644
--- a/src/gui/painting/qpaintengine_p.h
+++ b/src/gui/painting/qpaintengine_p.h
@@ -72,6 +72,7 @@ public:
QPaintDevice *pdev;
QPaintEngine *q_ptr;
QRegion systemClip;
+ QRect systemRect;
QRegion systemViewport;
QTransform systemTransform;
QWidget *currentClipWidget;
@@ -117,9 +118,6 @@ public:
virtual void systemStateChanged() { }
void drawBoxTextItem(const QPointF &p, const QTextItemInt &ti);
-
-private:
- QRect systemRect;
};
QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintengine_x11.cpp b/src/gui/painting/qpaintengine_x11.cpp
index b8781b16b..b627d365c 100644
--- a/src/gui/painting/qpaintengine_x11.cpp
+++ b/src/gui/painting/qpaintengine_x11.cpp
@@ -482,7 +482,7 @@ bool QX11PaintEngine::begin(QPaintDevice *pdev)
d->picture = (::Picture)w->x11PictureHandle();
} else if (pdev->devType() == QInternal::Pixmap) {
const QPixmap *pm = static_cast<const QPixmap *>(pdev);
- QX11PixmapData *data = static_cast<QX11PixmapData*>(pm->data);
+ QX11PixmapData *data = static_cast<QX11PixmapData*>(pm->data.data());
if (X11->use_xrender && data->depth() != 32 && data->x11_mask)
data->convertToARGB32();
d->picture = (::Picture)static_cast<const QPixmap *>(pdev)->x11PictureHandle();
@@ -1365,7 +1365,7 @@ void QX11PaintEngine::updateBrush(const QBrush &brush, const QPointF &origin)
XRenderPictureAttributes attrs;
attrs.repeat = true;
XRenderChangePicture(d->dpy, d->brush_pm.x11PictureHandle(), CPRepeat, &attrs);
- QX11PixmapData *data = static_cast<QX11PixmapData*>(d->brush_pm.data);
+ QX11PixmapData *data = static_cast<QX11PixmapData*>(d->brush_pm.data.data());
if (data->mask_picture)
XRenderChangePicture(d->dpy, data->mask_picture, CPRepeat, &attrs);
}
@@ -1402,13 +1402,13 @@ void QX11PaintEngine::updateBrush(const QBrush &brush, const QPointF &origin)
mask |= GCTile;
#ifndef QT_NO_XRENDER
if (d->pdev_depth == 32 && d->brush_pm.depth() != 32) {
- QX11PixmapData *brushData = static_cast<QX11PixmapData*>(d->brush_pm.data);
+ QX11PixmapData *brushData = static_cast<QX11PixmapData*>(d->brush_pm.data.data());
brushData->convertToARGB32();
}
#endif
vals.tile = (d->brush_pm.depth() == d->pdev_depth
? d->brush_pm.handle()
- : static_cast<QX11PixmapData*>(d->brush_pm.data)->x11ConvertToDefaultDepth());
+ : static_cast<QX11PixmapData*>(d->brush_pm.data.data())->x11ConvertToDefaultDepth());
s = FillTiled;
#if !defined(QT_NO_XRENDER)
d->current_brush = d->cbrush.texture().x11PictureHandle();
@@ -1915,7 +1915,7 @@ void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRect
QPixmap::x11SetDefaultScreen(pixmap.x11Info().screen());
#ifndef QT_NO_XRENDER
- ::Picture src_pict = static_cast<QX11PixmapData*>(pixmap.data)->picture;
+ ::Picture src_pict = static_cast<QX11PixmapData*>(pixmap.data.data())->picture;
if (src_pict && d->picture) {
const int pDepth = pixmap.depth();
if (pDepth == 1 && (d->has_alpha_pen)) {
@@ -1934,7 +1934,7 @@ void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRect
bool mono_dst = d->pdev_depth == 1;
bool restore_clip = false;
- if (static_cast<QX11PixmapData*>(pixmap.data)->x11_mask) { // pixmap has a mask
+ if (static_cast<QX11PixmapData*>(pixmap.data.data())->x11_mask) { // pixmap has a mask
QBitmap comb(sw, sh);
GC cgc = XCreateGC(d->dpy, comb.handle(), 0, 0);
XSetForeground(d->dpy, cgc, 0);
@@ -1951,7 +1951,7 @@ void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRect
XSetFillStyle(d->dpy, cgc, FillOpaqueStippled);
XSetTSOrigin(d->dpy, cgc, -sx, -sy);
XSetStipple(d->dpy, cgc,
- static_cast<QX11PixmapData*>(pixmap.data)->x11_mask);
+ static_cast<QX11PixmapData*>(pixmap.data.data())->x11_mask);
XFillRectangle(d->dpy, comb.handle(), cgc, 0, 0, sw, sh);
XFreeGC(d->dpy, cgc);
@@ -1994,8 +1994,8 @@ void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRect
if (d->pdev->devType() == QInternal::Pixmap) {
const QPixmap *px = static_cast<const QPixmap*>(d->pdev);
- Pixmap src_mask = static_cast<QX11PixmapData*>(pixmap.data)->x11_mask;
- Pixmap dst_mask = static_cast<QX11PixmapData*>(px->data)->x11_mask;
+ Pixmap src_mask = static_cast<QX11PixmapData*>(pixmap.data.data())->x11_mask;
+ Pixmap dst_mask = static_cast<QX11PixmapData*>(px->data.data())->x11_mask;
if (dst_mask) {
GC cgc = XCreateGC(d->dpy, dst_mask, 0, 0);
if (src_mask) { // copy src mask into dst mask
@@ -2214,7 +2214,7 @@ void QX11PaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, co
#endif
} else
#endif // !QT_NO_XRENDER
- if (pixmap.depth() > 1 && !static_cast<QX11PixmapData*>(pixmap.data)->x11_mask) {
+ if (pixmap.depth() > 1 && !static_cast<QX11PixmapData*>(pixmap.data.data())->x11_mask) {
XSetTile(d->dpy, d->gc, pixmap.handle());
XSetFillStyle(d->dpy, d->gc, FillTiled);
XSetTSOrigin(d->dpy, d->gc, x-sx, y-sy);
diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp
index 0bca8f7b6..e1a6e80f6 100644
--- a/src/gui/painting/qpainter.cpp
+++ b/src/gui/painting/qpainter.cpp
@@ -2563,6 +2563,8 @@ void QPainter::setClipRect(const QRectF &rect, Qt::ClipOperation op)
QVectorPath vp(pts, 4, 0, QVectorPath::RectangleHint);
d->state->clipEnabled = true;
d->extended->clip(vp, op);
+ if (op == Qt::ReplaceClip || op == Qt::NoClip)
+ d->state->clipInfo.clear();
d->state->clipInfo << QPainterClipInfo(rect, op, d->state->matrix);
d->state->clipOperation = op;
return;
@@ -2609,6 +2611,8 @@ void QPainter::setClipRect(const QRect &rect, Qt::ClipOperation op)
if (d->extended) {
d->state->clipEnabled = true;
d->extended->clip(rect, op);
+ if (op == Qt::ReplaceClip || op == Qt::NoClip)
+ d->state->clipInfo.clear();
d->state->clipInfo << QPainterClipInfo(rect, op, d->state->matrix);
d->state->clipOperation = op;
return;
@@ -2662,6 +2666,8 @@ void QPainter::setClipRegion(const QRegion &r, Qt::ClipOperation op)
if (d->extended) {
d->state->clipEnabled = true;
d->extended->clip(r, op);
+ if (op == Qt::NoClip || op == Qt::ReplaceClip)
+ d->state->clipInfo.clear();
d->state->clipInfo << QPainterClipInfo(r, op, d->state->matrix);
d->state->clipOperation = op;
return;
@@ -3066,6 +3072,8 @@ void QPainter::setClipPath(const QPainterPath &path, Qt::ClipOperation op)
if (d->extended) {
d->state->clipEnabled = true;
d->extended->clip(path, op);
+ if (op == Qt::NoClip || op == Qt::ReplaceClip)
+ d->state->clipInfo.clear();
d->state->clipInfo << QPainterClipInfo(path, op, d->state->matrix);
d->state->clipOperation = op;
return;
diff --git a/src/gui/painting/qwindowsurface_x11.cpp b/src/gui/painting/qwindowsurface_x11.cpp
index 783d1c59f..e878c5ceb 100644
--- a/src/gui/painting/qwindowsurface_x11.cpp
+++ b/src/gui/painting/qwindowsurface_x11.cpp
@@ -96,7 +96,7 @@ void QX11WindowSurface::beginPaint(const QRegion &rgn)
#ifndef QT_NO_XRENDER
if (d_ptr->translucentBackground) {
if (d_ptr->device.depth() != 32)
- static_cast<QX11PixmapData *>(d_ptr->device.data_ptr())->convertToARGB32();
+ static_cast<QX11PixmapData *>(d_ptr->device.data_ptr().data())->convertToARGB32();
::Picture src = X11->getSolidFill(d_ptr->device.x11Info().screen(), Qt::transparent);
::Picture dst = d_ptr->device.x11PictureHandle();
const QVector<QRect> rects = rgn.rects();
diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp
index a4dee6a3a..2110cac9e 100644
--- a/src/gui/text/qtextdocument_p.cpp
+++ b/src/gui/text/qtextdocument_p.cpp
@@ -63,10 +63,10 @@ QT_BEGIN_NAMESPACE
// The VxWorks DIAB compiler crashes when initializing the anonymouse union with { a7 }
#if !defined(Q_CC_DIAB)
# define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \
- QTextUndoCommand c = { a1, a2, a3, a4, a5, a6, { a7 }, a8 }
+ QTextUndoCommand c = { a1, a2, 0, 0, a3, a4, a5, a6, { a7 }, a8 }
#else
# define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \
- QTextUndoCommand c = { a1, a2, a3, a4, a5, a6 }; c.blockFormat = a7; c.revision = a8
+ QTextUndoCommand c = { a1, a2, 0, 0, a3, a4, a5, a6 }; c.blockFormat = a7; c.revision = a8
#endif
/*
@@ -421,7 +421,7 @@ int QTextDocumentPrivate::insertBlock(const QChar &blockSeparator,
int b = blocks.findNode(pos);
QTextBlockData *B = blocks.fragment(b);
- QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::BlockInserted, editBlock != 0,
+ QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::BlockInserted, (editBlock != 0),
op, charFormat, strPos, pos, blockFormat,
B->revision);
@@ -462,7 +462,7 @@ void QTextDocumentPrivate::insert(int pos, int strPos, int strLength, int format
int b = blocks.findNode(pos);
QTextBlockData *B = blocks.fragment(b);
- QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::Inserted, editBlock != 0,
+ QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::Inserted, (editBlock != 0),
QTextUndoCommand::MoveCursor, format, strPos, pos, strLength,
B->revision);
appendUndoItem(c);
@@ -621,10 +621,10 @@ void QTextDocumentPrivate::move(int pos, int to, int length, QTextUndoCommand::O
int blockRevision = B->revision;
QTextFragmentData *X = fragments.fragment(x);
- QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::Removed, editBlock != 0,
+ QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::Removed, (editBlock != 0),
op, X->format, X->stringPosition, key, X->size_array[0],
blockRevision);
- QT_INIT_TEXTUNDOCOMMAND(cInsert, QTextUndoCommand::Inserted, editBlock != 0,
+ QT_INIT_TEXTUNDOCOMMAND(cInsert, QTextUndoCommand::Inserted, (editBlock != 0),
op, X->format, X->stringPosition, dstKey, X->size_array[0],
blockRevision);
@@ -967,14 +967,18 @@ int QTextDocumentPrivate::undoRedo(bool undo)
B->revision = c.revision;
}
- if (undo) {
- if (undoState == 0 || !undoStack[undoState-1].block)
- break;
- } else {
+ if (!undo)
++undoState;
- if (undoState == undoStack.size() || !undoStack[undoState-1].block)
- break;
- }
+
+ bool inBlock = (
+ undoState > 0
+ && undoState < undoStack.size()
+ && undoStack[undoState].block_part
+ && undoStack[undoState-1].block_part
+ && !undoStack[undoState-1].block_end
+ );
+ if (!inBlock)
+ break;
}
undoEnabled = true;
int editPos = -1;
@@ -999,7 +1003,8 @@ void QTextDocumentPrivate::appendUndoItem(QAbstractUndoItem *item)
QTextUndoCommand c;
c.command = QTextUndoCommand::Custom;
- c.block = editBlock != 0;
+ c.block_part = editBlock != 0;
+ c.block_end = 0;
c.operation = QTextUndoCommand::MoveCursor;
c.format = 0;
c.strPos = 0;
@@ -1020,9 +1025,10 @@ void QTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c)
if (!undoStack.isEmpty() && modified) {
QTextUndoCommand &last = undoStack[undoState - 1];
- if ( (last.block && c.block) // part of the same block => can merge
- || (!c.block && !last.block // two single undo items => can merge
- && (undoState < 2 || !undoStack[undoState-2].block))) {
+
+ if ( (last.block_part && c.block_part && !last.block_end) // part of the same block => can merge
+ || (!c.block_part && !last.block_part)) { // two single undo items => can merge
+
if (last.tryMerge(c))
return;
}
@@ -1034,7 +1040,7 @@ void QTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c)
emitUndoAvailable(true);
emitRedoAvailable(false);
- if (!c.block)
+ if (!c.block_part)
emit document()->undoCommandAdded();
}
@@ -1100,7 +1106,7 @@ void QTextDocumentPrivate::joinPreviousEditBlock()
beginEditBlock();
if (undoEnabled && undoState)
- undoStack[undoState - 1].block = true;
+ undoStack[undoState - 1].block_end = false;
}
void QTextDocumentPrivate::endEditBlock()
@@ -1109,10 +1115,10 @@ void QTextDocumentPrivate::endEditBlock()
return;
if (undoEnabled && undoState > 0) {
- const bool wasBlocking = undoStack[undoState - 1].block;
- undoStack[undoState - 1].block = false;
- if (wasBlocking)
+ if (undoStack[undoState - 1].block_part) {
+ undoStack[undoState - 1].block_end = true;
emit document()->undoCommandAdded();
+ }
}
finishEdit();
@@ -1313,7 +1319,7 @@ void QTextDocumentPrivate::changeObjectFormat(QTextObject *obj, int format)
if (f)
documentChange(f->firstPosition(), f->lastPosition() - f->firstPosition());
- QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::GroupFormatChange, editBlock != 0, QTextUndoCommand::MoveCursor, oldFormatIndex,
+ QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::GroupFormatChange, (editBlock != 0), QTextUndoCommand::MoveCursor, oldFormatIndex,
0, 0, obj->d_func()->objectIndex, 0);
appendUndoItem(c);
diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h
index 55aa17eec..363309cdb 100644
--- a/src/gui/text/qtextdocument_p.h
+++ b/src/gui/text/qtextdocument_p.h
@@ -139,7 +139,9 @@ public:
MoveCursor = 1
};
quint16 command;
- quint8 block; ///< All undo commands that have this set to true are combined with the preceding command on undo/redo.
+ uint block_part : 1; // all commands that are part of an undo block (including the first and the last one) have this set to 1
+ uint block_end : 1; // the last command in an undo block has this set to 1.
+ uint block_padding : 6; // padding since block used to be a quint8
quint8 operation;
int format;
quint32 strPos;
diff --git a/src/gui/widgets/qabstractscrollarea.cpp b/src/gui/widgets/qabstractscrollarea.cpp
index f09768a1a..533a56b70 100644
--- a/src/gui/widgets/qabstractscrollarea.cpp
+++ b/src/gui/widgets/qabstractscrollarea.cpp
@@ -934,6 +934,10 @@ bool QAbstractScrollArea::event(QEvent *e)
case QEvent::DragMove:
case QEvent::DragLeave:
#endif
+ // ignore touch events in case they have been propagated from the viewport
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
return false;
case QEvent::StyleChange:
case QEvent::LayoutDirectionChange:
@@ -974,6 +978,9 @@ bool QAbstractScrollArea::viewportEvent(QEvent *e)
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
case QEvent::MouseMove:
case QEvent::ContextMenu:
#ifndef QT_NO_WHEELEVENT